mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			275 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: MPL-2.0
 | |
| 
 | |
| package command
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"reflect"
 | |
| 	"regexp"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // TestConstructTemplates tests the construcTemplates helper function
 | |
| func TestConstructTemplates(t *testing.T) {
 | |
| 	ctx, cancelContextFunc := context.WithTimeout(context.Background(), 5*time.Second)
 | |
| 	defer cancelContextFunc()
 | |
| 
 | |
| 	client, closer := testVaultServerWithSecrets(ctx, t)
 | |
| 	defer closer()
 | |
| 
 | |
| 	cases := map[string]struct {
 | |
| 		paths         []string
 | |
| 		expected      []generatedConfigEnvTemplate
 | |
| 		expectedError bool
 | |
| 	}{
 | |
| 		"kv-v1-simple": {
 | |
| 			paths: []string{"kv-v1/foo"},
 | |
| 			expected: []generatedConfigEnvTemplate{
 | |
| 				{Contents: `{{ with secret "kv-v1/foo" }}{{ .Data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v1/foo" }}{{ .Data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_USER"},
 | |
| 			},
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v2-simple": {
 | |
| 			paths: []string{"kv-v2/foo"},
 | |
| 			expected: []generatedConfigEnvTemplate{
 | |
| 				{Contents: `{{ with secret "kv-v2/data/foo" }}{{ .Data.data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/foo" }}{{ .Data.data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_USER"},
 | |
| 			},
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v2-data-in-path": {
 | |
| 			paths: []string{"kv-v2/data/foo"},
 | |
| 			expected: []generatedConfigEnvTemplate{
 | |
| 				{Contents: `{{ with secret "kv-v2/data/foo" }}{{ .Data.data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/foo" }}{{ .Data.data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_USER"},
 | |
| 			},
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v1-nested": {
 | |
| 			paths: []string{"kv-v1/app-1/*"},
 | |
| 			expected: []generatedConfigEnvTemplate{
 | |
| 				{Contents: `{{ with secret "kv-v1/app-1/bar" }}{{ .Data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAR_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v1/app-1/bar" }}{{ .Data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAR_USER"},
 | |
| 				{Contents: `{{ with secret "kv-v1/app-1/foo" }}{{ .Data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v1/app-1/foo" }}{{ .Data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_USER"},
 | |
| 				{Contents: `{{ with secret "kv-v1/app-1/nested/baz" }}{{ .Data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAZ_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v1/app-1/nested/baz" }}{{ .Data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAZ_USER"},
 | |
| 			},
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v2-nested": {
 | |
| 			paths: []string{"kv-v2/app-1/*"},
 | |
| 			expected: []generatedConfigEnvTemplate{
 | |
| 				{Contents: `{{ with secret "kv-v2/data/app-1/bar" }}{{ .Data.data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAR_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/app-1/bar" }}{{ .Data.data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAR_USER"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/app-1/foo" }}{{ .Data.data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/app-1/foo" }}{{ .Data.data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_USER"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/app-1/nested/baz" }}{{ .Data.data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAZ_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/app-1/nested/baz" }}{{ .Data.data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAZ_USER"},
 | |
| 			},
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v1-multi-path": {
 | |
| 			paths: []string{"kv-v1/foo", "kv-v1/app-1/bar"},
 | |
| 			expected: []generatedConfigEnvTemplate{
 | |
| 				{Contents: `{{ with secret "kv-v1/foo" }}{{ .Data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v1/foo" }}{{ .Data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_USER"},
 | |
| 				{Contents: `{{ with secret "kv-v1/app-1/bar" }}{{ .Data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAR_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v1/app-1/bar" }}{{ .Data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAR_USER"},
 | |
| 			},
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v2-multi-path": {
 | |
| 			paths: []string{"kv-v2/foo", "kv-v2/app-1/bar"},
 | |
| 			expected: []generatedConfigEnvTemplate{
 | |
| 				{Contents: `{{ with secret "kv-v2/data/foo" }}{{ .Data.data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/foo" }}{{ .Data.data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "FOO_USER"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/app-1/bar" }}{{ .Data.data.password }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAR_PASSWORD"},
 | |
| 				{Contents: `{{ with secret "kv-v2/data/app-1/bar" }}{{ .Data.data.user }}{{ end }}`, ErrorOnMissingKey: true, Name: "BAR_USER"},
 | |
| 			},
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v1-path-not-found": {
 | |
| 			paths:         []string{"kv-v1/does/not/exist"},
 | |
| 			expected:      nil,
 | |
| 			expectedError: true,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v2-path-not-found": {
 | |
| 			paths:         []string{"kv-v2/does/not/exist"},
 | |
| 			expected:      nil,
 | |
| 			expectedError: true,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v1-early-wildcard": {
 | |
| 			paths:         []string{"kv-v1/*/foo"},
 | |
| 			expected:      nil,
 | |
| 			expectedError: true,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v2-early-wildcard": {
 | |
| 			paths:         []string{"kv-v2/*/foo"},
 | |
| 			expected:      nil,
 | |
| 			expectedError: true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for name, tc := range cases {
 | |
| 		name, tc := name, tc
 | |
| 
 | |
| 		t.Run(name, func(t *testing.T) {
 | |
| 			templates, err := constructTemplates(ctx, client, tc.paths)
 | |
| 
 | |
| 			if tc.expectedError {
 | |
| 				if err == nil {
 | |
| 					t.Fatal("an error was expected but the test succeeded")
 | |
| 				}
 | |
| 			} else {
 | |
| 				if err != nil {
 | |
| 					t.Fatal(err)
 | |
| 				}
 | |
| 
 | |
| 				if !reflect.DeepEqual(tc.expected, templates) {
 | |
| 					t.Fatalf("unexpected output; want: %v, got: %v", tc.expected, templates)
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestGenerateConfiguration tests the generateConfiguration helper function
 | |
| func TestGenerateConfiguration(t *testing.T) {
 | |
| 	ctx, cancelContextFunc := context.WithTimeout(context.Background(), 5*time.Second)
 | |
| 	defer cancelContextFunc()
 | |
| 
 | |
| 	client, closer := testVaultServerWithSecrets(ctx, t)
 | |
| 	defer closer()
 | |
| 
 | |
| 	cases := map[string]struct {
 | |
| 		flagExec      string
 | |
| 		flagPaths     []string
 | |
| 		expected      *regexp.Regexp
 | |
| 		expectedError bool
 | |
| 	}{
 | |
| 		"kv-v1-simple": {
 | |
| 			flagExec:  "./my-app arg1 arg2",
 | |
| 			flagPaths: []string{"kv-v1/foo"},
 | |
| 			expected: regexp.MustCompile(`
 | |
| auto_auth \{
 | |
| 
 | |
|   method \{
 | |
|     type = "token_file"
 | |
| 
 | |
|     config \{
 | |
|       token_file_path = ".*/.vault-token"
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| template_config \{
 | |
|   static_secret_render_interval = "5m"
 | |
|   exit_on_retry_failure         = true
 | |
| }
 | |
| 
 | |
| vault \{
 | |
|   address = "https://127.0.0.1:[0-9]{5}"
 | |
| }
 | |
| 
 | |
| env_template "FOO_PASSWORD" \{
 | |
|   contents             = "\{\{ with secret \\"kv-v1/foo\\" }}\{\{ .Data.password }}\{\{ end }}"
 | |
|   error_on_missing_key = true
 | |
| }
 | |
| env_template "FOO_USER" \{
 | |
|   contents             = "\{\{ with secret \\"kv-v1/foo\\" }}\{\{ .Data.user }}\{\{ end }}"
 | |
|   error_on_missing_key = true
 | |
| }
 | |
| 
 | |
| exec \{
 | |
|   command                   = \["./my-app", "arg1", "arg2"\]
 | |
|   restart_on_secret_changes = "always"
 | |
|   restart_stop_signal       = "SIGTERM"
 | |
| }
 | |
| `),
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 
 | |
| 		"kv-v2-default-exec": {
 | |
| 			flagExec:  "",
 | |
| 			flagPaths: []string{"kv-v2/foo"},
 | |
| 			expected: regexp.MustCompile(`
 | |
| auto_auth \{
 | |
| 
 | |
|   method \{
 | |
|     type = "token_file"
 | |
| 
 | |
|     config \{
 | |
|       token_file_path = ".*/.vault-token"
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| template_config \{
 | |
|   static_secret_render_interval = "5m"
 | |
|   exit_on_retry_failure         = true
 | |
| }
 | |
| 
 | |
| vault \{
 | |
|   address = "https://127.0.0.1:[0-9]{5}"
 | |
| }
 | |
| 
 | |
| env_template "FOO_PASSWORD" \{
 | |
|   contents             = "\{\{ with secret \\"kv-v2/data/foo\\" }}\{\{ .Data.data.password }}\{\{ end }}"
 | |
|   error_on_missing_key = true
 | |
| }
 | |
| env_template "FOO_USER" \{
 | |
|   contents             = "\{\{ with secret \\"kv-v2/data/foo\\" }}\{\{ .Data.data.user }}\{\{ end }}"
 | |
|   error_on_missing_key = true
 | |
| }
 | |
| 
 | |
| exec \{
 | |
|   command                   = \["env"\]
 | |
|   restart_on_secret_changes = "always"
 | |
|   restart_stop_signal       = "SIGTERM"
 | |
| }
 | |
| `),
 | |
| 			expectedError: false,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for name, tc := range cases {
 | |
| 		name, tc := name, tc
 | |
| 
 | |
| 		t.Run(name, func(t *testing.T) {
 | |
| 			var config bytes.Buffer
 | |
| 
 | |
| 			c, err := generateConfiguration(ctx, client, tc.flagExec, tc.flagPaths)
 | |
| 			c.WriteTo(&config)
 | |
| 
 | |
| 			if tc.expectedError {
 | |
| 				if err == nil {
 | |
| 					t.Fatal("an error was expected but the test succeeded")
 | |
| 				}
 | |
| 			} else {
 | |
| 				if err != nil {
 | |
| 					t.Fatal(err)
 | |
| 				}
 | |
| 
 | |
| 				if !tc.expected.MatchString(config.String()) {
 | |
| 					t.Fatalf("unexpected output; want: %v, got: %v", tc.expected.String(), config.String())
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | 
