diff --git a/cmd/probod-bootstrap/main.go b/cmd/probod-bootstrap/main.go index 2cd3853bb..69b4baf7e 100644 --- a/cmd/probod-bootstrap/main.go +++ b/cmd/probod-bootstrap/main.go @@ -29,6 +29,7 @@ var ( func main() { outputPath := flag.String("output", "/etc/probod/config.yml", "output path for the generated config file") + format := flag.String("format", "yaml", "output format for the generated config file (yaml or json)") showVersion := flag.Bool("version", false, "print version and exit") flag.Parse() @@ -38,6 +39,17 @@ func main() { return } + var configFormat bootstrap.Format + switch *format { + case "yaml": + configFormat = bootstrap.FormatYAML + case "json": + configFormat = bootstrap.FormatJSON + default: + fmt.Fprintf(os.Stderr, "error: unsupported format %q, must be yaml or json\n", *format) + os.Exit(1) + } + builder := bootstrap.NewBuilder(bootstrap.NewResolver(nil)) cfg, err := builder.Build() @@ -46,7 +58,7 @@ func main() { os.Exit(1) } - if err := bootstrap.WriteConfig(cfg, *outputPath); err != nil { + if err := bootstrap.WriteConfig(cfg, *outputPath, configFormat); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } diff --git a/e2e/internal/testutil/testutil.go b/e2e/internal/testutil/testutil.go index 0265b3c52..1ca00fc41 100644 --- a/e2e/internal/testutil/testutil.go +++ b/e2e/internal/testutil/testutil.go @@ -331,7 +331,7 @@ func generateConfig() (string, error) { path := filepath.Join(tmpDir, "probod.yml") - if err := bootstrap.WriteConfig(cfg, path); err != nil { + if err := bootstrap.WriteConfig(cfg, path, bootstrap.FormatYAML); err != nil { return "", fmt.Errorf("write config: %w", err) } diff --git a/pkg/bootstrap/write.go b/pkg/bootstrap/write.go index 542c84c1c..f92436652 100644 --- a/pkg/bootstrap/write.go +++ b/pkg/bootstrap/write.go @@ -15,6 +15,7 @@ package bootstrap import ( + "encoding/json" "fmt" "os" "path/filepath" @@ -23,7 +24,14 @@ import ( "sigs.k8s.io/yaml" ) -func WriteConfig(cfg *probodconfig.FullConfig, path string) error { +type Format string + +const ( + FormatYAML Format = "yaml" + FormatJSON Format = "json" +) + +func WriteConfig(cfg *probodconfig.FullConfig, path string, format Format) error { dir := filepath.Dir(path) if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("create directory %s: %w", dir, err) @@ -41,9 +49,19 @@ func WriteConfig(cfg *probodconfig.FullConfig, path string) error { pruned := pruneEmptyStrings(tree) - data, err = yaml.Marshal(pruned) - if err != nil { - return fmt.Errorf("marshal pruned config: %w", err) + switch format { + case FormatJSON: + data, err = json.MarshalIndent(pruned, "", " ") + if err != nil { + return fmt.Errorf("marshal pruned config as json: %w", err) + } + case FormatYAML: + data, err = yaml.Marshal(pruned) + if err != nil { + return fmt.Errorf("marshal pruned config as yaml: %w", err) + } + default: + return fmt.Errorf("unsupported config format: %q", format) } if err := os.WriteFile(path, data, 0600); err != nil { diff --git a/pkg/bootstrap/write_test.go b/pkg/bootstrap/write_test.go index 273e91e64..3b9476963 100644 --- a/pkg/bootstrap/write_test.go +++ b/pkg/bootstrap/write_test.go @@ -15,6 +15,7 @@ package bootstrap import ( + "encoding/json" "os" "path/filepath" "testing" @@ -39,7 +40,7 @@ func TestWriteConfig(t *testing.T) { }, } - err := WriteConfig(cfg, configPath) + err := WriteConfig(cfg, configPath, FormatYAML) require.NoError(t, err) data, err := os.ReadFile(configPath) @@ -63,7 +64,7 @@ func TestWriteConfig_CreatesDirectory(t *testing.T) { Probod: probodconfig.Config{BaseURL: "http://localhost:8080"}, } - err := WriteConfig(cfg, configPath) + err := WriteConfig(cfg, configPath, FormatYAML) require.NoError(t, err) _, err = os.Stat(configPath) @@ -76,7 +77,7 @@ func TestWriteConfig_FilePermissions(t *testing.T) { cfg := &probodconfig.FullConfig{} - err := WriteConfig(cfg, configPath) + err := WriteConfig(cfg, configPath, FormatYAML) require.NoError(t, err) info, err := os.Stat(configPath) @@ -108,7 +109,7 @@ func TestWriteConfig_DropsEmptyStrings(t *testing.T) { }, } - err := WriteConfig(cfg, configPath) + err := WriteConfig(cfg, configPath, FormatYAML) require.NoError(t, err) data, err := os.ReadFile(configPath) @@ -205,7 +206,7 @@ func TestWriteConfig_CompleteConfig(t *testing.T) { }, } - err := WriteConfig(cfg, configPath) + err := WriteConfig(cfg, configPath, FormatYAML) require.NoError(t, err) data, err := os.ReadFile(configPath) @@ -226,3 +227,52 @@ func TestWriteConfig_CompleteConfig(t *testing.T) { require.Len(t, loaded.Probod.Connectors, 1) assert.Equal(t, "SLACK", loaded.Probod.Connectors[0].Provider) } + +func TestWriteConfig_JSON(t *testing.T) { + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, "probod.json") + + cfg := &probodconfig.FullConfig{ + Unit: probodconfig.UnitConfig{ + Metrics: probodconfig.MetricsConfig{Addr: "localhost:9090"}, + }, + Probod: probodconfig.Config{ + BaseURL: "http://localhost:8080", + EncryptionKey: "", + }, + } + + err := WriteConfig(cfg, configPath, FormatJSON) + require.NoError(t, err) + + data, err := os.ReadFile(configPath) + require.NoError(t, err) + + var tree map[string]any + + err = json.Unmarshal(data, &tree) + require.NoError(t, err) + + probod, ok := tree["probod"].(map[string]any) + require.True(t, ok) + assert.Equal(t, "http://localhost:8080", probod["base-url"]) + assert.NotContains(t, probod, "encryption-key") + + var loaded probodconfig.FullConfig + + err = json.Unmarshal(data, &loaded) + require.NoError(t, err) + + assert.Equal(t, cfg.Unit.Metrics.Addr, loaded.Unit.Metrics.Addr) + assert.Equal(t, cfg.Probod.BaseURL, loaded.Probod.BaseURL) +} + +func TestWriteConfig_UnsupportedFormat(t *testing.T) { + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, "probod.txt") + + cfg := &probodconfig.FullConfig{} + + err := WriteConfig(cfg, configPath, Format("toml")) + require.Error(t, err) +}