Skip to content

Commit

Permalink
Merge pull request #833 from dwillist/642_inspect_buildpack
Browse files Browse the repository at this point in the history
implement inspect-buildpack command
  • Loading branch information
jromero authored Sep 22, 2020
2 parents 1d20341 + d3de4ae commit 7e3612e
Show file tree
Hide file tree
Showing 12 changed files with 1,768 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ GOTESTFLAGS?=-v -count=1 -parallel=1
PACKAGE_BASE=github.com/buildpacks/pack
PACK_GITSHA1=$(shell git rev-parse --short=7 HEAD)
PACK_VERSION?=0.0.0
TEST_TIMEOUT?=900s
TEST_TIMEOUT?=1200s
UNIT_TIMEOUT?=$(TEST_TIMEOUT)

clean_build := $(strip ${PACK_BUILD})
Expand Down
97 changes: 97 additions & 0 deletions acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1943,6 +1943,103 @@ include = [ "*.jar", "media/mountain.jpg", "media/person.png" ]
})
})
})

when("buildpack is created", func() {
var (
tmpDir string
buildpackList = []string{
"simple-layers-parent-buildpack",
"simple-layers-buildpack",
}

inspectBuildpackTarget string
)

it.Before(func() {
h.SkipUnless(t, pack.Supports("inspect-buildpack"), "version of pack doesn't trust-builder command")

var err error
tmpDir, err = ioutil.TempDir("", "inspect-buildpack-tests")
assert.Nil(err)

// DETERMINE TEST DATA
buildpacksDir := buildpacksDir(lifecycle.EarliestBuildpackAPIVersion())
t.Log("using buildpacks from: ", buildpacksDir)
h.RecursiveCopy(t, buildpacksDir, tmpDir)

for _, v := range buildpackList {
tgz := h.CreateTGZ(t, filepath.Join(buildpacksDir, ""), "./", 0755)
err := os.Rename(tgz, filepath.Join(tmpDir, v+".tgz"))
assert.Nil(err)
}
})
when("buildpack archive", func() {
it.Before(func() {
inspectBuildpackTarget = packageBuildpackAsFile(t,
assert,
pack,
pack.FixtureManager().FixtureLocation("package_for_build_cmd.toml"),
tmpDir,
lifecycle,
buildpackList,
)
})

when("inspect-buildpack", func() {
it("succeeds", func() {
expectedOutput := pack.FixtureManager().TemplateVersionedFixture(
"inspect_%s_buildpack_output.txt",
createBuilderPack.Version(),
"inspect_buildpack_output.txt",
map[string]interface{}{
"buildpack_source": "LOCAL ARCHIVE",
"buildpack_name": inspectBuildpackTarget,
},
)

output := pack.RunSuccessfully("inspect-buildpack", inspectBuildpackTarget)
assert.TrimmedEq(output, expectedOutput)
})
})

})

when("buildpack image", func() {
it.Before(func() {
h.SkipIf(t, dockerHostOS() == "windows", "These tests are not yet compatible with Windows-based containers")

tmpDir, err := ioutil.TempDir("", "create-test-builder")
assert.Nil(err)
defer os.RemoveAll(tmpDir)

inspectBuildpackTarget = packageBuildpackAsImage(t,
assert,
pack,
pack.FixtureManager().FixtureLocation("package_for_build_cmd.toml"),
lifecycle,
buildpackList,
)

})
when("inspect-buildpack", func() {
it("succeeds", func() {
expectedOutput := pack.FixtureManager().TemplateVersionedFixture(
"inspect_%s_buildpack_output.txt",
createBuilderPack.Version(),
"inspect_buildpack_output.txt",
map[string]interface{}{
"buildpack_source": "LOCAL IMAGE",
"buildpack_name": inspectBuildpackTarget,
},
)

output := pack.RunSuccessfully("inspect-buildpack", inspectBuildpackTarget)
assert.TrimmedEq(output, expectedOutput)
})
})

})
})
}

func buildpacksDir(bpAPIVersion string) string {
Expand Down
19 changes: 19 additions & 0 deletions acceptance/testdata/pack_fixtures/inspect_buildpack_output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Inspecting buildpack: '{{ .buildpack_name }}'

{{ .buildpack_source }}:

Stacks:
ID: pack.test.stack
Mixins:
(none)

Buildpacks:
ID VERSION HOMEPAGE
simple/layers simple-layers-version
simple/layers/parent simple-layers-parent-version

Detection Order:
└ Group #1:
└ simple/layers/parent@simple-layers-parent-version
└ Group #1:
└ simple/layers@simple-layers-version
1 change: 1 addition & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func NewPackCommand(logger ConfigurableLogger) (*cobra.Command, error) {
rootCmd.AddCommand(commands.Build(logger, cfg, &packClient))
rootCmd.AddCommand(commands.Rebase(logger, cfg, &packClient))
rootCmd.AddCommand(commands.InspectImage(logger, &cfg, &packClient))
rootCmd.AddCommand(commands.InspectBuildpack(logger, &cfg, &packClient))
rootCmd.AddCommand(commands.SetRunImagesMirrors(logger, cfg))

rootCmd.AddCommand(commands.SetDefaultBuilder(logger, cfg, &packClient))
Expand Down
172 changes: 172 additions & 0 deletions inspect_buildpack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package pack

import (
"context"
"fmt"
"sort"

v1 "github.com/opencontainers/image-spec/specs-go/v1"

"github.com/buildpacks/pack/internal/style"

"github.com/buildpacks/pack/config"
"github.com/buildpacks/pack/internal/buildpack"
"github.com/buildpacks/pack/internal/buildpackage"
"github.com/buildpacks/pack/internal/dist"
)

type BuildpackInfo struct {
BuildpackMetadata buildpackage.Metadata
Buildpacks []dist.BuildpackInfo
Order dist.Order
BuildpackLayers dist.BuildpackLayers
Location buildpack.LocatorType
}

type InspectBuildpackOptions struct {
BuildpackName string
Daemon bool
Registry string
}

type ImgWrapper struct {
v1.ImageConfig
}

func (iw ImgWrapper) Label(name string) (string, error) {
return iw.Labels[name], nil
}

// Must return an BuildpackNotFoundError
func (c *Client) InspectBuildpack(opts InspectBuildpackOptions) (*BuildpackInfo, error) {
locatorType, err := buildpack.GetLocatorType(opts.BuildpackName, []dist.BuildpackInfo{})
if err != nil {
return nil, err
}
var layersMd dist.BuildpackLayers
var buildpackMd buildpackage.Metadata

switch locatorType {
case buildpack.RegistryLocator:
buildpackMd, layersMd, err = metadataFromRegistry(c, opts.BuildpackName, opts.Registry)
case buildpack.PackageLocator:
buildpackMd, layersMd, err = metadataFromImage(c, opts.BuildpackName, opts.Daemon)
case buildpack.URILocator:
buildpackMd, layersMd, err = metadataFromArchive(c.downloader, opts.BuildpackName)
default:
return nil, fmt.Errorf("unable to handle locator %q: for buildpack %q", locatorType, opts.BuildpackName)
}
if err != nil {
return nil, err
}

return &BuildpackInfo{
BuildpackMetadata: buildpackMd,
BuildpackLayers: layersMd,
Order: extractOrder(buildpackMd),
Buildpacks: extractBuildpacks(layersMd),
Location: locatorType,
}, nil
}

func metadataFromRegistry(client *Client, name, registry string) (buildpackMd buildpackage.Metadata, layersMd dist.BuildpackLayers, err error) {
registryCache, err := client.getRegistry(client.logger, registry)
if err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("invalid registry %s: %q", registry, err)
}

registryBp, err := registryCache.LocateBuildpack(name)
if err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to find %s in registry: %q", style.Symbol(name), err)
}
buildpackMd, layersMd, err = metadataFromImage(client, registryBp.Address, false)
if err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("error pulling registry specified image: %s", err)
}
return buildpackMd, layersMd, nil
}

func metadataFromArchive(downloader Downloader, path string) (buildpackMd buildpackage.Metadata, layersMd dist.BuildpackLayers, err error) {
// open archive, read it as an image and get all metadata.
// This looks like a prime candidate to be added to imgutil.

imgBlob, err := downloader.Download(context.Background(), path)
if err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to download archive: %q", err)
//return buildpackMd, layersMd, err
}

config, err := buildpackage.ConfigFromOCILayoutBlob(imgBlob)
if err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to fetch config from buildpack blob: %q", err)
}
wrapper := ImgWrapper{config}

if _, err := dist.GetLabel(wrapper, dist.BuildpackLayersLabel, &layersMd); err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, err
}

if _, err := dist.GetLabel(wrapper, buildpackage.MetadataLabel, &buildpackMd); err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, err
}
return buildpackMd, layersMd, nil
}

func metadataFromImage(client *Client, name string, daemon bool) (buildpackMd buildpackage.Metadata, layersMd dist.BuildpackLayers, err error) {
img, err := client.imageFetcher.Fetch(context.Background(), name, daemon, config.PullNever)
if err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, err
}
if _, err := dist.GetLabel(img, dist.BuildpackLayersLabel, &layersMd); err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to get image label %s: %q", dist.BuildpackLayersLabel, err)
}

if _, err := dist.GetLabel(img, buildpackage.MetadataLabel, &buildpackMd); err != nil {
return buildpackage.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to get image label %s: %q", buildpackage.MetadataLabel, err)
}
return buildpackMd, layersMd, nil
}

func extractOrder(buildpackMd buildpackage.Metadata) dist.Order {
return dist.Order{
{
Group: []dist.BuildpackRef{
{
BuildpackInfo: buildpackMd.BuildpackInfo,
},
},
},
}
}

func extractBuildpacks(layersMd dist.BuildpackLayers) []dist.BuildpackInfo {
result := []dist.BuildpackInfo{}
buildpackSet := map[dist.BuildpackInfo]bool{}

for buildpackID, buildpackMap := range layersMd {
for version, layerInfo := range buildpackMap {
bp := dist.BuildpackInfo{
ID: buildpackID,
Version: version,
Homepage: layerInfo.Homepage,
}
buildpackSet[bp] = true
}
}

for currentBuildpack := range buildpackSet {
result = append(result, currentBuildpack)
}

sort.Slice(result, func(i int, j int) bool {
switch {
case result[i].ID < result[j].ID:
return true
case result[i].ID == result[j].ID:
return result[i].Version < result[j].Version
default:
return false
}
})
return result
}
Loading

0 comments on commit 7e3612e

Please sign in to comment.