Skip to content

Commit b67a17f

Browse files
committed
feat(volumes): add filer client
1 parent 782a301 commit b67a17f

10 files changed

Lines changed: 269 additions & 5 deletions

File tree

.woodpecker/test-linux.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ steps:
1414
- make test
1515
secrets:
1616
- dev_registry
17+
- codecov_token
1718
when:
1819
event:
1920
- push

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ DEV_ENV_WORK_DIR := /opt/drycc/go/src/${REPO_PATH}
66

77
DIST_DIR ?= _dist
88

9-
DEV_ENV_CMD := podman run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE}
9+
DEV_ENV_CMD := podman run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} -e CODECOV_TOKEN=${CODECOV_TOKEN} ${DEV_ENV_IMAGE}
1010

1111
bootstrap:
1212
${DEV_ENV_CMD} go mod vendor

cmd/cmd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ type Commander interface {
124124
VolumesDelete(string, string) error
125125
VolumesList(string, int) error
126126
VolumesInfo(string, string) error
127+
VolumesClient(string, string, ...string) error
127128
VolumesMount(string, string, []string) error
128129
VolumesUnmount(string, string, []string) error
129130
ResourcesServices(int) error

cmd/volumes.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ package cmd
22

33
import (
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
161182
func (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+
220348
func 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
236376
func printVolumes(d *DryccCmd, volumes api.Volumes) {
237377
table := d.getDefaultFormatTable([]string{"NAME", "OWNER", "TYPE", "PTYPE", "PATH", "SIZE"})

cmd/volumes_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"fmt"
66
"net/http"
7+
"os"
78
"testing"
89

910
"github.com/drycc/controller-sdk-go/api"
@@ -133,6 +134,73 @@ Updated: 2020-08-26T00:00:00UTC
133134
`, "output")
134135
}
135136

137+
func TestVolumesFsLs(t *testing.T) {
138+
t.Parallel()
139+
cf, server, err := testutil.NewTestServerAndClient()
140+
if err != nil {
141+
t.Fatal(err)
142+
}
143+
defer server.Close()
144+
var b bytes.Buffer
145+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
146+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/myvolume/client/", func(w http.ResponseWriter, _ *http.Request) {
147+
testutil.SetHeaders(w)
148+
fmt.Fprintf(w, `{"results": [
149+
{"name":"handler.go","size":"4159","timestamp":"2024-06-25T22:55:16+08:00","type":"file"},
150+
{"name":"handler_test.go","size":"2310","timestamp":"2024-06-04T15:29:45+08:00","type":"file"}
151+
], "count": 2}`)
152+
})
153+
154+
err = cmdr.VolumesClient("example-go", "ls", "vol://myvolume")
155+
assert.NoError(t, err)
156+
assert.Contains(t, b.String(), "handler_test.go")
157+
}
158+
159+
func TestVolumesFsCp(t *testing.T) {
160+
t.Parallel()
161+
cf, server, err := testutil.NewTestServerAndClient()
162+
if err != nil {
163+
t.Fatal(err)
164+
}
165+
defer server.Close()
166+
var b bytes.Buffer
167+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
168+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/myvolume/client/hello.txt", func(w http.ResponseWriter, _ *http.Request) {
169+
testutil.SetHeaders(w)
170+
fmt.Fprintf(w, `hello word`)
171+
})
172+
// test download file
173+
err = cmdr.VolumesClient("example-go", "cp", "vol://myvolume/hello.txt", "/tmp/hello.txt")
174+
assert.NoError(t, err)
175+
result, err := os.ReadFile("/tmp/hello.txt")
176+
assert.NoError(t, err)
177+
assert.Equal(t, string(result), `hello word`, "output")
178+
// test upload file
179+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/myvolume/client/", func(w http.ResponseWriter, _ *http.Request) {
180+
testutil.SetHeaders(w)
181+
})
182+
err = cmdr.VolumesClient("example-go", "cp", "/tmp/hello.txt", "vol://myvolume/etc")
183+
assert.NoError(t, err)
184+
}
185+
186+
func TestVolumesFsRm(t *testing.T) {
187+
t.Parallel()
188+
cf, server, err := testutil.NewTestServerAndClient()
189+
if err != nil {
190+
t.Fatal(err)
191+
}
192+
defer server.Close()
193+
var b bytes.Buffer
194+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
195+
// test rm file
196+
server.Mux.HandleFunc("/v2/apps/example-go/volumes/myvolume/client/etc/hello.txt", func(w http.ResponseWriter, _ *http.Request) {
197+
testutil.SetHeaders(w)
198+
w.WriteHeader(http.StatusOK)
199+
})
200+
err = cmdr.VolumesClient("example-go", "rm", "vol://myvolume/etc/hello.txt")
201+
assert.NoError(t, err)
202+
}
203+
136204
func TestVolumesExpand(t *testing.T) {
137205
t.Parallel()
138206
cf, server, err := testutil.NewTestServerAndClient()

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.22
55
require (
66
github.com/containerd/console v1.0.4
77
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
8-
github.com/drycc/controller-sdk-go v0.0.0-20240524075643-99bda554d60c
8+
github.com/drycc/controller-sdk-go v0.0.0-20240701055356-ed968ffcc725
99
github.com/drycc/pkg v0.0.0-20240225112316-78fc9239f51f
1010
github.com/olekukonko/tablewriter v0.0.5
1111
github.com/stretchr/testify v1.9.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
44
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
55
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
66
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
7-
github.com/drycc/controller-sdk-go v0.0.0-20240524075643-99bda554d60c h1:wxbZlPUsQfs7a3TnfAhdJ37kS3Gy56WkQ1k0keJJs24=
8-
github.com/drycc/controller-sdk-go v0.0.0-20240524075643-99bda554d60c/go.mod h1:n6eQe1irJqjwLo/7t9+Dhdv6faSESQN+ATnZRBP3/Uc=
7+
github.com/drycc/controller-sdk-go v0.0.0-20240701055356-ed968ffcc725 h1:Kgo5LLLYD249F0m+9NwbOpTGJ1EuaBcdBCCNKa8Dye4=
8+
github.com/drycc/controller-sdk-go v0.0.0-20240701055356-ed968ffcc725/go.mod h1:a6WodXx5WyT9kOHHpOL62EIqJaYJeAr7NW1FCoKmG6Q=
99
github.com/drycc/pkg v0.0.0-20240225112316-78fc9239f51f h1:kgjvUQJeAszDoU1Vo4vTTE92KI8Av3JPb6Qn890niXg=
1010
github.com/drycc/pkg v0.0.0-20240225112316-78fc9239f51f/go.mod h1:n+QxGif6ha9CEoxVnlipxb9IdmerybcUSzTEDFkvjiA=
1111
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=

parser/ps.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ Print the logs for a container in a pod or specified resource.
7777
7878
Usage: drycc ps:logs <pod> [options]
7979
80+
Arguments:
81+
<pod> the pod name for the application.
82+
8083
Options:
8184
-a --app=<app>
8285
the uniquely identifiable name for the application.
@@ -112,6 +115,9 @@ Execute a command in a container.
112115
113116
Usage: drycc ps:exec <pod> [options] -- <command>...
114117
118+
Arguments:
119+
<pod> the pod name for the application.
120+
115121
Options:
116122
-a --app=<app>
117123
the uniquely identifiable name for the application.
@@ -196,6 +202,9 @@ Print a detailed description of the selected process.
196202
197203
Usage: drycc ps:describe <pod> [options]
198204
205+
Arguments:
206+
<pod> the pod name for the application.
207+
199208
Options:
200209
-a --app=<app>
201210
the uniquely identifiable name for the application.

0 commit comments

Comments
 (0)