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  }