@@ -2,7 +2,14 @@ package cmd
22
33import (
44 "fmt"
5+ "io"
6+ "net/http"
7+ "net/url"
8+ "os"
9+ "path"
510 "regexp"
11+ "strconv"
12+ "strings"
613
714 "github.com/drycc/controller-sdk-go/api"
815 "github.com/drycc/controller-sdk-go/volumes"
@@ -157,6 +164,20 @@ func (d *DryccCmd) VolumesDelete(appID, name string) error {
157164 return nil
158165}
159166
167+ // VolumesClient a client for manage volume file
168+ func (d * DryccCmd ) VolumesClient (appID , cmd string , args ... string ) error {
169+ switch cmd {
170+ case "ls" :
171+ return d .volumesClientLs (appID , args [0 ])
172+ case "cp" :
173+ return d .volumesClientCp (appID , args [0 ], args [1 ])
174+ case "rm" :
175+ return d .volumesClientRm (appID , args [0 ])
176+ default :
177+ return fmt .Errorf ("unknown command %s" , cmd )
178+ }
179+ }
180+
160181// VolumesMount mount a volume to process of the application
161182func (d * DryccCmd ) VolumesMount (appID string , name string , volumeVars []string ) error {
162183 s , appID , err := load (d .ConfigFile , appID )
@@ -217,6 +238,113 @@ func (d *DryccCmd) VolumesUnmount(appID string, name string, volumeVars []string
217238 return nil
218239}
219240
241+ // volumesClientLs get all directory entries sorted by filename.
242+ func (d * DryccCmd ) volumesClientLs (appID , vol string ) error {
243+
244+ s , appID , err := load (d .ConfigFile , appID )
245+ if err != nil {
246+ return err
247+ }
248+
249+ name , path , err := parseVol (vol )
250+ if err != nil {
251+ return err
252+ }
253+ dirs , _ , err := volumes .ListDir (s .Client , appID , name , path , 3000 )
254+ if err != nil {
255+ return err
256+ }
257+
258+ table := d .getDefaultFormatTable ([]string {})
259+ for _ , dir := range dirs {
260+ var size string
261+ s , err := strconv .ParseInt (dir .Size , 10 , 64 )
262+ if err != nil {
263+ return err
264+ }
265+ if dir .Type == "dir" {
266+ s = 4096
267+ dir .Name = fmt .Sprintf ("%s/" , dir .Name )
268+ }
269+ if s > 1024 {
270+ size = fmt .Sprintf ("%dKiB" , s / 1024 )
271+ } else if s > 1024 * 1024 {
272+ size = fmt .Sprintf ("%dMiB" , s / (1024 * 1024 ))
273+ } else if s > 1024 * 1024 * 1024 {
274+ size = fmt .Sprintf ("%dGiB" , s / (1024 * 1024 * 1024 ))
275+ } else {
276+ size = fmt .Sprintf ("%d" , s )
277+ }
278+ table .Append ([]string {fmt .Sprintf ("[%s]" , d .formatTime (dir .Timestamp )), size , dir .Name })
279+ }
280+ table .Render ()
281+ return nil
282+ }
283+
284+ // volumesClientCp copy files between volume and local file
285+ func (d * DryccCmd ) volumesClientCp (appID , src , dst string ) error {
286+ s , appID , err := load (d .ConfigFile , appID )
287+ if err != nil {
288+ return err
289+ }
290+ if strings .HasPrefix (src , "vol://" ) {
291+ name , urlpath , err := parseVol (src )
292+ if err != nil {
293+ return err
294+ }
295+ res , err := volumes .GetFile (s .Client , appID , name , urlpath )
296+ if err != nil {
297+ return err
298+ }
299+
300+ if f , err := os .Stat (dst ); err == nil {
301+ if f .IsDir () {
302+ arrays := strings .Split (urlpath , "/" )
303+ dst = path .Join (dst , arrays [len (arrays )- 1 ])
304+ }
305+ }
306+ w , err := os .OpenFile (dst , os .O_CREATE | os .O_WRONLY , 0644 )
307+ if err != nil {
308+ return err
309+ }
310+
311+ defer w .Close ()
312+ if _ , err = io .Copy (w , res .Body ); err != nil {
313+ return err
314+ }
315+ } else if strings .HasPrefix (dst , "vol://" ) {
316+ name , path , err := parseVol (dst )
317+ if err != nil {
318+ return err
319+ }
320+ if _ , err := volumes .PostFile (s .Client , appID , name , path , src ); err != nil {
321+ return err
322+ }
323+ }
324+ return nil
325+ }
326+
327+ // volumesClientRm delete a file from volume
328+ func (d * DryccCmd ) volumesClientRm (appID , vol string ) error {
329+ s , appID , err := load (d .ConfigFile , appID )
330+ if err != nil {
331+ return err
332+ }
333+ host , path , err := parseVol (vol )
334+ if err != nil {
335+ return err
336+ }
337+ res , err := volumes .DeleteFile (s .Client , appID , host , path )
338+ if err != nil {
339+ return err
340+ }
341+ if res .StatusCode != http .StatusOK {
342+ return fmt .Errorf ("incorrect http status code %d" , res .StatusCode )
343+ }
344+
345+ return nil
346+ }
347+
220348func parseVolume (volumeVars []string ) (map [string ]interface {}, error ) {
221349 volumeMap := make (map [string ]interface {})
222350 regex := regexp .MustCompile (`^([a-z0-9]+(?:-[a-z0-9]+)*)=(\/([\w]+[\w-]*\/?)+)$` )
@@ -232,6 +360,18 @@ func parseVolume(volumeVars []string) (map[string]interface{}, error) {
232360 return volumeMap , nil
233361}
234362
363+ // parseVol format volume url
364+ func parseVol (vol string ) (string , string , error ) {
365+ u , err := url .Parse (vol )
366+ if err != nil {
367+ return "" , "" , err
368+ }
369+ if u .Scheme != "vol" || u .Host == "" {
370+ return "" , "" , fmt .Errorf ("vol %s format err" , vol )
371+ }
372+ return u .Host , strings .TrimPrefix (u .Path , "/" ), nil
373+ }
374+
235375// printVolumes format volume data
236376func printVolumes (d * DryccCmd , volumes api.Volumes ) {
237377 table := d .getDefaultFormatTable ([]string {"NAME" , "OWNER" , "TYPE" , "PTYPE" , "PATH" , "SIZE" })
0 commit comments