github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/component/all/resource.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package all 5 6 import ( 7 "io" 8 "os" 9 "reflect" 10 11 jujucmd "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/api/base" 16 "github.com/juju/juju/apiserver/charmrevisionupdater" 17 "github.com/juju/juju/apiserver/common" 18 "github.com/juju/juju/apiserver/common/apihttp" 19 "github.com/juju/juju/cmd/juju/charmcmd" 20 "github.com/juju/juju/cmd/juju/commands" 21 "github.com/juju/juju/cmd/modelcmd" 22 "github.com/juju/juju/resource" 23 "github.com/juju/juju/resource/api" 24 "github.com/juju/juju/resource/api/client" 25 internalapi "github.com/juju/juju/resource/api/private" 26 internalclient "github.com/juju/juju/resource/api/private/client" 27 internalserver "github.com/juju/juju/resource/api/private/server" 28 "github.com/juju/juju/resource/api/server" 29 "github.com/juju/juju/resource/cmd" 30 "github.com/juju/juju/resource/context" 31 contextcmd "github.com/juju/juju/resource/context/cmd" 32 "github.com/juju/juju/resource/resourceadapters" 33 corestate "github.com/juju/juju/state" 34 unitercontext "github.com/juju/juju/worker/uniter/runner/context" 35 "github.com/juju/juju/worker/uniter/runner/jujuc" 36 ) 37 38 // resources exposes the registration methods needed 39 // for the top-level component machinery. 40 type resources struct{} 41 42 // RegisterForServer is the top-level registration method 43 // for the component in a jujud context. 44 func (r resources) registerForServer() error { 45 r.registerState() 46 r.registerAgentWorkers() 47 r.registerPublicFacade() 48 r.registerHookContext() 49 return nil 50 } 51 52 // RegisterForClient is the top-level registration method 53 // for the component in a "juju" command context. 54 func (r resources) registerForClient() error { 55 r.registerPublicCommands() 56 57 // needed for help-tool 58 r.registerHookContextCommands() 59 return nil 60 } 61 62 // registerPublicFacade adds the resources public API facade 63 // to the API server. 64 func (r resources) registerPublicFacade() { 65 if !markRegistered(resource.ComponentName, "public-facade") { 66 return 67 } 68 69 // NOTE: facade is also defined in api/facadeversions.go. 70 common.RegisterStandardFacade( 71 resource.FacadeName, 72 server.Version, 73 resourceadapters.NewPublicFacade, 74 ) 75 76 common.RegisterAPIModelEndpoint(api.HTTPEndpointPattern, apihttp.HandlerSpec{ 77 Constraints: apihttp.HandlerConstraints{ 78 AuthKind: names.UserTagKind, 79 StrictValidation: true, 80 ControllerModelOnly: false, 81 }, 82 NewHandler: resourceadapters.NewUploadHandler, 83 }) 84 } 85 86 // resourcesAPIClient adds a Close() method to the resources public API client. 87 type resourcesAPIClient struct { 88 *client.Client 89 closeConnFunc func() error 90 } 91 92 // Close implements io.Closer. 93 func (client resourcesAPIClient) Close() error { 94 return client.closeConnFunc() 95 } 96 97 // registerAgentWorkers adds the resources workers to the agents. 98 func (r resources) registerAgentWorkers() { 99 if !markRegistered(resource.ComponentName, "agent-workers") { 100 return 101 } 102 103 charmrevisionupdater.RegisterLatestCharmHandler("resources", resourceadapters.NewLatestCharmHandler) 104 } 105 106 // registerState registers the state functionality for resources. 107 func (resources) registerState() { 108 if !markRegistered(resource.ComponentName, "state") { 109 return 110 } 111 112 corestate.SetResourcesComponent(resourceadapters.NewResourceState) 113 corestate.SetResourcesPersistence(resourceadapters.NewResourcePersistence) 114 corestate.RegisterCleanupHandler(corestate.CleanupKindResourceBlob, resourceadapters.CleanUpBlob) 115 } 116 117 // registerPublicCommands adds the resources-related commands 118 // to the "juju" supercommand. 119 func (r resources) registerPublicCommands() { 120 if !markRegistered(resource.ComponentName, "public-commands") { 121 return 122 } 123 124 charmcmd.RegisterSubCommand(cmd.NewListCharmResourcesCommand()) 125 126 commands.RegisterEnvCommand(func() modelcmd.ModelCommand { 127 return cmd.NewUploadCommand(cmd.UploadDeps{ 128 NewClient: func(c *cmd.UploadCommand) (cmd.UploadClient, error) { 129 apiRoot, err := c.NewAPIRoot() 130 if err != nil { 131 return nil, errors.Trace(err) 132 } 133 return resourceadapters.NewAPIClient(apiRoot) 134 }, 135 OpenResource: func(s string) (cmd.ReadSeekCloser, error) { 136 return os.Open(s) 137 }, 138 }) 139 140 }) 141 142 commands.RegisterEnvCommand(func() modelcmd.ModelCommand { 143 return cmd.NewShowServiceCommand(cmd.ShowServiceDeps{ 144 NewClient: func(c *cmd.ShowServiceCommand) (cmd.ShowServiceClient, error) { 145 apiRoot, err := c.NewAPIRoot() 146 if err != nil { 147 return nil, errors.Trace(err) 148 } 149 return resourceadapters.NewAPIClient(apiRoot) 150 }, 151 }) 152 }) 153 } 154 155 // TODO(katco): This seems to be common across components. Pop up a 156 // level and genericize? 157 func (r resources) registerHookContext() { 158 if markRegistered(resource.ComponentName, "hook-context") == false { 159 return 160 } 161 162 unitercontext.RegisterComponentFunc( 163 resource.ComponentName, 164 func(config unitercontext.ComponentConfig) (jujuc.ContextComponent, error) { 165 unitID := names.NewUnitTag(config.UnitName).String() 166 hctxClient, err := r.newUnitFacadeClient(unitID, config.APICaller) 167 if err != nil { 168 return nil, errors.Trace(err) 169 } 170 // TODO(ericsnow) Pass the unit's tag through to the component? 171 return context.NewContextAPI(hctxClient, config.DataDir), nil 172 }, 173 ) 174 175 r.registerHookContextCommands() 176 r.registerHookContextFacade() 177 } 178 179 func (r resources) registerHookContextCommands() { 180 if markRegistered(resource.ComponentName, "hook-context-commands") == false { 181 return 182 } 183 184 jujuc.RegisterCommand( 185 contextcmd.GetCmdName, 186 func(ctx jujuc.Context) (jujucmd.Command, error) { 187 compCtx, err := ctx.Component(resource.ComponentName) 188 if err != nil { 189 return nil, errors.Trace(err) 190 } 191 cmd, err := contextcmd.NewGetCmd(compCtx) 192 if err != nil { 193 return nil, errors.Trace(err) 194 } 195 return cmd, nil 196 }, 197 ) 198 } 199 200 func (r resources) registerHookContextFacade() { 201 common.RegisterHookContextFacade( 202 context.HookContextFacade, 203 internalserver.FacadeVersion, 204 r.newHookContextFacade, 205 reflect.TypeOf(&internalserver.UnitFacade{}), 206 ) 207 208 common.RegisterAPIModelEndpoint(internalapi.HTTPEndpointPattern, apihttp.HandlerSpec{ 209 Constraints: apihttp.HandlerConstraints{ 210 AuthKind: names.UnitTagKind, 211 StrictValidation: true, 212 ControllerModelOnly: false, 213 }, 214 NewHandler: resourceadapters.NewDownloadHandler, 215 }) 216 } 217 218 // resourcesUnitDatastore is a shim to elide serviceName from 219 // ListResources. 220 type resourcesUnitDataStore struct { 221 resources corestate.Resources 222 unit *corestate.Unit 223 } 224 225 // ListResources implements resource/api/private/server.UnitDataStore. 226 func (ds *resourcesUnitDataStore) ListResources() (resource.ServiceResources, error) { 227 return ds.resources.ListResources(ds.unit.ApplicationName()) 228 } 229 230 // GetResource implements resource/api/private/server.UnitDataStore. 231 func (ds *resourcesUnitDataStore) GetResource(name string) (resource.Resource, error) { 232 return ds.resources.GetResource(ds.unit.ApplicationName(), name) 233 } 234 235 // OpenResource implements resource/api/private/server.UnitDataStore. 236 func (ds *resourcesUnitDataStore) OpenResource(name string) (resource.Resource, io.ReadCloser, error) { 237 return ds.resources.OpenResourceForUniter(ds.unit, name) 238 } 239 240 func (r resources) newHookContextFacade(st *corestate.State, unit *corestate.Unit) (interface{}, error) { 241 res, err := st.Resources() 242 if err != nil { 243 return nil, errors.Trace(err) 244 } 245 return internalserver.NewUnitFacade(&resourcesUnitDataStore{res, unit}), nil 246 } 247 248 func (r resources) newUnitFacadeClient(unitName string, caller base.APICaller) (context.APIClient, error) { 249 250 facadeCaller := base.NewFacadeCallerForVersion(caller, context.HookContextFacade, internalserver.FacadeVersion) 251 httpClient, err := caller.HTTPClient() 252 if err != nil { 253 return nil, errors.Trace(err) 254 } 255 unitHTTPClient := internalclient.NewUnitHTTPClient(httpClient, unitName) 256 257 return internalclient.NewUnitFacadeClient(facadeCaller, unitHTTPClient), nil 258 }