2017-02-11 00:48:15 +08:00
|
|
|
package buildah
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2017-05-09 23:56:44 +08:00
|
|
|
"io"
|
2017-02-24 06:56:48 +08:00
|
|
|
"io/ioutil"
|
2017-03-16 04:44:52 +08:00
|
|
|
"os"
|
2017-02-11 00:48:15 +08:00
|
|
|
"path/filepath"
|
|
|
|
|
2017-02-24 06:56:48 +08:00
|
|
|
"github.com/containers/storage/pkg/ioutils"
|
2017-02-11 00:48:15 +08:00
|
|
|
"github.com/containers/storage/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2017-02-11 03:45:06 +08:00
|
|
|
// Package is the name of this package, used in help output and to
|
|
|
|
// identify working containers.
|
2017-04-19 03:52:53 +08:00
|
|
|
Package = "buildah"
|
|
|
|
// Version for the Package
|
|
|
|
Version = "0.0.1"
|
2017-02-11 03:45:06 +08:00
|
|
|
containerType = Package + " 0.0.0"
|
2017-02-24 06:56:48 +08:00
|
|
|
stateFile = Package + ".json"
|
2017-02-11 00:48:15 +08:00
|
|
|
)
|
|
|
|
|
2017-04-11 02:15:30 +08:00
|
|
|
const (
|
|
|
|
// PullIfMissing is one of the values that BuilderOptions.PullPolicy
|
|
|
|
// can take, signalling that the source image should be pulled from a
|
|
|
|
// registry if a local copy of it is not already present.
|
|
|
|
PullIfMissing = iota
|
|
|
|
// PullAlways is one of the values that BuilderOptions.PullPolicy can
|
|
|
|
// take, signalling that a fresh, possibly updated, copy of the image
|
|
|
|
// should be pulled from a registry before the build proceeds.
|
|
|
|
PullAlways
|
|
|
|
// PullNever is one of the values that BuilderOptions.PullPolicy can
|
|
|
|
// take, signalling that the source image should not be pulled from a
|
|
|
|
// registry if a local copy of it is not already present.
|
|
|
|
PullNever
|
|
|
|
)
|
|
|
|
|
2017-02-11 03:45:06 +08:00
|
|
|
// Builder objects are used to represent containers which are being used to
|
|
|
|
// build images. They also carry potential updates which will be applied to
|
|
|
|
// the image's configuration when the container's contents are used to build an
|
|
|
|
// image.
|
2017-02-11 00:48:15 +08:00
|
|
|
type Builder struct {
|
|
|
|
store storage.Store
|
|
|
|
|
2017-02-11 03:45:06 +08:00
|
|
|
// Type is used to help identify a build container's metadata. It
|
|
|
|
// should not be modified.
|
|
|
|
Type string `json:"type"`
|
|
|
|
// FromImage is the name of the source image which was used to create
|
|
|
|
// the container, if one was used. It should not be modified.
|
|
|
|
FromImage string `json:"image,omitempty"`
|
2017-03-16 05:19:29 +08:00
|
|
|
// FromImageID is the ID of the source image which was used to create
|
|
|
|
// the container, if one was used. It should not be modified.
|
|
|
|
FromImageID string `json:"image-id"`
|
2017-02-11 03:45:06 +08:00
|
|
|
// Config is the source image's configuration. It should not be
|
|
|
|
// modified.
|
|
|
|
Config []byte `json:"config,omitempty"`
|
|
|
|
// Manifest is the source image's manifest. It should not be modified.
|
|
|
|
Manifest []byte `json:"manifest,omitempty"`
|
|
|
|
|
|
|
|
// Container is the name of the build container. It should not be modified.
|
|
|
|
Container string `json:"container-name,omitempty"`
|
|
|
|
// ContainerID is the ID of the build container. It should not be modified.
|
|
|
|
ContainerID string `json:"container-id,omitempty"`
|
|
|
|
// MountPoint is the last location where the container's root
|
|
|
|
// filesystem was mounted. It should not be modified.
|
|
|
|
MountPoint string `json:"mountpoint,omitempty"`
|
|
|
|
// Mounts is a list of places where the container's root filesystem has
|
|
|
|
// been mounted. It should not be modified.
|
|
|
|
Mounts []string `json:"mounts,omitempty"`
|
2017-02-11 00:48:15 +08:00
|
|
|
|
2017-02-11 03:45:06 +08:00
|
|
|
// Annotations is a set of key-value pairs which is stored in the
|
|
|
|
// image's manifest.
|
|
|
|
Annotations map[string]string `json:"annotations,omitempty"`
|
2017-02-11 00:48:15 +08:00
|
|
|
|
2017-02-11 03:45:06 +08:00
|
|
|
// 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"`
|
2017-02-11 00:48:15 +08:00
|
|
|
}
|
|
|
|
|
2017-02-11 03:45:06 +08:00
|
|
|
// BuilderOptions are used to initialize a Builder.
|
2017-02-11 00:48:15 +08:00
|
|
|
type BuilderOptions struct {
|
2017-02-11 03:45:06 +08:00
|
|
|
// 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
|
|
|
|
// or "scratch" to indicate that the container should not be based on
|
|
|
|
// an image.
|
|
|
|
FromImage string
|
|
|
|
// Container is a desired name for the build container.
|
|
|
|
Container string
|
2017-04-11 02:15:30 +08:00
|
|
|
// PullPolicy decides whether or not we should pull the image that
|
|
|
|
// we're using as a base image. It should be PullIfMissing,
|
|
|
|
// PullAlways, or PullNever.
|
|
|
|
PullPolicy int
|
2017-02-11 03:45:06 +08:00
|
|
|
// Registry is a value which is prepended to the image's name, if it
|
|
|
|
// needs to be pulled and the image name alone can not be resolved to a
|
|
|
|
// reference to a source image.
|
|
|
|
Registry string
|
|
|
|
// Mount signals to NewBuilder() that the container should be mounted
|
|
|
|
// immediately.
|
|
|
|
Mount bool
|
|
|
|
// SignaturePolicyPath specifies an override location for the signature
|
|
|
|
// policy which should be used for verifying the new image as it is
|
|
|
|
// being written. Except in specific circumstances, no value should be
|
|
|
|
// specified, indicating that the shared, system-wide default policy
|
|
|
|
// should be used.
|
2017-02-11 00:48:15 +08:00
|
|
|
SignaturePolicyPath string
|
2017-05-09 23:56:44 +08:00
|
|
|
// ReportWriter is an io.Writer which will be used to log the reading
|
|
|
|
// of the source image from a registry, if we end up pulling the image.
|
|
|
|
ReportWriter io.Writer
|
2017-02-11 00:48:15 +08:00
|
|
|
}
|
|
|
|
|
2017-02-25 04:18:35 +08:00
|
|
|
// ImportOptions are used to initialize a Builder.
|
|
|
|
type ImportOptions struct {
|
|
|
|
// Container is the name of the build container.
|
|
|
|
Container string
|
|
|
|
// SignaturePolicyPath specifies an override location for the signature
|
|
|
|
// policy which should be used for verifying the new image as it is
|
|
|
|
// being written. Except in specific circumstances, no value should be
|
|
|
|
// specified, indicating that the shared, system-wide default policy
|
|
|
|
// should be used.
|
|
|
|
SignaturePolicyPath string
|
|
|
|
}
|
|
|
|
|
2017-02-11 03:45:06 +08:00
|
|
|
// NewBuilder creates a new build container.
|
2017-02-11 00:48:15 +08:00
|
|
|
func NewBuilder(store storage.Store, options BuilderOptions) (*Builder, error) {
|
|
|
|
return newBuilder(store, options)
|
|
|
|
}
|
|
|
|
|
2017-02-25 04:18:35 +08:00
|
|
|
// ImportBuilder creates a new build configuration using an already-present
|
|
|
|
// container.
|
|
|
|
func ImportBuilder(store storage.Store, options ImportOptions) (*Builder, error) {
|
|
|
|
return importBuilder(store, options)
|
|
|
|
}
|
|
|
|
|
2017-02-11 03:45:06 +08:00
|
|
|
// OpenBuilder loads information about a build container given its name or ID.
|
2017-02-11 00:48:15 +08:00
|
|
|
func OpenBuilder(store storage.Store, container string) (*Builder, error) {
|
2017-02-24 06:56:48 +08:00
|
|
|
cdir, err := store.GetContainerDirectory(container)
|
2017-02-11 00:48:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-02-24 06:56:48 +08:00
|
|
|
buildstate, err := ioutil.ReadFile(filepath.Join(cdir, stateFile))
|
2017-02-11 00:48:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
b := &Builder{}
|
2017-04-04 05:44:23 +08:00
|
|
|
err = json.Unmarshal(buildstate, &b)
|
2017-02-11 00:48:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-02-11 03:45:06 +08:00
|
|
|
if b.Type != containerType {
|
2017-02-11 00:48:15 +08:00
|
|
|
return nil, fmt.Errorf("container is not a %s container", Package)
|
|
|
|
}
|
|
|
|
b.store = store
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
2017-03-24 01:32:16 +08:00
|
|
|
// OpenBuilderByPath loads information about a build container given a
|
|
|
|
// path to the container's root filesystem
|
2017-02-11 00:48:15 +08:00
|
|
|
func OpenBuilderByPath(store storage.Store, path string) (*Builder, error) {
|
|
|
|
containers, err := store.Containers()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
abs, err := filepath.Abs(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
builderMatchesPath := func(b *Builder, path string) bool {
|
|
|
|
if b.MountPoint == path {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, m := range b.Mounts {
|
|
|
|
if m == path {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for _, container := range containers {
|
2017-03-16 04:42:37 +08:00
|
|
|
cdir, err := store.GetContainerDirectory(container.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
buildstate, err := ioutil.ReadFile(filepath.Join(cdir, stateFile))
|
2017-02-11 00:48:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
b := &Builder{}
|
2017-04-04 05:44:23 +08:00
|
|
|
err = json.Unmarshal(buildstate, &b)
|
2017-02-11 03:45:06 +08:00
|
|
|
if err == nil && b.Type == containerType && builderMatchesPath(b, abs) {
|
2017-02-11 00:48:15 +08:00
|
|
|
b.store = store
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, storage.ErrContainerUnknown
|
|
|
|
}
|
|
|
|
|
2017-03-16 04:44:52 +08:00
|
|
|
// OpenAllBuilders loads all containers which have a state file that we use in
|
|
|
|
// their data directory, typically so that they can be listed.
|
|
|
|
func OpenAllBuilders(store storage.Store) (builders []*Builder, err error) {
|
|
|
|
containers, err := store.Containers()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, container := range containers {
|
|
|
|
cdir, err := store.GetContainerDirectory(container.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
buildstate, err := ioutil.ReadFile(filepath.Join(cdir, stateFile))
|
|
|
|
if err != nil && os.IsNotExist(err) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
b := &Builder{}
|
2017-04-04 05:44:23 +08:00
|
|
|
err = json.Unmarshal(buildstate, &b)
|
2017-03-16 04:44:52 +08:00
|
|
|
if err == nil && b.Type == containerType {
|
|
|
|
b.store = store
|
|
|
|
builders = append(builders, b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return builders, nil
|
|
|
|
}
|
|
|
|
|
2017-02-11 03:45:06 +08:00
|
|
|
// Save saves the builder's current state to the build container's metadata.
|
|
|
|
// This should not need to be called directly, as other methods of the Builder
|
|
|
|
// object take care of saving their state.
|
2017-02-11 00:48:15 +08:00
|
|
|
func (b *Builder) Save() error {
|
|
|
|
buildstate, err := json.Marshal(b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-02-24 06:56:48 +08:00
|
|
|
cdir, err := b.store.GetContainerDirectory(b.ContainerID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return ioutils.AtomicWriteFile(filepath.Join(cdir, stateFile), buildstate, 0600)
|
2017-02-11 00:48:15 +08:00
|
|
|
}
|