Skip to content

Commit d842539

Browse files
authored
Merge pull request #17 from StackVista/stac-23446
Use s3proxy for all backups
2 parents f5749b6 + 39c9cb1 commit d842539

31 files changed

+1422
-271
lines changed

cmd/clickhouse/check_and_finalize.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func checkAndFinalizeCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
3333
This command is useful when a restore was started without --wait flag or was interrupted.
3434
It will check the restore status and if complete, execute post-restore tasks and scale up resources.`,
3535
Run: func(_ *cobra.Command, _ []string) {
36-
cmdutils.Run(globalFlags, runCheckAndFinalize, cmdutils.MinioIsRequired)
36+
cmdutils.Run(globalFlags, runCheckAndFinalize, cmdutils.StorageIsRequired)
3737
},
3838
}
3939

cmd/clickhouse/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func listCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
1818
Short: "List available Clickhouse backups",
1919
Long: `List all Clickhouse backups from the ClickHouse Backup API.`,
2020
Run: func(_ *cobra.Command, _ []string) {
21-
cmdutils.Run(globalFlags, runList, cmdutils.MinioIsRequired)
21+
cmdutils.Run(globalFlags, runList, cmdutils.StorageIsRequired)
2222
},
2323
}
2424
}

cmd/clickhouse/restore.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func restoreCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
2727
Short: "Restore ClickHouse from a backup archive",
2828
Long: `Restore ClickHouse data from a backup archive via ClickHouse Backup API. Waits for completion by default; use --background to run asynchronously.`,
2929
Run: func(_ *cobra.Command, _ []string) {
30-
cmdutils.Run(globalFlags, runRestore, cmdutils.MinioIsRequired)
30+
cmdutils.Run(globalFlags, runRestore, cmdutils.StorageIsRequired)
3131
},
3232
}
3333

cmd/cmdutils/common.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ import (
99
)
1010

1111
const (
12-
MinioIsRequired bool = true
13-
MinioIsNotRequired bool = false
12+
StorageIsRequired bool = true
13+
StorageIsNotRequired bool = false
1414
)
1515

16-
func Run(globalFlags *config.CLIGlobalFlags, runFunc func(ctx *app.Context) error, minioRequired bool) {
16+
func Run(globalFlags *config.CLIGlobalFlags, runFunc func(ctx *app.Context) error, storageRequired bool) {
1717
appCtx, err := app.NewContext(globalFlags)
1818
if err != nil {
1919
_, _ = fmt.Fprintf(os.Stderr, "❌ Error: %v\n", err)
2020
os.Exit(1)
2121
}
22-
if minioRequired && !appCtx.Config.Minio.Enabled {
23-
appCtx.Logger.Errorf("commands that interact with Minio require SUSE Observability to be deployed with .Values.global.backup.enabled=true")
22+
if storageRequired && !appCtx.Config.GlobalBackupEnabled() {
23+
appCtx.Logger.Errorf("commands that interact with S3-compatible storage require SUSE Observability to be deployed with .Values.global.backup.enabled=true")
2424
os.Exit(1)
2525
}
2626
if err := runFunc(appCtx); err != nil {

cmd/elasticsearch/check_and_finalize.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func checkAndFinalizeCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
2626
Long: `Check the status of a restore operation and perform finalization (scale up deployments) if complete.
2727
If the restore is still running and --wait is specified, wait for completion before finalizing.`,
2828
Run: func(_ *cobra.Command, _ []string) {
29-
cmdutils.Run(globalFlags, runCheckAndFinalize, cmdutils.MinioIsRequired)
29+
cmdutils.Run(globalFlags, runCheckAndFinalize, cmdutils.StorageIsRequired)
3030
},
3131
}
3232

cmd/elasticsearch/configure.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func configureCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
1616
Short: "Configure Elasticsearch snapshot repository and SLM policy",
1717
Long: `Configure Elasticsearch snapshot repository and Snapshot Lifecycle Management (SLM) policy for automated backups.`,
1818
Run: func(_ *cobra.Command, _ []string) {
19-
cmdutils.Run(globalFlags, runConfigure, cmdutils.MinioIsRequired)
19+
cmdutils.Run(globalFlags, runConfigure, cmdutils.StorageIsRequired)
2020
},
2121
}
2222
}

cmd/elasticsearch/configure_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,162 @@ minio:
267267
}
268268
}
269269

270+
// TestConfigureCmd_StorageIntegration tests the integration with Kubernetes client using StorageConfig
271+
//
272+
//nolint:funlen
273+
func TestConfigureCmd_StorageIntegration(t *testing.T) {
274+
if testing.Short() {
275+
t.Skip("skipping integration test in short mode")
276+
}
277+
278+
tests := []struct {
279+
name string
280+
configData string
281+
secretData string
282+
expectError bool
283+
errorContains string
284+
}{
285+
{
286+
name: "successful configuration with complete data (storage mode)",
287+
configData: `
288+
elasticsearch:
289+
service:
290+
name: elasticsearch-master
291+
port: 9200
292+
localPortForwardPort: 9200
293+
restore:
294+
scaleDownLabelSelector: app=test
295+
indexPrefix: sts_
296+
datastreamIndexPrefix: sts_k8s_logs
297+
datastreamName: sts_k8s_logs
298+
indicesPattern: "sts_*"
299+
repository: backup-repo
300+
snapshotRepository:
301+
name: backup-repo
302+
bucket: backups
303+
endpoint: storage:9000
304+
basepath: snapshots
305+
accessKey: test-key
306+
secretKey: test-secret
307+
slm:
308+
name: daily
309+
schedule: "0 1 * * *"
310+
snapshotTemplateName: "<snap-{now/d}>"
311+
repository: backup-repo
312+
indices: "sts_*"
313+
retentionExpireAfter: 30d
314+
retentionMinCount: 5
315+
retentionMaxCount: 50
316+
` + minimalStorageStackgraphConfig,
317+
secretData: "",
318+
expectError: false,
319+
},
320+
{
321+
name: "missing credentials in config with secret override (storage mode)",
322+
configData: `
323+
elasticsearch:
324+
service:
325+
name: elasticsearch-master
326+
port: 9200
327+
localPortForwardPort: 9200
328+
restore:
329+
scaleDownLabelSelector: app=test
330+
indexPrefix: sts_
331+
datastreamIndexPrefix: sts_k8s_logs
332+
datastreamName: sts_k8s_logs
333+
indicesPattern: "sts_*"
334+
repository: backup-repo
335+
snapshotRepository:
336+
name: backup-repo
337+
bucket: backups
338+
endpoint: storage:9000
339+
basepath: snapshots
340+
accessKey: ""
341+
secretKey: ""
342+
slm:
343+
name: daily
344+
schedule: "0 1 * * *"
345+
snapshotTemplateName: "<snap-{now/d}>"
346+
repository: backup-repo
347+
indices: "sts_*"
348+
retentionExpireAfter: 30d
349+
retentionMinCount: 5
350+
retentionMaxCount: 50
351+
` + minimalStorageStackgraphConfig,
352+
secretData: `
353+
elasticsearch:
354+
snapshotRepository:
355+
accessKey: secret-key
356+
secretKey: secret-value
357+
storage:
358+
accessKey: secret-storage-key
359+
secretKey: secret-storage-value
360+
`,
361+
expectError: false,
362+
},
363+
}
364+
365+
for _, tt := range tests {
366+
t.Run(tt.name, func(t *testing.T) {
367+
fakeClient := fake.NewClientset()
368+
369+
// Create ConfigMap
370+
cm := &corev1.ConfigMap{
371+
ObjectMeta: metav1.ObjectMeta{
372+
Name: testConfigMapName,
373+
Namespace: testNamespace,
374+
},
375+
Data: map[string]string{
376+
"config": tt.configData,
377+
},
378+
}
379+
_, err := fakeClient.CoreV1().ConfigMaps(testNamespace).Create(
380+
context.Background(), cm, metav1.CreateOptions{},
381+
)
382+
require.NoError(t, err)
383+
384+
// Create Secret if provided
385+
if tt.secretData != "" {
386+
secret := &corev1.Secret{
387+
ObjectMeta: metav1.ObjectMeta{
388+
Name: testSecretName,
389+
Namespace: testNamespace,
390+
},
391+
Data: map[string][]byte{
392+
"config": []byte(tt.secretData),
393+
},
394+
}
395+
_, err := fakeClient.CoreV1().Secrets(testNamespace).Create(
396+
context.Background(), secret, metav1.CreateOptions{},
397+
)
398+
require.NoError(t, err)
399+
}
400+
401+
// Test that config loading works
402+
secretName := ""
403+
if tt.secretData != "" {
404+
secretName = testSecretName
405+
}
406+
cfg, err := config.LoadConfig(fakeClient, testNamespace, testConfigMapName, secretName)
407+
408+
if tt.expectError {
409+
assert.Error(t, err)
410+
if tt.errorContains != "" {
411+
assert.Contains(t, err.Error(), tt.errorContains)
412+
}
413+
} else {
414+
require.NoError(t, err)
415+
assert.NotNil(t, cfg)
416+
// Verify storage mode
417+
assert.False(t, cfg.IsLegacyMode())
418+
assert.True(t, cfg.GlobalBackupEnabled())
419+
assert.NotEmpty(t, cfg.Elasticsearch.SnapshotRepository.AccessKey)
420+
assert.NotEmpty(t, cfg.Elasticsearch.SnapshotRepository.SecretKey)
421+
}
422+
})
423+
}
424+
}
425+
270426
// TestMockESClientForConfigure demonstrates mock usage for configure
271427
//
272428
//nolint:funlen // Table-driven test

cmd/elasticsearch/list-indices.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func listIndicesCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
1616
Use: "list-indices",
1717
Short: "List Elasticsearch indices",
1818
Run: func(_ *cobra.Command, _ []string) {
19-
cmdutils.Run(globalFlags, runListIndices, cmdutils.MinioIsRequired)
19+
cmdutils.Run(globalFlags, runListIndices, cmdutils.StorageIsRequired)
2020
},
2121
}
2222
}

cmd/elasticsearch/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func listCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
1717
Use: "list",
1818
Short: "List available Elasticsearch snapshots",
1919
Run: func(_ *cobra.Command, _ []string) {
20-
cmdutils.Run(globalFlags, runListSnapshots, cmdutils.MinioIsRequired)
20+
cmdutils.Run(globalFlags, runListSnapshots, cmdutils.StorageIsRequired)
2121
},
2222
}
2323
}

0 commit comments

Comments
 (0)