github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/resource/cmd/upload.go (about)

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