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 }