Skip to content

Commit 3183bb3

Browse files
committed
Merge branch 'feature/storage-data-export' of github.com:deckhouse/deckhouse-cli into feature/storage-data-export
2 parents 62aea90 + 87b2c88 commit 3183bb3

File tree

2 files changed

+109
-12
lines changed

2 files changed

+109
-12
lines changed

internal/dataexport/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,57 @@ Subcommand for the command line client for Deckhouse.
99
```shell
1010
d8 data create export-name pvc/test-pvc-name
1111
```
12+
13+
#### create from a snapshot
14+
15+
1. Enable snapshot-controller module
16+
17+
```shell
18+
kubectl apply -f -<<EOF
19+
apiVersion: deckhouse.io/v1alpha1
20+
kind: ModuleConfig
21+
metadata:
22+
name: snapshot-controller
23+
spec:
24+
enabled: true
25+
version: 1
26+
EOF
27+
```
28+
29+
2. Get a volume snapshot class name - this entity is created automatically for each type of CSI and is needed for creating a snapshot.
30+
For example, "sds-local-volume-snapshot-class" as shown in the example below
31+
32+
```shell
33+
kubectl get volumesnapshotclass
34+
35+
sds-local-volume-snapshot-class local.csi.storage.deckhouse.io Delete 22h
36+
```
37+
38+
3. Create a snapshot of a needed volume
39+
40+
```shell
41+
kubectl apply -f -<<EOF
42+
apiVersion: snapshot.storage.k8s.io/v1
43+
kind: VolumeSnapshot
44+
metadata:
45+
name: my-snapshot
46+
namespace: <name of the namespace where the PVC is located>
47+
spec:
48+
volumeSnapshotClassName: <volume snapshot class name>
49+
source:
50+
persistentVolumeClaimName: <name of the PVC to snapshot>
51+
EOF
52+
```
53+
54+
4. Check that snapshot is created normally and ready to use (usually takes a few minutes to be ready):
55+
```shell
56+
kubectl -n <name of the namespace> get volumesnapshot my-snapshot
57+
58+
NAMESPACE NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT
59+
<namespace> my-snapshot true test-pvc-for-snapshot 2Gi sds-local-volume-snapshot-class snapcontent-faf2ab1f-891d-4e5e-972c-334a490c99d8
60+
```
61+
62+
5. Create data export from that snapshot using d8 command as shown in the example below
63+
```shell
64+
d8 data create export-name snapshot/my-snapshot
65+
```

internal/dataexport/cmd/download/download.go

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,54 @@ type dirResp struct {
104104
Items []dirItem `json:"items"`
105105
}
106106

107+
func forRespItems(jsonStream io.ReadCloser, workFunc func(*dirItem) error) error {
108+
dec := json.NewDecoder(jsonStream)
109+
110+
// find items list
111+
for {
112+
t, err := dec.Token()
113+
if err != nil {
114+
return err
115+
}
116+
117+
if t == "items" {
118+
t, err := dec.Token()
119+
if err != nil {
120+
return err
121+
}
122+
if t != json.Delim('[') {
123+
return fmt.Errorf("JSON items is not list")
124+
}
125+
break
126+
}
127+
dec.More()
128+
}
129+
130+
// read items
131+
for dec.More() {
132+
var i dirItem
133+
err := dec.Decode(&i)
134+
if err != nil {
135+
break
136+
}
137+
err = workFunc(&i)
138+
if err != nil {
139+
return err
140+
}
141+
}
142+
143+
// check items list closed
144+
t, err := dec.Token()
145+
if err != nil {
146+
return err
147+
}
148+
if t != json.Delim(']') {
149+
return fmt.Errorf("items loading is not completed")
150+
}
151+
152+
return nil
153+
}
154+
107155
func recursiveDownload(ctx context.Context, sClient *safeClient.SafeClient, log *slog.Logger, sem chan struct{}, url, srcPath, dstPath string) (err error) {
108156
select {
109157
case <-ctx.Done():
@@ -135,22 +183,11 @@ func recursiveDownload(ctx context.Context, sClient *safeClient.SafeClient, log
135183
}
136184

137185
if srcPath != "" && srcPath[len(srcPath)-1:] == "/" {
138-
dirListBody, err := io.ReadAll(resp.Body)
139-
if err != nil {
140-
return fmt.Errorf("Response body (%s) error: %s", srcPath, err.Error())
141-
}
142-
143-
var dir dirResp
144-
err = json.Unmarshal(dirListBody, &dir)
145-
if err != nil {
146-
return fmt.Errorf("Invalid dir (%s) data: %s", srcPath, err.Error())
147-
}
148-
149186
var wg sync.WaitGroup
150187
var mu sync.Mutex
151188
var firstErr error
152189

153-
for _, item := range dir.Items {
190+
err = forRespItems(resp.Body, func(item *dirItem) error {
154191
subPath := item.Name
155192
if item.Type == "dir" {
156193
err = os.MkdirAll(filepath.Join(dstPath, subPath), os.ModePerm)
@@ -172,7 +209,13 @@ func recursiveDownload(ctx context.Context, sClient *safeClient.SafeClient, log
172209
mu.Unlock()
173210
}
174211
}(subPath)
212+
213+
return nil
214+
})
215+
if err != nil {
216+
return fmt.Errorf("Response body (%s) error: %s", srcPath, err.Error())
175217
}
218+
176219
wg.Wait()
177220
return firstErr
178221
} else {

0 commit comments

Comments
 (0)