github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/resource/upload.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package resource
     5  
     6  import (
     7  	"io"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  
    12  	jujucmd "github.com/juju/juju/cmd"
    13  	"github.com/juju/juju/cmd/modelcmd"
    14  )
    15  
    16  // UploadClient has the API client methods needed by UploadCommand.
    17  type UploadClient interface {
    18  	// Upload sends the resource to Juju.
    19  	Upload(application, name, filename string, resource io.ReadSeeker) error
    20  
    21  	// Close closes the client.
    22  	Close() error
    23  }
    24  
    25  // ReadSeekCloser combines 2 interfaces.
    26  type ReadSeekCloser interface {
    27  	io.ReadCloser
    28  	io.Seeker
    29  }
    30  
    31  // UploadDeps is a type that contains external functions that Upload depends on
    32  // to function.
    33  type UploadDeps struct {
    34  	// NewClient returns the value that wraps the API for uploading to the server.
    35  	NewClient func(*UploadCommand) (UploadClient, error)
    36  
    37  	// OpenResource handles creating a reader from the resource path.
    38  	OpenResource func(path string) (ReadSeekCloser, error)
    39  }
    40  
    41  // UploadCommand implements the upload command.
    42  type UploadCommand struct {
    43  	deps UploadDeps
    44  	modelcmd.ModelCommandBase
    45  	application  string
    46  	resourceFile resourceFile
    47  }
    48  
    49  // NewUploadCommand returns a new command that lists resources defined
    50  // by a charm.
    51  func NewUploadCommand(deps UploadDeps) modelcmd.ModelCommand {
    52  	return modelcmd.Wrap(&UploadCommand{deps: deps})
    53  }
    54  
    55  // Info implements cmd.Command.Info
    56  func (c *UploadCommand) Info() *cmd.Info {
    57  	return jujucmd.Info(&cmd.Info{
    58  		Name:    "attach-resource",
    59  		Args:    "application name=file",
    60  		Purpose: "Upload a file as a resource for an application.",
    61  		Doc: `
    62  This command uploads a file from your local disk to the juju controller to be
    63  used as a resource for an application.
    64  `,
    65  		Aliases: []string{"attach"},
    66  	})
    67  }
    68  
    69  // Init implements cmd.Command.Init. It will return an error satisfying
    70  // errors.BadRequest if you give it an incorrect number of arguments.
    71  func (c *UploadCommand) Init(args []string) error {
    72  	switch len(args) {
    73  	case 0:
    74  		return errors.BadRequestf("missing application name")
    75  	case 1:
    76  		return errors.BadRequestf("no resource specified")
    77  	}
    78  
    79  	application := args[0]
    80  	if application == "" { // TODO(ericsnow) names.IsValidApplication
    81  		return errors.NewNotValid(nil, "missing application name")
    82  	}
    83  	c.application = application
    84  
    85  	if err := c.addResourceFile(args[1]); err != nil {
    86  		return errors.Trace(err)
    87  	}
    88  	if err := cmd.CheckEmpty(args[2:]); err != nil {
    89  		return errors.NewBadRequest(err, "")
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // addResourceFile parses the given arg into a name and a resource file,
    96  // and saves it in c.resourceFiles.
    97  func (c *UploadCommand) addResourceFile(arg string) error {
    98  	name, filename, err := parseResourceFileArg(arg)
    99  	if err != nil {
   100  		return errors.Annotatef(err, "bad resource arg %q", arg)
   101  	}
   102  	c.resourceFile = resourceFile{
   103  		application: c.application,
   104  		name:        name,
   105  		filename:    filename,
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  // Run implements cmd.Command.Run.
   112  func (c *UploadCommand) Run(*cmd.Context) error {
   113  	apiclient, err := c.deps.NewClient(c)
   114  	if err != nil {
   115  		return errors.Trace(err)
   116  	}
   117  	defer apiclient.Close()
   118  
   119  	if err := c.upload(c.resourceFile, apiclient); err != nil {
   120  		return errors.Annotatef(err, "failed to upload resource %q", c.resourceFile.name)
   121  	}
   122  	return nil
   123  }
   124  
   125  // upload opens the given file and calls the apiclient to upload it to the given
   126  // application with the given name.
   127  func (c *UploadCommand) upload(rf resourceFile, client UploadClient) error {
   128  	f, err := c.deps.OpenResource(rf.filename)
   129  	if err != nil {
   130  		return errors.Trace(err)
   131  	}
   132  	defer f.Close()
   133  	err = client.Upload(rf.application, rf.name, rf.filename, f)
   134  	return errors.Trace(err)
   135  }