Skip to content

Commit

Permalink
Write analyzed.toml
Browse files Browse the repository at this point in the history
* Export to multiple tags
* Use analyzed.toml in exporter

[#138]

Signed-off-by: Javier Romero <jromero@pivotal.io>
Signed-off-by: Danny Joyce <djoyce@pivotal.io>
  • Loading branch information
jromero authored and Danny Joyce committed Jun 13, 2019
1 parent af8b715 commit e0d8f58
Show file tree
Hide file tree
Showing 15 changed files with 400 additions and 695 deletions.
44 changes: 34 additions & 10 deletions analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ import (
)

type Analyzer struct {
Buildpacks []*Buildpack
AppDir string
LayersDir string
In []byte
Out, Err *log.Logger
UID int
GID int
Buildpacks []*Buildpack
AppDir string
AnalyzedPath string
LayersDir string
In []byte
Out, Err *log.Logger
UID int
GID int
}

func (a *Analyzer) Analyze(image imgutil.Image) error {
data, err := metadata.GetAppMetadata(image)
if err != nil {
return err
}

for _, buildpack := range a.Buildpacks {
cache, err := readBuildpackLayersDir(a.LayersDir, *buildpack)
if err != nil {
Expand Down Expand Up @@ -73,12 +75,34 @@ func (a *Analyzer) Analyze(image imgutil.Image) error {
}

// if analyzer is running as root it needs to fix the ownership of the layers dir
if current := os.Getuid(); err != nil {
return err
} else if current == 0 {
if current := os.Getuid(); current == 0 {
if err := recursiveChown(a.LayersDir, a.UID, a.GID); err != nil {
return errors.Wrapf(err, "chowning layers dir to '%d/%d'", a.UID, a.GID)
}
}

return a.writeAnalyzedMetadata(image)
}

func (a *Analyzer) writeAnalyzedMetadata(image imgutil.Image) error {
var (
err error
digest string
)
if image.Found() {
digest, err = image.Digest()
if err != nil {
return errors.Wrap(err, "retrieve image digest")
}
}

md := metadata.AnalyzedMetadata{
Repository: image.Name(),
Digest: digest,
}
if err := WriteTOML(a.AnalyzedPath, md); err != nil {
return errors.Wrap(err, "write analyzed.toml")
}

return nil
}
58 changes: 43 additions & 15 deletions analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,35 @@ func testAnalyzer(t *testing.T, when spec.G, it spec.S) {
stdout, stderr *bytes.Buffer
layerDir string
appDir string
tmpDir string
)

it.Before(func() {
var err error

tmpDir, err = ioutil.TempDir("", "analyzer-tests")
h.AssertNil(t, err)

layerDir, err = ioutil.TempDir("", "lifecycle-layer-dir")
if err != nil {
t.Fatalf("Error: %s\n", err)
}
appDir = filepath.Join(layerDir, "some-app-dir")
h.AssertNil(t, err)

appDir = filepath.Join(layerDir, "some-app-dir")
stdout, stderr = &bytes.Buffer{}, &bytes.Buffer{}
analyzer = &lifecycle.Analyzer{
Buildpacks: []*lifecycle.Buildpack{{ID: "metdata.buildpack"}, {ID: "no.cache.buildpack"}, {ID: "no.metadata.buildpack"}},
AppDir: appDir,
LayersDir: layerDir,
Out: log.New(stdout, "", 0),
Err: log.New(stderr, "", 0),
UID: 1234,
GID: 4321,
Buildpacks: []*lifecycle.Buildpack{{ID: "metdata.buildpack"}, {ID: "no.cache.buildpack"}, {ID: "no.metadata.buildpack"}},
AppDir: appDir,
LayersDir: layerDir,
AnalyzedPath: filepath.Join(tmpDir, "some-previous-file.toml"),
Out: log.New(stdout, "", 0),
Err: log.New(stderr, "", 0),
UID: 1234,
GID: 4321,
}
mockCtrl = gomock.NewController(t)
})

it.After(func() {
os.RemoveAll(tmpDir)
os.RemoveAll(layerDir)
mockCtrl.Finish()
})
Expand All @@ -68,8 +73,7 @@ func testAnalyzer(t *testing.T, when spec.G, it spec.S) {
)

it.Before(func() {
image = fakes.NewImage("image-repo-name", "", "")

image = fakes.NewImage("image-repo-name", "", "s0m3D1g3sT")
ref = testmock.NewMockReference(mockCtrl)
ref.EXPECT().Name().AnyTimes()
})
Expand Down Expand Up @@ -459,7 +463,7 @@ func testAnalyzer(t *testing.T, when spec.G, it spec.S) {
when("analyzer is running as root", func() {
it.Before(func() {
if os.Getuid() != 0 {
t.Skip()
t.Skip("Skipped when not running as root")
}
})

Expand All @@ -471,10 +475,22 @@ func testAnalyzer(t *testing.T, when spec.G, it spec.S) {
h.AssertUidGid(t, filepath.Join(layerDir, "no.cache.buildpack", "go.toml"), 1234, 4321)
})
})

when("analyzed path is provided", func() {
it("should write analyzed TOML at provided path", func() {
err := analyzer.Analyze(image)
h.AssertNil(t, err)

b, err := ioutil.ReadFile(analyzer.AnalyzedPath)
h.AssertNil(t, err)

h.AssertEq(t, string(b), "repository = \"image-repo-name\"\ndigest = \"s0m3D1g3sT\"\n")
})
})
})
})

when("the image cannot found", func() {
when("the image cannot be found", func() {
it.Before(func() {
h.AssertNil(t, image.Delete())
})
Expand All @@ -494,6 +510,18 @@ func testAnalyzer(t *testing.T, when spec.G, it spec.S) {
t.Fatalf("Missing some-app-dir")
}
})

it("should write an analyzed.toml without a digest", func() {
h.AssertNil(t, image.Delete())

err := analyzer.Analyze(image)
h.AssertNil(t, err)

b, err := ioutil.ReadFile(analyzer.AnalyzedPath)
h.AssertNil(t, err)

h.AssertEq(t, string(b), "repository = \"image-repo-name\"\ndigest = \"\"\n")
})
})

when("the image does not have the required label", func() {
Expand Down
39 changes: 21 additions & 18 deletions cmd/analyzer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,26 @@ import (
)

var (
repoName string
layersDir string
appDir string
groupPath string
useDaemon bool
useHelpers bool
uid int
gid int
repoName string
appDir string
layersDir string
analyzedPath string
groupPath string
useDaemon bool
useHelpers bool
uid int
gid int
)

func init() {
cmd.FlagLayersDir(&layersDir)
cmd.FlagAppDir(&appDir)
cmd.FlagGID(&gid)
cmd.FlagGroupPath(&groupPath)
cmd.FlagLayersDir(&layersDir)
cmd.FlagAnalyzedPath(&analyzedPath)
cmd.FlagUID(&uid)
cmd.FlagUseDaemon(&useDaemon)
cmd.FlagUseCredHelpers(&useHelpers)
cmd.FlagUID(&uid)
cmd.FlagGID(&gid)
}

func main() {
Expand Down Expand Up @@ -67,13 +69,14 @@ func analyzer() error {
}

analyzer := &lifecycle.Analyzer{
Buildpacks: group.Buildpacks,
AppDir: appDir,
LayersDir: layersDir,
Out: log.New(os.Stdout, "", 0),
Err: log.New(os.Stderr, "", 0),
UID: uid,
GID: gid,
Buildpacks: group.Buildpacks,
AppDir: appDir,
LayersDir: layersDir,
AnalyzedPath: analyzedPath,
Out: log.New(os.Stdout, "", 0),
Err: log.New(os.Stderr, "", 0),
UID: uid,
GID: gid,
}

var err error
Expand Down
64 changes: 35 additions & 29 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ const (
DefaultOrderPath = "/buildpacks/order.toml"
DefaultGroupPath = "./group.toml"
DefaultStackPath = "/buildpacks/stack.toml"
DefaultAnalyzedPath = "./analyzed.toml"
DefaultPlanPath = "./plan.toml"

EnvLayersDir = "CNB_LAYERS_DIR"
EnvAppDir = "CNB_APP_DIR"
EnvBuildpacksDir = "CNB_BUILDPACKS_DIR"
EnvPlatformDir = "CNB_PLATFORM_DIR"
EnvAnalyzedPath = "CNB_ANALYZED_PATH"
EnvOrderPath = "CNB_ORDER_PATH"
EnvGroupPath = "CNB_GROUP_PATH"
EnvStackPath = "CNB_STACK_PATH"
Expand All @@ -38,68 +40,72 @@ const (
EnvRegistryAuth = "CNB_REGISTRY_AUTH"
)

func FlagLayersDir(dir *string) {
flag.StringVar(dir, "layers", envWithDefault(EnvLayersDir, DefaultLayersDir), "path to layers directory")
}

func FlagAppDir(dir *string) {
flag.StringVar(dir, "app", envWithDefault(EnvAppDir, DefaultAppDir), "path to app directory")
flag.StringVar(dir, "app", envOrDefault(EnvAppDir, DefaultAppDir), "path to app directory")
}

func FlagBuildpacksDir(dir *string) {
flag.StringVar(dir, "buildpacks", envWithDefault(EnvBuildpacksDir, DefaultBuildpacksDir), "path to buildpacks directory")
flag.StringVar(dir, "buildpacks", envOrDefault(EnvBuildpacksDir, DefaultBuildpacksDir), "path to buildpacks directory")
}

func FlagPlatformDir(dir *string) {
flag.StringVar(dir, "platform", envWithDefault(EnvPlatformDir, DefaultPlatformDir), "path to platform directory")
func FlagCacheDir(dir *string) {
flag.StringVar(dir, "path", os.Getenv(EnvCacheDir), "path to cache directory")
}

func FlagOrderPath(path *string) {
flag.StringVar(path, "order", envWithDefault(EnvOrderPath, DefaultOrderPath), "path to order.toml")
func FlagCacheImage(image *string) {
flag.StringVar(image, "image", os.Getenv(EnvCacheImage), "cache image tag name")
}

func FlagGroupPath(path *string) {
flag.StringVar(path, "group", envWithDefault(EnvGroupPath, DefaultGroupPath), "path to group.toml")
func FlagGID(gid *int) {
flag.IntVar(gid, "gid", intEnv(EnvGID), "GID of user's group in the stack's build and run images")
}

func FlagStackPath(path *string) {
flag.StringVar(path, "stack", envWithDefault(EnvStackPath, DefaultStackPath), "path to stack.toml")
func FlagGroupPath(path *string) {
flag.StringVar(path, "group", envOrDefault(EnvGroupPath, DefaultGroupPath), "path to group.toml")
}

func FlagLaunchCacheDir(dir *string) {
flag.StringVar(dir, "launch-cache", os.Getenv(EnvLaunchCacheDir), "path to launch cache directory")
}

func FlagPlanPath(path *string) {
flag.StringVar(path, "plan", envWithDefault(EnvPlanPath, DefaultPlanPath), "path to plan.toml")
func FlagLayersDir(dir *string) {
flag.StringVar(dir, "layers", envOrDefault(EnvLayersDir, DefaultLayersDir), "path to layers directory")
}

func FlagRunImage(image *string) {
flag.StringVar(image, "image", os.Getenv(EnvRunImage), "reference to run image")
func FlagOrderPath(path *string) {
flag.StringVar(path, "order", envOrDefault(EnvOrderPath, DefaultOrderPath), "path to order.toml")
}

func FlagCacheImage(image *string) {
flag.StringVar(image, "image", os.Getenv(EnvCacheImage), "cache image tag name")
func FlagPlanPath(path *string) {
flag.StringVar(path, "plan", envOrDefault(EnvPlanPath, DefaultPlanPath), "path to plan.toml")
}

func FlagCacheDir(dir *string) {
flag.StringVar(dir, "path", os.Getenv(EnvCacheDir), "path to cache directory")
func FlagPlatformDir(dir *string) {
flag.StringVar(dir, "platform", envOrDefault(EnvPlatformDir, DefaultPlatformDir), "path to platform directory")
}

func FlagUseDaemon(use *bool) {
flag.BoolVar(use, "daemon", boolEnv(EnvUseDaemon), "export to docker daemon")
func FlagAnalyzedPath(dir *string) {
flag.StringVar(dir, "analyzed", envOrDefault(EnvAnalyzedPath, DefaultAnalyzedPath), "path to analyzed.toml")
}

func FlagUseCredHelpers(use *bool) {
flag.BoolVar(use, "helpers", boolEnv(EnvUseHelpers), "use credential helpers")
func FlagRunImage(image *string) {
flag.StringVar(image, "image", os.Getenv(EnvRunImage), "reference to run image")
}

func FlagStackPath(path *string) {
flag.StringVar(path, "stack", envOrDefault(EnvStackPath, DefaultStackPath), "path to stack.toml")
}

func FlagUID(uid *int) {
flag.IntVar(uid, "uid", intEnv(EnvUID), "UID of user in the stack's build and run images")
}

func FlagGID(gid *int) {
flag.IntVar(gid, "gid", intEnv(EnvGID), "GID of user's group in the stack's build and run images")
func FlagUseCredHelpers(use *bool) {
flag.BoolVar(use, "helpers", boolEnv(EnvUseHelpers), "use credential helpers")
}

func FlagUseDaemon(use *bool) {
flag.BoolVar(use, "daemon", boolEnv(EnvUseDaemon), "export to docker daemon")
}

const (
Expand Down Expand Up @@ -173,7 +179,7 @@ func boolEnv(k string) bool {
return b
}

func envWithDefault(key string, defaultVal string) string {
func envOrDefault(key string, defaultVal string) string {
if envVal := os.Getenv(key); envVal != "" {
return envVal
}
Expand Down
Loading

0 comments on commit e0d8f58

Please sign in to comment.