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 }