launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/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/loggo/loggo" 8 "launchpad.net/errgo/errors" 9 10 "launchpad.net/juju-core/charm" 11 "launchpad.net/juju-core/log" 12 "launchpad.net/juju-core/state" 13 "launchpad.net/juju-core/state/api/params" 14 "launchpad.net/juju-core/state/apiserver/common" 15 ) 16 17 var logger = loggo.GetLogger("juju.state.apiserver.charmrevisionupdater") 18 19 var mask = errors.Mask 20 21 // CharmRevisionUpdater defines the methods on the charmrevisionupdater API end point. 22 type CharmRevisionUpdater interface { 23 UpdateLatestRevisions() (params.ErrorResult, error) 24 } 25 26 // CharmRevisionUpdaterAPI implements the CharmRevisionUpdater interface and is the concrete 27 // implementation of the api end point. 28 type CharmRevisionUpdaterAPI struct { 29 state *state.State 30 resources *common.Resources 31 authorizer common.Authorizer 32 } 33 34 var _ CharmRevisionUpdater = (*CharmRevisionUpdaterAPI)(nil) 35 36 // NewCharmRevisionUpdaterAPI creates a new server-side charmrevisionupdater API end point. 37 func NewCharmRevisionUpdaterAPI( 38 st *state.State, 39 resources *common.Resources, 40 authorizer common.Authorizer, 41 ) (*CharmRevisionUpdaterAPI, error) { 42 if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() { 43 return nil, common.ErrPerm 44 } 45 return &CharmRevisionUpdaterAPI{ 46 state: st, resources: resources, authorizer: authorizer}, nil 47 } 48 49 // UpdateLatestRevisions retrieves the latest revision information from the charm store for all deployed charms 50 // and records this information in state. 51 func (api *CharmRevisionUpdaterAPI) UpdateLatestRevisions() (params.ErrorResult, error) { 52 // First get the uuid for the environment to use when querying the charm store. 53 env, err := api.state.Environment() 54 if err != nil { 55 return params.ErrorResult{common.ServerError(err)}, nil 56 } 57 uuid := env.UUID() 58 59 deployedCharms, err := fetchAllDeployedCharms(api.state) 60 if err != nil { 61 return params.ErrorResult{common.ServerError(err)}, nil 62 } 63 // Look up the revision information for all the deployed charms. 64 curls, err := retrieveLatestCharmInfo(deployedCharms, uuid) 65 if err != nil { 66 return params.ErrorResult{common.ServerError(err)}, nil 67 } 68 // Add the charms and latest revision info to state as charm placeholders. 69 for _, curl := range curls { 70 if err = api.state.AddStoreCharmPlaceholder(curl); err != nil { 71 return params.ErrorResult{common.ServerError(err)}, nil 72 } 73 } 74 return params.ErrorResult{}, nil 75 } 76 77 // fetchAllServicesAndUnits returns a map from service name to service 78 // and a map from service name to unit name to unit. 79 func fetchAllDeployedCharms(st *state.State) (map[string]*charm.URL, error) { 80 deployedCharms := make(map[string]*charm.URL) 81 services, err := st.AllServices() 82 if err != nil { 83 return nil, mask(err) 84 } 85 for _, s := range services { 86 url, _ := s.CharmURL() 87 // Record the basic charm information so it can be bulk processed later to 88 // get the available revision numbers from the repo. 89 baseCharm := url.WithRevision(-1) 90 deployedCharms[baseCharm.String()] = baseCharm 91 } 92 return deployedCharms, nil 93 } 94 95 // retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the 96 // latest revision of the deployed charms. 97 func retrieveLatestCharmInfo(deployedCharms map[string]*charm.URL, uuid string) ([]*charm.URL, error) { 98 var curls []*charm.URL 99 for _, curl := range deployedCharms { 100 if curl.Schema == "local" { 101 // Version checking for charms from local repositories is not 102 // currently supported, since we don't yet support passing in 103 // a path to the local repo. This may change if the need arises. 104 continue 105 } 106 curls = append(curls, curl) 107 } 108 109 // Do a bulk call to get the revision info for all charms. 110 logger.Infof("retrieving revision information for %d charms", len(curls)) 111 store := charm.Store.WithJujuAttrs("environment_uuid=" + uuid) 112 revInfo, err := store.Latest(curls...) 113 if err != nil { 114 return nil, log.LoggedErrorf(logger, "finding charm revision info: %v", err) 115 } 116 var latestCurls []*charm.URL 117 for i, info := range revInfo { 118 curl := curls[i] 119 if info.Err == nil { 120 latestCurls = append(latestCurls, curl.WithRevision(info.Revision)) 121 } else { 122 logger.Errorf("retrieving charm info for %s: %v", curl, info.Err) 123 } 124 } 125 return latestCurls, nil 126 }