get.porter.sh/porter@v1.3.0/tests/integration/build_integration_test.go (about)

     1  //go:build integration
     2  
     3  package integration
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"io/fs"
     9  	"testing"
    10  
    11  	"get.porter.sh/porter/pkg"
    12  	"get.porter.sh/porter/pkg/build"
    13  	configadapter "get.porter.sh/porter/pkg/cnab/config-adapter"
    14  	"get.porter.sh/porter/pkg/config"
    15  	"get.porter.sh/porter/pkg/linter"
    16  	"get.porter.sh/porter/pkg/manifest"
    17  	"get.porter.sh/porter/pkg/mixin"
    18  	"get.porter.sh/porter/pkg/porter"
    19  	"get.porter.sh/porter/pkg/schema"
    20  	"get.porter.sh/porter/pkg/yaml"
    21  	"get.porter.sh/porter/tests"
    22  	"github.com/cnabio/cnab-go/bundle"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  )
    26  
    27  func TestPorter_Build(t *testing.T) {
    28  	p := porter.NewTestPorter(t)
    29  	defer p.Close()
    30  
    31  	configTpl, err := p.Templates.GetManifest()
    32  	require.Nil(t, err)
    33  	p.TestConfig.TestContext.AddTestFileContents(configTpl, config.Name)
    34  
    35  	// Create some junk in the previous .cnab directory, build should clean it up and not copy it into the bundle
    36  	junkDir := ".cnab/test/junk"
    37  	require.NoError(t, p.FileSystem.MkdirAll(junkDir, pkg.FileModeDirectory), "could not create test junk files")
    38  	junkExists, _ := p.FileSystem.DirExists(junkDir)
    39  	assert.True(t, junkExists, "failed to create junk files for the test")
    40  
    41  	opts := porter.BuildOptions{}
    42  	require.NoError(t, opts.Validate(p.Porter), "Validate failed")
    43  
    44  	err = p.Build(context.Background(), opts)
    45  	require.NoError(t, err)
    46  
    47  	// Check file permissions on .cnab contents
    48  	bundleJSONStats, err := p.FileSystem.Stat(build.LOCAL_BUNDLE)
    49  	require.NoError(t, err)
    50  	tests.AssertFilePermissionsEqual(t, build.LOCAL_BUNDLE, pkg.FileModeWritable, bundleJSONStats.Mode())
    51  
    52  	runStats, err := p.FileSystem.Stat(build.LOCAL_RUN)
    53  	require.NoError(t, err)
    54  	tests.AssertFilePermissionsEqual(t, build.LOCAL_RUN, pkg.FileModeExecutable, runStats.Mode())
    55  
    56  	manifestStats, err := p.FileSystem.Stat(build.LOCAL_MANIFEST)
    57  	require.NoError(t, err)
    58  	tests.AssertFilePermissionsEqual(t, build.LOCAL_MANIFEST, pkg.FileModeWritable, manifestStats.Mode())
    59  
    60  	err = p.FileSystem.Walk(build.LOCAL_MIXINS, func(path string, info fs.FileInfo, err error) error {
    61  		if err != nil {
    62  			return err
    63  		}
    64  		if info.IsDir() {
    65  			return nil
    66  		}
    67  
    68  		tests.AssertFilePermissionsEqual(t, path, pkg.FileModeExecutable, runStats.Mode())
    69  		return nil
    70  	})
    71  	require.NoError(t, err)
    72  
    73  	// Check that the junk files were cleaned up
    74  	junkExists, _ = p.FileSystem.DirExists(junkDir)
    75  	assert.False(t, junkExists, "junk files were not cleaned up before building")
    76  
    77  	bun, err := p.CNAB.LoadBundle(build.LOCAL_BUNDLE)
    78  	require.NoError(t, err)
    79  
    80  	assert.Equal(t, "porter-hello", bun.Name)
    81  	assert.Equal(t, "1.2.0", string(bun.SchemaVersion))
    82  	assert.Equal(t, "0.1.0", bun.Version)
    83  	assert.Equal(t, "An example Porter configuration", bun.Description)
    84  
    85  	stamp, err := configadapter.LoadStamp(bun)
    86  	require.NoError(t, err)
    87  	assert.NotEmpty(t, stamp.ManifestDigest)
    88  
    89  	debugParam, ok := bun.Parameters["porter-debug"]
    90  	require.True(t, ok, "porter-debug parameter was not defined")
    91  	assert.Equal(t, "PORTER_DEBUG", debugParam.Destination.EnvironmentVariable)
    92  	debugDef, ok := bun.Definitions[debugParam.Definition]
    93  	require.True(t, ok, "porter-debug definition was not defined")
    94  	assert.Equal(t, "boolean", debugDef.Type)
    95  	assert.Equal(t, false, debugDef.Default)
    96  }
    97  
    98  func TestPorter_Build_ChecksManifestSchemaVersion(t *testing.T) {
    99  	testcases := []struct {
   100  		name          string
   101  		schemaVersion string
   102  		wantErr       string
   103  	}{
   104  		{name: "current version", schemaVersion: manifest.DefaultSchemaVersion.String()},
   105  		{name: "its an older code but it checks out", schemaVersion: "1.0.0-alpha.1"},
   106  		{name: "invalid version", schemaVersion: "", wantErr: schema.ErrInvalidSchemaVersion.Error()},
   107  	}
   108  	for _, tc := range testcases {
   109  		t.Run(tc.name, func(t *testing.T) {
   110  			p := porter.NewTestPorter(t)
   111  			defer p.Close()
   112  
   113  			// Make a bundle with the specified schemaVersion
   114  			p.TestConfig.TestContext.AddTestDirectoryFromRoot("tests/testdata/mybuns", "/")
   115  			e := yaml.NewEditor(p.FileSystem)
   116  			require.NoError(t, e.ReadFile("porter.yaml"))
   117  			require.NoError(t, e.SetValue("schemaVersion", tc.schemaVersion))
   118  			require.NoError(t, e.WriteFile("porter.yaml"))
   119  
   120  			opts := porter.BuildOptions{}
   121  			opts.File = "porter.yaml"
   122  			err := p.Build(context.Background(), opts)
   123  			if tc.wantErr == "" {
   124  				require.NoError(t, err)
   125  			} else {
   126  				require.ErrorIs(t, err, schema.ErrInvalidSchemaVersion)
   127  				tests.RequireErrorContains(t, err, tc.wantErr)
   128  			}
   129  		})
   130  	}
   131  }
   132  
   133  func TestPorter_LintDuringBuild(t *testing.T) {
   134  	lintResults := linter.Results{
   135  		{
   136  			Level: linter.LevelError,
   137  			Code:  "exec-100",
   138  		},
   139  	}
   140  
   141  	t.Run("failing lint should stop build", func(t *testing.T) {
   142  		p := porter.NewTestPorter(t)
   143  		defer p.Close()
   144  
   145  		testMixins := p.Mixins.(*mixin.TestMixinProvider)
   146  		testMixins.LintResults = lintResults
   147  
   148  		err := p.Create()
   149  		require.NoError(t, err, "Create failed")
   150  
   151  		opts := porter.BuildOptions{NoLint: false}
   152  		err = opts.Validate(p.Porter)
   153  		require.NoError(t, err)
   154  
   155  		err = p.Build(context.Background(), opts)
   156  		require.Errorf(t, err, "Build should have been aborted with lint errors")
   157  		assert.Contains(t, err.Error(), "lint errors were detected")
   158  	})
   159  
   160  	t.Run("ignores lint error with --no-lint", func(t *testing.T) {
   161  		p := porter.NewTestPorter(t)
   162  		defer p.Close()
   163  
   164  		testMixins := p.Mixins.(*mixin.TestMixinProvider)
   165  		testMixins.LintResults = lintResults
   166  
   167  		err := p.Create()
   168  		require.NoError(t, err, "Create failed")
   169  
   170  		opts := porter.BuildOptions{NoLint: true}
   171  		err = opts.Validate(p.Porter)
   172  		require.NoError(t, err)
   173  
   174  		err = p.Build(context.Background(), opts)
   175  		require.NoError(t, err, "Build failed but should have not run lint")
   176  	})
   177  
   178  }
   179  
   180  func TestPorter_paramRequired(t *testing.T) {
   181  	p := porter.NewTestPorter(t)
   182  	defer p.Close()
   183  
   184  	p.TestConfig.TestContext.AddTestFile("./testdata/paramafest.yaml", config.Name)
   185  
   186  	ctx := context.Background()
   187  	opts := porter.BuildOptions{}
   188  	require.NoError(t, opts.Validate(p.Porter), "Validate failed")
   189  
   190  	err := p.Build(ctx, opts)
   191  
   192  	bundleBytes, err := p.FileSystem.ReadFile(build.LOCAL_BUNDLE)
   193  	require.NoError(t, err)
   194  
   195  	var bundle bundle.Bundle
   196  	err = json.Unmarshal(bundleBytes, &bundle)
   197  	require.NoError(t, err)
   198  
   199  	require.False(t, bundle.Parameters["command"].Required, "expected command param to not be required")
   200  	require.True(t, bundle.Parameters["command2"].Required, "expected command2 param to be required")
   201  }
   202  
   203  func TestBuildOptions_Validate(t *testing.T) {
   204  	p := porter.NewTestPorter(t)
   205  	defer p.Close()
   206  
   207  	p.TestConfig.TestContext.AddTestFile("./testdata/porter.yaml", config.Name)
   208  
   209  	testcases := []struct {
   210  		name       string
   211  		opts       porter.BuildOptions
   212  		wantDriver string
   213  		wantError  string
   214  	}{{
   215  		name:       "no opts",
   216  		opts:       porter.BuildOptions{},
   217  		wantDriver: config.BuildDriverBuildkit,
   218  	}, {
   219  		name:      "invalid version set - latest",
   220  		opts:      porter.BuildOptions{MetadataOpts: porter.MetadataOpts{Version: "latest"}},
   221  		wantError: `invalid bundle version: "latest" is not a valid semantic version`,
   222  	}, {
   223  		name: "valid version - v prefix",
   224  		opts: porter.BuildOptions{MetadataOpts: porter.MetadataOpts{Version: "v1.0.0"}},
   225  	}, {
   226  		name: "valid version - with hash",
   227  		opts: porter.BuildOptions{MetadataOpts: porter.MetadataOpts{Version: "v0.1.7+58d98af56c3a4c40c69535654216bd4a1fa701e7"}},
   228  	}, {
   229  		name: "valid name and value set",
   230  		opts: porter.BuildOptions{MetadataOpts: porter.MetadataOpts{Name: "newname", Version: "1.0.0"}},
   231  	}, {
   232  		name:      "deprecated driver: docker",
   233  		opts:      porter.BuildOptions{Driver: config.BuildDriverDocker},
   234  		wantError: `invalid --driver value docker`,
   235  	}, {
   236  		name:       "valid driver: buildkit",
   237  		opts:       porter.BuildOptions{Driver: config.BuildDriverBuildkit},
   238  		wantDriver: config.BuildDriverBuildkit,
   239  	}, {
   240  		name:      "invalid driver",
   241  		opts:      porter.BuildOptions{Driver: "missing-driver"},
   242  		wantError: `invalid --driver value missing-driver`,
   243  	}}
   244  
   245  	for _, tc := range testcases {
   246  		t.Run(tc.name, func(t *testing.T) {
   247  			err := tc.opts.Validate(p.Porter)
   248  			if tc.wantError != "" {
   249  				require.EqualError(t, err, tc.wantError)
   250  			} else {
   251  				require.NoError(t, err)
   252  
   253  				if tc.wantDriver != "" {
   254  					assert.Equal(t, tc.wantDriver, p.Data.BuildDriver)
   255  				}
   256  			}
   257  		})
   258  	}
   259  }
   260  
   261  func TestBuildOptions_Defaults(t *testing.T) {
   262  	p := porter.NewTestPorter(t)
   263  	defer p.Close()
   264  
   265  	p.TestConfig.TestContext.AddTestFile("./testdata/porter.yaml", config.Name)
   266  
   267  	t.Run("default driver", func(t *testing.T) {
   268  		opts := porter.BuildOptions{}
   269  		err := opts.Validate(p.Porter)
   270  		require.NoError(t, err, "Validate failed")
   271  		assert.Equal(t, config.BuildDriverBuildkit, opts.Driver)
   272  	})
   273  }
   274  
   275  func TestPorter_BuildWithCustomValues(t *testing.T) {
   276  	p := porter.NewTestPorter(t)
   277  	defer p.Close()
   278  
   279  	p.TestConfig.TestContext.AddTestFile("./testdata/porter.yaml", config.Name)
   280  
   281  	ctx := context.Background()
   282  
   283  	opts := porter.BuildOptions{Customs: []string{"customKey1=editedCustomValue1"}}
   284  	require.NoError(t, opts.Validate(p.Porter), "Validate failed")
   285  
   286  	err := p.Build(ctx, opts)
   287  	require.NoError(t, err)
   288  
   289  	bun, err := p.CNAB.LoadBundle(build.LOCAL_BUNDLE)
   290  	require.NoError(t, err)
   291  
   292  	assert.Equal(t, bun.Custom["customKey1"], "editedCustomValue1")
   293  }
   294  
   295  func TestPorter_BuildWithPreserveTags(t *testing.T) {
   296  	p := porter.NewTestPorter(t)
   297  	defer p.Close()
   298  
   299  	p.TestConfig.TestContext.AddTestFile("./testdata/porter-with-image-tag.yaml", config.Name)
   300  
   301  	ctx := context.Background()
   302  	opts := porter.BuildOptions{
   303  		BundleDefinitionOptions: porter.BundleDefinitionOptions{
   304  			PreserveTags: true,
   305  		},
   306  	}
   307  	require.NoError(t, opts.Validate(p.Porter), "Validate failed")
   308  
   309  	err := p.Build(ctx, opts)
   310  	require.NoError(t, err)
   311  
   312  	bun, err := p.CNAB.LoadBundle(build.LOCAL_BUNDLE)
   313  	require.NoError(t, err)
   314  
   315  	stamp, err := configadapter.LoadStamp(bun)
   316  	require.NoError(t, err)
   317  	assert.Equal(t, stamp.PreserveTags, true)
   318  	assert.Empty(t, bun.Images["something"].Digest)
   319  }