Skip to content

Commit

Permalink
Add options for empty-layer history entries
Browse files Browse the repository at this point in the history
Add configuration methods for adding entries which will show up in a
committed image's history, both before and after the new layer that we
add while committing the image.  Expose them from the CLI in the form of
a new --add-history option for the "add", "config", "copy", and "run"
commands.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #1300
Approved by: rhatdan
  • Loading branch information
nalind authored and rh-atomic-bot committed Jan 21, 2019
1 parent c3b0048 commit 23ed595
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 5 deletions.
24 changes: 24 additions & 0 deletions buildah.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"time"

"github.com/containers/buildah/docker"
"github.com/containers/buildah/util"
Expand Down Expand Up @@ -175,6 +176,15 @@ type Builder struct {
// after processing the AddCapabilities set, when running commands in the container.
// If a capability appears in both lists, it will be dropped.
DropCapabilities []string
// PrependedEmptyLayers are history entries that we'll add to a
// committed image, after any history items that we inherit from a base
// image, but before the history item for the layer that we're
// committing.
PrependedEmptyLayers []v1.History
// AppendedEmptyLayers are history entries that we'll add to a
// committed image after the history item for the layer that we're
// committing.
AppendedEmptyLayers []v1.History

CommonBuildOpts *CommonBuildOptions
// TopLayer is the top layer of the image
Expand Down Expand Up @@ -209,11 +219,24 @@ type BuilderInfo struct {
DefaultCapabilities []string
AddCapabilities []string
DropCapabilities []string
History []v1.History
}

// GetBuildInfo gets a pointer to a Builder object and returns a BuilderInfo object from it.
// This is used in the inspect command to display Manifest and Config as string and not []byte.
func GetBuildInfo(b *Builder) BuilderInfo {
history := copyHistory(b.OCIv1.History)
history = append(history, copyHistory(b.PrependedEmptyLayers)...)
now := time.Now().UTC()
created := &now
history = append(history, v1.History{
Created: created,
CreatedBy: b.ImageCreatedBy,
Author: b.Maintainer(),
Comment: b.ImageHistoryComment,
EmptyLayer: false,
})
history = append(history, copyHistory(b.AppendedEmptyLayers)...)
return BuilderInfo{
Type: b.Type,
FromImage: b.FromImage,
Expand All @@ -239,6 +262,7 @@ func GetBuildInfo(b *Builder) BuilderInfo {
DefaultCapabilities: append([]string{}, util.DefaultCapabilities...),
AddCapabilities: append([]string{}, b.AddCapabilities...),
DropCapabilities: append([]string{}, b.DropCapabilities...),
History: history,
}
}

Expand Down
15 changes: 10 additions & 5 deletions cmd/buildah/addcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ import (

var (
addAndCopyFlags = []cli.Flag{
cli.BoolFlag{
Name: "add-history",
Usage: "add an entry for this operation to the image's history. Use BUILDAH_HISTORY environment variable to override. (default false)",
},
cli.StringFlag{
Name: "chown",
Usage: "Set the user and group ownership of the destination content",
Usage: "set the user and group ownership of the destination content",
},
cli.BoolFlag{
Name: "quiet, q",
Expand Down Expand Up @@ -48,7 +52,7 @@ var (
}
)

func addAndCopyCmd(c *cli.Context, extractLocalArchives bool) error {
func addAndCopyCmd(c *cli.Context, verb string, extractLocalArchives bool) error {
args := c.Args()
if len(args) == 0 {
return errors.Errorf("container ID must be specified")
Expand Down Expand Up @@ -97,13 +101,14 @@ func addAndCopyCmd(c *cli.Context, extractLocalArchives bool) error {
if !c.Bool("quiet") {
fmt.Printf("%s\n", digester.Digest().Hex())
}
return nil
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) %s file:%s", verb, digester.Digest().Hex())
return builder.Save()
}

func addCmd(c *cli.Context) error {
return addAndCopyCmd(c, true)
return addAndCopyCmd(c, "ADD", true)
}

func copyCmd(c *cli.Context) error {
return addAndCopyCmd(c, false)
return addAndCopyCmd(c, "COPY", false)
}
36 changes: 36 additions & 0 deletions cmd/buildah/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"encoding/json"
"fmt"
"strings"
"time"

Expand All @@ -17,6 +18,10 @@ import (

var (
configFlags = []cli.Flag{
cli.BoolFlag{
Name: "add-history",
Usage: "add an entry for this operation to the image's history. Use BUILDAH_HISTORY environment variable to override. (default false)",
},
cli.StringSliceFlag{
Name: "annotation, a",
Usage: "add `annotation` e.g. annotation=value, for the target image (default [])",
Expand Down Expand Up @@ -159,6 +164,19 @@ func updateEntrypoint(builder *buildah.Builder, c *cli.Context) {
builder.SetEntrypoint(entrypointSpec)
}

func conditionallyAddHistory(builder *buildah.Builder, c *cli.Context, createdByFmt string, args ...interface{}) {
history := buildahcli.DefaultHistory()
if c.IsSet("add-history") {
history = c.Bool("add-history")
}
if history {
now := time.Now().UTC()
created := &now
createdBy := fmt.Sprintf(createdByFmt, args...)
builder.AddPrependedEmptyLayer(created, createdBy, "", "")
}
}

func updateConfig(builder *buildah.Builder, c *cli.Context) {
if c.IsSet("author") {
builder.SetMaintainer(c.String("author"))
Expand All @@ -174,6 +192,7 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
}
if c.IsSet("user") {
builder.SetUser(c.String("user"))
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) USER %s", c.String("user"))
}
if c.IsSet("shell") {
shellSpec, err := shellwords.Parse(c.String("shell"))
Expand All @@ -182,14 +201,17 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
} else {
builder.SetShell(shellSpec)
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) SHELL %s", c.String("shell"))
}
if c.IsSet("stop-signal") {
builder.SetStopSignal(c.String("stop-signal"))
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) STOPSIGNAL %s", c.String("stop-signal"))
}
if c.IsSet("port") || c.IsSet("p") {
for _, portSpec := range c.StringSlice("port") {
builder.SetPort(portSpec)
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) EXPOSE %s", strings.Join(c.StringSlice("port"), " "))
}
if c.IsSet("env") || c.IsSet("e") {
for _, envSpec := range c.StringSlice("env") {
Expand All @@ -200,9 +222,11 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
builder.UnsetEnv(env[0])
}
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) ENV %s", strings.Join(c.StringSlice("env"), " "))
}
if c.IsSet("entrypoint") {
updateEntrypoint(builder, c)
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) ENTRYPOINT %s", c.String("entrypoint"))
}
// cmd should always run after entrypoint; setting entrypoint clears cmd
if c.IsSet("cmd") {
Expand All @@ -212,11 +236,13 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
} else {
builder.SetCmd(cmdSpec)
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) CMD %s", c.String("cmd"))
}
if c.IsSet("volume") {
if volSpec := c.StringSlice("volume"); len(volSpec) > 0 {
for _, spec := range volSpec {
builder.AddVolume(spec)
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) VOLUME %s", spec)
}
}
}
Expand All @@ -230,9 +256,11 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
builder.UnsetLabel(label[0])
}
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) LABEL %s", strings.Join(c.StringSlice("label"), " "))
}
if c.IsSet("workingdir") {
builder.SetWorkDir(c.String("workingdir"))
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) WORKDIR %s", c.String("workingdir"))
}
if c.IsSet("comment") {
builder.SetComment(c.String("comment"))
Expand All @@ -253,6 +281,7 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
if c.IsSet("onbuild") {
for _, onbuild := range c.StringSlice("onbuild") {
builder.SetOnBuild(onbuild)
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) ONBUILD %s", onbuild)
}
}
if c.IsSet("annotation") || c.IsSet("a") {
Expand All @@ -270,6 +299,7 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
func updateHealthcheck(builder *buildah.Builder, c *cli.Context) {
if c.IsSet("healthcheck") || c.IsSet("healthcheck-interval") || c.IsSet("healthcheck-retries") || c.IsSet("healthcheck-start-period") || c.IsSet("healthcheck-timeout") {
healthcheck := builder.Healthcheck()
args := ""
if healthcheck == nil {
healthcheck = &docker.HealthConfig{
Test: []string{"NONE"},
Expand All @@ -292,28 +322,34 @@ func updateHealthcheck(builder *buildah.Builder, c *cli.Context) {
logrus.Errorf("error parsing --healthcheck-interval %q: %v", c.String("healthcheck-interval"), err)
}
healthcheck.Interval = duration
args = args + "--interval=" + c.String("healthcheck-interval") + " "
}
if c.IsSet("healthcheck-retries") {
healthcheck.Retries = c.Int("healthcheck-retries")
args = args + "--retries=" + c.String("healthcheck-retries") + " "
}
if c.IsSet("healthcheck-start-period") {
duration, err := time.ParseDuration(c.String("healthcheck-start-period"))
if err != nil {
logrus.Errorf("error parsing --healthcheck-start-period %q: %v", c.String("healthcheck-start-period"), err)
}
healthcheck.StartPeriod = duration
args = args + "--start-period=" + c.String("healthcheck-start-period") + " "
}
if c.IsSet("healthcheck-timeout") {
duration, err := time.ParseDuration(c.String("healthcheck-timeout"))
if err != nil {
logrus.Errorf("error parsing --healthcheck-timeout %q: %v", c.String("healthcheck-timeout"), err)
}
healthcheck.Timeout = duration
args = args + "--timeout=" + c.String("healthcheck-timeout") + " "
}
if len(healthcheck.Test) == 0 {
builder.SetHealthcheck(nil)
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) HEALTHCHECK NONE")
} else {
builder.SetHealthcheck(healthcheck)
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) HEALTHCHECK %s%s", args, c.String("healthcheck"))
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions cmd/buildah/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import (

var (
runFlags = []cli.Flag{
cli.BoolFlag{
Name: "add-history",
Usage: "add an entry for this operation to the image's history. Use BUILDAH_HISTORY environment variable to override. (default false)",
},
cli.StringSliceFlag{
Name: "cap-add",
Usage: "add the specified capability (default [])",
Expand Down Expand Up @@ -176,5 +180,13 @@ func runCmd(c *cli.Context) error {
os.Exit(w.ExitStatus())
}
}
if runerr == nil {
shell := "/bin/sh -c"
if len(builder.Shell()) > 0 {
shell = strings.Join(builder.Shell(), " ")
}
conditionallyAddHistory(builder, c, "%s %s", shell, strings.Join(args, " "))
return builder.Save()
}
return runerr
}
47 changes: 47 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,50 @@ func (b *Builder) SetHealthcheck(config *docker.HealthConfig) {
}
}
}

// AddPrependedEmptyLayer adds an item to the history that we'll create when
// commiting the image, after any history we inherit from the base image, but
// before the history item that we'll use to describe the new layer that we're
// adding.
func (b *Builder) AddPrependedEmptyLayer(created *time.Time, createdBy, author, comment string) {
if created != nil {
copiedTimestamp := *created
created = &copiedTimestamp
}
b.PrependedEmptyLayers = append(b.PrependedEmptyLayers, ociv1.History{
Created: created,
CreatedBy: createdBy,
Author: author,
Comment: comment,
EmptyLayer: true,
})
}

// ClearPrependedEmptyLayers clears the list of history entries that we'll add
// to the committed image before the entry for the layer that we're adding.
func (b *Builder) ClearPrependedEmptyLayers() {
b.PrependedEmptyLayers = nil
}

// AddAppendedEmptyLayer adds an item to the history that we'll create when
// commiting the image, after the history item that we'll use to describe the
// new layer that we're adding.
func (b *Builder) AddAppendedEmptyLayer(created *time.Time, createdBy, author, comment string) {
if created != nil {
copiedTimestamp := *created
created = &copiedTimestamp
}
b.AppendedEmptyLayers = append(b.AppendedEmptyLayers, ociv1.History{
Created: created,
CreatedBy: createdBy,
Author: author,
Comment: comment,
EmptyLayer: true,
})
}

// ClearAppendedEmptyLayers clears the list of history entries that we'll add
// to the committed image after the entry for the layer that we're adding.
func (b *Builder) ClearAppendedEmptyLayers() {
b.AppendedEmptyLayers = nil
}
4 changes: 4 additions & 0 deletions contrib/completions/bash/buildah
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ return 1

_buildah_config() {
local boolean_options="
--add-history
--help
-h
"
Expand Down Expand Up @@ -447,6 +448,7 @@ return 1

_buildah_run() {
local boolean_options="
--add-history
--help
-t
--tty
Expand Down Expand Up @@ -494,6 +496,7 @@ return 1

_buildah_copy() {
local boolean_options="
--add-history
--help
-h
--quiet
Expand All @@ -515,6 +518,7 @@ return 1

_buildah_add() {
local boolean_options="
--add-history
--help
-h
--quiet
Expand Down
8 changes: 8 additions & 0 deletions docs/buildah-add.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ archive file itself. If a local directory is specified as a source, its

## OPTIONS

**--add-history**

Add an entry to the history which will note the digest of the added content.
Defaults to false.

Note: You can also override the default value of --add-history by setting the
BUILDAH\_HISTORY environment variable. `export BUILDAH_HISTORY=true`

**--chown** *owner*:*group*

Sets the user and group ownership of the destination content.
Expand Down
11 changes: 11 additions & 0 deletions docs/buildah-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ Updates one or more of the settings kept for a container.

## OPTIONS

**--add-history**

Add an entry to the image's history which will note changes to the settings for
**--cmd**, **--entrypoint**, **--env**, **--healthcheck**, **--label**,
**--onbuild**, **--port**, **--shell**, **--stop-signal**, **--user**,
**--volume**, and **--workingdir**.
Defaults to false.

Note: You can also override the default value of --add-history by setting the
BUILDAH\_HISTORY environment variable. `export BUILDAH_HISTORY=true`

**--annotation** *annotation*

Add an image *annotation* (e.g. annotation=*annotation*) to the image manifest
Expand Down
Loading

0 comments on commit 23ed595

Please sign in to comment.