From 852ffe398423b58f8a4482eea67885a28ad97305 Mon Sep 17 00:00:00 2001 From: OliverTrautvetter <66372584+OliverTrautvetter@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:59:21 +0200 Subject: [PATCH 1/4] fix(vault): trim trailing newlines from kubeconfig before storing in vault add unwrapSOPSData function to handle SOPS data extraction --- cli/cmd/install_k0s.go | 2 + cli/cmd/install_k0s_test.go | 32 ++++++++++++++-- internal/installer/export_test.go | 2 + internal/installer/vault_encryption.go | 2 +- internal/installer/vault_encryption_test.go | 38 +++++++++++++++++++ .../vault_templating_secret_store.go | 26 +++++++++++++ 6 files changed, 98 insertions(+), 4 deletions(-) diff --git a/cli/cmd/install_k0s.go b/cli/cmd/install_k0s.go index c5f3f040..5a5cd741 100644 --- a/cli/cmd/install_k0s.go +++ b/cli/cmd/install_k0s.go @@ -7,6 +7,7 @@ import ( "fmt" "log" "path/filepath" + "strings" packageio "github.com/codesphere-cloud/cs-go/pkg/io" "github.com/spf13/cobra" @@ -236,6 +237,7 @@ func (c *InstallK0sCmd) saveKubeconfigToVault(k0sctl installer.K0sctlManager, k0 if err != nil { return fmt.Errorf("failed to retrieve kubeconfig from k0sctl: %w", err) } + kubeconfigContent = strings.TrimRight(kubeconfigContent, "\n") vault, wasEncrypted, err := c.loadOrCreateVault() if err != nil { diff --git a/cli/cmd/install_k0s_test.go b/cli/cmd/install_k0s_test.go index aa21eec0..1eb70420 100644 --- a/cli/cmd/install_k0s_test.go +++ b/cli/cmd/install_k0s_test.go @@ -258,7 +258,9 @@ var _ = Describe("InstallK0sCmd", func() { setupCommonMocks() mockK0sctl.EXPECT().GetKubeconfig(mock.Anything, "/tmp/k0sctl").Return("apiVersion: v1\nkind: Config\n", nil) mockFileWriter.EXPECT().Exists(c.Opts.Vault).Return(false) - mockFileWriter.EXPECT().WriteFile(c.Opts.Vault, mock.Anything, os.FileMode(0600)).Return(nil) + mockFileWriter.EXPECT().WriteFile(c.Opts.Vault, mock.MatchedBy(func(data []byte) bool { + return !strings.Contains(string(data), "content: |+") + }), os.FileMode(0600)).Return(nil) err := c.InstallK0s(mockPM, mockK0s, mockK0sctl) Expect(err).NotTo(HaveOccurred()) @@ -290,7 +292,9 @@ var _ = Describe("InstallK0sCmd", func() { setupCommonMocks() mockK0sctl.EXPECT().GetKubeconfig(mock.Anything, "/tmp/k0sctl").Return("apiVersion: v1\nkind: Config\n", nil) mockFileWriter.EXPECT().Exists(c.Opts.Vault).Return(true) - mockFileWriter.EXPECT().WriteFile(c.Opts.Vault, mock.Anything, os.FileMode(0600)).Return(nil) + mockFileWriter.EXPECT().WriteFile(c.Opts.Vault, mock.MatchedBy(func(data []byte) bool { + return !strings.Contains(string(data), "content: |+") + }), os.FileMode(0600)).Return(nil) err = c.InstallK0s(mockPM, mockK0s, mockK0sctl) Expect(err).NotTo(HaveOccurred()) @@ -322,12 +326,34 @@ var _ = Describe("InstallK0sCmd", func() { setupCommonMocks() mockK0sctl.EXPECT().GetKubeconfig(mock.Anything, "/tmp/k0sctl").Return("apiVersion: v1\nkind: Config\nnew: true\n", nil) mockFileWriter.EXPECT().Exists(c.Opts.Vault).Return(true) - mockFileWriter.EXPECT().WriteFile(c.Opts.Vault, mock.Anything, os.FileMode(0600)).Return(nil) + mockFileWriter.EXPECT().WriteFile(c.Opts.Vault, mock.MatchedBy(func(data []byte) bool { + return !strings.Contains(string(data), "content: |+") + }), os.FileMode(0600)).Return(nil) err = c.InstallK0s(mockPM, mockK0s, mockK0sctl) Expect(err).NotTo(HaveOccurred()) }) + It("trims trailing newlines from kubeconfig before storing in vault", func() { + c.Opts.InstallConfig = writeTestConfig(createTestConfig(true)) + c.Opts.Package = "test-package.tar.gz" + c.Opts.Version = "v1.30.0+k0s.0" + c.Opts.Vault = filepath.Join(tempDir, "prod.vault.yaml") + + setupCommonMocks() + // kubeconfig with multiple trailing newlines — should be stripped + mockK0sctl.EXPECT().GetKubeconfig(mock.Anything, "/tmp/k0sctl"). + Return("apiVersion: v1\nkind: Config\n\n\n", nil) + mockFileWriter.EXPECT().Exists(c.Opts.Vault).Return(false) + mockFileWriter.EXPECT().WriteFile(c.Opts.Vault, mock.MatchedBy(func(data []byte) bool { + // Must not contain |+ chomping — trailing newlines should be stripped + return !strings.Contains(string(data), "content: |+") + }), os.FileMode(0600)).Return(nil) + + err := c.InstallK0s(mockPM, mockK0s, mockK0sctl) + Expect(err).NotTo(HaveOccurred()) + }) + It("fails when GetKubeconfig fails", func() { c.Opts.InstallConfig = writeTestConfig(createTestConfig(true)) c.Opts.Package = "test-package.tar.gz" diff --git a/internal/installer/export_test.go b/internal/installer/export_test.go index 299969db..768c7265 100644 --- a/internal/installer/export_test.go +++ b/internal/installer/export_test.go @@ -64,3 +64,5 @@ func (o *OpenBaoInstaller) ValidateConfig() error { func (o *OpenBaoInstaller) ReadinessTimeout() time.Duration { return o.readinessTimeout() } + +var UnwrapSOPSData = unwrapSOPSData diff --git a/internal/installer/vault_encryption.go b/internal/installer/vault_encryption.go index 91a108b3..ddd609a7 100644 --- a/internal/installer/vault_encryption.go +++ b/internal/installer/vault_encryption.go @@ -158,7 +158,7 @@ func generateAgeKey(keyPath string) (string, error) { // EncryptFileWithSOPS encrypts src with SOPS+age and writes ciphertext to target. func EncryptFileWithSOPS(src, target, recipient string) error { - cmd := exec.Command("sops", "--encrypt", "--age", recipient, "--output", target, src) + cmd := exec.Command("sops", "--encrypt", "--input-type", "yaml", "--age", recipient, "--output", target, src) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("sops encrypt failed: %w: %s", err, out) diff --git a/internal/installer/vault_encryption_test.go b/internal/installer/vault_encryption_test.go index 8b64ebbd..40090240 100644 --- a/internal/installer/vault_encryption_test.go +++ b/internal/installer/vault_encryption_test.go @@ -14,6 +14,44 @@ import ( "github.com/codesphere-cloud/oms/internal/installer" ) +var _ = Describe("unwrapSOPSData", func() { + It("returns data unchanged when there is no data: wrapper", func() { + input := []byte("secrets:\n - name: foo\n fields:\n password: bar\n") + output := installer.UnwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) + + It("strips a top-level data: | wrapper and returns inner content", func() { + input := []byte("data: |\n secrets:\n - name: foo\n fields:\n password: bar\n") + output := installer.UnwrapSOPSData(input) + Expect(string(output)).To(Equal("secrets:\n - name: foo\n fields:\n password: bar\n")) + }) + + It("returns data unchanged for an empty document", func() { + input := []byte("") + output := installer.UnwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) + + It("returns data unchanged when root has multiple keys", func() { + input := []byte("data: some-value\nsops:\n key: val\n") + output := installer.UnwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) + + It("returns data unchanged for invalid YAML", func() { + input := []byte("not: valid: yaml: [[") + output := installer.UnwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) + + It("returns data unchanged when data is not a scalar", func() { + input := []byte("data:\n nested: value\n") + output := installer.UnwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) +}) + func sopsAndAgeAvailable() bool { if _, err := exec.LookPath("sops"); err != nil { return false diff --git a/internal/installer/vault_templating_secret_store.go b/internal/installer/vault_templating_secret_store.go index 42a7efcf..61f328cd 100644 --- a/internal/installer/vault_templating_secret_store.go +++ b/internal/installer/vault_templating_secret_store.go @@ -179,9 +179,35 @@ func isSOPSEncryptedYAML(data []byte) (bool, error) { } func parseVaultData(data []byte) (*files.InstallVault, error) { + data = unwrapSOPSData(data) + vault := &files.InstallVault{} if err := vault.Unmarshal(data); err != nil { return nil, err } return vault, nil } + +// unwrapSOPSData strips a top-level "data" literal block scalar wrapper if +// present. SOPS whole-file encryption stores the entire document +// under a data key. +func unwrapSOPSData(data []byte) []byte { + var doc yaml.Node + if err := yaml.Unmarshal(data, &doc); err != nil { + return data + } + if len(doc.Content) == 0 { + return data + } + root := doc.Content[0] + if root.Kind != yaml.MappingNode || len(root.Content) != 2 { + return data + } + keyNode := root.Content[0] + valNode := root.Content[1] + if keyNode.Value != "data" || valNode.Kind != yaml.ScalarNode { + return data + } + // The scalar value is the inner YAML content. + return []byte(valNode.Value) +} From a7faf3d67595977cd293e7bab397bb9bf725c950 Mon Sep 17 00:00:00 2001 From: OliverTrautvetter <66372584+OliverTrautvetter@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:53:16 +0200 Subject: [PATCH 2/4] fix(vault): enhance kubeconfig trimming and add unwrapSOPSData function for SOPS data handling --- cli/cmd/install_k0s.go | 2 +- cli/cmd/install_k0s_test.go | 15 +++- internal/installer/vault_encryption.go | 27 ++++++- internal/installer/vault_encryption_test.go | 77 +++++++++++++++++++ .../vault_templating_secret_store.go | 24 ------ 5 files changed, 118 insertions(+), 27 deletions(-) diff --git a/cli/cmd/install_k0s.go b/cli/cmd/install_k0s.go index 5a5cd741..16f2191e 100644 --- a/cli/cmd/install_k0s.go +++ b/cli/cmd/install_k0s.go @@ -237,7 +237,7 @@ func (c *InstallK0sCmd) saveKubeconfigToVault(k0sctl installer.K0sctlManager, k0 if err != nil { return fmt.Errorf("failed to retrieve kubeconfig from k0sctl: %w", err) } - kubeconfigContent = strings.TrimRight(kubeconfigContent, "\n") + kubeconfigContent = strings.TrimRight(kubeconfigContent, "\n\r") vault, wasEncrypted, err := c.loadOrCreateVault() if err != nil { diff --git a/cli/cmd/install_k0s_test.go b/cli/cmd/install_k0s_test.go index 1eb70420..3c65f937 100644 --- a/cli/cmd/install_k0s_test.go +++ b/cli/cmd/install_k0s_test.go @@ -347,7 +347,20 @@ var _ = Describe("InstallK0sCmd", func() { mockFileWriter.EXPECT().Exists(c.Opts.Vault).Return(false) mockFileWriter.EXPECT().WriteFile(c.Opts.Vault, mock.MatchedBy(func(data []byte) bool { // Must not contain |+ chomping — trailing newlines should be stripped - return !strings.Contains(string(data), "content: |+") + if strings.Contains(string(data), "content: |+") { + return false + } + // Verify the stored kubeconfig has no trailing newlines + var vault files.InstallVault + if err := yaml.Unmarshal(data, &vault); err != nil { + return false + } + for _, s := range vault.Secrets { + if s.Name == "kubeConfig" && s.File != nil { + return s.File.Content == "apiVersion: v1\nkind: Config" + } + } + return false }), os.FileMode(0600)).Return(nil) err := c.InstallK0s(mockPM, mockK0s, mockK0sctl) diff --git a/internal/installer/vault_encryption.go b/internal/installer/vault_encryption.go index ddd609a7..321ddab7 100644 --- a/internal/installer/vault_encryption.go +++ b/internal/installer/vault_encryption.go @@ -14,6 +14,7 @@ import ( "filippo.io/age" sopsage "github.com/getsops/sops/v3/age" + "go.yaml.in/yaml/v3" ) var ( @@ -169,7 +170,7 @@ func EncryptFileWithSOPS(src, target, recipient string) error { // DecryptFileWithSOPS decrypts a SOPS-encrypted file and returns the plaintext bytes. // If keyPath is non-empty, SOPS_AGE_KEY_FILE is set for the sops process. func DecryptFileWithSOPS(src, keyPath string) ([]byte, error) { - cmd := exec.Command("sops", "--decrypt", src) + cmd := exec.Command("sops", "--decrypt", "--input-type", "yaml", src) if keyPath != "" { cmd.Env = append(os.Environ(), "SOPS_AGE_KEY_FILE="+keyPath) } @@ -182,3 +183,27 @@ func DecryptFileWithSOPS(src, keyPath string) ([]byte, error) { } return out, nil } + +// unwrapSOPSData strips a top-level "data" literal block scalar wrapper if +// present. When SOPS encrypts with --input-type yaml, it +// wraps the entire document under a data: | key. +func unwrapSOPSData(data []byte) []byte { + var doc yaml.Node + if err := yaml.Unmarshal(data, &doc); err != nil { + return data + } + if len(doc.Content) == 0 { + return data + } + root := doc.Content[0] + if root.Kind != yaml.MappingNode || len(root.Content) != 2 { + return data + } + keyNode := root.Content[0] + valNode := root.Content[1] + if keyNode.Value != "data" || valNode.Kind != yaml.ScalarNode { + return data + } + // The scalar value is the inner YAML content. + return []byte(valNode.Value) +} diff --git a/internal/installer/vault_encryption_test.go b/internal/installer/vault_encryption_test.go index 40090240..2dd0f160 100644 --- a/internal/installer/vault_encryption_test.go +++ b/internal/installer/vault_encryption_test.go @@ -245,6 +245,83 @@ var _ = Describe("VaultEncryption", func() { Expect(err).To(HaveOccurred()) }) }) + + Describe("LoadVaultData", func() { + var tmpDir string + + BeforeEach(func() { + var err error + tmpDir, err = os.MkdirTemp("", "load-vault-test-*") + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + Expect(os.RemoveAll(tmpDir)).To(Succeed()) + }) + + It("parses a plain vault file without data: wrapper", func() { + vaultPath := filepath.Join(tmpDir, "plain.vault.yaml") + 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, "") + Expect(err).ToNot(HaveOccurred()) + Expect(vault.Secrets).To(HaveLen(1)) + Expect(vault.Secrets[0].Name).To(Equal("test-secret")) + Expect(vault.Secrets[0].Fields.Password).To(Equal("hunter2")) + }) + + It("unwraps a plain file with data: | wrapper (SOPS whole-file format edge case)", func() { + vaultPath := filepath.Join(tmpDir, "wrapped.vault.yaml") + 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, "") + Expect(err).ToNot(HaveOccurred()) + Expect(vault.Secrets).To(HaveLen(1)) + Expect(vault.Secrets[0].Name).To(Equal("test-secret")) + Expect(vault.Secrets[0].Fields.Password).To(Equal("hunter2")) + }) + + It("loads and decrypts a SOPS-encrypted vault end-to-end", func() { + if !sopsAndAgeAvailable() { + Skip("sops and age-keygen not available") + } + + // Generate an age keypair. + ageKeyPath := filepath.Join(tmpDir, "age_key.txt") + out, err := exec.Command("age-keygen", "-o", ageKeyPath).CombinedOutput() + Expect(err).ToNot(HaveOccurred(), string(out)) + + // Extract the public key (recipient). + recipient, _, err := installer.ResolveAgeKey(ageKeyPath, tmpDir) + Expect(err).ToNot(HaveOccurred()) + + // Write a plain vault file. + plainPath := filepath.Join(tmpDir, "plain.vault.yaml") + plainYAML := "secrets:\n - name: sops-secret\n fields:\n password: s3cr3t\n" + Expect(os.WriteFile(plainPath, []byte(plainYAML), 0644)).To(Succeed()) + + // Encrypt with SOPS using --input-type yaml (whole-file mode, + // which wraps content under data: |). + vaultPath := filepath.Join(tmpDir, "encrypted.vault.yaml") + encryptCmd := exec.Command("sops", "--encrypt", "--input-type", "yaml", "--age", recipient, "--output", vaultPath, plainPath) + encOut, err := encryptCmd.CombinedOutput() + Expect(err).ToNot(HaveOccurred(), string(encOut)) + + // LoadVaultData should detect SOPS, decrypt, unwrap data: |, and parse. + vault, err := installer.LoadVaultData(vaultPath, ageKeyPath) + Expect(err).ToNot(HaveOccurred()) + Expect(vault.Secrets).To(HaveLen(1)) + Expect(vault.Secrets[0].Name).To(Equal("sops-secret")) + Expect(vault.Secrets[0].Fields.Password).To(Equal("s3cr3t")) + }) + + It("returns an error for a non-existent file", func() { + _, err := installer.LoadVaultData(filepath.Join(tmpDir, "missing.yaml"), "") + Expect(err).To(HaveOccurred()) + }) + }) }) func splitLines(s string) []string { diff --git a/internal/installer/vault_templating_secret_store.go b/internal/installer/vault_templating_secret_store.go index 61f328cd..4bcbd500 100644 --- a/internal/installer/vault_templating_secret_store.go +++ b/internal/installer/vault_templating_secret_store.go @@ -187,27 +187,3 @@ func parseVaultData(data []byte) (*files.InstallVault, error) { } return vault, nil } - -// unwrapSOPSData strips a top-level "data" literal block scalar wrapper if -// present. SOPS whole-file encryption stores the entire document -// under a data key. -func unwrapSOPSData(data []byte) []byte { - var doc yaml.Node - if err := yaml.Unmarshal(data, &doc); err != nil { - return data - } - if len(doc.Content) == 0 { - return data - } - root := doc.Content[0] - if root.Kind != yaml.MappingNode || len(root.Content) != 2 { - return data - } - keyNode := root.Content[0] - valNode := root.Content[1] - if keyNode.Value != "data" || valNode.Kind != yaml.ScalarNode { - return data - } - // The scalar value is the inner YAML content. - return []byte(valNode.Value) -} From 1a9fa67c5bb22113620ee65ede005a309e695772 Mon Sep 17 00:00:00 2001 From: OliverTrautvetter <66372584+OliverTrautvetter@users.noreply.github.com> Date: Tue, 30 Jun 2026 16:40:30 +0200 Subject: [PATCH 3/4] ref(tests): move unwrapSOPSData tests to separate file and remove duplicates from vault encryption tests --- internal/installer/export_test.go | 2 - internal/installer/unwrapsops_data_test.go | 47 +++++++++++++++++++++ internal/installer/vault_encryption_test.go | 38 ----------------- 3 files changed, 47 insertions(+), 40 deletions(-) create mode 100644 internal/installer/unwrapsops_data_test.go diff --git a/internal/installer/export_test.go b/internal/installer/export_test.go index 768c7265..299969db 100644 --- a/internal/installer/export_test.go +++ b/internal/installer/export_test.go @@ -64,5 +64,3 @@ func (o *OpenBaoInstaller) ValidateConfig() error { func (o *OpenBaoInstaller) ReadinessTimeout() time.Duration { return o.readinessTimeout() } - -var UnwrapSOPSData = unwrapSOPSData diff --git a/internal/installer/unwrapsops_data_test.go b/internal/installer/unwrapsops_data_test.go new file mode 100644 index 00000000..2697450f --- /dev/null +++ b/internal/installer/unwrapsops_data_test.go @@ -0,0 +1,47 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package installer + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("unwrapSOPSData", func() { + It("returns data unchanged when there is no data: wrapper", func() { + input := []byte("secrets:\n - name: foo\n fields:\n password: bar\n") + output := unwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) + + It("strips a top-level data: | wrapper and returns inner content", func() { + input := []byte("data: |\n secrets:\n - name: foo\n fields:\n password: bar\n") + output := unwrapSOPSData(input) + Expect(string(output)).To(Equal("secrets:\n - name: foo\n fields:\n password: bar\n")) + }) + + It("returns data unchanged for an empty document", func() { + input := []byte("") + output := unwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) + + It("returns data unchanged when root has multiple keys", func() { + input := []byte("data: some-value\nsops:\n key: val\n") + output := unwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) + + It("returns data unchanged for invalid YAML", func() { + input := []byte("not: valid: yaml: [[") + output := unwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) + + It("returns data unchanged when data is not a scalar", func() { + input := []byte("data:\n nested: value\n") + output := unwrapSOPSData(input) + Expect(string(output)).To(Equal(string(input))) + }) +}) diff --git a/internal/installer/vault_encryption_test.go b/internal/installer/vault_encryption_test.go index 2dd0f160..f40ea89a 100644 --- a/internal/installer/vault_encryption_test.go +++ b/internal/installer/vault_encryption_test.go @@ -14,44 +14,6 @@ import ( "github.com/codesphere-cloud/oms/internal/installer" ) -var _ = Describe("unwrapSOPSData", func() { - It("returns data unchanged when there is no data: wrapper", func() { - input := []byte("secrets:\n - name: foo\n fields:\n password: bar\n") - output := installer.UnwrapSOPSData(input) - Expect(string(output)).To(Equal(string(input))) - }) - - It("strips a top-level data: | wrapper and returns inner content", func() { - input := []byte("data: |\n secrets:\n - name: foo\n fields:\n password: bar\n") - output := installer.UnwrapSOPSData(input) - Expect(string(output)).To(Equal("secrets:\n - name: foo\n fields:\n password: bar\n")) - }) - - It("returns data unchanged for an empty document", func() { - input := []byte("") - output := installer.UnwrapSOPSData(input) - Expect(string(output)).To(Equal(string(input))) - }) - - It("returns data unchanged when root has multiple keys", func() { - input := []byte("data: some-value\nsops:\n key: val\n") - output := installer.UnwrapSOPSData(input) - Expect(string(output)).To(Equal(string(input))) - }) - - It("returns data unchanged for invalid YAML", func() { - input := []byte("not: valid: yaml: [[") - output := installer.UnwrapSOPSData(input) - Expect(string(output)).To(Equal(string(input))) - }) - - It("returns data unchanged when data is not a scalar", func() { - input := []byte("data:\n nested: value\n") - output := installer.UnwrapSOPSData(input) - Expect(string(output)).To(Equal(string(input))) - }) -}) - func sopsAndAgeAvailable() bool { if _, err := exec.LookPath("sops"); err != nil { return false From f857bf808559197363dc9560405c458fca2c75c7 Mon Sep 17 00:00:00 2001 From: OliverTrautvetter <66372584+OliverTrautvetter@users.noreply.github.com> Date: Tue, 30 Jun 2026 17:17:28 +0200 Subject: [PATCH 4/4] ref(vault): renamed to vault_encryption_unexported_test.go --- ...nwrapsops_data_test.go => vault_encryption_unexported_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/installer/{unwrapsops_data_test.go => vault_encryption_unexported_test.go} (100%) diff --git a/internal/installer/unwrapsops_data_test.go b/internal/installer/vault_encryption_unexported_test.go similarity index 100% rename from internal/installer/unwrapsops_data_test.go rename to internal/installer/vault_encryption_unexported_test.go