1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
11
jujucmd "github.com/juju/cmd"
12
"github.com/juju/errors"
13
"gopkg.in/juju/names.v2"
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"
38
// resources exposes the registration methods needed
39
// for the top-level component machinery.
40
type resources struct{}
42
// RegisterForServer is the top-level registration method
43
// for the component in a jujud context.
44
func (r resources) registerForServer() error {
46
r.registerAgentWorkers()
47
r.registerPublicFacade()
48
r.registerHookContext()
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()
59
// registerPublicFacade adds the resources public API facade
61
func (r resources) registerPublicFacade() {
62
if !markRegistered(resource.ComponentName, "public-facade") {
66
// NOTE: facade is also defined in api/facadeversions.go.
67
common.RegisterStandardFacade(
70
resourceadapters.NewPublicFacade,
73
common.RegisterAPIModelEndpoint(api.HTTPEndpointPattern, apihttp.HandlerSpec{
74
Constraints: apihttp.HandlerConstraints{
75
AuthKind: names.UserTagKind,
76
StrictValidation: true,
77
ControllerModelOnly: false,
79
NewHandler: resourceadapters.NewUploadHandler,
83
// resourcesApiClient adds a Close() method to the resources public API client.
84
type resourcesAPIClient struct {
86
closeConnFunc func() error
89
// Close implements io.Closer.
90
func (client resourcesAPIClient) Close() error {
91
return client.closeConnFunc()
94
// registerAgentWorkers adds the resources workers to the agents.
95
func (r resources) registerAgentWorkers() {
96
if !markRegistered(resource.ComponentName, "agent-workers") {
100
charmrevisionupdater.RegisterLatestCharmHandler("resources", resourceadapters.NewLatestCharmHandler)
103
// registerState registers the state functionality for resources.
104
func (resources) registerState() {
105
if !markRegistered(resource.ComponentName, "state") {
109
corestate.SetResourcesComponent(resourceadapters.NewResourceState)
110
corestate.SetResourcesPersistence(resourceadapters.NewResourcePersistence)
111
corestate.RegisterCleanupHandler(corestate.CleanupKindResourceBlob, resourceadapters.CleanUpBlob)
114
// registerPublicCommands adds the resources-related commands
115
// to the "juju" supercommand.
116
func (r resources) registerPublicCommands() {
117
if !markRegistered(resource.ComponentName, "public-commands") {
121
charmcmd.RegisterSubCommand(func(spec charmcmd.CharmstoreSpec) jujucmd.Command {
122
base := charmcmd.NewCommandBase(spec)
123
resBase := &resourceadapters.CharmCmdBase{base}
124
return cmd.NewListCharmResourcesCommand(resBase)
127
commands.RegisterEnvCommand(func() modelcmd.ModelCommand {
128
return cmd.NewUploadCommand(cmd.UploadDeps{
129
NewClient: func(c *cmd.UploadCommand) (cmd.UploadClient, error) {
130
return resourceadapters.NewAPIClient(c.NewAPIRoot)
132
OpenResource: func(s string) (cmd.ReadSeekCloser, error) {
139
commands.RegisterEnvCommand(func() modelcmd.ModelCommand {
140
return cmd.NewShowServiceCommand(cmd.ShowServiceDeps{
141
NewClient: func(c *cmd.ShowServiceCommand) (cmd.ShowServiceClient, error) {
142
return resourceadapters.NewAPIClient(c.NewAPIRoot)
148
// TODO(katco): This seems to be common across components. Pop up a
149
// level and genericize?
150
func (r resources) registerHookContext() {
151
if markRegistered(resource.ComponentName, "hook-context") == false {
155
unitercontext.RegisterComponentFunc(
156
resource.ComponentName,
157
func(config unitercontext.ComponentConfig) (jujuc.ContextComponent, error) {
158
unitID := names.NewUnitTag(config.UnitName).String()
159
hctxClient, err := r.newUnitFacadeClient(unitID, config.APICaller)
161
return nil, errors.Trace(err)
163
// TODO(ericsnow) Pass the unit's tag through to the component?
164
return context.NewContextAPI(hctxClient, config.DataDir), nil
168
r.registerHookContextCommands()
169
r.registerHookContextFacade()
172
func (c resources) registerHookContextCommands() {
173
if markRegistered(resource.ComponentName, "hook-context-commands") == false {
177
jujuc.RegisterCommand(
178
contextcmd.GetCmdName,
179
func(ctx jujuc.Context) (jujucmd.Command, error) {
180
compCtx, err := ctx.Component(resource.ComponentName)
182
return nil, errors.Trace(err)
184
typedCtx, ok := compCtx.(*context.Context)
186
return nil, errors.Trace(err)
188
cmd, err := contextcmd.NewGetCmd(typedCtx)
190
return nil, errors.Trace(err)
197
func (r resources) registerHookContextFacade() {
198
common.RegisterHookContextFacade(
199
context.HookContextFacade,
200
internalserver.FacadeVersion,
201
r.newHookContextFacade,
202
reflect.TypeOf(&internalserver.UnitFacade{}),
205
common.RegisterAPIModelEndpoint(internalapi.HTTPEndpointPattern, apihttp.HandlerSpec{
206
Constraints: apihttp.HandlerConstraints{
207
AuthKind: names.UnitTagKind,
208
StrictValidation: true,
209
ControllerModelOnly: false,
211
NewHandler: resourceadapters.NewDownloadHandler,
215
// resourcesUnitDatastore is a shim to elide serviceName from
217
type resourcesUnitDataStore struct {
218
resources corestate.Resources
222
// ListResources implements resource/api/private/server.UnitDataStore.
223
func (ds *resourcesUnitDataStore) ListResources() (resource.ServiceResources, error) {
224
return ds.resources.ListResources(ds.unit.ApplicationName())
227
// GetResource implements resource/api/private/server.UnitDataStore.
228
func (ds *resourcesUnitDataStore) GetResource(name string) (resource.Resource, error) {
229
return ds.resources.GetResource(ds.unit.ApplicationName(), name)
232
// OpenResource implements resource/api/private/server.UnitDataStore.
233
func (ds *resourcesUnitDataStore) OpenResource(name string) (resource.Resource, io.ReadCloser, error) {
234
return ds.resources.OpenResourceForUniter(ds.unit, name)
237
func (r resources) newHookContextFacade(st *corestate.State, unit *corestate.Unit) (interface{}, error) {
238
res, err := st.Resources()
240
return nil, errors.Trace(err)
242
return internalserver.NewUnitFacade(&resourcesUnitDataStore{res, unit}), nil
245
func (r resources) newUnitFacadeClient(unitName string, caller base.APICaller) (context.APIClient, error) {
247
facadeCaller := base.NewFacadeCallerForVersion(caller, context.HookContextFacade, internalserver.FacadeVersion)
248
httpClient, err := caller.HTTPClient()
250
return nil, errors.Trace(err)
252
unitHTTPClient := internalclient.NewUnitHTTPClient(httpClient, unitName)
254
return internalclient.NewUnitFacadeClient(facadeCaller, unitHTTPClient), nil