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  }