github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/internal/bundle/parameters_test.go (about)

     1  package bundle
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/docker/app/internal/image"
    11  
    12  	"github.com/deislabs/cnab-go/bundle"
    13  	"github.com/deislabs/cnab-go/bundle/definition"
    14  	"github.com/deislabs/cnab-go/claim"
    15  	"github.com/docker/app/internal"
    16  	"github.com/docker/app/internal/packager"
    17  	"github.com/docker/app/internal/store"
    18  	"gotest.tools/assert"
    19  	"gotest.tools/assert/cmp"
    20  	"gotest.tools/fs"
    21  )
    22  
    23  func TestWithLoadFiles(t *testing.T) {
    24  	tmpDir := fs.NewDir(t,
    25  		t.Name(),
    26  		fs.WithFile("params.yaml", `param1:
    27    param2: value1
    28  param3: 3
    29  overridden: bar`))
    30  	defer tmpDir.Remove()
    31  
    32  	var bundle *bundle.Bundle
    33  	actual := map[string]string{
    34  		"overridden": "foo",
    35  	}
    36  	err := WithFileParameters([]string{tmpDir.Join("params.yaml")})(
    37  		&MergeBundleConfig{
    38  			bundle: bundle,
    39  			params: actual,
    40  		})
    41  	assert.NilError(t, err)
    42  	expected := map[string]string{
    43  		"param1.param2": "value1",
    44  		"param3":        "3",
    45  		"overridden":    "bar",
    46  	}
    47  	assert.Assert(t, cmp.DeepEqual(actual, expected))
    48  }
    49  
    50  func TestWithCommandLineParameters(t *testing.T) {
    51  	var bundle *bundle.Bundle
    52  	actual := map[string]string{
    53  		"overridden": "foo",
    54  	}
    55  
    56  	err := WithCommandLineParameters([]string{"param1.param2=value1", "param3=3", "overridden=bar"})(
    57  		&MergeBundleConfig{
    58  			bundle: bundle,
    59  			params: actual,
    60  		})
    61  	assert.NilError(t, err)
    62  	expected := map[string]string{
    63  		"param1.param2": "value1",
    64  		"param3":        "3",
    65  		"overridden":    "bar",
    66  	}
    67  	assert.Assert(t, cmp.DeepEqual(actual, expected))
    68  }
    69  
    70  type bundleOperator func(*bundle.Bundle)
    71  
    72  func prepareBundleWithParameters(b *bundle.Bundle) {
    73  	if b.Parameters != nil {
    74  		return
    75  	}
    76  	b.Parameters = map[string]bundle.Parameter{}
    77  	b.Definitions = definition.Definitions{}
    78  }
    79  
    80  func withParameter(name, typ string) bundleOperator {
    81  	return func(b *bundle.Bundle) {
    82  		prepareBundleWithParameters(b)
    83  		b.Parameters[name] = bundle.Parameter{
    84  			Definition: name,
    85  		}
    86  		b.Definitions[name] = &definition.Schema{
    87  			Type: typ,
    88  		}
    89  	}
    90  }
    91  
    92  func withParameterAndDefault(name, typ string, def interface{}) bundleOperator {
    93  	return func(b *bundle.Bundle) {
    94  		prepareBundleWithParameters(b)
    95  		b.Parameters[name] = bundle.Parameter{
    96  			Definition: name,
    97  		}
    98  		b.Definitions[name] = &definition.Schema{
    99  			Type:    typ,
   100  			Default: def,
   101  		}
   102  	}
   103  }
   104  
   105  func withParameterAndValues(name, typ string, allowedValues []interface{}) bundleOperator {
   106  	return func(b *bundle.Bundle) {
   107  		prepareBundleWithParameters(b)
   108  		b.Parameters[name] = bundle.Parameter{
   109  			Definition: name,
   110  		}
   111  		b.Definitions[name] = &definition.Schema{
   112  			Type: typ,
   113  			Enum: allowedValues,
   114  		}
   115  	}
   116  }
   117  
   118  func prepareBundle(ops ...bundleOperator) *image.AppImage {
   119  	b := image.FromBundle(&bundle.Bundle{})
   120  	for _, op := range ops {
   121  		op(b.Bundle)
   122  	}
   123  	return b
   124  }
   125  
   126  func TestWithOrchestratorParameters(t *testing.T) {
   127  	testCases := []struct {
   128  		name     string
   129  		bundle   *image.AppImage
   130  		expected map[string]string
   131  	}{
   132  		{
   133  			name:   "AppImage with orchestrator params",
   134  			bundle: prepareBundle(withParameter(internal.ParameterOrchestratorName, "string"), withParameter(internal.ParameterKubernetesNamespaceName, "string")),
   135  			expected: map[string]string{
   136  				internal.ParameterOrchestratorName:        "kubernetes",
   137  				internal.ParameterKubernetesNamespaceName: "my-namespace",
   138  			},
   139  		},
   140  		{
   141  			name:     "AppImage without orchestrator params",
   142  			bundle:   prepareBundle(),
   143  			expected: map[string]string{},
   144  		},
   145  	}
   146  
   147  	for _, testCase := range testCases {
   148  		t.Run(testCase.name, func(t *testing.T) {
   149  			actual := map[string]string{}
   150  			err := WithOrchestratorParameters("kubernetes", "my-namespace")(&MergeBundleConfig{
   151  				bundle: testCase.bundle.Bundle,
   152  				params: actual,
   153  			})
   154  			assert.NilError(t, err)
   155  			assert.Assert(t, cmp.DeepEqual(actual, testCase.expected))
   156  		})
   157  	}
   158  }
   159  
   160  func TestMergeBundleParameters(t *testing.T) {
   161  	t.Run("Override Order", func(t *testing.T) {
   162  		first := func(c *MergeBundleConfig) error {
   163  			c.params["param"] = "first"
   164  			return nil
   165  		}
   166  		second := func(c *MergeBundleConfig) error {
   167  			c.params["param"] = "second"
   168  			return nil
   169  		}
   170  		bundle := prepareBundle(withParameterAndDefault("param", "string", "default"))
   171  		i := &store.Installation{
   172  			Claim:         claim.Claim{Bundle: bundle.Bundle},
   173  			RelocationMap: bundle.RelocationMap,
   174  		}
   175  		err := MergeBundleParameters(i,
   176  			first,
   177  			second,
   178  		)
   179  		assert.NilError(t, err)
   180  		expected := map[string]interface{}{
   181  			"param": "second",
   182  		}
   183  		assert.Assert(t, cmp.DeepEqual(i.Parameters, expected))
   184  	})
   185  
   186  	t.Run("Default values", func(t *testing.T) {
   187  		bundle := prepareBundle(withParameterAndDefault("param", "string", "default"))
   188  		i := &store.Installation{
   189  			Claim:         claim.Claim{Bundle: bundle.Bundle},
   190  			RelocationMap: bundle.RelocationMap,
   191  		}
   192  		err := MergeBundleParameters(i)
   193  		assert.NilError(t, err)
   194  		expected := map[string]interface{}{
   195  			"param": "default",
   196  		}
   197  		assert.Assert(t, cmp.DeepEqual(i.Parameters, expected))
   198  	})
   199  
   200  	t.Run("Converting values", func(t *testing.T) {
   201  		withIntValue := func(c *MergeBundleConfig) error {
   202  			c.params["param"] = "1"
   203  			return nil
   204  		}
   205  		bundle := prepareBundle(withParameter("param", "integer"))
   206  		i := &store.Installation{
   207  			Claim:         claim.Claim{Bundle: bundle.Bundle},
   208  			RelocationMap: bundle.RelocationMap,
   209  		}
   210  		err := MergeBundleParameters(i, withIntValue)
   211  		assert.NilError(t, err)
   212  		expected := map[string]interface{}{
   213  			"param": 1,
   214  		}
   215  		assert.Assert(t, cmp.DeepEqual(i.Parameters, expected))
   216  	})
   217  
   218  	t.Run("Default values", func(t *testing.T) {
   219  		bundle := prepareBundle(withParameterAndDefault("param", "string", "default"))
   220  		i := &store.Installation{
   221  			Claim:         claim.Claim{Bundle: bundle.Bundle},
   222  			RelocationMap: bundle.RelocationMap,
   223  		}
   224  		err := MergeBundleParameters(i)
   225  		assert.NilError(t, err)
   226  		expected := map[string]interface{}{
   227  			"param": "default",
   228  		}
   229  		assert.Assert(t, cmp.DeepEqual(i.Parameters, expected))
   230  	})
   231  
   232  	t.Run("Undefined parameter throws warning", func(t *testing.T) {
   233  		withUndefined := func(c *MergeBundleConfig) error {
   234  			c.params["param"] = "1"
   235  			return nil
   236  		}
   237  		bundle := prepareBundle()
   238  		i := &store.Installation{
   239  			Claim:         claim.Claim{Bundle: bundle.Bundle},
   240  			RelocationMap: bundle.RelocationMap,
   241  		}
   242  		buf := new(bytes.Buffer)
   243  		err := MergeBundleParameters(i, withUndefined, WithErrorWriter(buf))
   244  		assert.NilError(t, err)
   245  		assert.Assert(t, strings.Contains(buf.String(), "is not defined in the bundle"))
   246  	})
   247  
   248  	t.Run("Warn on undefined parameter", func(t *testing.T) {
   249  		withUndefined := func(c *MergeBundleConfig) error {
   250  			c.params["param"] = "1"
   251  			return nil
   252  		}
   253  		w := bytes.NewBuffer(nil)
   254  		withStdErr := func(c *MergeBundleConfig) error {
   255  			c.stderr = w
   256  			return nil
   257  		}
   258  		bundle := prepareBundle()
   259  		i := &store.Installation{
   260  			Claim:         claim.Claim{Bundle: bundle.Bundle},
   261  			RelocationMap: bundle.RelocationMap,
   262  		}
   263  		err := MergeBundleParameters(i, withUndefined, withStdErr)
   264  		assert.NilError(t, err)
   265  		assert.Equal(t, w.String(), "Warning: parameter \"param\" is not defined in the bundle\n")
   266  	})
   267  
   268  	t.Run("Invalid type is rejected", func(t *testing.T) {
   269  		withIntValue := func(c *MergeBundleConfig) error {
   270  			c.params["param"] = "foo"
   271  			return nil
   272  		}
   273  		bundle := prepareBundle(withParameter("param", "integer"))
   274  		i := &store.Installation{
   275  			Claim:         claim.Claim{Bundle: bundle.Bundle},
   276  			RelocationMap: bundle.RelocationMap,
   277  		}
   278  		err := MergeBundleParameters(i, withIntValue)
   279  		assert.ErrorContains(t, err, "invalid value for parameter")
   280  	})
   281  
   282  	t.Run("Invalid value is rejected", func(t *testing.T) {
   283  		withInvalidValue := func(c *MergeBundleConfig) error {
   284  			c.params["param"] = "invalid"
   285  			return nil
   286  		}
   287  		bundle := prepareBundle(withParameterAndValues("param", "string", []interface{}{"valid"}))
   288  		i := &store.Installation{
   289  			Claim:         claim.Claim{Bundle: bundle.Bundle},
   290  			RelocationMap: bundle.RelocationMap,
   291  		}
   292  		err := MergeBundleParameters(i, withInvalidValue)
   293  		assert.ErrorContains(t, err, "invalid value for parameter")
   294  	})
   295  }
   296  
   297  func TestLabels(t *testing.T) {
   298  	expected := packager.DockerAppArgs{
   299  		Labels: map[string]string{
   300  			"label": "value",
   301  		},
   302  	}
   303  	expectedStr, err := json.Marshal(expected)
   304  	assert.NilError(t, err)
   305  
   306  	labels := []string{
   307  		"label=value",
   308  	}
   309  	op := WithLabels(labels)
   310  
   311  	config := &MergeBundleConfig{
   312  		bundle: &bundle.Bundle{
   313  			Parameters: map[string]bundle.Parameter{
   314  				internal.ParameterArgs: {},
   315  			},
   316  		},
   317  		params: map[string]string{},
   318  	}
   319  	err = op(config)
   320  	assert.NilError(t, err)
   321  	fmt.Println(config.params)
   322  	l := config.params[internal.ParameterArgs]
   323  	assert.Equal(t, l, string(expectedStr))
   324  }
   325  
   326  func TestInvalidLabels(t *testing.T) {
   327  	labels := []string{
   328  		"com.docker.app.label=value",
   329  	}
   330  	op := WithLabels(labels)
   331  	err := op(&MergeBundleConfig{})
   332  	assert.ErrorContains(t, err, fmt.Sprintf("labels cannot start with %q", internal.Namespace))
   333  }