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  }