Skip to content

Commit e56e8c1

Browse files
authored
feat(config): add import command (#2995)
1 parent cb52f9d commit e56e8c1

File tree

5 files changed

+195
-0
lines changed

5 files changed

+195
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
Import configurations from another file
4+
5+
USAGE:
6+
scw config import <file ...> [arg=value ...]
7+
8+
ARGS:
9+
file Path to the configuration file to import
10+
11+
FLAGS:
12+
-h, --help help for import
13+
14+
GLOBAL FLAGS:
15+
-c, --config string The path to the config file
16+
-D, --debug Enable debug mode
17+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
18+
-p, --profile string The config profile to use

docs/commands/config.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Read more about the config management engine at https://github.com/scaleway/scal
2929
- [Destroy the config file](#destroy-the-config-file)
3030
- [Dump the config file](#dump-the-config-file)
3131
- [Get a value from the config file](#get-a-value-from-the-config-file)
32+
- [Import configurations from another file](#import-configurations-from-another-file)
3233
- [Get config values from the config file for the current profile](#get-config-values-from-the-config-file-for-the-current-profile)
3334
- [Allows the deletion of a profile from the config file](#allows-the-deletion-of-a-profile-from-the-config-file)
3435
- [Mark a profile as active in the config file](#mark-a-profile-as-active-in-the-config-file)
@@ -102,6 +103,27 @@ scw -p prod config get default_region
102103

103104

104105

106+
## Import configurations from another file
107+
108+
109+
110+
111+
112+
**Usage:**
113+
114+
```
115+
scw config import <file ...> [arg=value ...]
116+
```
117+
118+
119+
**Args:**
120+
121+
| Name | | Description |
122+
|------|---|-------------|
123+
| file | Required | Path to the configuration file to import |
124+
125+
126+
105127
## Get config values from the config file for the current profile
106128

107129

internal/namespaces/config/commands.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func GetCommands() *core.Commands {
3232
configResetCommand(),
3333
configDestroyCommand(),
3434
configInfoCommand(),
35+
configImportCommand(),
3536
)
3637
}
3738

@@ -628,6 +629,72 @@ func configInfoCommand() *core.Command {
628629
}
629630
}
630631

632+
// configImportCommand imports an external config
633+
func configImportCommand() *core.Command {
634+
type configImportArgs struct {
635+
File string
636+
}
637+
638+
return &core.Command{
639+
Groups: []string{"config"},
640+
Short: "Import configurations from another file",
641+
Namespace: "config",
642+
Resource: "import",
643+
AllowAnonymousClient: true,
644+
ArgsType: reflect.TypeOf(configImportArgs{}),
645+
ArgSpecs: core.ArgSpecs{
646+
{
647+
Name: "file",
648+
Short: "Path to the configuration file to import",
649+
Required: true,
650+
Positional: true,
651+
},
652+
},
653+
Run: func(ctx context.Context, argsI interface{}) (i interface{}, e error) {
654+
args := argsI.(*configImportArgs)
655+
configPath := core.ExtractConfigPath(ctx)
656+
657+
currentConfig, err := scw.LoadConfigFromPath(configPath)
658+
if err != nil {
659+
return nil, err
660+
}
661+
currentProfileName := core.ExtractProfileName(ctx)
662+
currentProfile, err := currentConfig.GetProfile(currentProfileName)
663+
if err != nil {
664+
return nil, err
665+
}
666+
667+
// Read the content of the file to import
668+
importedConfig, err := scw.LoadConfigFromPath(args.File)
669+
if err != nil {
670+
return nil, err
671+
}
672+
importedProfile := importedConfig.Profile
673+
674+
// Merge the imported configurations into the existing configuration
675+
currentConfig.Profile = *scw.MergeProfiles(currentProfile, &importedProfile)
676+
677+
for profileName, profile := range importedConfig.Profiles {
678+
existingProfile, exists := currentConfig.Profiles[profileName]
679+
if exists {
680+
currentConfig.Profiles[profileName] = scw.MergeProfiles(existingProfile, profile)
681+
} else {
682+
currentConfig.Profiles[profileName] = profile
683+
}
684+
}
685+
686+
err = currentConfig.SaveTo(configPath)
687+
if err != nil {
688+
return nil, fmt.Errorf("failed to save updated configuration: %v", err)
689+
}
690+
691+
return &core.SuccessResult{
692+
Message: "successfully import config",
693+
}, nil
694+
},
695+
}
696+
}
697+
631698
// Helper functions
632699
func getProfileValue(profile *scw.Profile, fieldName string) (interface{}, error) {
633700
field, err := getProfileField(profile, fieldName)

internal/namespaces/config/commands_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package config
22

33
import (
4+
"fmt"
5+
"io/ioutil"
46
"os"
57
"path"
68
"regexp"
@@ -303,6 +305,38 @@ func Test_ConfigInfoCommand(t *testing.T) {
303305
}))
304306
}
305307

308+
func Test_ConfigImportCommand(t *testing.T) {
309+
t.Run("Simple", func(t *testing.T) {
310+
tmpFile, err := createTempConfigFile()
311+
if err != nil {
312+
t.Fatal(err)
313+
}
314+
defer os.Remove(tmpFile.Name())
315+
316+
core.Test(&core.TestConfig{
317+
Commands: GetCommands(),
318+
BeforeFunc: beforeFuncCreateFullConfig(),
319+
Cmd: fmt.Sprintf("scw config import %s", tmpFile.Name()),
320+
Check: core.TestCheckCombine(
321+
core.TestCheckExitCode(0),
322+
core.TestCheckGolden(),
323+
checkConfig(func(t *testing.T, config *scw.Config) {
324+
// config
325+
assert.Equal(t, "22222222-2222-2222-2222-222222222222", *config.SecretKey)
326+
assert.Equal(t, "nl-ams", *config.DefaultRegion)
327+
// modified p1
328+
assert.Equal(t, "99999999-9999-9999-9999-999999999999", *config.Profiles["p1"].SecretKey)
329+
assert.Equal(t, "nl-ams", *config.Profiles["p1"].DefaultRegion)
330+
// new p3
331+
assert.Equal(t, "33333333-3333-3333-3333-333333333333", *config.Profiles["p3"].SecretKey)
332+
assert.Equal(t, "fr-par", *config.Profiles["p3"].DefaultRegion)
333+
}),
334+
),
335+
TmpHomeDir: true,
336+
})(t)
337+
})
338+
}
339+
306340
func checkConfig(f func(t *testing.T, config *scw.Config)) core.TestCheck {
307341
return func(t *testing.T, ctx *core.CheckFuncCtx) {
308342
homeDir := ctx.OverrideEnv["HOME"]
@@ -361,3 +395,49 @@ func beforeFuncCreateFullConfig() core.BeforeFunc {
361395
},
362396
})
363397
}
398+
399+
func createTempConfigFile() (*os.File, error) {
400+
tmpFile, err := ioutil.TempFile("", "tmp.yaml")
401+
if err != nil {
402+
return nil, err
403+
}
404+
405+
configContent := `
406+
access_key: SCWXXXXXXXXXXXXXXXXX
407+
secret_key: 22222222-2222-2222-2222-222222222222
408+
api_url: https://mock-api-url.com
409+
insecure: true
410+
default_organization_id: 22222222-2222-2222-2222-222222222222
411+
default_region: nl-ams
412+
default_zone: nl-ams-1
413+
send_telemetry: true
414+
profiles:
415+
p1:
416+
access_key: SCWP1XXXXXXXXXXXXXXX
417+
secret_key: 99999999-9999-9999-9999-999999999999
418+
api_url: https://p1-mock-api-url.com
419+
insecure: true
420+
default_organization_id: 99999999-9999-9999-9999-999999999999
421+
default_region: nl-ams
422+
default_zone: nl-ams-1
423+
send_telemetry: true
424+
p3:
425+
access_key: SCWP3XXXXXXXXXXXXXXX
426+
secret_key: 33333333-3333-3333-3333-333333333333
427+
api_url: https://p3-mock-api-url.com
428+
insecure: true
429+
default_organization_id: 33333333-3333-3333-3333-333333333333
430+
default_region: fr-par
431+
default_zone: fr-par-1
432+
send_telemetry: true
433+
`
434+
435+
if _, err := tmpFile.Write([]byte(configContent)); err != nil {
436+
return nil, err
437+
}
438+
if err := tmpFile.Close(); err != nil {
439+
return nil, err
440+
}
441+
442+
return tmpFile, nil
443+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟩🟩🟩 STDOUT️ 🟩🟩🟩️
3+
✅ Successfully import config.
4+
🟩🟩🟩 JSON STDOUT 🟩🟩🟩
5+
{
6+
"message": "successfully import config",
7+
"details": ""
8+
}

0 commit comments

Comments
 (0)