Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ func NewConfigCmd() *cobra.Command {
}

masked := *cfg
masked.APIKey = maskAPIKey(masked.APIKey)
masked.APIKey = maskSecret(masked.APIKey)
masked.RegistryPassword = maskSecret(masked.RegistryPassword)
return writeConfig(cmd.OutOrStdout(), &masked)
},
}
}

func maskAPIKey(apiKey string) string {
if apiKey == "" {
// maskSecret masks a sensitive config value.
func maskSecret(secret string) string {
if secret == "" {
return ""
}
return "****"
Expand Down
22 changes: 16 additions & 6 deletions cmd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ func TestConfigCmd(t *testing.T) {
t.Setenv("UCLOUD_SANDBOX_REGION", "cn-sh")
t.Setenv("UCLOUD_SANDBOX_DOMAIN", "")
t.Setenv("UCLOUD_SANDBOX_INSECURE_HTTP", "true")
t.Setenv("UCLOUD_SANDBOX_REGISTRY_USERNAME", "registry-user")
t.Setenv("UCLOUD_SANDBOX_REGISTRY_PASSWORD", "registry-pass")

var output bytes.Buffer
cmd := NewConfigCmd()
Expand All @@ -26,9 +28,12 @@ func TestConfigCmd(t *testing.T) {
assert.JSONEq(t, `{
"api_key": "****",
"region": "cn-sh",
"insecure_http": true
"insecure_http": true,
"registry_username": "registry-user",
"registry_password": "****"
}`, output.String())
assert.NotContains(t, output.String(), "abcd12345678wxyz")
assert.NotContains(t, output.String(), "registry-pass")
}

func TestConfigCmd_UsesEffectiveConfig(t *testing.T) {
Expand All @@ -43,7 +48,9 @@ func TestConfigCmd_UsesEffectiveConfig(t *testing.T) {
require.NoError(t, os.MkdirAll(configDir, 0700))
require.NoError(t, os.WriteFile(filepath.Join(configDir, "config.json"), []byte(`{
"api_key": "file-api-key",
"region": "cn-bj"
"region": "cn-bj",
"registry_username": "file-user",
"registry_password": "file-pass"
}`), 0600))

var output bytes.Buffer
Expand All @@ -53,12 +60,15 @@ func TestConfigCmd_UsesEffectiveConfig(t *testing.T) {

assert.Contains(t, output.String(), `"api_key": "****"`)
assert.Contains(t, output.String(), `"region": "cn-bj"`)
assert.Contains(t, output.String(), `"registry_username": "file-user"`)
assert.Contains(t, output.String(), `"registry_password": "****"`)
assert.NotContains(t, output.String(), "environment-key")
assert.NotContains(t, output.String(), "file-api-key")
assert.NotContains(t, output.String(), "file-pass")
}

func TestMaskAPIKey(t *testing.T) {
assert.Empty(t, maskAPIKey(""))
assert.Equal(t, "****", maskAPIKey("short"))
assert.Equal(t, "****", maskAPIKey("abcd1234wxyz"))
func TestMaskSecret(t *testing.T) {
assert.Empty(t, maskSecret(""))
assert.Equal(t, "****", maskSecret("short"))
assert.Equal(t, "****", maskSecret("abcd1234wxyz"))
}
19 changes: 16 additions & 3 deletions cmd/template/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,16 @@ func runBuild(name string, flags *buildFlags) error {
if flags.startCmd == "" && flags.readyCmd != "" {
return fmt.Errorf("both --cmd and --ready-cmd must be provided together")
}
if (flags.registryUsername == "") != (flags.registryPassword == "") {
return fmt.Errorf("both --registry-username and --registry-password must be provided together")
}

cfg, err := config.Load()
if err != nil {
return err
}
applyRegistryCredentials(flags, cfg)
if (flags.registryUsername == "") != (flags.registryPassword == "") {
return fmt.Errorf("both --registry-username and --registry-password must be provided together (or set them in the config file)")
}

client, err := config.NewClient(cfg)
if err != nil {
return err
Expand Down Expand Up @@ -160,6 +162,17 @@ func runBuild(name string, flags *buildFlags) error {
return nil
}

// applyRegistryCredentials fills missing registry credentials from the global
// config; explicit flags take precedence.
func applyRegistryCredentials(flags *buildFlags, cfg *config.Config) {
if flags.registryUsername == "" {
flags.registryUsername = cfg.RegistryUsername
}
if flags.registryPassword == "" {
flags.registryPassword = cfg.RegistryPassword
}
}

// resolveBuildContext prefers the template's named directory, then --path itself.
func resolveBuildContext(name, path string) (string, *LocalConfig) {
candidate := filepath.Join(path, name)
Expand Down
19 changes: 15 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,19 @@ const (
envRegion = "UCLOUD_SANDBOX_REGION"
envDomain = "UCLOUD_SANDBOX_DOMAIN"
envInsecureHTTP = "UCLOUD_SANDBOX_INSECURE_HTTP"

envRegistryUsername = "UCLOUD_SANDBOX_REGISTRY_USERNAME"
envRegistryPassword = "UCLOUD_SANDBOX_REGISTRY_PASSWORD"
)

// Config holds the CLI configuration.
type Config struct {
APIKey string `json:"api_key,omitempty"`
Region string `json:"region,omitempty"`
Domain string `json:"domain,omitempty"`
InsecureHTTP bool `json:"insecure_http,omitempty"`
APIKey string `json:"api_key,omitempty"`
Region string `json:"region,omitempty"`
Domain string `json:"domain,omitempty"`
InsecureHTTP bool `json:"insecure_http,omitempty"`
RegistryUsername string `json:"registry_username,omitempty"`
RegistryPassword string `json:"registry_password,omitempty"`
}

// configPath returns the path to the config file.
Expand Down Expand Up @@ -75,6 +80,12 @@ func Load() (*Config, error) {
}
cfg.InsecureHTTP = insecureHTTP
}
if v := os.Getenv(envRegistryUsername); v != "" {
cfg.RegistryUsername = v
}
if v := os.Getenv(envRegistryPassword); v != "" {
cfg.RegistryPassword = v
}

return cfg, nil
}
Expand Down
16 changes: 13 additions & 3 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,24 @@ func writeConfig(t *testing.T, home string, cfg *Config) {

func clearEnv(t *testing.T) {
t.Helper()
for _, k := range []string{envAPIKey, envRegion, envDomain, envInsecureHTTP} {
for _, k := range []string{envAPIKey, envRegion, envDomain, envInsecureHTTP, envRegistryUsername, envRegistryPassword} {
t.Setenv(k, "")
}
}

func TestLoad_FileOnly(t *testing.T) {
home := setupHome(t)
clearEnv(t)
writeConfig(t, home, &Config{APIKey: "key1", Region: "cn-sh", InsecureHTTP: true})
writeConfig(t, home, &Config{APIKey: "key1", Region: "cn-sh", InsecureHTTP: true,
RegistryUsername: "file-user", RegistryPassword: "file-pass"})

cfg, err := Load()
require.NoError(t, err)
assert.Equal(t, "key1", cfg.APIKey)
assert.Equal(t, "cn-sh", cfg.Region)
assert.True(t, cfg.InsecureHTTP)
assert.Equal(t, "file-user", cfg.RegistryUsername)
assert.Equal(t, "file-pass", cfg.RegistryPassword)
}

func TestLoad_EnvOverride(t *testing.T) {
Expand All @@ -51,13 +54,17 @@ func TestLoad_EnvOverride(t *testing.T) {
t.Setenv(envRegion, "env-region")
t.Setenv(envDomain, "env.example.com")
t.Setenv(envInsecureHTTP, "false")
t.Setenv(envRegistryUsername, "env-user")
t.Setenv(envRegistryPassword, "env-pass")

cfg, err := Load()
require.NoError(t, err)
assert.Equal(t, "env-key", cfg.APIKey)
assert.Equal(t, "env-region", cfg.Region)
assert.Equal(t, "env.example.com", cfg.Domain)
assert.False(t, cfg.InsecureHTTP)
assert.Equal(t, "env-user", cfg.RegistryUsername)
assert.Equal(t, "env-pass", cfg.RegistryPassword)
}

func TestLoad_EnvInsecureHTTPTrue(t *testing.T) {
Expand Down Expand Up @@ -94,7 +101,8 @@ func TestLoad_NoFile(t *testing.T) {
func TestSave(t *testing.T) {
home := setupHome(t)

in := &Config{APIKey: "save-key", Region: "cn-bj", InsecureHTTP: true}
in := &Config{APIKey: "save-key", Region: "cn-bj", InsecureHTTP: true,
RegistryUsername: "save-user", RegistryPassword: "save-pass"}
require.NoError(t, Save(in))

data, err := os.ReadFile(filepath.Join(home, configDir, configFile))
Expand All @@ -104,6 +112,8 @@ func TestSave(t *testing.T) {
assert.Equal(t, in.APIKey, out.APIKey)
assert.Equal(t, in.Region, out.Region)
assert.Equal(t, in.InsecureHTTP, out.InsecureHTTP)
assert.Equal(t, in.RegistryUsername, out.RegistryUsername)
assert.Equal(t, in.RegistryPassword, out.RegistryPassword)
assert.Contains(t, string(data), `"insecure_http": true`)
assert.NotContains(t, string(data), `"insecure":`)
}
Expand Down
Loading