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

     1  package porter
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"get.porter.sh/porter/pkg/build"
    11  	"get.porter.sh/porter/pkg/cache"
    12  	"get.porter.sh/porter/pkg/cnab"
    13  	cnabtooci "get.porter.sh/porter/pkg/cnab/cnab-to-oci"
    14  	"get.porter.sh/porter/pkg/config"
    15  	"get.porter.sh/porter/pkg/experimental"
    16  	"get.porter.sh/porter/pkg/test"
    17  	"github.com/docker/docker/api/types/image"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func Test_generateInternalManifest(t *testing.T) {
    22  	testcases := []struct {
    23  		name         string
    24  		opts         BuildOptions
    25  		wantErr      string
    26  		wantManifest string
    27  	}{{
    28  		name:         "no opts",
    29  		opts:         BuildOptions{},
    30  		wantManifest: "expected-result.yaml",
    31  	}, {
    32  		name:         "preserve tags",
    33  		opts:         BuildOptions{BundleDefinitionOptions: BundleDefinitionOptions{PreserveTags: true}},
    34  		wantManifest: "expected-result-preserve-tags.yaml",
    35  	}, {
    36  		name: "--file set",
    37  		opts: BuildOptions{
    38  			BundleDefinitionOptions: BundleDefinitionOptions{
    39  				File: "alternate.yaml",
    40  			},
    41  		},
    42  		wantManifest: "expected-result.yaml",
    43  	}, {
    44  		name:         "name set",
    45  		opts:         BuildOptions{MetadataOpts: MetadataOpts{Name: "newname"}},
    46  		wantManifest: "new-name.yaml",
    47  	}, {
    48  		name:         "version set",
    49  		opts:         BuildOptions{MetadataOpts: MetadataOpts{Version: "1.0.0"}},
    50  		wantManifest: "new-version.yaml",
    51  	}, {
    52  		name:         "name and value set",
    53  		opts:         BuildOptions{MetadataOpts: MetadataOpts{Name: "newname", Version: "1.0.0"}},
    54  		wantManifest: "all-fields.yaml",
    55  	}, {
    56  		name:         "custom input set",
    57  		opts:         BuildOptions{Customs: []string{"key1=editedValue1", "key2.nestedKey2=editedValue2"}},
    58  		wantManifest: "custom-input.yaml",
    59  	}, {
    60  		name:         "failed to fetch image metadata",
    61  		opts:         BuildOptions{},
    62  		wantErr:      "failed to fetch image metadata",
    63  		wantManifest: "expected-result.yaml",
    64  	},
    65  	}
    66  
    67  	for _, tc := range testcases {
    68  		tc := tc
    69  		t.Run(tc.name, func(t *testing.T) {
    70  			p := NewTestPorter(t)
    71  			defer p.Close()
    72  
    73  			manifest := config.Name
    74  			if tc.opts.File != "" {
    75  				manifest = tc.opts.File
    76  			}
    77  			p.TestConfig.TestContext.AddTestFile("testdata/generateManifest/original.yaml", manifest)
    78  
    79  			err := tc.opts.Validate(p.Porter)
    80  			require.NoError(t, err)
    81  
    82  			if tc.wantErr == "" {
    83  				p.TestRegistry.MockGetCachedImage = mockGetCachedImage
    84  			} else {
    85  				p.TestRegistry.MockGetImageMetadata = mockGetImageMetadataFailure
    86  			}
    87  
    88  			err = p.generateInternalManifest(context.Background(), tc.opts)
    89  			if tc.wantErr != "" {
    90  				require.ErrorContains(t, err, tc.wantErr)
    91  				return
    92  			}
    93  			require.NoError(t, err)
    94  
    95  			goldenFile := filepath.Join("testdata/generateManifest", tc.wantManifest)
    96  			p.TestConfig.TestContext.AddTestFile(goldenFile, tc.wantManifest)
    97  			got, err := p.FileSystem.ReadFile(build.LOCAL_MANIFEST)
    98  			require.NoError(t, err)
    99  			test.CompareGoldenFile(t, goldenFile, string(got))
   100  		})
   101  	}
   102  }
   103  
   104  func mockGetImageMetadataFailure(ctx context.Context, ref cnab.OCIReference, opts cnabtooci.RegistryOptions) (cnabtooci.ImageMetadata, error) {
   105  	return cnabtooci.ImageMetadata{}, fmt.Errorf("failed to fetch image metadata %s", ref)
   106  }
   107  
   108  func mockGetCachedImage(ctx context.Context, ref cnab.OCIReference) (cnabtooci.ImageMetadata, error) {
   109  	sum := image.InspectResponse{
   110  		ID:          "test-id",
   111  		RepoDigests: []string{"test/whalesayd@sha256:8b92b7269f59e3ed824e811a1ff1ee64f0d44c0218efefada57a4bebc2d7ef6f"},
   112  	}
   113  	return cnabtooci.NewImageSummaryFromInspect(ref, sum)
   114  }
   115  
   116  func Test_getImageLatestDigest(t *testing.T) {
   117  	defaultMockGetCachedImage := func(ctx context.Context, ref cnab.OCIReference) (cnabtooci.ImageMetadata, error) {
   118  		sum := image.InspectResponse{
   119  			ID:          "test-id",
   120  			RepoDigests: []string{"test/repo@sha256:8b92b7269f59e3ed824e811a1ff1ee64f0d44c0218efefada57a4bebc2d7ef6f"},
   121  		}
   122  		return cnabtooci.NewImageSummaryFromInspect(ref, sum)
   123  	}
   124  
   125  	testcases := []struct {
   126  		name                 string
   127  		imgRef               string
   128  		mockGetCachedImage   func(ctx context.Context, ref cnab.OCIReference) (cnabtooci.ImageMetadata, error)
   129  		mockGetImageMetadata func(ctx context.Context, ref cnab.OCIReference, opts cnabtooci.RegistryOptions) (cnabtooci.ImageMetadata, error)
   130  		wantErr              string
   131  		wantDigest           string
   132  	}{{
   133  		name:       "success",
   134  		imgRef:     "test/repo",
   135  		wantDigest: "sha256:8b92b7269f59e3ed824e811a1ff1ee64f0d44c0218efefada57a4bebc2d7ef6f",
   136  	}, {
   137  		name:   "non-default image tag",
   138  		imgRef: "test/repo:v0.1.0",
   139  		mockGetCachedImage: func(ctx context.Context, ref cnab.OCIReference) (cnabtooci.ImageMetadata, error) {
   140  			return cnabtooci.ImageMetadata{}, fmt.Errorf("not in cache")
   141  		},
   142  		mockGetImageMetadata: func(ctx context.Context, ref cnab.OCIReference, opts cnabtooci.RegistryOptions) (cnabtooci.ImageMetadata, error) {
   143  			require.True(t, ref.HasTag())
   144  			require.Equal(t, "v0.1.0", ref.Tag())
   145  			return cnabtooci.ImageMetadata{
   146  				Reference: ref,
   147  				RepoDigests: []string{
   148  					"test/repo@sha256:d2134b0be91e9e293bc872b719538440e5f933e9828cd96430c85d904afb5aa9",
   149  				},
   150  			}, nil
   151  		},
   152  		wantDigest: "sha256:d2134b0be91e9e293bc872b719538440e5f933e9828cd96430c85d904afb5aa9",
   153  	}, {
   154  		name:   "failure",
   155  		imgRef: "test/repo",
   156  		mockGetCachedImage: func(ctx context.Context, ref cnab.OCIReference) (cnabtooci.ImageMetadata, error) {
   157  			return cnabtooci.ImageMetadata{}, errors.New("failed to get cached image")
   158  		},
   159  		mockGetImageMetadata: mockGetImageMetadataFailure,
   160  		wantErr:              "failed to fetch image metadata",
   161  	},
   162  	}
   163  
   164  	for _, tc := range testcases {
   165  		tc := tc
   166  		t.Run(tc.name, func(t *testing.T) {
   167  			p := NewTestPorter(t)
   168  			defer p.Close()
   169  
   170  			ref, err := cnab.ParseOCIReference(tc.imgRef)
   171  			require.NoError(t, err)
   172  
   173  			if tc.mockGetCachedImage != nil {
   174  				p.TestRegistry.MockGetCachedImage = tc.mockGetCachedImage
   175  			} else {
   176  				p.TestRegistry.MockGetCachedImage = defaultMockGetCachedImage
   177  			}
   178  			if tc.mockGetImageMetadata != nil {
   179  				p.TestRegistry.MockGetImageMetadata = tc.mockGetImageMetadata
   180  			}
   181  
   182  			regOpts := cnabtooci.RegistryOptions{}
   183  			digest, err := p.getImageDigest(context.Background(), ref, regOpts)
   184  			if tc.wantErr != "" {
   185  				require.ErrorContains(t, err, tc.wantErr)
   186  				return
   187  			}
   188  
   189  			require.Equal(t, tc.wantDigest, digest.String())
   190  		})
   191  	}
   192  }
   193  
   194  func Test_depv2_bundleDigest(t *testing.T) {
   195  	defaultMockFindBundle := func(ref cnab.OCIReference) (cache.CachedBundle, bool, error) {
   196  		cachedBundle := cache.CachedBundle{
   197  			BundleReference: cnab.BundleReference{
   198  				Reference: ref,
   199  				Digest:    "sha256:3abc67269f59e3ed824e811a1ff1ee64f0d44c0218efefada57a4bebc2d7ef6f",
   200  			},
   201  		}
   202  
   203  		return cachedBundle, true, nil
   204  	}
   205  
   206  	testcases := []struct {
   207  		name             string
   208  		originalManifest string
   209  		wantManifest     string
   210  		wantErr          string
   211  		mockFindBundle   func(ref cnab.OCIReference) (cache.CachedBundle, bool, error)
   212  		mockPullBundle   func(ctx context.Context, ref cnab.OCIReference, opts cnabtooci.RegistryOptions) (cnab.BundleReference, error)
   213  	}{
   214  		{
   215  			name:             "use digest in bundle reference",
   216  			wantManifest:     "expected-result-depv2.yaml",
   217  			originalManifest: "original-depv2.yaml",
   218  		},
   219  		{
   220  			name:             "not found reference",
   221  			wantManifest:     "expected-result-depv2.yaml",
   222  			originalManifest: "original-depv2.yaml",
   223  			mockFindBundle: func(ref cnab.OCIReference) (cache.CachedBundle, bool, error) {
   224  				return cache.CachedBundle{}, false, nil
   225  			},
   226  			mockPullBundle: func(ctx context.Context, ref cnab.OCIReference, opts cnabtooci.RegistryOptions) (cnab.BundleReference, error) {
   227  				return cnab.BundleReference{}, errors.New("failed to pull bundle")
   228  			},
   229  			wantErr: "failed to pull bundle",
   230  		},
   231  		{
   232  			name:             "no default bundle reference",
   233  			wantManifest:     "expected-result-depv2-no-default-ref.yaml",
   234  			originalManifest: "original-depv2-no-default-ref.yaml",
   235  		},
   236  	}
   237  
   238  	for _, tc := range testcases {
   239  		t.Run(tc.name, func(t *testing.T) {
   240  			p := NewTestPorter(t)
   241  			p.SetExperimentalFlags(experimental.FlagDependenciesV2)
   242  			defer p.Close()
   243  			if tc.mockFindBundle != nil {
   244  				p.TestCache.FindBundleMock = tc.mockFindBundle
   245  			} else {
   246  				p.TestCache.FindBundleMock = defaultMockFindBundle
   247  			}
   248  			if tc.mockPullBundle != nil {
   249  				p.TestRegistry.MockPullBundle = tc.mockPullBundle
   250  			}
   251  			p.TestConfig.TestContext.AddTestFile(filepath.Join("testdata/generateManifest", tc.originalManifest), config.Name)
   252  			opts := BuildOptions{}
   253  			require.NoError(t, opts.Validate(p.Porter))
   254  
   255  			err := p.generateInternalManifest(context.Background(), opts)
   256  			if tc.wantErr != "" {
   257  				require.ErrorContains(t, err, tc.wantErr)
   258  				return
   259  			}
   260  			require.NoError(t, err)
   261  
   262  			goldenFile := filepath.Join("testdata/generateManifest", tc.wantManifest)
   263  			p.TestConfig.TestContext.AddTestFile(goldenFile, tc.wantManifest)
   264  			got, err := p.FileSystem.ReadFile(build.LOCAL_MANIFEST)
   265  			require.NoError(t, err)
   266  			test.CompareGoldenFile(t, goldenFile, string(got))
   267  		})
   268  	}
   269  }