Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah object, initializing one based on the other, depending on which format the source image used for its configuration. Replace directly manipulated fields in the Buildah object (Annotations, CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd, Entrypoint, Expose, Labels, and Volumes) with accessor functions which update both configurations and which read from whichever one we consider to be authoritative. Drop Args because we weren't using them. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com> Closes: #102 Approved by: rhatdan
This commit is contained in:
parent
58e56e3efc
commit
fc880bcc86
6
add.go
6
add.go
|
@ -72,10 +72,10 @@ func (b *Builder) Add(destination string, extract bool, source ...string) error
|
|||
if destination != "" && filepath.IsAbs(destination) {
|
||||
dest = filepath.Join(dest, destination)
|
||||
} else {
|
||||
if err = os.MkdirAll(filepath.Join(dest, b.Workdir), 0755); err != nil {
|
||||
return fmt.Errorf("error ensuring directory %q exists: %v)", filepath.Join(dest, b.Workdir), err)
|
||||
if err = os.MkdirAll(filepath.Join(dest, b.WorkDir()), 0755); err != nil {
|
||||
return fmt.Errorf("error ensuring directory %q exists: %v)", filepath.Join(dest, b.WorkDir()), err)
|
||||
}
|
||||
dest = filepath.Join(dest, b.Workdir, destination)
|
||||
dest = filepath.Join(dest, b.WorkDir(), destination)
|
||||
}
|
||||
// If the destination was explicitly marked as a directory by ending it
|
||||
// with a '/', create it so that we can be sure that it's a directory,
|
||||
|
|
54
buildah.go
54
buildah.go
|
@ -10,6 +10,8 @@ import (
|
|||
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/projectatomic/buildah/docker"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -18,7 +20,7 @@ const (
|
|||
Package = "buildah"
|
||||
// Version for the Package
|
||||
Version = "0.0.1"
|
||||
containerType = Package + " 0.0.0"
|
||||
containerType = Package + " 0.0.1"
|
||||
stateFile = Package + ".json"
|
||||
)
|
||||
|
||||
|
@ -70,46 +72,18 @@ type Builder struct {
|
|||
// been mounted. It should not be modified.
|
||||
Mounts []string `json:"mounts,omitempty"`
|
||||
|
||||
// Annotations is a set of key-value pairs which is stored in the
|
||||
// ImageAnnotations is a set of key-value pairs which is stored in the
|
||||
// image's manifest.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
ImageAnnotations map[string]string `json:"annotations,omitempty"`
|
||||
// ImageCreatedBy is a description of how this container was built.
|
||||
ImageCreatedBy string `json:"created-by,omitempty"`
|
||||
|
||||
// CreatedBy is a description of how this container was built.
|
||||
CreatedBy string `json:"created-by,omitempty"`
|
||||
// OS is the operating system for which binaries in the image are
|
||||
// built. The default is the current OS.
|
||||
OS string `json:"os,omitempty"`
|
||||
// Architecture is the type of processor for which binaries in the
|
||||
// image are built. The default is the current architecture.
|
||||
Architecture string `json:"arch,omitempty"`
|
||||
// Maintainer is the point of contact for this container.
|
||||
Maintainer string `json:"maintainer,omitempty"`
|
||||
// User is the user as whom commands are run in the container.
|
||||
User string `json:"user,omitempty"`
|
||||
// Workdir is the default working directory for commands started in the
|
||||
// container.
|
||||
Workdir string `json:"workingdir,omitempty"`
|
||||
// Env is a list of environment variables to set for the container, in
|
||||
// the form NAME=VALUE.
|
||||
Env []string `json:"env,omitempty"`
|
||||
// Cmd sets a default command to run in containers based on the image.
|
||||
Cmd []string `json:"cmd,omitempty"`
|
||||
// Entrypoint is an entry point for containers based on the image.
|
||||
Entrypoint []string `json:"entrypoint,omitempty"`
|
||||
// Expose is a map keyed by specifications of ports to expose when a
|
||||
// container based on the image is run.
|
||||
Expose map[string]interface{} `json:"expose,omitempty"`
|
||||
// Labels is a set of key-value pairs which is stored in the
|
||||
// image's configuration.
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
// Volumes is a list of data volumes which will be created in
|
||||
// containers based on the image.
|
||||
Volumes []string `json:"volumes,omitempty"`
|
||||
// Arg is a set of build-time variables.
|
||||
Arg map[string]string `json:"arg,omitempty"`
|
||||
// Image metadata and runtime settings, in multiple formats.
|
||||
OCIv1 v1.Image `json:"ociv1,omitempty"`
|
||||
Docker docker.Image `json:"docker,omitempty"`
|
||||
}
|
||||
|
||||
// BuilderOptions are used to initialize a Builder.
|
||||
// BuilderOptions are used to initialize a new Builder.
|
||||
type BuilderOptions struct {
|
||||
// FromImage is the name of the image which should be used as the
|
||||
// starting point for the container. It can be set to an empty value
|
||||
|
@ -140,7 +114,8 @@ type BuilderOptions struct {
|
|||
ReportWriter io.Writer
|
||||
}
|
||||
|
||||
// ImportOptions are used to initialize a Builder.
|
||||
// ImportOptions are used to initialize a Builder from an existing container
|
||||
// which was created elsewhere.
|
||||
type ImportOptions struct {
|
||||
// Container is the name of the build container.
|
||||
Container string
|
||||
|
@ -182,6 +157,7 @@ func OpenBuilder(store storage.Store, container string) (*Builder, error) {
|
|||
return nil, fmt.Errorf("container is not a %s container", Package)
|
||||
}
|
||||
b.store = store
|
||||
b.fixupConfig()
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
@ -220,6 +196,7 @@ func OpenBuilderByPath(store storage.Store, path string) (*Builder, error) {
|
|||
err = json.Unmarshal(buildstate, &b)
|
||||
if err == nil && b.Type == containerType && builderMatchesPath(b, abs) {
|
||||
b.store = store
|
||||
b.fixupConfig()
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
@ -246,6 +223,7 @@ func OpenAllBuilders(store storage.Store) (builders []*Builder, err error) {
|
|||
err = json.Unmarshal(buildstate, &b)
|
||||
if err == nil && b.Type == containerType {
|
||||
b.store = store
|
||||
b.fixupConfig()
|
||||
builders = append(builders, b)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,31 +85,33 @@ var (
|
|||
|
||||
func updateConfig(builder *buildah.Builder, c *cli.Context) {
|
||||
if c.IsSet("author") {
|
||||
builder.Maintainer = c.String("author")
|
||||
builder.SetMaintainer(c.String("author"))
|
||||
}
|
||||
if c.IsSet("created-by") {
|
||||
builder.CreatedBy = c.String("created-by")
|
||||
builder.SetCreatedBy(c.String("created-by"))
|
||||
}
|
||||
if c.IsSet("arch") {
|
||||
builder.Architecture = c.String("arch")
|
||||
builder.SetArchitecture(c.String("arch"))
|
||||
}
|
||||
if c.IsSet("os") {
|
||||
builder.OS = c.String("os")
|
||||
builder.SetOS(c.String("os"))
|
||||
}
|
||||
if c.IsSet("user") {
|
||||
builder.User = c.String("user")
|
||||
builder.SetUser(c.String("user"))
|
||||
}
|
||||
if c.IsSet("port") || c.IsSet("p") {
|
||||
if builder.Expose == nil {
|
||||
builder.Expose = make(map[string]interface{})
|
||||
}
|
||||
for _, portSpec := range c.StringSlice("port") {
|
||||
builder.Expose[portSpec] = struct{}{}
|
||||
builder.SetPort(portSpec)
|
||||
}
|
||||
}
|
||||
if c.IsSet("env") {
|
||||
if envSpec := c.StringSlice("env"); len(envSpec) > 0 {
|
||||
builder.Env = append(builder.Env, envSpec...)
|
||||
if c.IsSet("env") || c.IsSet("e") {
|
||||
for _, envSpec := range c.StringSlice("env") {
|
||||
env := strings.SplitN(envSpec, "=", 2)
|
||||
if len(env) > 1 {
|
||||
builder.SetEnv(env[0], env[1])
|
||||
} else {
|
||||
builder.UnsetEnv(env[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.IsSet("entrypoint") {
|
||||
|
@ -117,7 +119,7 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
|
|||
if err != nil {
|
||||
logrus.Errorf("error parsing --entrypoint %q: %v", c.String("entrypoint"), err)
|
||||
} else {
|
||||
builder.Entrypoint = entrypointSpec
|
||||
builder.SetEntrypoint(entrypointSpec)
|
||||
}
|
||||
}
|
||||
if c.IsSet("cmd") {
|
||||
|
@ -125,40 +127,36 @@ func updateConfig(builder *buildah.Builder, c *cli.Context) {
|
|||
if err != nil {
|
||||
logrus.Errorf("error parsing --cmd %q: %v", c.String("cmd"), err)
|
||||
} else {
|
||||
builder.Cmd = cmdSpec
|
||||
builder.SetCmd(cmdSpec)
|
||||
}
|
||||
}
|
||||
if c.IsSet("volume") {
|
||||
if volSpec := c.StringSlice("volume"); len(volSpec) > 0 {
|
||||
builder.Volumes = append(builder.Volumes, volSpec...)
|
||||
for _, spec := range volSpec {
|
||||
builder.AddVolume(spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.IsSet("label") || c.IsSet("l") {
|
||||
if builder.Labels == nil {
|
||||
builder.Labels = make(map[string]string)
|
||||
}
|
||||
for _, labelSpec := range c.StringSlice("label") {
|
||||
label := strings.SplitN(labelSpec, "=", 2)
|
||||
if len(label) > 1 {
|
||||
builder.Labels[label[0]] = label[1]
|
||||
builder.SetLabel(label[0], label[1])
|
||||
} else {
|
||||
delete(builder.Labels, label[0])
|
||||
builder.UnsetLabel(label[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.IsSet("workingdir") {
|
||||
builder.Workdir = c.String("workingdir")
|
||||
builder.SetWorkDir(c.String("workingdir"))
|
||||
}
|
||||
if c.IsSet("annotation") || c.IsSet("a") {
|
||||
if builder.Annotations == nil {
|
||||
builder.Annotations = make(map[string]string)
|
||||
}
|
||||
for _, annotationSpec := range c.StringSlice("annotation") {
|
||||
annotation := strings.SplitN(annotationSpec, "=", 2)
|
||||
if len(annotation) > 1 {
|
||||
builder.Annotations[annotation[0]] = annotation[1]
|
||||
builder.SetAnnotation(annotation[0], annotation[1])
|
||||
} else {
|
||||
delete(builder.Annotations, annotation[0])
|
||||
builder.UnsetAnnotation(annotation[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
485
config.go
485
config.go
|
@ -2,29 +2,36 @@ package buildah
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/projectatomic/buildah/docker"
|
||||
)
|
||||
|
||||
func copyDockerImageConfig(dimage *docker.Image) (ociv1.Image, error) {
|
||||
// makeOCIv1Image builds the best OCIv1 image structure we can from the
|
||||
// contents of the docker image structure.
|
||||
func makeOCIv1Image(dimage *docker.Image) (ociv1.Image, error) {
|
||||
config := dimage.Config
|
||||
if config == nil {
|
||||
config = &dimage.ContainerConfig
|
||||
}
|
||||
image := ociv1.Image{
|
||||
Created: dimage.Created.UTC(),
|
||||
Author: dimage.Author,
|
||||
Architecture: dimage.Architecture,
|
||||
OS: dimage.OS,
|
||||
Config: ociv1.ImageConfig{
|
||||
User: dimage.Config.User,
|
||||
User: config.User,
|
||||
ExposedPorts: map[string]struct{}{},
|
||||
Env: dimage.Config.Env,
|
||||
Entrypoint: dimage.Config.Entrypoint,
|
||||
Cmd: dimage.Config.Cmd,
|
||||
Volumes: dimage.Config.Volumes,
|
||||
WorkingDir: dimage.Config.WorkingDir,
|
||||
Labels: dimage.Config.Labels,
|
||||
Env: config.Env,
|
||||
Entrypoint: config.Entrypoint,
|
||||
Cmd: config.Cmd,
|
||||
Volumes: config.Volumes,
|
||||
WorkingDir: config.WorkingDir,
|
||||
Labels: config.Labels,
|
||||
},
|
||||
RootFS: ociv1.RootFS{
|
||||
Type: "",
|
||||
|
@ -32,7 +39,7 @@ func copyDockerImageConfig(dimage *docker.Image) (ociv1.Image, error) {
|
|||
},
|
||||
History: []ociv1.History{},
|
||||
}
|
||||
for port, what := range dimage.Config.ExposedPorts {
|
||||
for port, what := range config.ExposedPorts {
|
||||
image.Config.ExposedPorts[string(port)] = what
|
||||
}
|
||||
RootFS := docker.RootFS{}
|
||||
|
@ -46,9 +53,8 @@ func copyDockerImageConfig(dimage *docker.Image) (ociv1.Image, error) {
|
|||
}
|
||||
}
|
||||
for _, history := range dimage.History {
|
||||
created := history.Created.UTC()
|
||||
ohistory := ociv1.History{
|
||||
Created: created,
|
||||
Created: history.Created.UTC(),
|
||||
CreatedBy: history.CreatedBy,
|
||||
Author: history.Author,
|
||||
Comment: history.Comment,
|
||||
|
@ -59,89 +65,402 @@ func copyDockerImageConfig(dimage *docker.Image) (ociv1.Image, error) {
|
|||
return image, nil
|
||||
}
|
||||
|
||||
func (b *Builder) updatedConfig() []byte {
|
||||
// makeDockerV2S2Image builds the best docker image structure we can from the
|
||||
// contents of the OCI image structure.
|
||||
func makeDockerV2S2Image(oimage *ociv1.Image) (docker.Image, error) {
|
||||
image := docker.Image{
|
||||
V1Image: docker.V1Image{Created: oimage.Created.UTC(),
|
||||
Author: oimage.Author,
|
||||
Architecture: oimage.Architecture,
|
||||
OS: oimage.OS,
|
||||
ContainerConfig: docker.Config{
|
||||
User: oimage.Config.User,
|
||||
ExposedPorts: docker.PortSet{},
|
||||
Env: oimage.Config.Env,
|
||||
Entrypoint: oimage.Config.Entrypoint,
|
||||
Cmd: oimage.Config.Cmd,
|
||||
Volumes: oimage.Config.Volumes,
|
||||
WorkingDir: oimage.Config.WorkingDir,
|
||||
Labels: oimage.Config.Labels,
|
||||
},
|
||||
},
|
||||
RootFS: &docker.RootFS{
|
||||
Type: "",
|
||||
DiffIDs: []digest.Digest{},
|
||||
},
|
||||
History: []docker.History{},
|
||||
}
|
||||
for port, what := range oimage.Config.ExposedPorts {
|
||||
image.ContainerConfig.ExposedPorts[docker.Port(port)] = what
|
||||
}
|
||||
if oimage.RootFS.Type == docker.TypeLayers {
|
||||
image.RootFS.Type = docker.TypeLayers
|
||||
for _, id := range oimage.RootFS.DiffIDs {
|
||||
d, err := digest.Parse(id)
|
||||
if err != nil {
|
||||
return docker.Image{}, err
|
||||
}
|
||||
image.RootFS.DiffIDs = append(image.RootFS.DiffIDs, d)
|
||||
}
|
||||
}
|
||||
for _, history := range oimage.History {
|
||||
dhistory := docker.History{
|
||||
Created: history.Created.UTC(),
|
||||
CreatedBy: history.CreatedBy,
|
||||
Author: history.Author,
|
||||
Comment: history.Comment,
|
||||
EmptyLayer: history.EmptyLayer,
|
||||
}
|
||||
image.History = append(image.History, dhistory)
|
||||
}
|
||||
image.Config = &image.ContainerConfig
|
||||
return image, nil
|
||||
}
|
||||
|
||||
func (b *Builder) initConfig() {
|
||||
image := ociv1.Image{}
|
||||
dimage := docker.Image{}
|
||||
if len(b.Config) > 0 {
|
||||
// Try to parse the image configuration. If we fail start over from scratch.
|
||||
if err := json.Unmarshal(b.Config, &dimage); err == nil && dimage.DockerVersion != "" {
|
||||
if image, err = copyDockerImageConfig(&dimage); err != nil {
|
||||
if image, err = makeOCIv1Image(&dimage); err != nil {
|
||||
image = ociv1.Image{}
|
||||
}
|
||||
} else {
|
||||
if err := json.Unmarshal(b.Config, &image); err != nil {
|
||||
image = ociv1.Image{}
|
||||
if dimage, err = makeDockerV2S2Image(&image); err != nil {
|
||||
dimage = docker.Image{}
|
||||
}
|
||||
}
|
||||
}
|
||||
b.OCIv1 = image
|
||||
b.Docker = dimage
|
||||
}
|
||||
image.Created = time.Now().UTC()
|
||||
if image.Architecture == "" {
|
||||
image.Architecture = runtime.GOARCH
|
||||
}
|
||||
if image.OS == "" {
|
||||
image.OS = runtime.GOOS
|
||||
}
|
||||
if b.Architecture != "" {
|
||||
image.Architecture = b.Architecture
|
||||
}
|
||||
if b.OS != "" {
|
||||
image.OS = b.OS
|
||||
}
|
||||
if b.Maintainer != "" {
|
||||
image.Author = b.Maintainer
|
||||
}
|
||||
if b.User != "" {
|
||||
image.Config.User = b.User
|
||||
}
|
||||
if len(b.Volumes) > 0 {
|
||||
for _, volSpec := range b.Volumes {
|
||||
image.Config.Volumes[volSpec] = struct{}{}
|
||||
}
|
||||
}
|
||||
if b.Workdir != "" {
|
||||
image.Config.WorkingDir = b.Workdir
|
||||
}
|
||||
if len(b.Env) > 0 {
|
||||
image.Config.Env = append(image.Config.Env, b.Env...)
|
||||
}
|
||||
if len(b.Cmd) > 0 {
|
||||
image.Config.Cmd = b.Cmd
|
||||
}
|
||||
if len(b.Entrypoint) > 0 {
|
||||
image.Config.Entrypoint = b.Entrypoint
|
||||
}
|
||||
if len(b.Expose) > 0 {
|
||||
if image.Config.ExposedPorts == nil {
|
||||
image.Config.ExposedPorts = make(map[string]struct{})
|
||||
}
|
||||
for k := range b.Expose {
|
||||
image.Config.ExposedPorts[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
if len(b.Labels) > 0 {
|
||||
if image.Config.Labels == nil {
|
||||
image.Config.Labels = make(map[string]string)
|
||||
}
|
||||
for k, v := range b.Labels {
|
||||
image.Config.Labels[k] = v
|
||||
}
|
||||
}
|
||||
updatedImageConfig, err := json.Marshal(&image)
|
||||
if err != nil {
|
||||
logrus.Errorf("error exporting updated image configuration, using original configuration")
|
||||
return b.Config
|
||||
}
|
||||
return updatedImageConfig
|
||||
b.fixupConfig()
|
||||
}
|
||||
|
||||
// UpdatedEnv returns the environment list from the source image, with the
|
||||
// builder's own list appended to it.
|
||||
func (b *Builder) UpdatedEnv() []string {
|
||||
config := b.updatedConfig()
|
||||
image := ociv1.Image{}
|
||||
if err := json.Unmarshal(config, &image); err != nil {
|
||||
logrus.Errorf("error parsing updated image information")
|
||||
return []string{}
|
||||
func (b *Builder) fixupConfig() {
|
||||
if b.Docker.Config != nil {
|
||||
// Prefer image-level settings over those from the container it was built from.
|
||||
b.Docker.ContainerConfig = *b.Docker.Config
|
||||
}
|
||||
b.Docker.Config = &b.Docker.ContainerConfig
|
||||
if b.FromImageID != "" {
|
||||
if d, err := digest.Parse(b.FromImageID); err == nil {
|
||||
b.Docker.Parent = docker.ID(d)
|
||||
} else {
|
||||
b.Docker.Parent = docker.ID(digest.NewDigestFromHex(digest.Canonical.String(), b.FromImageID))
|
||||
}
|
||||
}
|
||||
if b.FromImage != "" {
|
||||
b.Docker.Config.Image = b.FromImage
|
||||
}
|
||||
if b.OS() == "" {
|
||||
b.SetOS(runtime.GOOS)
|
||||
}
|
||||
if b.Architecture() == "" {
|
||||
b.SetArchitecture(runtime.GOARCH)
|
||||
}
|
||||
if b.WorkDir() == "" {
|
||||
b.SetWorkDir(string(filepath.Separator))
|
||||
}
|
||||
return append(image.Config.Env, b.Env...)
|
||||
}
|
||||
|
||||
// Annotations returns a set of key-value pairs from the image's manifest.
|
||||
func (b *Builder) Annotations() map[string]string {
|
||||
return copyStringStringMap(b.ImageAnnotations)
|
||||
}
|
||||
|
||||
// SetAnnotation adds or overwrites a key's value from the image's manifest.
|
||||
func (b *Builder) SetAnnotation(key, value string) {
|
||||
b.ImageAnnotations[key] = value
|
||||
}
|
||||
|
||||
// UnsetAnnotation removes a key and its value from the image's manifest, if
|
||||
// it's present.
|
||||
func (b *Builder) UnsetAnnotation(key string) {
|
||||
delete(b.ImageAnnotations, key)
|
||||
}
|
||||
|
||||
// ClearAnnotations removes all keys and their values from the image's
|
||||
// manifest.
|
||||
func (b *Builder) ClearAnnotations() {
|
||||
b.ImageAnnotations = map[string]string{}
|
||||
}
|
||||
|
||||
// CreatedBy returns a description of how this image was built.
|
||||
func (b *Builder) CreatedBy() string {
|
||||
return b.ImageCreatedBy
|
||||
}
|
||||
|
||||
// SetCreatedBy sets the description of how this image was built.
|
||||
func (b *Builder) SetCreatedBy(how string) {
|
||||
b.ImageCreatedBy = how
|
||||
}
|
||||
|
||||
// OS returns a name of the OS on which the container, or a container built
|
||||
// using an image built from this container, is intended to be run.
|
||||
func (b *Builder) OS() string {
|
||||
return b.OCIv1.OS
|
||||
}
|
||||
|
||||
// SetOS sets the name of the OS on which the container, or a container built
|
||||
// using an image built from this container, is intended to be run.
|
||||
func (b *Builder) SetOS(os string) {
|
||||
b.OCIv1.OS = os
|
||||
b.Docker.OS = os
|
||||
}
|
||||
|
||||
// Architecture returns a name of the architecture on which the container, or a
|
||||
// container built using an image built from this container, is intended to be
|
||||
// run.
|
||||
func (b *Builder) Architecture() string {
|
||||
return b.OCIv1.Architecture
|
||||
}
|
||||
|
||||
// SetArchitecture sets the name of the architecture on which the container, or
|
||||
// a container built using an image built from this container, is intended to
|
||||
// be run.
|
||||
func (b *Builder) SetArchitecture(arch string) {
|
||||
b.OCIv1.Architecture = arch
|
||||
b.Docker.Architecture = arch
|
||||
}
|
||||
|
||||
// Maintainer returns contact information for the person who built the image.
|
||||
func (b *Builder) Maintainer() string {
|
||||
return b.OCIv1.Author
|
||||
}
|
||||
|
||||
// SetMaintainer sets contact information for the person who built the image.
|
||||
func (b *Builder) SetMaintainer(who string) {
|
||||
b.OCIv1.Author = who
|
||||
b.Docker.Author = who
|
||||
}
|
||||
|
||||
// User returns information about the user as whom the container, or a
|
||||
// container built using an image built from this container, should be run.
|
||||
func (b *Builder) User() string {
|
||||
return b.OCIv1.Config.User
|
||||
}
|
||||
|
||||
// SetUser sets information about the user as whom the container, or a
|
||||
// container built using an image built from this container, should be run.
|
||||
// Acceptable forms are a user name or ID, optionally followed by a colon and a
|
||||
// group name or ID.
|
||||
func (b *Builder) SetUser(spec string) {
|
||||
b.OCIv1.Config.User = spec
|
||||
b.Docker.Config.User = spec
|
||||
}
|
||||
|
||||
// WorkDir returns the default working directory for running commands in the
|
||||
// container, or in a container built using an image built from this container.
|
||||
func (b *Builder) WorkDir() string {
|
||||
return b.OCIv1.Config.WorkingDir
|
||||
}
|
||||
|
||||
// SetWorkDir sets the location of the default working directory for running
|
||||
// commands in the container, or in a container built using an image built from
|
||||
// this container.
|
||||
func (b *Builder) SetWorkDir(there string) {
|
||||
b.OCIv1.Config.WorkingDir = there
|
||||
b.Docker.Config.WorkingDir = there
|
||||
}
|
||||
|
||||
// Env returns a list of key-value pairs to be set when running commands in the
|
||||
// container, or in a container built using an image built from this container.
|
||||
func (b *Builder) Env() []string {
|
||||
return copyStringSlice(b.OCIv1.Config.Env)
|
||||
}
|
||||
|
||||
// SetEnv adds or overwrites a value to the set of environment strings which
|
||||
// should be set when running commands in the container, or in a container
|
||||
// built using an image built from this container.
|
||||
func (b *Builder) SetEnv(k string, v string) {
|
||||
reset := func(s *[]string) {
|
||||
n := []string{}
|
||||
for i := range *s {
|
||||
if !strings.HasPrefix((*s)[i], k+"=") {
|
||||
n = append(n, (*s)[i])
|
||||
}
|
||||
}
|
||||
n = append(n, k+"="+v)
|
||||
*s = n
|
||||
}
|
||||
reset(&b.OCIv1.Config.Env)
|
||||
reset(&b.Docker.Config.Env)
|
||||
}
|
||||
|
||||
// UnsetEnv removes a value from the set of environment strings which should be
|
||||
// set when running commands in this container, or in a container built using
|
||||
// an image built from this container.
|
||||
func (b *Builder) UnsetEnv(k string) {
|
||||
unset := func(s *[]string) {
|
||||
n := []string{}
|
||||
for i := range *s {
|
||||
if !strings.HasPrefix((*s)[i], k+"=") {
|
||||
n = append(n, (*s)[i])
|
||||
}
|
||||
}
|
||||
*s = n
|
||||
}
|
||||
unset(&b.OCIv1.Config.Env)
|
||||
unset(&b.Docker.Config.Env)
|
||||
}
|
||||
|
||||
// ClearEnv removes all values from the set of environment strings which should
|
||||
// be set when running commands in this container, or in a container built
|
||||
// using an image built from this container.
|
||||
func (b *Builder) ClearEnv() {
|
||||
b.OCIv1.Config.Env = []string{}
|
||||
b.Docker.Config.Env = []string{}
|
||||
}
|
||||
|
||||
// Cmd returns the default command, or command parameters if an Entrypoint is
|
||||
// set, to use when running a container built from an image built from this
|
||||
// container.
|
||||
func (b *Builder) Cmd() []string {
|
||||
return copyStringSlice(b.OCIv1.Config.Cmd)
|
||||
}
|
||||
|
||||
// SetCmd sets the default command, or command parameters if an Entrypoint is
|
||||
// set, to use when running a container built from an image built from this
|
||||
// container.
|
||||
func (b *Builder) SetCmd(cmd []string) {
|
||||
b.OCIv1.Config.Cmd = copyStringSlice(cmd)
|
||||
b.Docker.Config.Cmd = copyStringSlice(cmd)
|
||||
}
|
||||
|
||||
// Entrypoint returns the command to be run for containers built from images
|
||||
// built from this container.
|
||||
func (b *Builder) Entrypoint() []string {
|
||||
return copyStringSlice(b.OCIv1.Config.Entrypoint)
|
||||
}
|
||||
|
||||
// SetEntrypoint sets the command to be run for in containers built from images
|
||||
// built from this container.
|
||||
func (b *Builder) SetEntrypoint(ep []string) {
|
||||
b.OCIv1.Config.Entrypoint = copyStringSlice(ep)
|
||||
b.Docker.Config.Entrypoint = copyStringSlice(ep)
|
||||
}
|
||||
|
||||
// Labels returns a set of key-value pairs from the image's runtime
|
||||
// configuration.
|
||||
func (b *Builder) Labels() map[string]string {
|
||||
return copyStringStringMap(b.OCIv1.Config.Labels)
|
||||
}
|
||||
|
||||
// SetLabel adds or overwrites a key's value from the image's runtime
|
||||
// configuration.
|
||||
func (b *Builder) SetLabel(k string, v string) {
|
||||
b.OCIv1.Config.Labels[k] = v
|
||||
b.Docker.Config.Labels[k] = v
|
||||
}
|
||||
|
||||
// UnsetLabel removes a key and its value from the image's runtime
|
||||
// configuration, if it's present.
|
||||
func (b *Builder) UnsetLabel(k string) {
|
||||
delete(b.OCIv1.Config.Labels, k)
|
||||
delete(b.Docker.Config.Labels, k)
|
||||
}
|
||||
|
||||
// ClearLabels removes all keys and their values from the image's runtime
|
||||
// configuration.
|
||||
func (b *Builder) ClearLabels() {
|
||||
b.OCIv1.Config.Labels = map[string]string{}
|
||||
b.Docker.Config.Labels = map[string]string{}
|
||||
}
|
||||
|
||||
// Ports returns the set of ports which should be exposed when a container
|
||||
// based on an image built from this container is run.
|
||||
func (b *Builder) Ports() []string {
|
||||
p := []string{}
|
||||
for k := range b.OCIv1.Config.ExposedPorts {
|
||||
p = append(p, k)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// SetPort adds or overwrites an exported port in the set of ports which should
|
||||
// be exposed when a container based on an image built from this container is
|
||||
// run.
|
||||
func (b *Builder) SetPort(p string) {
|
||||
b.OCIv1.Config.ExposedPorts[p] = struct{}{}
|
||||
b.Docker.Config.ExposedPorts[docker.Port(p)] = struct{}{}
|
||||
}
|
||||
|
||||
// UnsetPort removes an exposed port from the set of ports which should be
|
||||
// exposed when a container based on an image built from this container is run.
|
||||
func (b *Builder) UnsetPort(p string) {
|
||||
delete(b.OCIv1.Config.ExposedPorts, p)
|
||||
delete(b.Docker.Config.ExposedPorts, docker.Port(p))
|
||||
}
|
||||
|
||||
// ClearPorts empties the set of ports which should be exposed when a container
|
||||
// based on an image built from this container is run.
|
||||
func (b *Builder) ClearPorts() {
|
||||
b.OCIv1.Config.ExposedPorts = map[string]struct{}{}
|
||||
b.Docker.Config.ExposedPorts = docker.PortSet{}
|
||||
}
|
||||
|
||||
// Volumes returns a list of filesystem locations which should be mounted from
|
||||
// outside of the container when a container built from an image built from
|
||||
// this container is run.
|
||||
func (b *Builder) Volumes() []string {
|
||||
v := []string{}
|
||||
for k := range b.OCIv1.Config.Volumes {
|
||||
v = append(v, k)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// AddVolume adds a location to the image's list of locations which should be
|
||||
// mounted from outside of the container when a container based on an image
|
||||
// built from this container is run.
|
||||
func (b *Builder) AddVolume(v string) {
|
||||
b.OCIv1.Config.Volumes[v] = struct{}{}
|
||||
b.Docker.Config.Volumes[v] = struct{}{}
|
||||
}
|
||||
|
||||
// RemoveVolume removes a location from the list of locations which should be
|
||||
// mounted from outside of the container when a container based on an image
|
||||
// built from this container is run.
|
||||
func (b *Builder) RemoveVolume(v string) {
|
||||
delete(b.OCIv1.Config.Volumes, v)
|
||||
delete(b.Docker.Config.Volumes, v)
|
||||
}
|
||||
|
||||
// ClearVolumes removes all locations from the image's list of locations which
|
||||
// should be mounted from outside of the container when a container based on an
|
||||
// image built from this container is run.
|
||||
func (b *Builder) ClearVolumes() {
|
||||
b.OCIv1.Config.Volumes = map[string]struct{}{}
|
||||
b.Docker.Config.Volumes = map[string]struct{}{}
|
||||
}
|
||||
|
||||
// Hostname returns the hostname which will be set in the container and in
|
||||
// containers built using images built from the container.
|
||||
func (b *Builder) Hostname() string {
|
||||
return b.Docker.Config.Hostname
|
||||
}
|
||||
|
||||
// SetHostname sets the hostname which will be set in the container and in
|
||||
// containers built using images built from the container.
|
||||
// Note: this setting is not present in the OCIv1 image format, so it is
|
||||
// discarded when writing images using OCIv1 formats.
|
||||
func (b *Builder) SetHostname(name string) {
|
||||
b.Docker.Config.Hostname = name
|
||||
}
|
||||
|
||||
// Domainname returns the domainname which will be set in the container and in
|
||||
// containers built using images built from the container.
|
||||
func (b *Builder) Domainname() string {
|
||||
return b.Docker.Config.Domainname
|
||||
}
|
||||
|
||||
// SetDomainname sets the domainname which will be set in the container and in
|
||||
// containers built using images built from the container.
|
||||
// Note: this setting is not present in the OCIv1 image format, so it is
|
||||
// discarded when writing images using OCIv1 formats.
|
||||
func (b *Builder) SetDomainname(name string) {
|
||||
b.Docker.Config.Domainname = name
|
||||
}
|
||||
|
|
10
image.go
10
image.go
|
@ -321,14 +321,18 @@ func (b *Builder) makeContainerImageRef(compress archive.Compression) (types.Ima
|
|||
name = nil
|
||||
}
|
||||
}
|
||||
config, err := json.Marshal(&b.OCIv1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref := &containerImageRef{
|
||||
store: b.store,
|
||||
container: container,
|
||||
compression: compress,
|
||||
name: name,
|
||||
config: b.updatedConfig(),
|
||||
createdBy: b.CreatedBy,
|
||||
annotations: b.Annotations,
|
||||
config: config,
|
||||
createdBy: b.CreatedBy(),
|
||||
annotations: b.Annotations(),
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
|
|
@ -439,11 +439,13 @@ func (b *Executor) Prepare(ib *imagebuilder.Builder, node *parser.Node, from str
|
|||
if err != nil {
|
||||
return fmt.Errorf("error creating build container: %v", err)
|
||||
}
|
||||
dConfig := docker.Config{
|
||||
Env: builder.Env(),
|
||||
Image: from,
|
||||
}
|
||||
dImage := docker.Image{
|
||||
Config: &docker.Config{
|
||||
Env: builder.UpdatedEnv(),
|
||||
Image: from,
|
||||
},
|
||||
Config: &dConfig,
|
||||
ContainerConfig: dConfig,
|
||||
}
|
||||
err = ib.FromImage(&dImage, node)
|
||||
if err != nil {
|
||||
|
|
29
import.go
29
import.go
|
@ -53,25 +53,20 @@ func importBuilder(store storage.Store, options ImportOptions) (*Builder, error)
|
|||
name := options.Container
|
||||
|
||||
builder := &Builder{
|
||||
store: store,
|
||||
Type: containerType,
|
||||
FromImage: imageName,
|
||||
FromImageID: image,
|
||||
Config: config,
|
||||
Manifest: manifest,
|
||||
Container: name,
|
||||
ContainerID: c.ID,
|
||||
Mounts: []string{},
|
||||
Annotations: map[string]string{},
|
||||
Env: []string{},
|
||||
Cmd: []string{},
|
||||
Entrypoint: []string{},
|
||||
Expose: map[string]interface{}{},
|
||||
Labels: map[string]string{},
|
||||
Volumes: []string{},
|
||||
Arg: map[string]string{},
|
||||
store: store,
|
||||
Type: containerType,
|
||||
FromImage: imageName,
|
||||
FromImageID: image,
|
||||
Config: config,
|
||||
Manifest: manifest,
|
||||
Container: name,
|
||||
ContainerID: c.ID,
|
||||
Mounts: []string{},
|
||||
ImageAnnotations: map[string]string{},
|
||||
ImageCreatedBy: "",
|
||||
}
|
||||
|
||||
builder.initConfig()
|
||||
err = builder.Save()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error saving builder state: %v", err)
|
||||
|
|
29
new.go
29
new.go
|
@ -123,23 +123,17 @@ func newBuilder(store storage.Store, options BuilderOptions) (*Builder, error) {
|
|||
}()
|
||||
|
||||
builder := &Builder{
|
||||
store: store,
|
||||
Type: containerType,
|
||||
FromImage: image,
|
||||
FromImageID: imageID,
|
||||
Config: config,
|
||||
Manifest: manifest,
|
||||
Container: name,
|
||||
ContainerID: container.ID,
|
||||
Mounts: []string{},
|
||||
Annotations: map[string]string{},
|
||||
Env: []string{},
|
||||
Cmd: []string{},
|
||||
Entrypoint: []string{},
|
||||
Expose: map[string]interface{}{},
|
||||
Labels: map[string]string{},
|
||||
Volumes: []string{},
|
||||
Arg: map[string]string{},
|
||||
store: store,
|
||||
Type: containerType,
|
||||
FromImage: image,
|
||||
FromImageID: imageID,
|
||||
Config: config,
|
||||
Manifest: manifest,
|
||||
Container: name,
|
||||
ContainerID: container.ID,
|
||||
Mounts: []string{},
|
||||
ImageAnnotations: map[string]string{},
|
||||
ImageCreatedBy: "",
|
||||
}
|
||||
|
||||
if options.Mount {
|
||||
|
@ -149,6 +143,7 @@ func newBuilder(store storage.Store, options BuilderOptions) (*Builder, error) {
|
|||
}
|
||||
}
|
||||
|
||||
builder.initConfig()
|
||||
err = builder.Save()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error saving builder state: %v", err)
|
||||
|
|
37
run.go
37
run.go
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/runtime-tools/generate"
|
||||
)
|
||||
|
@ -78,21 +77,15 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
logrus.Errorf("error removing %q: %v", path, err2)
|
||||
}
|
||||
}()
|
||||
config := b.updatedConfig()
|
||||
image := v1.Image{}
|
||||
err = json.Unmarshal(config, &image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g := generate.New()
|
||||
|
||||
if image.OS != "" {
|
||||
g.SetPlatformOS(image.OS)
|
||||
if b.OS() != "" {
|
||||
g.SetPlatformOS(b.OS())
|
||||
}
|
||||
if image.Architecture != "" {
|
||||
g.SetPlatformArch(image.Architecture)
|
||||
if b.Architecture() != "" {
|
||||
g.SetPlatformArch(b.Architecture())
|
||||
}
|
||||
for _, envSpec := range append(image.Config.Env, options.Env...) {
|
||||
for _, envSpec := range append(b.Env(), options.Env...) {
|
||||
env := strings.SplitN(envSpec, "=", 2)
|
||||
if len(env) > 1 {
|
||||
g.AddProcessEnv(env[0], env[1])
|
||||
|
@ -102,22 +95,22 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
g.SetProcessArgs(command)
|
||||
} else if len(options.Cmd) != 0 {
|
||||
g.SetProcessArgs(options.Cmd)
|
||||
} else if len(image.Config.Cmd) != 0 {
|
||||
g.SetProcessArgs(image.Config.Cmd)
|
||||
} else if len(b.Cmd()) != 0 {
|
||||
g.SetProcessArgs(b.Cmd())
|
||||
} else if len(options.Entrypoint) != 0 {
|
||||
g.SetProcessArgs(options.Entrypoint)
|
||||
} else if len(image.Config.Entrypoint) != 0 {
|
||||
g.SetProcessArgs(image.Config.Entrypoint)
|
||||
} else if len(b.Entrypoint()) != 0 {
|
||||
g.SetProcessArgs(b.Entrypoint())
|
||||
}
|
||||
if options.WorkingDir != "" {
|
||||
g.SetProcessCwd(options.WorkingDir)
|
||||
} else if image.Config.WorkingDir != "" {
|
||||
g.SetProcessCwd(image.Config.WorkingDir)
|
||||
} else if b.WorkDir() != "" {
|
||||
g.SetProcessCwd(b.WorkDir())
|
||||
}
|
||||
if options.Hostname != "" {
|
||||
g.SetHostname(options.Hostname)
|
||||
}
|
||||
for volume := range image.Config.Volumes {
|
||||
for _, volume := range b.Volumes() {
|
||||
g.AddTmpfsMount(volume, nil)
|
||||
}
|
||||
mountPoint, err := b.Mount("")
|
||||
|
@ -146,7 +139,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
if options.User != "" {
|
||||
user, err = getUser(mountPoint, options.User)
|
||||
} else {
|
||||
user, err = getUser(mountPoint, image.Config.User)
|
||||
user, err = getUser(mountPoint, b.User())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -157,8 +150,8 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||
if spec.Process.Cwd == "" {
|
||||
spec.Process.Cwd = DefaultWorkingDir
|
||||
}
|
||||
if err = os.MkdirAll(filepath.Join(mountPoint, b.Workdir), 0755); err != nil {
|
||||
return fmt.Errorf("error ensuring working directory %q exists: %v)", b.Workdir, err)
|
||||
if err = os.MkdirAll(filepath.Join(mountPoint, b.WorkDir()), 0755); err != nil {
|
||||
return fmt.Errorf("error ensuring working directory %q exists: %v)", b.WorkDir(), err)
|
||||
}
|
||||
mounts := options.Mounts
|
||||
boundMounts := []specs.Mount{}
|
||||
|
|
14
util.go
14
util.go
|
@ -10,3 +10,17 @@ import (
|
|||
func InitReexec() bool {
|
||||
return reexec.Init()
|
||||
}
|
||||
|
||||
func copyStringStringMap(m map[string]string) map[string]string {
|
||||
n := map[string]string{}
|
||||
for k, v := range m {
|
||||
n[k] = v
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func copyStringSlice(s []string) []string {
|
||||
t := make([]string, len(s))
|
||||
copy(t, s)
|
||||
return t
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue