Skip to content

Commit a80e354

Browse files
author
Your Name
committed
feat(workflow-cli): add a volume command
1 parent 42e481b commit a80e354

7 files changed

Lines changed: 650 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ _dist
2828
workflow-cli
2929
coverage.txt
3030
.DS_Store
31+
.idea/*

cmd/cmd.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ type Commander interface {
9999
PrintErr(...interface{}) (int, error)
100100
PrintErrf(string, ...interface{}) (int, error)
101101
Version(bool) error
102+
VolumesCreate(string, string, string) error
103+
VolumesDelete(string, string) error
104+
VolumesList(string, int) error
105+
VolumesMount(string, string, []string) error
106+
VolumesUnmount(string, string, []string) error
102107
}
103108

104109
// DryccCmd is an implementation of Commander.

cmd/volumes.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"regexp"
7+
8+
"github.com/drycc/controller-sdk-go/api"
9+
"github.com/drycc/controller-sdk-go/volumes"
10+
)
11+
12+
// VolumesList list volumes in the application
13+
func (d *DryccCmd) VolumesList(appID string, results int) error {
14+
s, appID, err := load(d.ConfigFile, appID)
15+
16+
if err != nil {
17+
return err
18+
}
19+
20+
if results == defaultLimit {
21+
results = s.Limit
22+
}
23+
fmt.Println("-----app-----", appID)
24+
volumes, count, err := volumes.List(s.Client, appID, results)
25+
if d.checkAPICompatibility(s.Client, err) != nil {
26+
return err
27+
}
28+
29+
if count == 0 {
30+
d.Println("Could not find any volume")
31+
} else {
32+
printVolumes(d, appID, volumes, d.WOut)
33+
}
34+
return nil
35+
}
36+
37+
// printVolumes format volume data
38+
func printVolumes(d *DryccCmd, appID string, volumes api.Volumes, wOut io.Writer) {
39+
//volumes := ps.ByType(input)
40+
41+
fmt.Fprintf(wOut, "=== %s Volumes\n", appID)
42+
43+
for _, volume := range volumes {
44+
fmt.Fprintf(wOut, "--- %s %s\n", volume.Name, volume.Size)
45+
for k, v := range volume.Path {
46+
fmt.Println(k, v)
47+
}
48+
}
49+
}
50+
51+
// VolumesCreate create a volume for the application
52+
func (d *DryccCmd) VolumesCreate(appID, name string, size string) error {
53+
s, appID, err := load(d.ConfigFile, appID)
54+
55+
if err != nil {
56+
return err
57+
}
58+
59+
d.Printf("Creating %s to %s... ", name, appID)
60+
61+
quit := progress(d.WOut)
62+
volume := api.Volume{
63+
Name: name,
64+
Size: size,
65+
}
66+
_, err = volumes.Create(s.Client, appID, volume)
67+
quit <- true
68+
<-quit
69+
if d.checkAPICompatibility(s.Client, err) != nil {
70+
return err
71+
}
72+
73+
d.Println("done")
74+
return nil
75+
}
76+
77+
// VolumesDelete delete a volume from the application
78+
func (d *DryccCmd) VolumesDelete(appID, name string) error {
79+
s, appID, err := load(d.ConfigFile, appID)
80+
81+
if err != nil {
82+
return err
83+
}
84+
85+
d.Printf("Deleting %s from %s... ", name, appID)
86+
87+
quit := progress(d.WOut)
88+
err = volumes.Delete(s.Client, appID, name)
89+
quit <- true
90+
<-quit
91+
if d.checkAPICompatibility(s.Client, err) != nil {
92+
return err
93+
}
94+
95+
d.Println("done")
96+
return nil
97+
}
98+
99+
// VolumesMount mount a volume to process of the application
100+
func (d *DryccCmd) VolumesMount(appID string, name string, volumeVars []string) error {
101+
s, appID, err := load(d.ConfigFile, appID)
102+
103+
if err != nil {
104+
return err
105+
}
106+
107+
volumeMap, err := parseVolume(volumeVars)
108+
if err != nil {
109+
return err
110+
}
111+
112+
d.Print("Mounting volume... ")
113+
114+
quit := progress(d.WOut)
115+
volumeObj := api.Volume{Path: volumeMap}
116+
_, err = volumes.Mount(s.Client, appID, name, volumeObj)
117+
quit <- true
118+
<-quit
119+
if d.checkAPICompatibility(s.Client, err) != nil {
120+
return err
121+
}
122+
123+
d.Print("done\n\n")
124+
125+
return nil
126+
}
127+
128+
// VolumesUnmount unmount a volume from process of the application
129+
func (d *DryccCmd) VolumesUnmount(appID string, name string, volumeVars []string) error {
130+
s, appID, err := load(d.ConfigFile, appID)
131+
132+
if err != nil {
133+
return err
134+
}
135+
136+
// volumeMap, err := parseVolume(volumeVars)
137+
valuesMap := make(map[string]interface{})
138+
for _, volumeVar := range volumeVars {
139+
valuesMap[volumeVar] = nil
140+
}
141+
if err != nil {
142+
return err
143+
}
144+
145+
d.Print("Unmounting volume... ")
146+
147+
quit := progress(d.WOut)
148+
volumeObj := api.Volume{Path: valuesMap}
149+
_, err = volumes.Mount(s.Client, appID, name, volumeObj)
150+
quit <- true
151+
<-quit
152+
if d.checkAPICompatibility(s.Client, err) != nil {
153+
return err
154+
}
155+
156+
d.Print("done\n\n")
157+
158+
return nil
159+
}
160+
161+
func parseVolume(volumeVars []string) (map[string]interface{}, error) {
162+
volumeMap := make(map[string]interface{})
163+
164+
regex := regexp.MustCompile(`^([A-z_]+[A-z0-9_]*)=([\s\S]*)$`)
165+
for _, volume := range volumeVars {
166+
if regex.MatchString(volume) {
167+
captures := regex.FindStringSubmatch(volume)
168+
volumeMap[captures[1]] = captures[2]
169+
} else {
170+
return nil, fmt.Errorf("'%s' does not match the pattern 'key=var', ex: MODE=test", volume)
171+
}
172+
}
173+
174+
return volumeMap, nil
175+
}

cmd/volumes_test.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package cmd
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"net/http"
7+
"testing"
8+
9+
"github.com/arschles/assert"
10+
"github.com/drycc/controller-sdk-go/api"
11+
"github.com/drycc/workflow-cli/pkg/testutil"
12+
)
13+
14+
func TestVolumesList(t *testing.T) {
15+
t.Parallel()
16+
cf, server, err := testutil.NewTestServerAndClient()
17+
if err != nil {
18+
t.Fatal(err)
19+
}
20+
defer server.Close()
21+
var b bytes.Buffer
22+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
23+
24+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/", func(w http.ResponseWriter, r *http.Request) {
25+
testutil.SetHeaders(w)
26+
fmt.Fprintf(w, `{
27+
"count": 1,
28+
"next": null,
29+
"previous": null,
30+
"results": [
31+
{
32+
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75",
33+
"owner": "test",
34+
"app": "example-go",
35+
"name": "myvolume",
36+
"size": "500M",
37+
"path": {},
38+
"created": "2020-08-26T00:00:00UTC",
39+
"updated": "2020-08-26T00:00:00UTC"
40+
}
41+
]
42+
}`)
43+
})
44+
45+
err = cmdr.VolumesList("example-go", -1)
46+
assert.NoErr(t, err)
47+
48+
assert.Equal(t, b.String(), `=== example-go Volumes
49+
--- myvolume 500M
50+
`, "output")
51+
}
52+
53+
func TestVolumesCreate(t *testing.T) {
54+
t.Parallel()
55+
cf, server, err := testutil.NewTestServerAndClient()
56+
if err != nil {
57+
t.Fatal(err)
58+
}
59+
defer server.Close()
60+
var b bytes.Buffer
61+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
62+
63+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/", func(w http.ResponseWriter, r *http.Request) {
64+
testutil.AssertBody(t, api.Volume{Name: "myvolume", Size: "500M"}, r)
65+
testutil.SetHeaders(w)
66+
w.WriteHeader(http.StatusCreated)
67+
// Body isn't used by CLI, so it isn't set.
68+
w.Write([]byte("{}"))
69+
})
70+
71+
err = cmdr.VolumesCreate("example-go", "myvolume", "500M")
72+
assert.NoErr(t, err)
73+
74+
assert.Equal(t, testutil.StripProgress(b.String()), "Creating myvolume to example-go... done\n", "output")
75+
}
76+
77+
func TestVolumesDelete(t *testing.T) {
78+
t.Parallel()
79+
cf, server, err := testutil.NewTestServerAndClient()
80+
if err != nil {
81+
t.Fatal(err)
82+
}
83+
defer server.Close()
84+
var b bytes.Buffer
85+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
86+
87+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/myvolume/", func(w http.ResponseWriter, r *http.Request) {
88+
testutil.SetHeaders(w)
89+
w.WriteHeader(http.StatusNoContent)
90+
})
91+
92+
err = cmdr.VolumesDelete("example-go", "myvolume")
93+
assert.NoErr(t, err)
94+
95+
assert.Equal(t, testutil.StripProgress(b.String()), "Deleting myvolume from example-go... done\n", "output")
96+
}
97+
98+
func TestVolumesMount(t *testing.T) {
99+
t.Parallel()
100+
cf, server, err := testutil.NewTestServerAndClient()
101+
if err != nil {
102+
t.Fatal(err)
103+
}
104+
defer server.Close()
105+
106+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/myvolume/path/", func(w http.ResponseWriter, r *http.Request) {
107+
testutil.SetHeaders(w)
108+
if r.Method == "PATCH" {
109+
testutil.AssertBody(t, api.Volume{
110+
Path: map[string]interface{}{
111+
"cmd": "/data/cmd1",
112+
},
113+
}, r)
114+
}
115+
fmt.Fprintf(w, `{
116+
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75",
117+
"owner": "test",
118+
"app": "example-go",
119+
"name": "myvolume",
120+
"size": "500M",
121+
"path": {"cmd": "/data/cmd1"},
122+
"created": "2020-08-26T00:00:00UTC",
123+
"updated": "2020-08-26T00:00:00UTC"
124+
}`)
125+
})
126+
127+
var b bytes.Buffer
128+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
129+
130+
err = cmdr.VolumesMount("example-go", "myvolume", []string{"cmd=/data/cmd1"})
131+
assert.NoErr(t, err)
132+
133+
assert.Equal(t, testutil.StripProgress(b.String()), `Mounting volume... done
134+
135+
`, "output")
136+
//
137+
//=== example-go Volumes
138+
//--- myvolume 500M
139+
//cmd /data/cmd1
140+
}
141+
142+
func TestVolumesUnmount(t *testing.T) {
143+
t.Parallel()
144+
cf, server, err := testutil.NewTestServerAndClient()
145+
if err != nil {
146+
t.Fatal(err)
147+
}
148+
defer server.Close()
149+
150+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/myvolume/path/", func(w http.ResponseWriter, r *http.Request) {
151+
testutil.SetHeaders(w)
152+
if r.Method == "PATCH" {
153+
testutil.AssertBody(t, api.Volume{
154+
Path: map[string]interface{}{
155+
"cmd": nil,
156+
},
157+
}, r)
158+
}
159+
fmt.Fprintf(w, `{
160+
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75",
161+
"owner": "test",
162+
"app": "example-go",
163+
"name": "myvolume",
164+
"size": "500M",
165+
"path": {},
166+
"created": "2020-08-26T00:00:00UTC",
167+
"updated": "2020-08-26T00:00:00UTC"
168+
}`)
169+
})
170+
171+
var b bytes.Buffer
172+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
173+
174+
err = cmdr.VolumesUnmount("example-go", "myvolume", []string{"cmd"})
175+
assert.NoErr(t, err)
176+
177+
assert.Equal(t, testutil.StripProgress(b.String()), `Unmounting volume... done
178+
179+
`, "output")
180+
181+
//=== example-go Volumes
182+
//--- myvolume 500M
183+
}

drycc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ Use 'git push drycc master' to deploy to an application.
160160
err = parser.Version(argv, &cmdr)
161161
case "whitelist":
162162
err = parser.Whitelist(argv, &cmdr)
163+
case "volumes":
164+
err = parser.Volumes(argv, &cmdr)
163165
default:
164166
env := os.Environ()
165167

0 commit comments

Comments
 (0)