github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/facade" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/charmstore" 14 "github.com/juju/juju/state" 15 ) 16 17 var logger = loggo.GetLogger("juju.apiserver.charmrevisionupdater") 18 19 func init() { 20 common.RegisterStandardFacade("CharmRevisionUpdater", 2, 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 facade.Resources 33 authorizer facade.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 facade.Resources, 42 authorizer facade.Authorizer, 43 ) (*CharmRevisionUpdaterAPI, error) { 44 if !authorizer.AuthMachineAgent() && !authorizer.AuthModelManager() { 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 if err := api.updateLatestRevisions(); err != nil { 55 return params.ErrorResult{Error: common.ServerError(err)}, nil 56 } 57 return params.ErrorResult{}, nil 58 } 59 60 func (api *CharmRevisionUpdaterAPI) updateLatestRevisions() error { 61 // Get the handlers to use. 62 handlers, err := createHandlers(api.state) 63 if err != nil { 64 return err 65 } 66 67 // Look up the information for all the deployed charms. This is the 68 // "expensive" part. 69 latest, err := retrieveLatestCharmInfo(api.state) 70 if err != nil { 71 return err 72 } 73 74 // Process the resulting info for each charm. 75 for _, info := range latest { 76 // First, add a charm placeholder to the model for each. 77 if err = api.state.AddStoreCharmPlaceholder(info.LatestURL()); err != nil { 78 return err 79 } 80 81 // Then run through the handlers. 82 serviceID := info.service.ApplicationTag() 83 for _, handler := range handlers { 84 if err := handler.HandleLatest(serviceID, info.CharmInfo); err != nil { 85 return err 86 } 87 } 88 } 89 90 return nil 91 } 92 93 // NewCharmStoreClient instantiates a new charm store repository. Exported so 94 // we can change it during testing. 95 var NewCharmStoreClient = func(st *state.State) (charmstore.Client, error) { 96 return charmstore.NewCachingClient(state.MacaroonCache{st}, nil) 97 } 98 99 type latestCharmInfo struct { 100 charmstore.CharmInfo 101 service *state.Application 102 } 103 104 // retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the 105 // latest revision of the deployed charms. 106 func retrieveLatestCharmInfo(st *state.State) ([]latestCharmInfo, error) { 107 // First get the uuid for the environment to use when querying the charm store. 108 env, err := st.Model() 109 if err != nil { 110 return nil, err 111 } 112 113 services, err := st.AllApplications() 114 if err != nil { 115 return nil, err 116 } 117 118 client, err := NewCharmStoreClient(st) 119 if err != nil { 120 return nil, errors.Trace(err) 121 } 122 123 var charms []charmstore.CharmID 124 var resultsIndexedServices []*state.Application 125 for _, service := range services { 126 curl, _ := service.CharmURL() 127 if curl.Schema == "local" { 128 // Version checking for charms from local repositories is not 129 // currently supported, since we don't yet support passing in 130 // a path to the local repo. This may change if the need arises. 131 continue 132 } 133 134 cid := charmstore.CharmID{ 135 URL: curl, 136 Channel: service.Channel(), 137 } 138 charms = append(charms, cid) 139 resultsIndexedServices = append(resultsIndexedServices, service) 140 } 141 142 metadata := map[string]string{ 143 "environment_uuid": env.UUID(), 144 "cloud": env.Cloud(), 145 "cloud_region": env.CloudRegion(), 146 } 147 cloud, err := st.Cloud(env.Cloud()) 148 if err != nil { 149 metadata["provider"] = "unknown" 150 } else { 151 metadata["provider"] = cloud.Type 152 } 153 results, err := charmstore.LatestCharmInfo(client, charms, metadata) 154 if err != nil { 155 return nil, err 156 } 157 158 var latest []latestCharmInfo 159 for i, result := range results { 160 if result.Error != nil { 161 logger.Errorf("retrieving charm info for %s: %v", charms[i].URL, result.Error) 162 continue 163 } 164 service := resultsIndexedServices[i] 165 latest = append(latest, latestCharmInfo{ 166 CharmInfo: result.CharmInfo, 167 service: service, 168 }) 169 } 170 return latest, nil 171 }