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

     1  package client_test
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"testing"
    11  
    12  	"github.com/buildpacks/imgutil/fakes"
    13  	"github.com/buildpacks/lifecycle/api"
    14  	"github.com/golang/mock/gomock"
    15  	"github.com/google/go-containerregistry/pkg/v1/empty"
    16  	"github.com/google/go-containerregistry/pkg/v1/layout"
    17  	"github.com/google/go-containerregistry/pkg/v1/mutate"
    18  	"github.com/heroku/color"
    19  	"github.com/pkg/errors"
    20  	"github.com/sclevine/spec"
    21  	"github.com/sclevine/spec/report"
    22  
    23  	cfg "github.com/buildpacks/pack/internal/config"
    24  	"github.com/buildpacks/pack/pkg/archive"
    25  	"github.com/buildpacks/pack/pkg/blob"
    26  	"github.com/buildpacks/pack/pkg/buildpack"
    27  	"github.com/buildpacks/pack/pkg/client"
    28  	"github.com/buildpacks/pack/pkg/dist"
    29  	"github.com/buildpacks/pack/pkg/image"
    30  	"github.com/buildpacks/pack/pkg/logging"
    31  	"github.com/buildpacks/pack/pkg/testmocks"
    32  	h "github.com/buildpacks/pack/testhelpers"
    33  )
    34  
    35  const buildpackageMetadataTag = `{
    36    "id": "some/top-buildpack",
    37    "version": "0.0.1",
    38    "name": "top",
    39    "homepage": "top-buildpack-homepage",
    40    "stacks": [
    41      {
    42        "id": "io.buildpacks.stacks.first-stack"
    43      },
    44      {
    45        "id": "io.buildpacks.stacks.second-stack"
    46      }
    47    ]
    48  }`
    49  
    50  const buildpackLayersTag = `{
    51     "some/first-inner-buildpack":{
    52        "1.0.0":{
    53           "api":"0.2",
    54           "order":[
    55              {
    56                 "group":[
    57                    {
    58                       "id":"some/first-inner-buildpack",
    59                       "version":"1.0.0"
    60                    },
    61                    {
    62                       "id":"some/second-inner-buildpack",
    63                       "version":"3.0.0"
    64                    }
    65                 ]
    66              },
    67              {
    68                 "group":[
    69                    {
    70                       "id":"some/second-inner-buildpack",
    71                       "version":"3.0.0"
    72                    }
    73                 ]
    74              }
    75           ],
    76           "stacks":[
    77              {
    78                 "id":"io.buildpacks.stacks.first-stack"
    79              },
    80              {
    81                 "id":"io.buildpacks.stacks.second-stack"
    82              }
    83           ],
    84           "layerDiffID":"sha256:first-inner-buildpack-diff-id",
    85           "homepage":"first-inner-buildpack-homepage"
    86        }
    87     },
    88     "some/second-inner-buildpack":{
    89        "2.0.0":{
    90           "api":"0.2",
    91           "stacks":[
    92              {
    93                 "id":"io.buildpacks.stacks.first-stack"
    94              },
    95              {
    96                 "id":"io.buildpacks.stacks.second-stack"
    97              }
    98           ],
    99           "layerDiffID":"sha256:second-inner-buildpack-diff-id",
   100           "homepage":"second-inner-buildpack-homepage"
   101        },
   102        "3.0.0":{
   103           "api":"0.2",
   104           "stacks":[
   105              {
   106                 "id":"io.buildpacks.stacks.first-stack"
   107              },
   108              {
   109                 "id":"io.buildpacks.stacks.second-stack"
   110              }
   111           ],
   112           "layerDiffID":"sha256:third-inner-buildpack-diff-id",
   113           "homepage":"third-inner-buildpack-homepage"
   114        }
   115     },
   116     "some/top-buildpack":{
   117        "0.0.1":{
   118           "api":"0.2",
   119           "order":[
   120              {
   121                 "group":[
   122                    {
   123                       "id":"some/first-inner-buildpack",
   124                       "version":"1.0.0"
   125                    },
   126                    {
   127                       "id":"some/second-inner-buildpack",
   128                       "version":"2.0.0"
   129                    }
   130                 ]
   131              },
   132              {
   133                 "group":[
   134                    {
   135                       "id":"some/first-inner-buildpack",
   136                       "version":"1.0.0"
   137                    }
   138                 ]
   139              }
   140           ],
   141           "layerDiffID":"sha256:top-buildpack-diff-id",
   142           "homepage":"top-buildpack-homepage",
   143  		 "name": "top"
   144        }
   145     }
   146  }`
   147  
   148  func TestInspectBuildpack(t *testing.T) {
   149  	color.Disable(true)
   150  	defer color.Disable(false)
   151  	spec.Run(t, "InspectBuilder", testInspectBuildpack, spec.Sequential(), spec.Report(report.Terminal{}))
   152  }
   153  
   154  func testInspectBuildpack(t *testing.T, when spec.G, it spec.S) {
   155  	var (
   156  		subject          *client.Client
   157  		mockImageFetcher *testmocks.MockImageFetcher
   158  		mockController   *gomock.Controller
   159  		out              bytes.Buffer
   160  		buildpackImage   *fakes.Image
   161  		apiVersion       *api.Version
   162  		expectedInfo     *client.BuildpackInfo
   163  		mockDownloader   *testmocks.MockBlobDownloader
   164  
   165  		tmpDir        string
   166  		buildpackPath string
   167  	)
   168  
   169  	it.Before(func() {
   170  		mockController = gomock.NewController(t)
   171  		mockImageFetcher = testmocks.NewMockImageFetcher(mockController)
   172  		mockDownloader = testmocks.NewMockBlobDownloader(mockController)
   173  
   174  		subject = &client.Client{}
   175  		client.WithLogger(logging.NewLogWithWriters(&out, &out))(subject)
   176  		client.WithFetcher(mockImageFetcher)(subject)
   177  		client.WithDownloader(mockDownloader)(subject)
   178  
   179  		buildpackImage = fakes.NewImage("some/buildpack", "", nil)
   180  		h.AssertNil(t, buildpackImage.SetLabel(buildpack.MetadataLabel, buildpackageMetadataTag))
   181  		h.AssertNil(t, buildpackImage.SetLabel(dist.BuildpackLayersLabel, buildpackLayersTag))
   182  
   183  		var err error
   184  		apiVersion, err = api.NewVersion("0.2")
   185  		h.AssertNil(t, err)
   186  
   187  		tmpDir, err = os.MkdirTemp("", "inspectBuildpack")
   188  		h.AssertNil(t, err)
   189  
   190  		buildpackPath = filepath.Join(tmpDir, "buildpackTarFile.tar")
   191  
   192  		expectedInfo = &client.BuildpackInfo{
   193  			BuildpackMetadata: buildpack.Metadata{
   194  				ModuleInfo: dist.ModuleInfo{
   195  					ID:       "some/top-buildpack",
   196  					Version:  "0.0.1",
   197  					Name:     "top",
   198  					Homepage: "top-buildpack-homepage",
   199  				},
   200  				Stacks: []dist.Stack{
   201  					{ID: "io.buildpacks.stacks.first-stack"},
   202  					{ID: "io.buildpacks.stacks.second-stack"},
   203  				},
   204  			},
   205  			Buildpacks: []dist.ModuleInfo{
   206  				{
   207  					ID:       "some/first-inner-buildpack",
   208  					Version:  "1.0.0",
   209  					Homepage: "first-inner-buildpack-homepage",
   210  				},
   211  				{
   212  					ID:       "some/second-inner-buildpack",
   213  					Version:  "2.0.0",
   214  					Homepage: "second-inner-buildpack-homepage",
   215  				},
   216  				{
   217  					ID:       "some/second-inner-buildpack",
   218  					Version:  "3.0.0",
   219  					Homepage: "third-inner-buildpack-homepage",
   220  				},
   221  				{
   222  					ID:       "some/top-buildpack",
   223  					Version:  "0.0.1",
   224  					Name:     "top",
   225  					Homepage: "top-buildpack-homepage",
   226  				},
   227  			},
   228  			Order: dist.Order{
   229  				{
   230  					Group: []dist.ModuleRef{
   231  						{
   232  							ModuleInfo: dist.ModuleInfo{
   233  								ID:       "some/top-buildpack",
   234  								Version:  "0.0.1",
   235  								Name:     "top",
   236  								Homepage: "top-buildpack-homepage",
   237  							},
   238  							Optional: false,
   239  						},
   240  					},
   241  				},
   242  			},
   243  			BuildpackLayers: dist.ModuleLayers{
   244  				"some/first-inner-buildpack": {
   245  					"1.0.0": {
   246  						API: apiVersion,
   247  						Stacks: []dist.Stack{
   248  							{ID: "io.buildpacks.stacks.first-stack"},
   249  							{ID: "io.buildpacks.stacks.second-stack"},
   250  						},
   251  						Order: dist.Order{
   252  							{
   253  								Group: []dist.ModuleRef{
   254  									{
   255  										ModuleInfo: dist.ModuleInfo{
   256  											ID:      "some/first-inner-buildpack",
   257  											Version: "1.0.0",
   258  										},
   259  										Optional: false,
   260  									},
   261  									{
   262  										ModuleInfo: dist.ModuleInfo{
   263  											ID:      "some/second-inner-buildpack",
   264  											Version: "3.0.0",
   265  										},
   266  										Optional: false,
   267  									},
   268  								},
   269  							},
   270  							{
   271  								Group: []dist.ModuleRef{
   272  									{
   273  										ModuleInfo: dist.ModuleInfo{
   274  											ID:      "some/second-inner-buildpack",
   275  											Version: "3.0.0",
   276  										},
   277  										Optional: false,
   278  									},
   279  								},
   280  							},
   281  						},
   282  						LayerDiffID: "sha256:first-inner-buildpack-diff-id",
   283  						Homepage:    "first-inner-buildpack-homepage",
   284  					},
   285  				},
   286  				"some/second-inner-buildpack": {
   287  					"2.0.0": {
   288  						API: apiVersion,
   289  						Stacks: []dist.Stack{
   290  							{ID: "io.buildpacks.stacks.first-stack"},
   291  							{ID: "io.buildpacks.stacks.second-stack"},
   292  						},
   293  						LayerDiffID: "sha256:second-inner-buildpack-diff-id",
   294  						Homepage:    "second-inner-buildpack-homepage",
   295  					},
   296  					"3.0.0": {
   297  						API: apiVersion,
   298  						Stacks: []dist.Stack{
   299  							{ID: "io.buildpacks.stacks.first-stack"},
   300  							{ID: "io.buildpacks.stacks.second-stack"},
   301  						},
   302  						LayerDiffID: "sha256:third-inner-buildpack-diff-id",
   303  						Homepage:    "third-inner-buildpack-homepage",
   304  					},
   305  				},
   306  				"some/top-buildpack": {
   307  					"0.0.1": {
   308  						API: apiVersion,
   309  						Order: dist.Order{
   310  							{
   311  								Group: []dist.ModuleRef{
   312  									{
   313  										ModuleInfo: dist.ModuleInfo{
   314  											ID:      "some/first-inner-buildpack",
   315  											Version: "1.0.0",
   316  										},
   317  										Optional: false,
   318  									},
   319  									{
   320  										ModuleInfo: dist.ModuleInfo{
   321  											ID:      "some/second-inner-buildpack",
   322  											Version: "2.0.0",
   323  										},
   324  										Optional: false,
   325  									},
   326  								},
   327  							},
   328  							{
   329  								Group: []dist.ModuleRef{
   330  									{
   331  										ModuleInfo: dist.ModuleInfo{
   332  											ID:      "some/first-inner-buildpack",
   333  											Version: "1.0.0",
   334  										},
   335  										Optional: false,
   336  									},
   337  								},
   338  							},
   339  						},
   340  						LayerDiffID: "sha256:top-buildpack-diff-id",
   341  						Homepage:    "top-buildpack-homepage",
   342  						Name:        "top",
   343  					},
   344  				},
   345  			},
   346  		}
   347  	})
   348  
   349  	it.After(func() {
   350  		mockController.Finish()
   351  		err := os.RemoveAll(tmpDir)
   352  		if runtime.GOOS != "windows" {
   353  			h.AssertNil(t, err)
   354  		}
   355  	})
   356  
   357  	when("inspect-buildpack", func() {
   358  		when("inspecting a registry buildpack", func() {
   359  			var registryFixture string
   360  			var configPath string
   361  			it.Before(func() {
   362  				expectedInfo.Location = buildpack.RegistryLocator
   363  
   364  				registryFixture = h.CreateRegistryFixture(t, tmpDir, filepath.Join("testdata", "registry"))
   365  				packHome := filepath.Join(tmpDir, "packHome")
   366  				h.AssertNil(t, os.Setenv("PACK_HOME", packHome))
   367  
   368  				configPath = filepath.Join(packHome, "config.toml")
   369  				h.AssertNil(t, cfg.Write(cfg.Config{
   370  					Registries: []cfg.Registry{
   371  						{
   372  							Name: "some-registry",
   373  							Type: "github",
   374  							URL:  registryFixture,
   375  						},
   376  					},
   377  				}, configPath))
   378  
   379  				mockImageFetcher.EXPECT().Fetch(
   380  					gomock.Any(),
   381  					"example.com/some/package@sha256:8c27fe111c11b722081701dfed3bd55e039b9ce92865473cf4cdfa918071c566",
   382  					image.FetchOptions{Daemon: false, PullPolicy: image.PullNever}).Return(buildpackImage, nil)
   383  			})
   384  
   385  			it.After(func() {
   386  				h.AssertNil(t, os.Unsetenv("PACK_HOME"))
   387  			})
   388  
   389  			it("succeeds", func() {
   390  				registryBuildpack := "urn:cnb:registry:example/java"
   391  				inspectOptions := client.InspectBuildpackOptions{
   392  					BuildpackName: registryBuildpack,
   393  					Registry:      "some-registry",
   394  				}
   395  				info, err := subject.InspectBuildpack(inspectOptions)
   396  				h.AssertNil(t, err)
   397  
   398  				h.AssertEq(t, info, expectedInfo)
   399  			})
   400  
   401  			// TODO add test case when buildpack is flattened
   402  		})
   403  
   404  		when("inspecting local buildpack archive", func() {
   405  			it.Before(func() {
   406  				expectedInfo.Location = buildpack.URILocator
   407  
   408  				assert := h.NewAssertionManager(t)
   409  				writeBuildpackArchive(buildpackPath, tmpDir, assert)
   410  			})
   411  
   412  			it("succeeds", func() {
   413  				mockDownloader.EXPECT().Download(gomock.Any(), buildpackPath).Return(blob.NewBlob(buildpackPath), nil)
   414  				inspectOptions := client.InspectBuildpackOptions{
   415  					BuildpackName: buildpackPath,
   416  					Daemon:        false,
   417  				}
   418  				info, err := subject.InspectBuildpack(inspectOptions)
   419  				h.AssertNil(t, err)
   420  
   421  				h.AssertEq(t, info, expectedInfo)
   422  			})
   423  
   424  			// TODO add test case when buildpack is flattened
   425  		})
   426  
   427  		when("inspecting an image", func() {
   428  			for _, useDaemon := range []bool{true, false} {
   429  				useDaemon := useDaemon
   430  				when(fmt.Sprintf("daemon is %t", useDaemon), func() {
   431  					it.Before(func() {
   432  						expectedInfo.Location = buildpack.PackageLocator
   433  						if useDaemon {
   434  							mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/buildpack", image.FetchOptions{Daemon: true, PullPolicy: image.PullNever}).Return(buildpackImage, nil)
   435  						} else {
   436  							mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/buildpack", image.FetchOptions{Daemon: false, PullPolicy: image.PullNever}).Return(buildpackImage, nil)
   437  						}
   438  					})
   439  
   440  					it("succeeds", func() {
   441  						inspectOptions := client.InspectBuildpackOptions{
   442  							BuildpackName: "docker://some/buildpack",
   443  							Daemon:        useDaemon,
   444  						}
   445  						info, err := subject.InspectBuildpack(inspectOptions)
   446  						h.AssertNil(t, err)
   447  
   448  						h.AssertEq(t, info, expectedInfo)
   449  					})
   450  				})
   451  			}
   452  		})
   453  	})
   454  	when("failure cases", func() {
   455  		when("invalid buildpack name", func() {
   456  			it("returns an error", func() {
   457  				invalidBuildpackName := ""
   458  				inspectOptions := client.InspectBuildpackOptions{
   459  					BuildpackName: invalidBuildpackName,
   460  				}
   461  				_, err := subject.InspectBuildpack(inspectOptions)
   462  
   463  				h.AssertError(t, err, "unable to handle locator ")
   464  				h.AssertFalse(t, errors.Is(err, image.ErrNotFound))
   465  			})
   466  		})
   467  		when("buildpack image", func() {
   468  			when("unable to fetch buildpack image", func() {
   469  				it.Before(func() {
   470  					mockImageFetcher.EXPECT().Fetch(gomock.Any(), "missing/buildpack", image.FetchOptions{Daemon: true, PullPolicy: image.PullNever}).Return(nil, errors.Wrapf(image.ErrNotFound, "big bad error"))
   471  				})
   472  				it("returns an ErrNotFound error", func() {
   473  					inspectOptions := client.InspectBuildpackOptions{
   474  						BuildpackName: "docker://missing/buildpack",
   475  						Daemon:        true,
   476  					}
   477  					_, err := subject.InspectBuildpack(inspectOptions)
   478  					h.AssertTrue(t, errors.Is(err, image.ErrNotFound))
   479  				})
   480  			})
   481  
   482  			when("image does not have buildpackage metadata", func() {
   483  				it.Before(func() {
   484  					fakeImage := fakes.NewImage("empty", "", nil)
   485  					h.AssertNil(t, fakeImage.SetLabel(dist.BuildpackLayersLabel, ":::"))
   486  					mockImageFetcher.EXPECT().Fetch(gomock.Any(), "missing-metadata/buildpack", image.FetchOptions{Daemon: true, PullPolicy: image.PullNever}).Return(fakeImage, nil)
   487  				})
   488  				it("returns an error", func() {
   489  					inspectOptions := client.InspectBuildpackOptions{
   490  						BuildpackName: "docker://missing-metadata/buildpack",
   491  						Daemon:        true,
   492  					}
   493  					_, err := subject.InspectBuildpack(inspectOptions)
   494  
   495  					h.AssertError(t, err, fmt.Sprintf("unable to get image label %s", dist.BuildpackLayersLabel))
   496  					h.AssertFalse(t, errors.Is(err, image.ErrNotFound))
   497  				})
   498  			})
   499  		})
   500  		when("buildpack archive", func() {
   501  			when("archive is not a buildpack", func() {
   502  				it.Before(func() {
   503  					invalidBuildpackPath := filepath.Join(tmpDir, "fake-buildpack-path")
   504  					h.AssertNil(t, os.WriteFile(invalidBuildpackPath, []byte("not a buildpack"), os.ModePerm))
   505  
   506  					mockDownloader.EXPECT().Download(gomock.Any(), "https://invalid/buildpack").Return(blob.NewBlob(invalidBuildpackPath), nil)
   507  				})
   508  				it("returns an error", func() {
   509  					inspectOptions := client.InspectBuildpackOptions{
   510  						BuildpackName: "https://invalid/buildpack",
   511  						Daemon:        true,
   512  					}
   513  
   514  					_, err := subject.InspectBuildpack(inspectOptions)
   515  					h.AssertNotNil(t, err)
   516  					h.AssertFalse(t, errors.Is(err, image.ErrNotFound))
   517  					h.AssertError(t, err, "unable to fetch config from buildpack blob:")
   518  				})
   519  			})
   520  			when("unable to download buildpack archive", func() {
   521  				it.Before(func() {
   522  					mockDownloader.EXPECT().Download(gomock.Any(), "https://missing/buildpack").Return(nil, errors.New("unable to download archive"))
   523  				})
   524  				it("returns a untyped error", func() {
   525  					inspectOptions := client.InspectBuildpackOptions{
   526  						BuildpackName: "https://missing/buildpack",
   527  						Daemon:        true,
   528  					}
   529  
   530  					_, err := subject.InspectBuildpack(inspectOptions)
   531  					h.AssertNotNil(t, err)
   532  					h.AssertFalse(t, errors.Is(err, image.ErrNotFound))
   533  					h.AssertError(t, err, "unable to download archive")
   534  				})
   535  			})
   536  		})
   537  
   538  		when("buildpack on registry", func() {
   539  			when("unable to get registry", func() {
   540  				it("returns an error", func() {
   541  					registryBuildpack := "urn:cnb:registry:example/foo"
   542  					inspectOptions := client.InspectBuildpackOptions{
   543  						BuildpackName: registryBuildpack,
   544  						Daemon:        true,
   545  						Registry:      ":::",
   546  					}
   547  
   548  					_, err := subject.InspectBuildpack(inspectOptions)
   549  
   550  					h.AssertError(t, err, "invalid registry :::")
   551  					h.AssertFalse(t, errors.Is(err, image.ErrNotFound))
   552  				})
   553  			})
   554  			when("buildpack is not on registry", func() {
   555  				var registryFixture string
   556  				var configPath string
   557  
   558  				it.Before(func() {
   559  					registryFixture = h.CreateRegistryFixture(t, tmpDir, filepath.Join("testdata", "registry"))
   560  					packHome := filepath.Join(tmpDir, "packHome")
   561  					h.AssertNil(t, os.Setenv("PACK_HOME", packHome))
   562  					configPath = filepath.Join(packHome, "config.toml")
   563  					h.AssertNil(t, cfg.Write(cfg.Config{
   564  						Registries: []cfg.Registry{
   565  							{
   566  								Name: "some-registry",
   567  								Type: "github",
   568  								URL:  registryFixture,
   569  							},
   570  						},
   571  					}, configPath))
   572  				})
   573  				it("returns an error", func() {
   574  					registryBuildpack := "urn:cnb:registry:example/not-present"
   575  					inspectOptions := client.InspectBuildpackOptions{
   576  						BuildpackName: registryBuildpack,
   577  						Daemon:        true,
   578  						Registry:      "some-registry",
   579  					}
   580  
   581  					_, err := subject.InspectBuildpack(inspectOptions)
   582  
   583  					h.AssertError(t, err, "unable to find 'urn:cnb:registry:example/not-present' in registry:")
   584  				})
   585  			})
   586  			when("unable to fetch buildpack from registry", func() {
   587  				var registryFixture string
   588  				var configPath string
   589  
   590  				it.Before(func() {
   591  					registryFixture = h.CreateRegistryFixture(t, tmpDir, filepath.Join("testdata", "registry"))
   592  					packHome := filepath.Join(tmpDir, "packHome")
   593  					h.AssertNil(t, os.Setenv("PACK_HOME", packHome))
   594  
   595  					configPath = filepath.Join(packHome, "config.toml")
   596  					h.AssertNil(t, cfg.Write(cfg.Config{
   597  						Registries: []cfg.Registry{
   598  							{
   599  								Name: "some-registry",
   600  								Type: "github",
   601  								URL:  registryFixture,
   602  							},
   603  						},
   604  					}, configPath))
   605  					mockImageFetcher.EXPECT().Fetch(
   606  						gomock.Any(),
   607  						"example.com/some/package@sha256:2560f05307e8de9d830f144d09556e19dd1eb7d928aee900ed02208ae9727e7a",
   608  						image.FetchOptions{Daemon: false, PullPolicy: image.PullNever}).Return(nil, image.ErrNotFound)
   609  				})
   610  				it("returns an untyped error", func() {
   611  					registryBuildpack := "urn:cnb:registry:example/foo"
   612  					inspectOptions := client.InspectBuildpackOptions{
   613  						BuildpackName: registryBuildpack,
   614  						Daemon:        true,
   615  						Registry:      "some-registry",
   616  					}
   617  
   618  					_, err := subject.InspectBuildpack(inspectOptions)
   619  					h.AssertNotNil(t, err)
   620  					h.AssertFalse(t, errors.Is(err, image.ErrNotFound))
   621  					h.AssertError(t, err, "error pulling registry specified image")
   622  				})
   623  			})
   624  		})
   625  	})
   626  }
   627  
   628  // write an OCI image using GGCR lib
   629  func writeBuildpackArchive(buildpackPath, tmpDir string, assert h.AssertionManager) {
   630  	layoutDir := filepath.Join(tmpDir, "layout")
   631  	imgIndex := empty.Index
   632  	img := empty.Image
   633  	c, err := img.ConfigFile()
   634  	assert.Nil(err)
   635  
   636  	c.Config.Labels = map[string]string{}
   637  	c.Config.Labels[buildpack.MetadataLabel] = buildpackageMetadataTag
   638  	c.Config.Labels[dist.BuildpackLayersLabel] = buildpackLayersTag
   639  	img, err = mutate.Config(img, c.Config)
   640  	assert.Nil(err)
   641  
   642  	p, err := layout.Write(layoutDir, imgIndex)
   643  	assert.Nil(err)
   644  
   645  	assert.Nil(p.AppendImage(img))
   646  	assert.Nil(err)
   647  
   648  	buildpackWriter, err := os.Create(buildpackPath)
   649  	assert.Nil(err)
   650  	defer buildpackWriter.Close()
   651  
   652  	tw := tar.NewWriter(buildpackWriter)
   653  	defer tw.Close()
   654  
   655  	assert.Nil(archive.WriteDirToTar(tw, layoutDir, "/", 0, 0, 0755, true, false, nil))
   656  }