github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/charmrevisionupdater/updater.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package charmrevisionupdater 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "gopkg.in/juju/charm.v6-unstable" 10 "gopkg.in/juju/charmrepo.v1" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/state" 15 ) 16 17 var logger = loggo.GetLogger("juju.apiserver.charmrevisionupdater") 18 19 func init() { 20 common.RegisterStandardFacade("CharmRevisionUpdater", 0, NewCharmRevisionUpdaterAPI) 21 } 22 23 // CharmRevisionUpdater defines the methods on the charmrevisionupdater API end point. 24 type CharmRevisionUpdater interface { 25 UpdateLatestRevisions() (params.ErrorResult, error) 26 } 27 28 // CharmRevisionUpdaterAPI implements the CharmRevisionUpdater interface and is the concrete 29 // implementation of the api end point. 30 type CharmRevisionUpdaterAPI struct { 31 state *state.State 32 resources *common.Resources 33 authorizer common.Authorizer 34 } 35 36 var _ CharmRevisionUpdater = (*CharmRevisionUpdaterAPI)(nil) 37 38 // NewCharmRevisionUpdaterAPI creates a new server-side charmrevisionupdater API end point. 39 func NewCharmRevisionUpdaterAPI( 40 st *state.State, 41 resources *common.Resources, 42 authorizer common.Authorizer, 43 ) (*CharmRevisionUpdaterAPI, error) { 44 if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() { 45 return nil, common.ErrPerm 46 } 47 return &CharmRevisionUpdaterAPI{ 48 state: st, resources: resources, authorizer: authorizer}, nil 49 } 50 51 // UpdateLatestRevisions retrieves the latest revision information from the charm store for all deployed charms 52 // and records this information in state. 53 func (api *CharmRevisionUpdaterAPI) UpdateLatestRevisions() (params.ErrorResult, error) { 54 // First get the uuid for the environment to use when querying the charm store. 55 env, err := api.state.Environment() 56 if err != nil { 57 return params.ErrorResult{Error: common.ServerError(err)}, nil 58 } 59 uuid := env.UUID() 60 61 deployedCharms, err := fetchAllDeployedCharms(api.state) 62 if err != nil { 63 return params.ErrorResult{Error: common.ServerError(err)}, nil 64 } 65 // Look up the revision information for all the deployed charms. 66 curls, err := retrieveLatestCharmInfo(deployedCharms, uuid) 67 if err != nil { 68 return params.ErrorResult{Error: common.ServerError(err)}, nil 69 } 70 // Add the charms and latest revision info to state as charm placeholders. 71 for _, curl := range curls { 72 if err = api.state.AddStoreCharmPlaceholder(curl); err != nil { 73 return params.ErrorResult{Error: common.ServerError(err)}, nil 74 } 75 } 76 return params.ErrorResult{}, nil 77 } 78 79 // fetchAllDeployedCharms returns a map from service name to service 80 // and a map from service name to unit name to unit. 81 func fetchAllDeployedCharms(st *state.State) (map[string]*charm.URL, error) { 82 deployedCharms := make(map[string]*charm.URL) 83 services, err := st.AllServices() 84 if err != nil { 85 return nil, err 86 } 87 for _, s := range services { 88 url, _ := s.CharmURL() 89 // Record the basic charm information so it can be bulk processed later to 90 // get the available revision numbers from the repo. 91 baseCharm := url.WithRevision(-1) 92 deployedCharms[baseCharm.String()] = baseCharm 93 } 94 return deployedCharms, nil 95 } 96 97 // NewCharmStore instantiates a new charm store repository. 98 // It is defined at top level for testing purposes. 99 var NewCharmStore = charmrepo.NewCharmStore 100 101 // retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the 102 // latest revision of the deployed charms. 103 func retrieveLatestCharmInfo(deployedCharms map[string]*charm.URL, uuid string) ([]*charm.URL, error) { 104 var curls []*charm.URL 105 for _, curl := range deployedCharms { 106 if curl.Schema == "local" { 107 // Version checking for charms from local repositories is not 108 // currently supported, since we don't yet support passing in 109 // a path to the local repo. This may change if the need arises. 110 continue 111 } 112 curls = append(curls, curl) 113 } 114 115 // Do a bulk call to get the revision info for all charms. 116 logger.Infof("retrieving revision information for %d charms", len(curls)) 117 repo := NewCharmStore(charmrepo.NewCharmStoreParams{}) 118 repo = repo.(*charmrepo.CharmStore).WithJujuAttrs(map[string]string{ 119 "environment_uuid": uuid, 120 }) 121 revInfo, err := repo.Latest(curls...) 122 if err != nil { 123 err = errors.Annotate(err, "finding charm revision info") 124 logger.Infof(err.Error()) 125 return nil, err 126 } 127 var latestCurls []*charm.URL 128 for i, info := range revInfo { 129 curl := curls[i] 130 if info.Err == nil { 131 latestCurls = append(latestCurls, curl.WithRevision(info.Revision)) 132 } else { 133 logger.Errorf("retrieving charm info for %s: %v", curl, info.Err) 134 } 135 } 136 return latestCurls, nil 137 }