github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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/params" 12 "github.com/juju/juju/charmstore" 13 "github.com/juju/juju/state" 14 ) 15 16 var logger = loggo.GetLogger("juju.apiserver.charmrevisionupdater") 17 18 func init() { 19 common.RegisterStandardFacade("CharmRevisionUpdater", 1, NewCharmRevisionUpdaterAPI) 20 } 21 22 // CharmRevisionUpdater defines the methods on the charmrevisionupdater API end point. 23 type CharmRevisionUpdater interface { 24 UpdateLatestRevisions() (params.ErrorResult, error) 25 } 26 27 // CharmRevisionUpdaterAPI implements the CharmRevisionUpdater interface and is the concrete 28 // implementation of the api end point. 29 type CharmRevisionUpdaterAPI struct { 30 state *state.State 31 resources *common.Resources 32 authorizer common.Authorizer 33 } 34 35 var _ CharmRevisionUpdater = (*CharmRevisionUpdaterAPI)(nil) 36 37 // NewCharmRevisionUpdaterAPI creates a new server-side charmrevisionupdater API end point. 38 func NewCharmRevisionUpdaterAPI( 39 st *state.State, 40 resources *common.Resources, 41 authorizer common.Authorizer, 42 ) (*CharmRevisionUpdaterAPI, error) { 43 if !authorizer.AuthMachineAgent() && !authorizer.AuthModelManager() { 44 return nil, common.ErrPerm 45 } 46 return &CharmRevisionUpdaterAPI{ 47 state: st, resources: resources, authorizer: authorizer}, nil 48 } 49 50 // UpdateLatestRevisions retrieves the latest revision information from the charm store for all deployed charms 51 // and records this information in state. 52 func (api *CharmRevisionUpdaterAPI) UpdateLatestRevisions() (params.ErrorResult, error) { 53 if err := api.updateLatestRevisions(); err != nil { 54 return params.ErrorResult{Error: common.ServerError(err)}, nil 55 } 56 return params.ErrorResult{}, nil 57 } 58 59 func (api *CharmRevisionUpdaterAPI) updateLatestRevisions() error { 60 // Get the handlers to use. 61 handlers, err := createHandlers(api.state) 62 if err != nil { 63 return err 64 } 65 66 // Look up the information for all the deployed charms. This is the 67 // "expensive" part. 68 latest, err := retrieveLatestCharmInfo(api.state) 69 if err != nil { 70 return err 71 } 72 73 // Process the resulting info for each charm. 74 for _, info := range latest { 75 // First, add a charm placeholder to the model for each. 76 if err = api.state.AddStoreCharmPlaceholder(info.LatestURL()); err != nil { 77 return err 78 } 79 80 // Then run through the handlers. 81 serviceID := info.service.ServiceTag() 82 for _, handler := range handlers { 83 if err := handler.HandleLatest(serviceID, info.CharmInfo); err != nil { 84 return err 85 } 86 } 87 } 88 89 return nil 90 } 91 92 // NewCharmStoreClient instantiates a new charm store repository. Exported so 93 // we can change it during testing. 94 var NewCharmStoreClient = func(st *state.State) (charmstore.Client, error) { 95 return charmstore.NewCachingClient(state.MacaroonCache{st}, nil) 96 } 97 98 type latestCharmInfo struct { 99 charmstore.CharmInfo 100 service *state.Service 101 } 102 103 // retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the 104 // latest revision of the deployed charms. 105 func retrieveLatestCharmInfo(st *state.State) ([]latestCharmInfo, error) { 106 // First get the uuid for the environment to use when querying the charm store. 107 env, err := st.Model() 108 if err != nil { 109 return nil, err 110 } 111 112 services, err := st.AllServices() 113 if err != nil { 114 return nil, err 115 } 116 117 client, err := NewCharmStoreClient(st) 118 if err != nil { 119 return nil, errors.Trace(err) 120 } 121 122 var charms []charmstore.CharmID 123 var resultsIndexedServices []*state.Service 124 for _, service := range services { 125 curl, _ := service.CharmURL() 126 if curl.Schema == "local" { 127 // Version checking for charms from local repositories is not 128 // currently supported, since we don't yet support passing in 129 // a path to the local repo. This may change if the need arises. 130 continue 131 } 132 133 cid := charmstore.CharmID{ 134 URL: curl, 135 Channel: service.Channel(), 136 } 137 charms = append(charms, cid) 138 resultsIndexedServices = append(resultsIndexedServices, service) 139 } 140 141 results, err := charmstore.LatestCharmInfo(client, charms, env.UUID()) 142 if err != nil { 143 return nil, err 144 } 145 146 var latest []latestCharmInfo 147 for i, result := range results { 148 if result.Error != nil { 149 logger.Errorf("retrieving charm info for %s: %v", charms[i].URL, result.Error) 150 continue 151 } 152 service := resultsIndexedServices[i] 153 latest = append(latest, latestCharmInfo{ 154 CharmInfo: result.CharmInfo, 155 service: service, 156 }) 157 } 158 return latest, nil 159 }