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