diff --git a/cli/cmd/beta_vault_secret.go b/cli/cmd/beta_vault_secret.go index 1803eb1d..a5115501 100644 --- a/cli/cmd/beta_vault_secret.go +++ b/cli/cmd/beta_vault_secret.go @@ -7,7 +7,7 @@ import ( "fmt" packageio "github.com/codesphere-cloud/cs-go/pkg/io" - "github.com/codesphere-cloud/oms/internal/installer" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/runtime" @@ -45,7 +45,8 @@ func (c *BetaVaultSecretCmd) RunE(_ *cobra.Command, _ []string) error { return fmt.Errorf("failed to create kubernetes client: %w", err) } - creator := installer.NewVaultSecretCreator(kubeClient) + creator := vault.NewVaultSecretCreator(kubeClient) + return creator.CreateSecretFromFile(c.cmd.Context(), c.Opts.VaultFile, c.Opts.AgeKeyPath, c.Opts.Namespace, c.Opts.SecretName) } diff --git a/cli/cmd/init_install_config_test.go b/cli/cmd/init_install_config_test.go index a073f498..0304d81d 100644 --- a/cli/cmd/init_install_config_test.go +++ b/cli/cmd/init_install_config_test.go @@ -13,6 +13,7 @@ import ( . "github.com/onsi/gomega" "github.com/codesphere-cloud/oms/internal/installer" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" ) @@ -193,7 +194,7 @@ codesphere: Expect(exec.Command("age-keygen", "-o", ageKeyPath).Run()).To(Succeed()) recipient, err := exec.Command("age-keygen", "-y", ageKeyPath).Output() Expect(err).NotTo(HaveOccurred()) - Expect(installer.EncryptFileWithSOPS(plaintextVaultPath, vaultFile.Name(), strings.TrimSpace(string(recipient)))).To(Succeed()) + Expect(vault.EncryptFileWithSOPS(plaintextVaultPath, vaultFile.Name(), strings.TrimSpace(string(recipient)))).To(Succeed()) previousAgeKeyFile, hadPreviousAgeKeyFile := os.LookupEnv("SOPS_AGE_KEY_FILE") Expect(os.Setenv("SOPS_AGE_KEY_FILE", ageKeyPath)).To(Succeed()) DeferCleanup(func() { diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index 674d60b3..f55dacc1 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -14,6 +14,7 @@ import ( "github.com/codesphere-cloud/oms/internal/installer" "github.com/codesphere-cloud/oms/internal/installer/argocd" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" "github.com/spf13/cobra" "go.yaml.in/yaml/v3" @@ -166,7 +167,7 @@ func prepareInstallConfig(opts *InstallCodesphereOpts, cm installer.ConfigManage return nil, files.RootConfig{}, func() {}, fmt.Errorf("no config.yaml input provided: at least one config file is required") } - store := installer.NewLazyVaultTemplatingSecretStore(opts.Vault, opts.PrivKey) + store := vault.NewLazyVaultTemplatingSecretStore(opts.Vault, opts.PrivKey) cleanupFns := []func(){} cleanup := func() { for i := len(cleanupFns) - 1; i >= 0; i-- { diff --git a/cli/cmd/install_codesphere_config_test.go b/cli/cmd/install_codesphere_config_test.go index cfb0f983..04f73077 100644 --- a/cli/cmd/install_codesphere_config_test.go +++ b/cli/cmd/install_codesphere_config_test.go @@ -12,6 +12,7 @@ import ( "github.com/codesphere-cloud/oms/internal/installer" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -149,7 +150,7 @@ pcApps: targetRevision: '{{ secret "pcAppsRevision" }}' `), 0644)).To(Succeed()) - vault := &files.InstallVault{ + testVault := &files.InstallVault{ Secrets: []files.SecretEntry{ {Name: "dcCity", File: &files.SecretFile{Content: "Templated City"}}, {Name: "baseDomain", File: &files.SecretFile{Content: "templated.example.com"}}, @@ -157,13 +158,13 @@ pcApps: {Name: "pcAppsRevision", File: &files.SecretFile{Content: "from-vault"}}, }, } - vaultYAML, err := vault.Marshal() + vaultYAML, err := testVault.Marshal() Expect(err).ToNot(HaveOccurred()) Expect(os.WriteFile(plaintextVaultPath, vaultYAML, 0600)).To(Succeed()) Expect(exec.Command("age-keygen", "-o", ageKeyPath).Run()).To(Succeed()) recipient, err := exec.Command("age-keygen", "-y", ageKeyPath).Output() Expect(err).ToNot(HaveOccurred()) - Expect(installer.EncryptFileWithSOPS(plaintextVaultPath, vaultPath, strings.TrimSpace(string(recipient)))).To(Succeed()) + Expect(vault.EncryptFileWithSOPS(plaintextVaultPath, vaultPath, strings.TrimSpace(string(recipient)))).To(Succeed()) opts := &InstallCodesphereOpts{ Configs: []string{basePath, overlayPath}, diff --git a/cli/cmd/install_codesphere_dependencies.go b/cli/cmd/install_codesphere_dependencies.go index caa4adf8..0ea33164 100644 --- a/cli/cmd/install_codesphere_dependencies.go +++ b/cli/cmd/install_codesphere_dependencies.go @@ -16,6 +16,7 @@ import ( argocdinstaller "github.com/codesphere-cloud/oms/internal/installer/argocd" "github.com/codesphere-cloud/oms/internal/installer/files" "github.com/codesphere-cloud/oms/internal/installer/secrets" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/system" "github.com/spf13/cobra" "k8s.io/client-go/rest" @@ -168,8 +169,8 @@ func (i *argoCDAndAppsInstall) syncVaultSecret() error { if err := secrets.EnsureServiceAccountTokens(i.vault); err != nil { return fmt.Errorf("failed to ensure service account tokens: %w", err) } - creator := installer.NewVaultSecretCreator(i.kubeClient) - if err := creator.CreateSecretFromVault(i.ctx, i.vault, installer.VaultSecretNamespace, installer.VaultSecretName); err != nil { + creator := vault.NewVaultSecretCreator(i.kubeClient) + if err := creator.CreateSecretFromVault(i.ctx, i.vault, vault.VaultSecretNamespace, vault.VaultSecretName); err != nil { return fmt.Errorf("failed to sync vault secret: %w", err) } return nil diff --git a/cli/cmd/install_k0s.go b/cli/cmd/install_k0s.go index 52a8baaa..e0e939de 100644 --- a/cli/cmd/install_k0s.go +++ b/cli/cmd/install_k0s.go @@ -15,6 +15,7 @@ import ( "github.com/codesphere-cloud/oms/internal/env" "github.com/codesphere-cloud/oms/internal/installer" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/portal" "github.com/codesphere-cloud/oms/internal/util" ) @@ -287,13 +288,13 @@ func (c *InstallK0sCmd) writeEncryptedVault(vaultYAML []byte) error { return fmt.Errorf("failed to write temporary vault file: %w", err) } - recipient, _, err := installer.ResolveAgeKey(c.Opts.VaultPrivKey, "") + recipient, _, err := vault.ResolveAgeKey(c.Opts.VaultPrivKey, "") if err != nil { _ = c.FileWriter.Remove(tmpPath) return fmt.Errorf("failed to resolve age key for vault rencryption: %w", err) } - if err := installer.EncryptFileWithSOPS(tmpPath, c.Opts.Vault, recipient); err != nil { + if err := vault.EncryptFileWithSOPS(tmpPath, c.Opts.Vault, recipient); err != nil { _ = c.FileWriter.Remove(tmpPath) return fmt.Errorf("failed to encrypt vault file: %w", err) } @@ -307,12 +308,12 @@ func (c *InstallK0sCmd) loadOrCreateVault() (*files.InstallVault, bool, error) { return &files.InstallVault{}, false, nil } - wasEncrypted, err := installer.IsSOPSEncryptedFile(c.Opts.Vault) + wasEncrypted, err := vault.IsSOPSEncryptedFile(c.Opts.Vault) if err != nil { return nil, false, fmt.Errorf("failed to check if vault is encrypted: %w", err) } - vault, err := installer.LoadVaultData(c.Opts.Vault, c.Opts.VaultPrivKey) + vault, err := vault.LoadVaultData(c.Opts.Vault, c.Opts.VaultPrivKey) if err != nil { return nil, false, fmt.Errorf("failed to load vault: %w", err) } diff --git a/cli/cmd/install_k0s_test.go b/cli/cmd/install_k0s_test.go index 3c65f937..118acb76 100644 --- a/cli/cmd/install_k0s_test.go +++ b/cli/cmd/install_k0s_test.go @@ -18,6 +18,7 @@ import ( "github.com/codesphere-cloud/oms/internal/env" "github.com/codesphere-cloud/oms/internal/installer" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" ) @@ -418,7 +419,7 @@ var _ = Describe("InstallK0sCmd", func() { Expect(err).NotTo(HaveOccurred(), string(encryptOut)) Expect(os.Remove(plainPath)).To(Succeed()) - encrypted, err := installer.IsSOPSEncryptedFile(vaultPath) + encrypted, err := vault.IsSOPSEncryptedFile(vaultPath) Expect(err).NotTo(HaveOccurred()) Expect(encrypted).To(BeTrue()) @@ -439,7 +440,7 @@ var _ = Describe("InstallK0sCmd", func() { Expect(err).NotTo(HaveOccurred()) // Verify the vault was re-encrypted after saving kubeconfig. - encrypted, err = installer.IsSOPSEncryptedFile(vaultPath) + encrypted, err = vault.IsSOPSEncryptedFile(vaultPath) Expect(err).NotTo(HaveOccurred()) Expect(encrypted).To(BeTrue(), "vault should be re-encrypted after saving kubeconfig") diff --git a/cli/cmd/install_openbao.go b/cli/cmd/install_openbao.go index f7f49776..b464fe99 100644 --- a/cli/cmd/install_openbao.go +++ b/cli/cmd/install_openbao.go @@ -22,6 +22,7 @@ import ( "github.com/spf13/cobra" "github.com/codesphere-cloud/oms/internal/installer" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" ) @@ -59,7 +60,7 @@ func (c *InstallOpenBaoCmd) RunE(_ *cobra.Command, _ []string) error { // Pass --age-key-file explicitly so ResolveAgeKey prefers it without // mutating the process environment. When empty, the normal // auto-discovery chain (env vars, default location, generation) applies. - recipient, keyPath, err := installer.ResolveAgeKey(c.Opts.AgeKeyFile, fallbackDir) + recipient, keyPath, err := vault.ResolveAgeKey(c.Opts.AgeKeyFile, fallbackDir) if err != nil { return fmt.Errorf("resolving age key: %w", err) } diff --git a/cli/cmd/template_config.go b/cli/cmd/template_config.go index 7cdd00b9..a0163480 100644 --- a/cli/cmd/template_config.go +++ b/cli/cmd/template_config.go @@ -9,7 +9,7 @@ import ( "github.com/codesphere-cloud/cs-go/pkg/io" "github.com/codesphere-cloud/oms/internal/configtemplating" - "github.com/codesphere-cloud/oms/internal/installer" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" "github.com/spf13/cobra" ) @@ -95,7 +95,7 @@ func (c *TemplateConfigCmd) Render() ([]byte, error) { return nil, fmt.Errorf("failed to read config file %s: %w", c.Opts.Config, err) } - store := installer.NewLazyVaultTemplatingSecretStore(c.Opts.Vault, c.Opts.AgeKey) + store := vault.NewLazyVaultTemplatingSecretStore(c.Opts.Vault, c.Opts.AgeKey) rendered, err := configtemplating.RenderInstallConfigTemplate(data, store) if err != nil { return nil, fmt.Errorf("failed to render config template: %w", err) diff --git a/cli/cmd/template_config_test.go b/cli/cmd/template_config_test.go index c107f20d..09b88402 100644 --- a/cli/cmd/template_config_test.go +++ b/cli/cmd/template_config_test.go @@ -11,8 +11,8 @@ import ( "strings" "github.com/codesphere-cloud/oms/cli/cmd" - "github.com/codesphere-cloud/oms/internal/installer" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -43,7 +43,7 @@ postgres: `), 0644)).To(Succeed()) Expect(exec.Command("age-keygen", "-o", ageKeyPath).Run()).To(Succeed()) - vault := &files.InstallVault{ + testVault := &files.InstallVault{ Secrets: []files.SecretEntry{ { Name: "codesphereLicenseKey", @@ -58,12 +58,12 @@ postgres: }, }, } - vaultYaml, err := vault.Marshal() + vaultYaml, err := testVault.Marshal() Expect(err).NotTo(HaveOccurred()) Expect(os.WriteFile(plaintextVaultPath, vaultYaml, 0600)).To(Succeed()) recipient, err := exec.Command("age-keygen", "-y", ageKeyPath).Output() Expect(err).NotTo(HaveOccurred()) - Expect(installer.EncryptFileWithSOPS(plaintextVaultPath, vaultPath, strings.TrimSpace(string(recipient)))).To(Succeed()) + Expect(vault.EncryptFileWithSOPS(plaintextVaultPath, vaultPath, strings.TrimSpace(string(recipient)))).To(Succeed()) rootCmd := cmd.GetRootCmd() var output bytes.Buffer diff --git a/cli/cmd/update_install_config_test.go b/cli/cmd/update_install_config_test.go index 3ead587d..5d1848b8 100644 --- a/cli/cmd/update_install_config_test.go +++ b/cli/cmd/update_install_config_test.go @@ -16,6 +16,7 @@ import ( "github.com/codesphere-cloud/oms/internal/installer" "github.com/codesphere-cloud/oms/internal/installer/files" "github.com/codesphere-cloud/oms/internal/installer/secrets" + "github.com/codesphere-cloud/oms/internal/installer/vault" ) func quoteYAMLString(s string) string { @@ -193,7 +194,7 @@ codesphere: Expect(exec.Command("age-keygen", "-o", ageKeyPath).Run()).To(Succeed()) recipient, err := exec.Command("age-keygen", "-y", ageKeyPath).Output() Expect(err).NotTo(HaveOccurred()) - Expect(installer.EncryptFileWithSOPS(plaintextVaultPath, vaultFile.Name(), strings.TrimSpace(string(recipient)))).To(Succeed()) + Expect(vault.EncryptFileWithSOPS(plaintextVaultPath, vaultFile.Name(), strings.TrimSpace(string(recipient)))).To(Succeed()) previousAgeKeyFile, hadPreviousAgeKeyFile := os.LookupEnv("SOPS_AGE_KEY_FILE") Expect(os.Setenv("SOPS_AGE_KEY_FILE", ageKeyPath)).To(Succeed()) DeferCleanup(func() { diff --git a/internal/bootstrap/local/local.go b/internal/bootstrap/local/local.go index 8e0ac35b..1eb00988 100644 --- a/internal/bootstrap/local/local.go +++ b/internal/bootstrap/local/local.go @@ -16,6 +16,7 @@ import ( "github.com/codesphere-cloud/oms/internal/installer" "github.com/codesphere-cloud/oms/internal/installer/argocd" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -384,7 +385,6 @@ func (b *LocalBootstrapper) ReadClusterCIDRs() (podCIDR string, serviceCIDR stri log.Printf("can't read service CIDR from cluster, trying proc filesystem next: %s", err) serviceCIDR, err = b.readServiceCIDRFromProc() - if err != nil { err = fmt.Errorf("failed to determine service CIDR: %w", err) } @@ -518,7 +518,7 @@ func (b *LocalBootstrapper) EnsureSecrets() error { } func (b *LocalBootstrapper) ResolveAgeKey() error { - recipient, keyPath, err := installer.ResolveAgeKey("", filepath.Dir(b.Env.SecretsFilePath)) + recipient, keyPath, err := vault.ResolveAgeKey("", filepath.Dir(b.Env.SecretsFilePath)) if err != nil { return fmt.Errorf("failed to resolve age key: %w", err) } @@ -655,12 +655,12 @@ func (b *LocalBootstrapper) UpdateInstallConfig() (err error) { if err := b.icg.WriteVault(b.Env.SecretsFilePath, true); err != nil { return fmt.Errorf("failed to write vault file: %w", err) } - if err := installer.EncryptFileWithSOPS(b.Env.SecretsFilePath, filepath.Join(b.Env.InstallConfig.Secrets.BaseDir, "prod.vault.yaml"), b.ageRecipient); err != nil { + if err := vault.EncryptFileWithSOPS(b.Env.SecretsFilePath, filepath.Join(b.Env.InstallConfig.Secrets.BaseDir, "prod.vault.yaml"), b.ageRecipient); err != nil { return fmt.Errorf("failed to encrypt vault file: %w", err) } - creator := installer.NewVaultSecretCreator(b.kubeClient) - if err := creator.CreateSecretFromVault(b.ctx, b.icg.GetVault(), installer.VaultSecretNamespace, installer.VaultSecretName); err != nil { + creator := vault.NewVaultSecretCreator(b.kubeClient) + if err := creator.CreateSecretFromVault(b.ctx, b.icg.GetVault(), vault.VaultSecretNamespace, vault.VaultSecretName); err != nil { return fmt.Errorf("failed to create vault secret: %w", err) } diff --git a/internal/installer/cluster_admin.go b/internal/installer/cluster_admin.go index 6e85eb7f..448d07a6 100644 --- a/internal/installer/cluster_admin.go +++ b/internal/installer/cluster_admin.go @@ -11,6 +11,7 @@ import ( "github.com/codesphere-cloud/oms/internal/clusteradmin" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -22,9 +23,11 @@ func ResolveVaultPath(vaultPath string, config files.RootConfig) (string, error) if strings.TrimSpace(vaultPath) != "" { return vaultPath, nil } + if strings.TrimSpace(config.Secrets.BaseDir) == "" { return "", fmt.Errorf("vault path is not set and config.yaml secrets.baseDir is empty") } + return filepath.Join(config.Secrets.BaseDir, "prod.vault.yaml"), nil } @@ -36,18 +39,22 @@ func VaultAndRESTConfig(vaultPath, privKey string, cfg files.RootConfig) (*files if err != nil { return nil, nil, err } - vault, err := LoadVaultData(resolvedPath, privKey) + + vault, err := vault.LoadVaultData(resolvedPath, privKey) if err != nil { return nil, nil, fmt.Errorf("failed to load vault %s: %w", resolvedPath, err) } + kubeConfigContent, err := kubeConfigContentFromVault(vault) if err != nil { return nil, nil, err } + restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeConfigContent)) if err != nil { return nil, nil, fmt.Errorf("failed to load kubernetes config from vault: %w", err) } + return vault, restConfig, nil } @@ -55,10 +62,12 @@ func kubeConfigContentFromVault(vault *files.InstallVault) (string, error) { if vault == nil { return "", fmt.Errorf("vault is not loaded") } + kubeConfig := vault.GetSecret(files.SecretKubeConfig) if kubeConfig == nil || kubeConfig.File == nil || strings.TrimSpace(kubeConfig.File.Content) == "" { return "", fmt.Errorf("kubeconfig not found in vault (secret %q)", files.SecretKubeConfig) } + return kubeConfig.File.Content, nil } diff --git a/internal/installer/codesphere.go b/internal/installer/codesphere.go index 6027b35c..4d8e2ed7 100644 --- a/internal/installer/codesphere.go +++ b/internal/installer/codesphere.go @@ -17,6 +17,7 @@ import ( "github.com/codesphere-cloud/oms/internal/configtemplating" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/system" "github.com/codesphere-cloud/oms/internal/util" ) @@ -121,7 +122,7 @@ func (ci *CodesphereInstaller) prepareConfig(cm ConfigManager) (files.RootConfig } if ci.VaultPath != "" { - store := NewLazyVaultTemplatingSecretStore(ci.VaultPath, ci.PrivKey) + store := vault.NewLazyVaultTemplatingSecretStore(ci.VaultPath, ci.PrivKey) renderedConfig, renderCleanup, err := configtemplating.RenderConfigFileToTemp(ci.ConfigPath, store) if err != nil { return files.RootConfig{}, cleanup, fmt.Errorf("failed to render config template: %w", err) diff --git a/internal/installer/config_manager.go b/internal/installer/config_manager.go index 1d2731fe..bc118170 100644 --- a/internal/installer/config_manager.go +++ b/internal/installer/config_manager.go @@ -11,6 +11,7 @@ import ( "github.com/codesphere-cloud/oms/internal/configtemplating" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" "github.com/codesphere-cloud/oms/internal/util" ) @@ -68,7 +69,7 @@ func (g *InstallConfig) LoadInstallConfigFromFile(configPath string) error { return fmt.Errorf("failed to read %s: %w", configPath, err) } - store := NewVaultTemplatingSecretStore(g.Vault) + store := vault.NewVaultTemplatingSecretStore(g.Vault) data, err = configtemplating.RenderInstallConfigTemplate(data, store) if err != nil { return err @@ -84,7 +85,7 @@ func (g *InstallConfig) LoadInstallConfigFromFile(configPath string) error { } func (g *InstallConfig) LoadVaultFromFile(vaultPath string) error { - vault, err := LoadVaultData(vaultPath, "") + vault, err := vault.LoadVaultData(vaultPath, "") if err != nil { return err } diff --git a/internal/installer/config_template_test.go b/internal/installer/config_template_test.go index 17d3e918..2ef22dba 100644 --- a/internal/installer/config_template_test.go +++ b/internal/installer/config_template_test.go @@ -14,15 +14,25 @@ import ( . "github.com/onsi/gomega" "github.com/codesphere-cloud/oms/internal/configtemplating" - "github.com/codesphere-cloud/oms/internal/installer" "github.com/codesphere-cloud/oms/internal/installer/files" + "github.com/codesphere-cloud/oms/internal/installer/vault" ) +func sopsAndAgeAvailable() bool { + if _, err := exec.LookPath("sops"); err != nil { + return false + } + if _, err := exec.LookPath("age-keygen"); err != nil { + return false + } + return true +} + var _ = Describe("Config templating", func() { - var vault *files.InstallVault + var installVault *files.InstallVault BeforeEach(func() { - vault = &files.InstallVault{ + installVault = &files.InstallVault{ Secrets: []files.SecretEntry{ { Name: "apiToken", @@ -45,7 +55,7 @@ var _ = Describe("Config templating", func() { It("renders secret values from the vault dynamically", func() { rendered, err := configtemplating.RenderInstallConfigTemplate( []byte(`password: "{{ secret "apiToken" }}"`), - installer.NewVaultTemplatingSecretStore(vault), + vault.NewVaultTemplatingSecretStore(installVault), ) Expect(err).NotTo(HaveOccurred()) @@ -55,7 +65,7 @@ var _ = Describe("Config templating", func() { It("supports selecting specific secret fields", func() { rendered, err := configtemplating.RenderInstallConfigTemplate( []byte(`username: "{{ secret "apiToken" "fields.username" }}"`), - installer.NewVaultTemplatingSecretStore(vault), + vault.NewVaultTemplatingSecretStore(installVault), ) Expect(err).NotTo(HaveOccurred()) @@ -65,7 +75,7 @@ var _ = Describe("Config templating", func() { It("returns an error for missing secrets", func() { _, err := configtemplating.RenderInstallConfigTemplate( []byte(`password: "{{ secret "missing" }}"`), - installer.NewVaultTemplatingSecretStore(vault), + vault.NewVaultTemplatingSecretStore(installVault), ) Expect(err).To(HaveOccurred()) @@ -90,17 +100,17 @@ codesphere: apiToken: "{{ secret "apiToken" }}" `, tempDir) Expect(os.WriteFile(configPath, []byte(configYaml), 0644)).To(Succeed()) - vaultYaml, err := vault.Marshal() + vaultYaml, err := installVault.Marshal() Expect(err).NotTo(HaveOccurred()) Expect(os.WriteFile(plaintextVaultPath, vaultYaml, 0600)).To(Succeed()) Expect(exec.Command("age-keygen", "-o", ageKeyPath).Run()).To(Succeed()) recipient, err := exec.Command("age-keygen", "-y", ageKeyPath).Output() Expect(err).NotTo(HaveOccurred()) - Expect(installer.EncryptFileWithSOPS(plaintextVaultPath, vaultPath, strings.TrimSpace(string(recipient)))).To(Succeed()) + Expect(vault.EncryptFileWithSOPS(plaintextVaultPath, vaultPath, strings.TrimSpace(string(recipient)))).To(Succeed()) renderedPath, cleanup, err := configtemplating.RenderConfigFileToTemp( configPath, - installer.NewLazyVaultTemplatingSecretStore(vaultPath, ageKeyPath), + vault.NewLazyVaultTemplatingSecretStore(vaultPath, ageKeyPath), ) defer cleanup() Expect(err).NotTo(HaveOccurred()) @@ -114,11 +124,11 @@ codesphere: tempDir := GinkgoT().TempDir() vaultPath := filepath.Join(tempDir, "prod.vault.yaml") - vaultYaml, err := vault.Marshal() + vaultYaml, err := installVault.Marshal() Expect(err).NotTo(HaveOccurred()) Expect(os.WriteFile(vaultPath, vaultYaml, 0600)).To(Succeed()) - _, err = installer.LoadVaultData(vaultPath, "") + _, err = vault.LoadVaultData(vaultPath, "") Expect(err).ToNot(HaveOccurred()) }) diff --git a/internal/installer/openbao.go b/internal/installer/openbao.go index 88cbdaed..88575089 100644 --- a/internal/installer/openbao.go +++ b/internal/installer/openbao.go @@ -18,6 +18,7 @@ import ( "time" "github.com/codesphere-cloud/oms/internal/bootstrap" + "github.com/codesphere-cloud/oms/internal/installer/vault" k8s "github.com/codesphere-cloud/oms/internal/util" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -279,7 +280,7 @@ func (o *OpenBaoInstaller) PreFlightDRCheck() error { o.Logger.Logf("Found existing DR backup at %s", o.Config.DRBackupPath) - decrypted, err := DecryptFileWithSOPS(o.Config.DRBackupPath, o.Config.AgeKeyPath) + decrypted, err := vault.DecryptFileWithSOPS(o.Config.DRBackupPath, o.Config.AgeKeyPath) if err != nil { return err } @@ -686,7 +687,7 @@ func (o *OpenBaoInstaller) ExtractAndEncrypt() error { return fmt.Errorf("closing temp backup file: %w", err) } - if err := EncryptFileWithSOPS(tmpPath, o.Config.DRBackupPath, o.Config.AgeRecipient); err != nil { + if err := vault.EncryptFileWithSOPS(tmpPath, o.Config.DRBackupPath, o.Config.AgeRecipient); err != nil { return fmt.Errorf("encrypting DR backup: %w", err) } diff --git a/internal/installer/vault_encryption.go b/internal/installer/vault/vault_encryption.go similarity index 99% rename from internal/installer/vault_encryption.go rename to internal/installer/vault/vault_encryption.go index 321ddab7..67e6c3e9 100644 --- a/internal/installer/vault_encryption.go +++ b/internal/installer/vault/vault_encryption.go @@ -1,7 +1,7 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer +package vault import ( "fmt" @@ -17,9 +17,7 @@ import ( "go.yaml.in/yaml/v3" ) -var ( - xdgConfigHome = "XDG_CONFIG_HOME" -) +var xdgConfigHome = "XDG_CONFIG_HOME" // ResolveAgeKey resolves an existing age key or generates a new one. // diff --git a/internal/installer/vault_encryption_test.go b/internal/installer/vault/vault_encryption_test.go similarity index 89% rename from internal/installer/vault_encryption_test.go rename to internal/installer/vault/vault_encryption_test.go index f40ea89a..ba392b56 100644 --- a/internal/installer/vault_encryption_test.go +++ b/internal/installer/vault/vault_encryption_test.go @@ -1,7 +1,7 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer_test +package vault_test import ( "os" @@ -11,7 +11,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/codesphere-cloud/oms/internal/installer" + "github.com/codesphere-cloud/oms/internal/installer/vault" ) func sopsAndAgeAvailable() bool { @@ -73,14 +73,14 @@ var _ = Describe("VaultEncryption", func() { // Set conflicting env vars to prove the explicit file takes priority. Expect(os.Setenv("SOPS_AGE_KEY_FILE", filepath.Join(tmpDir, "ignored.txt"))).To(Succeed()) - recipient, keyPath, err := installer.ResolveAgeKey(keyFile, tmpDir) + recipient, keyPath, err := vault.ResolveAgeKey(keyFile, tmpDir) Expect(err).ToNot(HaveOccurred()) Expect(recipient).To(HavePrefix("age1")) Expect(keyPath).To(Equal(keyFile)) }) It("returns an error if the explicit file does not exist", func() { - _, _, err := installer.ResolveAgeKey(filepath.Join(tmpDir, "missing.txt"), tmpDir) + _, _, err := vault.ResolveAgeKey(filepath.Join(tmpDir, "missing.txt"), tmpDir) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("failed to read age key")) }) @@ -111,7 +111,7 @@ var _ = Describe("VaultEncryption", func() { Expect(os.Setenv("SOPS_AGE_KEY", privKeyLine)).To(Succeed()) - recipient, keyPath, err := installer.ResolveAgeKey("", tmpDir) + recipient, keyPath, err := vault.ResolveAgeKey("", tmpDir) Expect(err).ToNot(HaveOccurred()) Expect(recipient).To(HavePrefix("age1")) Expect(keyPath).To(BeEmpty()) @@ -129,7 +129,7 @@ var _ = Describe("VaultEncryption", func() { Expect(os.Setenv("SOPS_AGE_KEY_FILE", keyFile)).To(Succeed()) - recipient, keyPath, err := installer.ResolveAgeKey("", tmpDir) + recipient, keyPath, err := vault.ResolveAgeKey("", tmpDir) Expect(err).ToNot(HaveOccurred()) Expect(recipient).To(HavePrefix("age1")) Expect(keyPath).To(Equal(keyFile)) @@ -138,7 +138,7 @@ var _ = Describe("VaultEncryption", func() { It("should return error if the file does not exist", func() { Expect(os.Setenv("SOPS_AGE_KEY_FILE", filepath.Join(tmpDir, "nonexistent.txt"))).To(Succeed()) - _, _, err := installer.ResolveAgeKey("", tmpDir) + _, _, err := vault.ResolveAgeKey("", tmpDir) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("failed to read age key")) }) @@ -150,7 +150,7 @@ var _ = Describe("VaultEncryption", func() { Skip("age-keygen not available") } - recipient, keyPath, err := installer.ResolveAgeKey("", tmpDir) + recipient, keyPath, err := vault.ResolveAgeKey("", tmpDir) Expect(err).ToNot(HaveOccurred()) Expect(recipient).To(HavePrefix("age1")) Expect(keyPath).To(Equal(filepath.Join(tmpDir, "age_key.txt"))) @@ -178,7 +178,7 @@ var _ = Describe("VaultEncryption", func() { path := filepath.Join(tmpDir, "plain.yaml") Expect(os.WriteFile(path, []byte("key: value\n"), 0644)).To(Succeed()) - encrypted, err := installer.IsSOPSEncryptedFile(path) + encrypted, err := vault.IsSOPSEncryptedFile(path) Expect(err).ToNot(HaveOccurred()) Expect(encrypted).To(BeFalse()) }) @@ -187,7 +187,7 @@ var _ = Describe("VaultEncryption", func() { path := filepath.Join(tmpDir, "sops.yaml") Expect(os.WriteFile(path, []byte("sops:\n age: age1abc\n"), 0644)).To(Succeed()) - encrypted, err := installer.IsSOPSEncryptedFile(path) + encrypted, err := vault.IsSOPSEncryptedFile(path) Expect(err).ToNot(HaveOccurred()) Expect(encrypted).To(BeTrue()) }) @@ -196,14 +196,14 @@ var _ = Describe("VaultEncryption", func() { path := filepath.Join(tmpDir, "empty.yaml") Expect(os.WriteFile(path, []byte{}, 0644)).To(Succeed()) - encrypted, err := installer.IsSOPSEncryptedFile(path) + encrypted, err := vault.IsSOPSEncryptedFile(path) Expect(err).ToNot(HaveOccurred()) Expect(encrypted).To(BeFalse()) }) It("returns an error for a non-existent file", func() { path := filepath.Join(tmpDir, "missing.yaml") - _, err := installer.IsSOPSEncryptedFile(path) + _, err := vault.IsSOPSEncryptedFile(path) Expect(err).To(HaveOccurred()) }) }) @@ -226,7 +226,7 @@ var _ = Describe("VaultEncryption", func() { plainYAML := "secrets:\n - name: test-secret\n fields:\n password: hunter2\n" Expect(os.WriteFile(vaultPath, []byte(plainYAML), 0644)).To(Succeed()) - vault, err := installer.LoadVaultData(vaultPath, "") + vault, err := vault.LoadVaultData(vaultPath, "") Expect(err).ToNot(HaveOccurred()) Expect(vault.Secrets).To(HaveLen(1)) Expect(vault.Secrets[0].Name).To(Equal("test-secret")) @@ -238,7 +238,7 @@ var _ = Describe("VaultEncryption", func() { wrappedYAML := "data: |\n secrets:\n - name: test-secret\n fields:\n password: hunter2\n" Expect(os.WriteFile(vaultPath, []byte(wrappedYAML), 0644)).To(Succeed()) - vault, err := installer.LoadVaultData(vaultPath, "") + vault, err := vault.LoadVaultData(vaultPath, "") Expect(err).ToNot(HaveOccurred()) Expect(vault.Secrets).To(HaveLen(1)) Expect(vault.Secrets[0].Name).To(Equal("test-secret")) @@ -256,7 +256,7 @@ var _ = Describe("VaultEncryption", func() { Expect(err).ToNot(HaveOccurred(), string(out)) // Extract the public key (recipient). - recipient, _, err := installer.ResolveAgeKey(ageKeyPath, tmpDir) + recipient, _, err := vault.ResolveAgeKey(ageKeyPath, tmpDir) Expect(err).ToNot(HaveOccurred()) // Write a plain vault file. @@ -272,7 +272,7 @@ var _ = Describe("VaultEncryption", func() { Expect(err).ToNot(HaveOccurred(), string(encOut)) // LoadVaultData should detect SOPS, decrypt, unwrap data: |, and parse. - vault, err := installer.LoadVaultData(vaultPath, ageKeyPath) + vault, err := vault.LoadVaultData(vaultPath, ageKeyPath) Expect(err).ToNot(HaveOccurred()) Expect(vault.Secrets).To(HaveLen(1)) Expect(vault.Secrets[0].Name).To(Equal("sops-secret")) @@ -280,7 +280,7 @@ var _ = Describe("VaultEncryption", func() { }) It("returns an error for a non-existent file", func() { - _, err := installer.LoadVaultData(filepath.Join(tmpDir, "missing.yaml"), "") + _, err := vault.LoadVaultData(filepath.Join(tmpDir, "missing.yaml"), "") Expect(err).To(HaveOccurred()) }) }) diff --git a/internal/installer/vault_encryption_unexported_test.go b/internal/installer/vault/vault_encryption_unexported_test.go similarity index 98% rename from internal/installer/vault_encryption_unexported_test.go rename to internal/installer/vault/vault_encryption_unexported_test.go index 2697450f..e83e67ca 100644 --- a/internal/installer/vault_encryption_unexported_test.go +++ b/internal/installer/vault/vault_encryption_unexported_test.go @@ -1,7 +1,7 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer +package vault import ( . "github.com/onsi/ginkgo/v2" diff --git a/internal/installer/vault_secret_creator.go b/internal/installer/vault/vault_secret_creator.go similarity index 99% rename from internal/installer/vault_secret_creator.go rename to internal/installer/vault/vault_secret_creator.go index dda0a4a8..67951a5a 100644 --- a/internal/installer/vault_secret_creator.go +++ b/internal/installer/vault/vault_secret_creator.go @@ -1,7 +1,7 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer +package vault import ( "context" diff --git a/internal/installer/vault_secret_creator_test.go b/internal/installer/vault/vault_secret_creator_test.go similarity index 99% rename from internal/installer/vault_secret_creator_test.go rename to internal/installer/vault/vault_secret_creator_test.go index 2f6531ac..a2dc9974 100644 --- a/internal/installer/vault_secret_creator_test.go +++ b/internal/installer/vault/vault_secret_creator_test.go @@ -1,7 +1,7 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer +package vault import ( "github.com/codesphere-cloud/oms/internal/installer/files" diff --git a/internal/installer/vault_templating_secret_store.go b/internal/installer/vault/vault_templating_secret_store.go similarity index 99% rename from internal/installer/vault_templating_secret_store.go rename to internal/installer/vault/vault_templating_secret_store.go index 4bcbd500..15fba1b4 100644 --- a/internal/installer/vault_templating_secret_store.go +++ b/internal/installer/vault/vault_templating_secret_store.go @@ -1,7 +1,7 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer +package vault import ( "errors"