github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/client/manifest_add_test.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/buildpacks/imgutil"
    12  	"github.com/golang/mock/gomock"
    13  	"github.com/google/go-containerregistry/pkg/authn"
    14  	"github.com/heroku/color"
    15  	"github.com/sclevine/spec"
    16  	"github.com/sclevine/spec/report"
    17  
    18  	ifakes "github.com/buildpacks/pack/internal/fakes"
    19  	"github.com/buildpacks/pack/pkg/logging"
    20  	"github.com/buildpacks/pack/pkg/testmocks"
    21  	h "github.com/buildpacks/pack/testhelpers"
    22  )
    23  
    24  func TestAddManifest(t *testing.T) {
    25  	color.Disable(true)
    26  	defer color.Disable(false)
    27  
    28  	spec.Run(t, "build", testAddManifest, spec.Report(report.Terminal{}))
    29  }
    30  
    31  func testAddManifest(t *testing.T, when spec.G, it spec.S) {
    32  	var (
    33  		mockController   *gomock.Controller
    34  		mockIndexFactory *testmocks.MockIndexFactory
    35  		fakeImageFetcher *ifakes.FakeImageFetcher
    36  		out              bytes.Buffer
    37  		logger           logging.Logger
    38  		subject          *Client
    39  		err              error
    40  		tmpDir           string
    41  	)
    42  
    43  	it.Before(func() {
    44  		fakeImageFetcher = ifakes.NewFakeImageFetcher()
    45  		logger = logging.NewLogWithWriters(&out, &out, logging.WithVerbose())
    46  		mockController = gomock.NewController(t)
    47  		mockIndexFactory = testmocks.NewMockIndexFactory(mockController)
    48  
    49  		tmpDir, err = os.MkdirTemp("", "add-manifest-test")
    50  		h.AssertNil(t, err)
    51  		os.Setenv("XDG_RUNTIME_DIR", tmpDir)
    52  
    53  		subject, err = NewClient(
    54  			WithLogger(logger),
    55  			WithFetcher(fakeImageFetcher),
    56  			WithIndexFactory(mockIndexFactory),
    57  			WithExperimental(true),
    58  			WithKeychain(authn.DefaultKeychain),
    59  		)
    60  		h.AssertSameInstance(t, mockIndexFactory, subject.indexFactory)
    61  		h.AssertNil(t, err)
    62  
    63  		// Create a remote image to be fetched when adding to the image index
    64  		fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, "pack/image", nil)
    65  		fakeImageFetcher.RemoteImages["index.docker.io/pack/image:latest"] = fakeImage
    66  	})
    67  	it.After(func() {
    68  		mockController.Finish()
    69  		os.RemoveAll(tmpDir)
    70  	})
    71  
    72  	when("#AddManifest", func() {
    73  		when("index doesn't exist", func() {
    74  			it.Before(func() {
    75  				mockIndexFactory.EXPECT().LoadIndex(gomock.Any(), gomock.Any()).Return(nil, errors.New("index not found locally"))
    76  			})
    77  
    78  			it("should return an error", func() {
    79  				err = subject.AddManifest(
    80  					context.TODO(),
    81  					ManifestAddOptions{
    82  						IndexRepoName: "pack/none-existent-index",
    83  						RepoName:      "pack/image",
    84  					},
    85  				)
    86  				h.AssertError(t, err, "index not found locally")
    87  			})
    88  		})
    89  
    90  		when("index exists", func() {
    91  			var (
    92  				indexPath     string
    93  				indexRepoName string
    94  			)
    95  
    96  			when("no errors on save", func() {
    97  				when("valid manifest is provided", func() {
    98  					it.Before(func() {
    99  						indexRepoName = h.NewRandomIndexRepoName()
   100  						indexPath = filepath.Join(tmpDir, imgutil.MakeFileSafeName(indexRepoName))
   101  						// Initialize the Index with 2 image manifest
   102  						idx := h.RandomCNBIndex(t, indexRepoName, 1, 2)
   103  						h.AssertNil(t, idx.SaveDir())
   104  						mockIndexFactory.EXPECT().LoadIndex(gomock.Eq(indexRepoName), gomock.Any()).Return(idx, nil)
   105  					})
   106  
   107  					it("adds the given image", func() {
   108  						err = subject.AddManifest(
   109  							context.TODO(),
   110  							ManifestAddOptions{
   111  								IndexRepoName: indexRepoName,
   112  								RepoName:      "pack/image",
   113  							},
   114  						)
   115  						h.AssertNil(t, err)
   116  						h.AssertContains(t, out.String(), "Successfully added image 'pack/image' to index")
   117  
   118  						// We expect one more manifest to be added
   119  						index := h.ReadIndexManifest(t, indexPath)
   120  						h.AssertEq(t, len(index.Manifests), 3)
   121  					})
   122  				})
   123  
   124  				when("invalid manifest reference name is used", func() {
   125  					it.Before(func() {
   126  						indexRepoName = h.NewRandomIndexRepoName()
   127  						indexPath = filepath.Join(tmpDir, imgutil.MakeFileSafeName(indexRepoName))
   128  						// Initialize the Index with 2 image manifest
   129  						idx := h.RandomCNBIndex(t, indexRepoName, 1, 2)
   130  						mockIndexFactory.EXPECT().LoadIndex(gomock.Eq(indexRepoName), gomock.Any()).Return(idx, nil)
   131  					})
   132  
   133  					it("errors a message", func() {
   134  						err = subject.AddManifest(
   135  							context.TODO(),
   136  							ManifestAddOptions{
   137  								IndexRepoName: indexRepoName,
   138  								RepoName:      "pack@@image",
   139  							},
   140  						)
   141  						h.AssertNotNil(t, err)
   142  						h.AssertError(t, err, "is not a valid manifest reference")
   143  					})
   144  				})
   145  
   146  				when("when manifest reference doesn't exist in the registry", func() {
   147  					it.Before(func() {
   148  						indexRepoName = h.NewRandomIndexRepoName()
   149  						indexPath = filepath.Join(tmpDir, imgutil.MakeFileSafeName(indexRepoName))
   150  						// Initialize the Index with 2 image manifest
   151  						idx := h.RandomCNBIndex(t, indexRepoName, 1, 2)
   152  						mockIndexFactory.EXPECT().LoadIndex(gomock.Eq(indexRepoName), gomock.Any()).Return(idx, nil)
   153  					})
   154  
   155  					it("it errors a message", func() {
   156  						err = subject.AddManifest(
   157  							context.TODO(),
   158  							ManifestAddOptions{
   159  								IndexRepoName: indexRepoName,
   160  								RepoName:      "pack/image-not-found",
   161  							},
   162  						)
   163  						h.AssertNotNil(t, err)
   164  						h.AssertError(t, err, "does not exist in registry")
   165  					})
   166  				})
   167  			})
   168  
   169  			when("errors on save", func() {
   170  				it.Before(func() {
   171  					indexRepoName = h.NewRandomIndexRepoName()
   172  					cnbIdx := h.NewMockImageIndex(t, indexRepoName, 1, 2)
   173  					cnbIdx.ErrorOnSave = true
   174  					mockIndexFactory.
   175  						EXPECT().
   176  						LoadIndex(gomock.Eq(indexRepoName), gomock.Any()).
   177  						Return(cnbIdx, nil).
   178  						AnyTimes()
   179  				})
   180  
   181  				it("errors when the manifest list couldn't be saved locally", func() {
   182  					err = subject.AddManifest(
   183  						context.TODO(),
   184  						ManifestAddOptions{
   185  							IndexRepoName: indexRepoName,
   186  							RepoName:      "pack/image",
   187  						},
   188  					)
   189  					h.AssertNotNil(t, err)
   190  					h.AssertError(t, err, "failed to save manifest list")
   191  				})
   192  			})
   193  		})
   194  	})
   195  }