github.com/grafana/tanka@v0.26.1-0.20240506093700-c22cfc35c21a/pkg/tanka/load_test.go (about) 1 package tanka 2 3 import ( 4 "os" 5 "path/filepath" 6 "testing" 7 8 "github.com/grafana/tanka/pkg/jsonnet/implementations/binary" 9 "github.com/grafana/tanka/pkg/jsonnet/implementations/goimpl" 10 "github.com/grafana/tanka/pkg/jsonnet/implementations/types" 11 "github.com/grafana/tanka/pkg/kubernetes/manifest" 12 "github.com/grafana/tanka/pkg/spec/v1alpha1" 13 "github.com/pkg/errors" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestLoad(t *testing.T) { 19 cases := []struct { 20 name string 21 baseDir string 22 expected interface{} 23 env *v1alpha1.Environment 24 }{ 25 { 26 name: "static", 27 baseDir: "./testdata/cases/withspecjson/", 28 expected: manifest.List{{ 29 "apiVersion": "v1", 30 "kind": "ConfigMap", 31 "metadata": map[string]interface{}{ 32 "name": "config", 33 "namespace": "withspec", 34 }, 35 }}, 36 env: &v1alpha1.Environment{ 37 APIVersion: v1alpha1.New().APIVersion, 38 Kind: v1alpha1.New().Kind, 39 Metadata: v1alpha1.Metadata{ 40 Name: "cases/withspecjson", 41 Namespace: "cases/withspecjson/main.jsonnet", 42 Labels: v1alpha1.New().Metadata.Labels, 43 }, 44 Spec: v1alpha1.Spec{ 45 APIServer: "https://localhost", 46 Namespace: "withspec", 47 }, 48 Data: map[string]interface{}{ 49 "apiVersion": "v1", 50 "kind": "ConfigMap", 51 "metadata": map[string]interface{}{"name": "config", "namespace": "withspec"}, 52 }, 53 }, 54 }, 55 { 56 name: "static-filename", 57 baseDir: "./testdata/cases/withspecjson/main.jsonnet", 58 expected: manifest.List{{ 59 "apiVersion": "v1", 60 "kind": "ConfigMap", 61 "metadata": map[string]interface{}{ 62 "name": "config", 63 "namespace": "withspec", 64 }, 65 }}, 66 env: &v1alpha1.Environment{ 67 APIVersion: v1alpha1.New().APIVersion, 68 Kind: v1alpha1.New().Kind, 69 Metadata: v1alpha1.Metadata{ 70 Name: "cases/withspecjson", 71 Namespace: "cases/withspecjson/main.jsonnet", 72 Labels: v1alpha1.New().Metadata.Labels, 73 }, 74 Spec: v1alpha1.Spec{ 75 APIServer: "https://localhost", 76 Namespace: "withspec", 77 }, 78 Data: map[string]interface{}{ 79 "apiVersion": "v1", 80 "kind": "ConfigMap", 81 "metadata": map[string]interface{}{"name": "config", "namespace": "withspec"}, 82 }, 83 }, 84 }, 85 86 { 87 name: "inline", 88 baseDir: "./testdata/cases/withenv/", 89 expected: manifest.List{{ 90 "apiVersion": "v1", 91 "kind": "ConfigMap", 92 "metadata": map[string]interface{}{ 93 "name": "config", 94 "namespace": "withenv", 95 }, 96 }}, 97 env: &v1alpha1.Environment{ 98 APIVersion: v1alpha1.New().APIVersion, 99 Kind: v1alpha1.New().Kind, 100 Metadata: v1alpha1.Metadata{ 101 Name: "withenv", 102 Namespace: "cases/withenv/main.jsonnet", 103 Labels: v1alpha1.New().Metadata.Labels, 104 }, 105 Spec: v1alpha1.Spec{ 106 APIServer: "https://localhost", 107 Namespace: "withenv", 108 }, 109 Data: map[string]interface{}{ 110 "apiVersion": "v1", 111 "kind": "ConfigMap", 112 "metadata": map[string]interface{}{"name": "config", "namespace": "withenv"}, 113 }, 114 }, 115 }, 116 { 117 name: "inline-filename", 118 baseDir: "./testdata/cases/withenv/main.jsonnet", 119 expected: manifest.List{{ 120 "apiVersion": "v1", 121 "kind": "ConfigMap", 122 "metadata": map[string]interface{}{ 123 "name": "config", 124 "namespace": "withenv", 125 }, 126 }}, 127 env: &v1alpha1.Environment{ 128 APIVersion: v1alpha1.New().APIVersion, 129 Kind: v1alpha1.New().Kind, 130 Metadata: v1alpha1.Metadata{ 131 Name: "withenv", 132 Namespace: "cases/withenv/main.jsonnet", 133 Labels: v1alpha1.New().Metadata.Labels, 134 }, 135 Spec: v1alpha1.Spec{ 136 APIServer: "https://localhost", 137 Namespace: "withenv", 138 }, 139 Data: map[string]interface{}{ 140 "apiVersion": "v1", 141 "kind": "ConfigMap", 142 "metadata": map[string]interface{}{"name": "config", "namespace": "withenv"}, 143 }, 144 }, 145 }, 146 } 147 148 for _, test := range cases { 149 t.Run(test.name, func(t *testing.T) { 150 l, err := Load(test.baseDir, Opts{}) 151 require.NoError(t, err) 152 153 assert.Equal(t, test.expected, l.Resources) 154 assert.Equal(t, test.env, l.Env) 155 }) 156 } 157 } 158 159 func TestLoadSelectEnvironment(t *testing.T) { 160 // No match 161 _, err := Load("./testdata/cases/multiple-inline-envs", Opts{Name: "no match"}) 162 assert.EqualError(t, err, "found no matching environments; run 'tk env list ./testdata/cases/multiple-inline-envs' to view available options") 163 164 // Empty options, match all environments 165 _, err = Load("./testdata/cases/multiple-inline-envs", Opts{}) 166 assert.EqualError(t, err, "found multiple Environments in \"./testdata/cases/multiple-inline-envs\". Use `--name` to select a single one: \n - project1-env1\n - project1-env2\n - project2-env1") 167 168 // Partial match two environments 169 _, err = Load("./testdata/cases/multiple-inline-envs", Opts{Name: "env1"}) 170 assert.EqualError(t, err, "found multiple Environments in \"./testdata/cases/multiple-inline-envs\" matching \"env1\". Provide a more specific name that matches a single one: \n - project1-env1\n - project2-env1") 171 172 // Partial match 173 result, err := Load("./testdata/cases/multiple-inline-envs", Opts{Name: "project2"}) 174 assert.NoError(t, err) 175 assert.Equal(t, "project2-env1", result.Env.Metadata.Name) 176 177 // Full match 178 result, err = Load("./testdata/cases/multiple-inline-envs", Opts{Name: "project1-env1"}) 179 assert.NoError(t, err) 180 assert.Equal(t, "project1-env1", result.Env.Metadata.Name) 181 } 182 183 // Tests that the load function will consider the path to be an environment name if it is not found 184 func TestLoadEnvironmentFallbackToName(t *testing.T) { 185 // Temporarily change the working directory to the testdata directory 186 cwd, err := os.Getwd() 187 require.NoError(t, err) 188 err = os.Chdir("./testdata/cases/multiple-inline-envs") 189 require.NoError(t, err) 190 defer func() { require.NoError(t, os.Chdir(cwd)) }() 191 192 // Partial match two environments 193 _, err = Load("env1", Opts{}) 194 assert.EqualError(t, err, "found multiple Environments in \".\" matching \"env1\". Provide a more specific name that matches a single one: \n - project1-env1\n - project2-env1") 195 196 // Partial match 197 result, err := Load("project2", Opts{}) 198 require.NoError(t, err) 199 assert.Equal(t, "project2-env1", result.Env.Metadata.Name) 200 201 // Full match 202 result, err = Load("project1-env1", Opts{}) 203 require.NoError(t, err) 204 assert.Equal(t, "project1-env1", result.Env.Metadata.Name) 205 } 206 207 func TestLoadSelectEnvironmentFullMatchHasPriority(t *testing.T) { 208 // `base` matches both `base` and `base-and-more` 209 // However, the full match should win 210 result, err := Load("./testdata/cases/inline-name-conflict", Opts{Name: "base"}) 211 assert.NoError(t, err) 212 assert.Equal(t, "base", result.Env.Metadata.Name) 213 } 214 215 func TestLoadFailsWhenBothSpecAndInline(t *testing.T) { 216 _, err := Load("./testdata/cases/static-and-inline", Opts{Name: "inline"}) 217 assert.EqualError(t, err, "found a tanka Environment resource. Check that you aren't using a spec.json and inline environments simultaneously") 218 } 219 220 func TestGetJsonnetImplementation(t *testing.T) { 221 tempDir := t.TempDir() 222 223 // Write file that is not executable 224 notExecutablePath := filepath.Join(tempDir, "not-executable") 225 require.NoError(t, os.WriteFile(notExecutablePath, []byte("not executable"), 0644)) 226 227 // Write file that is executable 228 executablePath := filepath.Join(tempDir, "executable") 229 require.NoError(t, os.WriteFile(executablePath, []byte("executable"), 0755)) 230 231 cases := []struct { 232 implementationName string 233 path string 234 expected types.JsonnetImplementation 235 expectedErr error 236 }{ 237 { 238 implementationName: "", 239 path: "my-dir", 240 expected: &goimpl.JsonnetGoImplementation{ 241 Path: "my-dir", 242 }, 243 }, 244 { 245 implementationName: "go", 246 path: "my-dir", 247 expected: &goimpl.JsonnetGoImplementation{ 248 Path: "my-dir", 249 }, 250 }, 251 { 252 implementationName: "binary:does-not-exist", 253 expectedErr: errors.New(`binary "does-not-exist" does not exist`), 254 }, 255 { 256 implementationName: "binary:" + notExecutablePath, 257 expectedErr: errors.New(`binary "` + notExecutablePath + `" is not executable`), 258 }, 259 { 260 implementationName: "binary:" + executablePath, 261 expected: &binary.JsonnetBinaryImplementation{ 262 BinPath: executablePath, 263 }, 264 }, 265 { 266 implementationName: "invalid", 267 expectedErr: errors.New("unknown jsonnet implementation: invalid"), 268 }, 269 } 270 271 for _, tt := range cases { 272 t.Run(tt.implementationName, func(t *testing.T) { 273 implementation, err := getJsonnetImplementation(tt.path, Opts{JsonnetImplementation: tt.implementationName}) 274 275 if tt.expectedErr != nil { 276 assert.EqualError(t, err, tt.expectedErr.Error()) 277 return 278 } 279 280 assert.Equal(t, tt.expected, implementation) 281 }) 282 } 283 }