get.porter.sh/porter@v1.3.0/pkg/porter/explain_test.go (about)

     1  package porter
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"get.porter.sh/porter/pkg/cnab"
     8  	depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1"
     9  	"get.porter.sh/porter/pkg/portercontext"
    10  	"get.porter.sh/porter/pkg/test"
    11  	"github.com/cnabio/cnab-go/bundle"
    12  	"github.com/cnabio/cnab-go/bundle/definition"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestExplain_ValidateReference(t *testing.T) {
    18  	const ref = "ghcr.io/getporter/examples/porter-hello:v0.2.0"
    19  	t.Run("--reference specified", func(t *testing.T) {
    20  		porterCtx := portercontext.NewTestContext(t)
    21  		opts := ExplainOpts{}
    22  		opts.Reference = ref
    23  
    24  		err := opts.Validate(nil, porterCtx.Context)
    25  		require.NoError(t, err, "Validate failed")
    26  		assert.Equal(t, ref, opts.Reference)
    27  	})
    28  	t.Run("reference positional argument specified", func(t *testing.T) {
    29  		porterCtx := portercontext.NewTestContext(t)
    30  		opts := ExplainOpts{}
    31  
    32  		err := opts.Validate([]string{ref}, porterCtx.Context)
    33  		require.NoError(t, err, "Validate failed")
    34  		assert.Equal(t, ref, opts.Reference)
    35  	})
    36  }
    37  
    38  func TestExplain_validateBadFormat(t *testing.T) {
    39  	p := NewTestPorter(t)
    40  	defer p.Close()
    41  
    42  	opts := ExplainOpts{}
    43  	opts.RawFormat = "vpml"
    44  
    45  	err := opts.Validate([]string{}, p.Context)
    46  	assert.EqualError(t, err, "invalid format: vpml")
    47  }
    48  
    49  func TestExplain_generateTable(t *testing.T) {
    50  	p := NewTestPorter(t)
    51  	defer p.Close()
    52  
    53  	p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle.json", "params-bundle.json")
    54  	b, err := p.CNAB.LoadBundle("params-bundle.json")
    55  	require.NoError(t, err)
    56  
    57  	pb, err := generatePrintable(b, "")
    58  	require.NoError(t, err)
    59  	opts := ExplainOpts{}
    60  	opts.RawFormat = "plaintext"
    61  
    62  	err = opts.Validate([]string{}, p.Context)
    63  	require.NoError(t, err)
    64  
    65  	err = p.printBundleExplain(opts, pb, b)
    66  	assert.NoError(t, err)
    67  
    68  	gotOutput := p.TestConfig.TestContext.GetOutput()
    69  	test.CompareGoldenFile(t, "testdata/explain/expected-table-output.txt", gotOutput)
    70  }
    71  
    72  func TestExplain_generateTableRequireDockerHostAccess(t *testing.T) {
    73  	p := NewTestPorter(t)
    74  	defer p.Close()
    75  
    76  	p.TestConfig.TestContext.AddTestFile("testdata/explain/bundle-docker.json", "bundle-docker.json")
    77  	b, err := p.CNAB.LoadBundle("bundle-docker.json")
    78  	require.NoError(t, err)
    79  
    80  	pb, err := generatePrintable(b, "")
    81  	require.NoError(t, err)
    82  	opts := ExplainOpts{}
    83  	opts.RawFormat = "plaintext"
    84  
    85  	err = opts.Validate([]string{}, p.Context)
    86  	require.NoError(t, err)
    87  
    88  	err = p.printBundleExplain(opts, pb, b)
    89  	assert.NoError(t, err)
    90  	gotOutput := p.TestConfig.TestContext.GetOutput()
    91  	p.CompareGoldenFile("testdata/explain/expected-table-output-docker.txt", gotOutput)
    92  }
    93  
    94  func TestExplain_generateJSON(t *testing.T) {
    95  	p := NewTestPorter(t)
    96  	defer p.Close()
    97  
    98  	p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle.json", "params-bundle.json")
    99  	b, err := p.CNAB.LoadBundle("params-bundle.json")
   100  	require.NoError(t, err)
   101  
   102  	pb, err := generatePrintable(b, "")
   103  	require.NoError(t, err)
   104  	opts := ExplainOpts{}
   105  	opts.RawFormat = "json"
   106  
   107  	err = opts.Validate([]string{}, p.Context)
   108  	require.NoError(t, err)
   109  
   110  	err = p.printBundleExplain(opts, pb, b)
   111  	assert.NoError(t, err)
   112  	gotOutput := p.TestConfig.TestContext.GetOutput()
   113  	p.CompareGoldenFile("testdata/explain/expected-json-output.json", gotOutput)
   114  }
   115  
   116  func TestExplain_generateYAML(t *testing.T) {
   117  	p := NewTestPorter(t)
   118  	defer p.Close()
   119  
   120  	p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle.json", "params-bundle.json")
   121  	b, err := p.CNAB.LoadBundle("params-bundle.json")
   122  	require.NoError(t, err)
   123  
   124  	pb, err := generatePrintable(b, "")
   125  	require.NoError(t, err)
   126  
   127  	opts := ExplainOpts{}
   128  	opts.RawFormat = "yaml"
   129  
   130  	err = opts.Validate([]string{}, p.Context)
   131  	require.NoError(t, err)
   132  
   133  	err = p.printBundleExplain(opts, pb, b)
   134  	assert.NoError(t, err)
   135  	gotOutput := p.TestConfig.TestContext.GetOutput()
   136  	p.CompareGoldenFile("testdata/explain/expected-yaml-output.yaml", gotOutput)
   137  }
   138  
   139  func TestExplain_generatePrintableBundleParams(t *testing.T) {
   140  	bun := cnab.NewBundle(bundle.Bundle{
   141  		RequiredExtensions: []string{
   142  			cnab.FileParameterExtensionKey,
   143  		},
   144  		Definitions: definition.Definitions{
   145  			"string": &definition.Schema{
   146  				Type:    "string",
   147  				Default: "clippy",
   148  			},
   149  			"file": &definition.Schema{
   150  				Type:            "string",
   151  				ContentEncoding: "base64",
   152  			},
   153  		},
   154  		Parameters: map[string]bundle.Parameter{
   155  			"debug": {
   156  				Definition: "string",
   157  				Required:   true,
   158  			},
   159  			"tfstate": {
   160  				Definition: "file",
   161  			},
   162  		},
   163  		Custom: map[string]interface{}{
   164  			"sh.porter": map[string]interface{}{
   165  				"manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0",
   166  				"version":  "v0.30.0",
   167  				"commit":   "3b7c85ba",
   168  			},
   169  		},
   170  	})
   171  
   172  	pb, err := generatePrintable(bun, "")
   173  	require.NoError(t, err)
   174  
   175  	require.Equal(t, 2, len(pb.Parameters), "expected 2 parameters")
   176  	d := pb.Parameters[0]
   177  	require.Equal(t, "debug", d.Name)
   178  	assert.Equal(t, "clippy", fmt.Sprintf("%v", d.Default))
   179  	assert.Equal(t, "string", d.Type)
   180  	f := pb.Parameters[1]
   181  	require.Equal(t, "tfstate", f.Name)
   182  	assert.Equal(t, "file", f.Type)
   183  
   184  	assert.Equal(t, 0, len(pb.Outputs))
   185  	assert.Equal(t, 0, len(pb.Credentials))
   186  	assert.Equal(t, 0, len(pb.Actions))
   187  }
   188  
   189  func TestExplain_generatePrintableBundleParamsWithAction(t *testing.T) {
   190  	bun := cnab.NewBundle(bundle.Bundle{
   191  		RequiredExtensions: []string{
   192  			cnab.FileParameterExtensionKey,
   193  		},
   194  		Definitions: definition.Definitions{
   195  			"string": &definition.Schema{
   196  				Type:    "string",
   197  				Default: "clippy",
   198  			},
   199  			"file": &definition.Schema{
   200  				Type:            "string",
   201  				ContentEncoding: "base64",
   202  			},
   203  		},
   204  		Parameters: map[string]bundle.Parameter{
   205  			"debug": {
   206  				Definition: "string",
   207  				Required:   true,
   208  				ApplyTo:    []string{"install"},
   209  			},
   210  			"tfstate": {
   211  				Definition: "file",
   212  			},
   213  		},
   214  		Custom: map[string]interface{}{
   215  			"sh.porter": map[string]interface{}{
   216  				"manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0",
   217  				"version":  "v0.30.0",
   218  				"commit":   "3b7c85ba",
   219  			},
   220  		},
   221  	})
   222  
   223  	t.Run("action applies", func(t *testing.T) {
   224  		pb, err := generatePrintable(bun, "install")
   225  		require.NoError(t, err)
   226  
   227  		require.Equal(t, 2, len(pb.Parameters), "expected 2 parameters")
   228  
   229  		d := pb.Parameters[0]
   230  		require.Equal(t, "debug", d.Name)
   231  		assert.Equal(t, "install", d.ApplyTo)
   232  
   233  		f := pb.Parameters[1]
   234  		require.Equal(t, "tfstate", f.Name)
   235  		assert.Equal(t, "All Actions", f.ApplyTo)
   236  	})
   237  
   238  	t.Run("action does not apply", func(t *testing.T) {
   239  		pb, err := generatePrintable(bun, "upgrade")
   240  		require.NoError(t, err)
   241  
   242  		require.Equal(t, 1, len(pb.Parameters), "expected only 1 parameter since debug parameter doesn't apply to upgrade command")
   243  		require.Equal(t, "tfstate", pb.Parameters[0].Name)
   244  	})
   245  
   246  	t.Run("all actions", func(t *testing.T) {
   247  		pb, err := generatePrintable(bun, "")
   248  		require.NoError(t, err)
   249  
   250  		require.Equal(t, 2, len(pb.Parameters), "expected 2 parameters")
   251  
   252  		d := pb.Parameters[0]
   253  		require.Equal(t, "debug", d.Name)
   254  		assert.Equal(t, "install", d.ApplyTo)
   255  
   256  		f := pb.Parameters[1]
   257  		require.Equal(t, "tfstate", f.Name)
   258  		assert.Equal(t, "All Actions", f.ApplyTo)
   259  	})
   260  }
   261  
   262  func TestExplain_generatePrintableBundleOutputs(t *testing.T) {
   263  	bun := cnab.NewBundle(bundle.Bundle{
   264  		Definitions: definition.Definitions{
   265  			"string": &definition.Schema{
   266  				Type:    "string",
   267  				Default: "clippy",
   268  			},
   269  		},
   270  		Outputs: map[string]bundle.Output{
   271  			"debug": {
   272  				Definition: "string",
   273  			},
   274  			"someoutput": {
   275  				Definition: "string",
   276  				ApplyTo:    []string{"install"},
   277  			},
   278  		},
   279  		Custom: map[string]interface{}{
   280  			"sh.porter": map[string]interface{}{
   281  				"manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0",
   282  				"version":  "v0.30.0",
   283  				"commit":   "3b7c85ba",
   284  			},
   285  		},
   286  	})
   287  
   288  	pb, err := generatePrintable(bun, "")
   289  	require.NoError(t, err)
   290  
   291  	require.Equal(t, 2, len(pb.Outputs), "expected someoutput to be included because the action is unset")
   292  	debugOutput := pb.Outputs[0]
   293  	assert.Equal(t, "string", fmt.Sprintf("%v", debugOutput.Type))
   294  	assert.Equal(t, "debug", debugOutput.Name)
   295  	assert.Equal(t, "All Actions", debugOutput.ApplyTo)
   296  
   297  	someOutput := pb.Outputs[1]
   298  	assert.Equal(t, "string", fmt.Sprintf("%v", someOutput.Type))
   299  	assert.Equal(t, "someoutput", someOutput.Name)
   300  	assert.Equal(t, "install", someOutput.ApplyTo)
   301  
   302  	assert.Equal(t, 0, len(pb.Parameters))
   303  	assert.Equal(t, 0, len(pb.Credentials))
   304  	assert.Equal(t, 0, len(pb.Actions))
   305  
   306  	// Check outputs for install action
   307  	pb, err = generatePrintable(bun, "install")
   308  	require.NoError(t, err)
   309  	assert.Equal(t, 2, len(pb.Outputs), "expected someoutput to be included")
   310  
   311  	// Check outputs for upgrade action action (someoutput doesn't apply)
   312  	pb, err = generatePrintable(bun, "upgrade")
   313  	require.NoError(t, err)
   314  	assert.Equal(t, 1, len(pb.Outputs), "expected someoutput to be excluded by its applyTo")
   315  }
   316  
   317  func TestExplain_generatePrintableBundleCreds(t *testing.T) {
   318  	bun := cnab.NewBundle(bundle.Bundle{
   319  		Credentials: map[string]bundle.Credential{
   320  			"kubeconfig": {
   321  				Required:    true,
   322  				Description: "a cred",
   323  				ApplyTo:     []string{"install"},
   324  			},
   325  			"password": {
   326  				Description: "another cred",
   327  			},
   328  		},
   329  		Custom: map[string]interface{}{
   330  			"sh.porter": map[string]interface{}{
   331  				"manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0",
   332  				"version":  "v0.30.0",
   333  				"commit":   "3b7c85ba",
   334  			},
   335  		},
   336  	})
   337  
   338  	t.Run("action applies", func(t *testing.T) {
   339  		pb, err := generatePrintable(bun, "install")
   340  		require.NoError(t, err)
   341  
   342  		require.Equal(t, 2, len(pb.Credentials), "expected 2 credentials")
   343  
   344  		d := pb.Credentials[0]
   345  		require.Equal(t, "kubeconfig", d.Name)
   346  		assert.Equal(t, "install", d.ApplyTo)
   347  
   348  		f := pb.Credentials[1]
   349  		require.Equal(t, "password", f.Name)
   350  		assert.Equal(t, "All Actions", f.ApplyTo)
   351  	})
   352  
   353  	t.Run("action does not apply", func(t *testing.T) {
   354  		pb, err := generatePrintable(bun, "upgrade")
   355  		require.NoError(t, err)
   356  
   357  		require.Equal(t, 1, len(pb.Credentials), "expected only 1 credential since kubeconfig credential doesn't apply to upgrade command")
   358  		require.Equal(t, "password", pb.Credentials[0].Name)
   359  	})
   360  
   361  	t.Run("all actions", func(t *testing.T) {
   362  		pb, err := generatePrintable(bun, "")
   363  		require.NoError(t, err)
   364  
   365  		require.Equal(t, 2, len(pb.Credentials), "expected 2 credentials")
   366  
   367  		d := pb.Credentials[0]
   368  		require.Equal(t, "kubeconfig", d.Name)
   369  		assert.Equal(t, "install", d.ApplyTo)
   370  
   371  		f := pb.Credentials[1]
   372  		require.Equal(t, "password", f.Name)
   373  		assert.Equal(t, "All Actions", f.ApplyTo)
   374  	})
   375  }
   376  
   377  func TestExplain_generatePrintableBundlePorterVersion(t *testing.T) {
   378  	bun := cnab.NewBundle(bundle.Bundle{
   379  		Definitions: definition.Definitions{
   380  			"string": &definition.Schema{
   381  				Type:    "string",
   382  				Default: "clippy",
   383  			},
   384  		},
   385  		Custom: map[string]interface{}{
   386  			"sh.porter": map[string]interface{}{
   387  				"manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0",
   388  				"version":  "v0.30.0",
   389  				"commit":   "3b7c85ba",
   390  			},
   391  		},
   392  	})
   393  
   394  	pb, err := generatePrintable(bun, "")
   395  	assert.NoError(t, err)
   396  
   397  	assert.Equal(t, "v0.30.0", pb.PorterVersion)
   398  }
   399  
   400  func TestExplain_generatePrintableBundlePorterVersionNonPorterBundle(t *testing.T) {
   401  	bun := cnab.NewBundle(bundle.Bundle{
   402  		Definitions: definition.Definitions{
   403  			"string": &definition.Schema{
   404  				Type:    "string",
   405  				Default: "clippy",
   406  			},
   407  		},
   408  	})
   409  
   410  	pb, err := generatePrintable(bun, "")
   411  	assert.NoError(t, err)
   412  
   413  	assert.Equal(t, "", pb.PorterVersion)
   414  }
   415  
   416  func TestExplain_generatePrintableBundleDependencies(t *testing.T) {
   417  	sequenceMock := []string{"nginx", "storage", "mysql"}
   418  	bun := cnab.NewBundle(bundle.Bundle{
   419  		Custom: map[string]interface{}{
   420  			cnab.DependenciesV1ExtensionKey: depsv1ext.Dependencies{
   421  				Sequence: sequenceMock,
   422  				Requires: map[string]depsv1ext.Dependency{
   423  					"mysql": {
   424  						Name:   "mysql",
   425  						Bundle: "somecloud/mysql:0.1.0",
   426  					},
   427  					"storage": {
   428  						Name:   "storage",
   429  						Bundle: "localhost:5000/blob-storage:0.1.0",
   430  					},
   431  					"nginx": {
   432  						Name:   "nginx",
   433  						Bundle: "localhost:5000/nginx:1.19",
   434  					},
   435  				},
   436  			},
   437  			"sh.porter": map[string]interface{}{
   438  				"manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0",
   439  				"version":  "v0.30.0",
   440  				"commit":   "3b7c85ba",
   441  			},
   442  		},
   443  	})
   444  
   445  	pd, err := generatePrintable(bun, "")
   446  	assert.NoError(t, err)
   447  	assert.Equal(t, 3, len(pd.Dependencies))
   448  	assert.Equal(t, 0, len(pd.Parameters))
   449  	assert.Equal(t, 0, len(pd.Outputs))
   450  	assert.Equal(t, 0, len(pd.Actions))
   451  	assert.Equal(t, "nginx", pd.Dependencies[0].Alias)
   452  	assert.Equal(t, "somecloud/mysql:0.1.0", pd.Dependencies[2].Reference)
   453  }
   454  
   455  func TestExplain_generateJSONForDependencies(t *testing.T) {
   456  	p := NewTestPorter(t)
   457  	defer p.Close()
   458  
   459  	p.TestConfig.TestContext.AddTestFile("testdata/explain/dependencies-bundle.json", "dependencies-bundle.json")
   460  	b, err := p.CNAB.LoadBundle("dependencies-bundle.json")
   461  	require.NoError(t, err)
   462  
   463  	pb, err := generatePrintable(b, "")
   464  	require.NoError(t, err)
   465  	opts := ExplainOpts{}
   466  	opts.RawFormat = "json"
   467  
   468  	err = opts.Validate([]string{}, p.Context)
   469  	require.NoError(t, err)
   470  
   471  	err = p.printBundleExplain(opts, pb, b)
   472  	assert.NoError(t, err)
   473  	gotOutput := p.TestConfig.TestContext.GetOutput()
   474  
   475  	p.CompareGoldenFile("testdata/explain/expected-json-dependencies-output.json", gotOutput)
   476  }
   477  
   478  func TestExplain_generateTableNonPorterBundle(t *testing.T) {
   479  	p := NewTestPorter(t)
   480  	defer p.Close()
   481  
   482  	p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle-non-porter.json", "params-bundle.json")
   483  	b, err := p.CNAB.LoadBundle("params-bundle.json")
   484  	require.NoError(t, err)
   485  
   486  	pb, err := generatePrintable(b, "")
   487  	require.NoError(t, err)
   488  	opts := ExplainOpts{}
   489  	opts.RawFormat = "plaintext"
   490  
   491  	err = opts.Validate([]string{}, p.Context)
   492  	require.NoError(t, err)
   493  
   494  	err = p.printBundleExplain(opts, pb, b)
   495  	assert.NoError(t, err)
   496  
   497  	gotOutput := p.TestConfig.TestContext.GetOutput()
   498  	test.CompareGoldenFile(t, "testdata/explain/expected-table-output-non-porter.txt", gotOutput)
   499  }
   500  
   501  func TestExplain_generateTableBundleWithNoMixins(t *testing.T) {
   502  	p := NewTestPorter(t)
   503  	defer p.Close()
   504  
   505  	p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle-no-mixins.json", "params-bundle.json")
   506  	b, err := p.CNAB.LoadBundle("params-bundle.json")
   507  	require.NoError(t, err)
   508  
   509  	pb, err := generatePrintable(b, "")
   510  	require.NoError(t, err)
   511  	opts := ExplainOpts{}
   512  	opts.RawFormat = "plaintext"
   513  
   514  	err = opts.Validate([]string{}, p.Context)
   515  	require.NoError(t, err)
   516  
   517  	err = p.printBundleExplain(opts, pb, b)
   518  	assert.NoError(t, err)
   519  
   520  	gotOutput := p.TestConfig.TestContext.GetOutput()
   521  	test.CompareGoldenFile(t, "testdata/explain/expected-table-output-no-mixins.txt", gotOutput)
   522  }