mirror of
https://github.com/containers/buildah.git
synced 2025-04-18 07:04:05 +03:00
831 lines
29 KiB
Go
831 lines
29 KiB
Go
package buildah
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"maps"
|
|
"os"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containerd/platforms"
|
|
"github.com/containers/buildah/define"
|
|
"github.com/containers/buildah/docker"
|
|
internalUtil "github.com/containers/buildah/internal/util"
|
|
"github.com/containers/image/v5/manifest"
|
|
"github.com/containers/image/v5/pkg/compression"
|
|
"github.com/containers/image/v5/transports"
|
|
"github.com/containers/image/v5/types"
|
|
"github.com/containers/storage/pkg/stringid"
|
|
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// unmarshalConvertedConfig obtains the config blob of img valid for the wantedManifestMIMEType format
|
|
// (either as it exists, or converting the image if necessary), and unmarshals it into dest.
|
|
// NOTE: The MIME type is of the _manifest_, not of the _config_ that is returned.
|
|
func unmarshalConvertedConfig(ctx context.Context, dest any, img types.Image, wantedManifestMIMEType string) error {
|
|
_, actualManifestMIMEType, err := img.Manifest(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("getting manifest MIME type for %q: %w", transports.ImageName(img.Reference()), err)
|
|
}
|
|
if wantedManifestMIMEType != actualManifestMIMEType {
|
|
layerInfos := img.LayerInfos()
|
|
for i := range layerInfos { // force the "compression" to gzip, which is supported by all of the formats we care about
|
|
layerInfos[i].CompressionOperation = types.Compress
|
|
layerInfos[i].CompressionAlgorithm = &compression.Gzip
|
|
}
|
|
updatedImg, err := img.UpdatedImage(ctx, types.ManifestUpdateOptions{
|
|
LayerInfos: layerInfos,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("resetting recorded compression for %q: %w", transports.ImageName(img.Reference()), err)
|
|
}
|
|
secondUpdatedImg, err := updatedImg.UpdatedImage(ctx, types.ManifestUpdateOptions{
|
|
ManifestMIMEType: wantedManifestMIMEType,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("converting image %q from %q to %q: %w", transports.ImageName(img.Reference()), actualManifestMIMEType, wantedManifestMIMEType, err)
|
|
}
|
|
img = secondUpdatedImg
|
|
}
|
|
config, err := img.ConfigBlob(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("reading %s config from %q: %w", wantedManifestMIMEType, transports.ImageName(img.Reference()), err)
|
|
}
|
|
if err := json.Unmarshal(config, dest); err != nil {
|
|
return fmt.Errorf("parsing %s configuration %q from %q: %w", wantedManifestMIMEType, string(config), transports.ImageName(img.Reference()), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) initConfig(ctx context.Context, sys *types.SystemContext, img types.Image, options *BuilderOptions) error {
|
|
if img != nil { // A pre-existing image, as opposed to a "FROM scratch" new one.
|
|
rawManifest, manifestMIMEType, err := img.Manifest(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("reading image manifest for %q: %w", transports.ImageName(img.Reference()), err)
|
|
}
|
|
rawConfig, err := img.ConfigBlob(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("reading image configuration for %q: %w", transports.ImageName(img.Reference()), err)
|
|
}
|
|
b.Manifest = rawManifest
|
|
b.Config = rawConfig
|
|
|
|
dimage := docker.V2Image{}
|
|
if err := unmarshalConvertedConfig(ctx, &dimage, img, manifest.DockerV2Schema2MediaType); err != nil {
|
|
return err
|
|
}
|
|
b.Docker = dimage
|
|
|
|
oimage := ociv1.Image{}
|
|
if err := unmarshalConvertedConfig(ctx, &oimage, img, ociv1.MediaTypeImageManifest); err != nil {
|
|
return err
|
|
}
|
|
b.OCIv1 = oimage
|
|
|
|
if manifestMIMEType == ociv1.MediaTypeImageManifest {
|
|
// Attempt to recover format-specific data from the manifest.
|
|
v1Manifest := ociv1.Manifest{}
|
|
if err := json.Unmarshal(b.Manifest, &v1Manifest); err != nil {
|
|
return fmt.Errorf("parsing OCI manifest %q: %w", string(b.Manifest), err)
|
|
}
|
|
if len(v1Manifest.Annotations) > 0 {
|
|
if b.ImageAnnotations == nil {
|
|
b.ImageAnnotations = make(map[string]string, len(v1Manifest.Annotations))
|
|
}
|
|
maps.Copy(b.ImageAnnotations, v1Manifest.Annotations)
|
|
}
|
|
}
|
|
} else {
|
|
if options == nil || options.CompatScratchConfig != types.OptionalBoolTrue {
|
|
b.Docker = docker.V2Image{
|
|
V1Image: docker.V1Image{
|
|
Config: &docker.Config{
|
|
WorkingDir: "/",
|
|
},
|
|
},
|
|
}
|
|
b.OCIv1 = ociv1.Image{
|
|
Config: ociv1.ImageConfig{
|
|
WorkingDir: "/",
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
b.setupLogger()
|
|
b.fixupConfig(sys)
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) fixupConfig(sys *types.SystemContext) {
|
|
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
|
|
b.Docker.DockerVersion = ""
|
|
now := time.Now().UTC()
|
|
if b.Docker.Created.IsZero() {
|
|
b.Docker.Created = now
|
|
}
|
|
if b.OCIv1.Created == nil || b.OCIv1.Created.IsZero() {
|
|
b.OCIv1.Created = &now
|
|
}
|
|
currentPlatformSpecification := platforms.DefaultSpec()
|
|
if b.OS() == "" {
|
|
if sys != nil && sys.OSChoice != "" {
|
|
b.SetOS(sys.OSChoice)
|
|
} else {
|
|
b.SetOS(currentPlatformSpecification.OS)
|
|
}
|
|
}
|
|
if b.Architecture() == "" {
|
|
if sys != nil && sys.ArchitectureChoice != "" {
|
|
b.SetArchitecture(sys.ArchitectureChoice)
|
|
} else {
|
|
b.SetArchitecture(currentPlatformSpecification.Architecture)
|
|
}
|
|
// in case the arch string we started with was shorthand for a known arch+variant pair, normalize it
|
|
ps := internalUtil.NormalizePlatform(ociv1.Platform{OS: b.OS(), Architecture: b.Architecture(), Variant: b.Variant()})
|
|
b.SetArchitecture(ps.Architecture)
|
|
b.SetVariant(ps.Variant)
|
|
}
|
|
if b.Variant() == "" {
|
|
if sys != nil && sys.VariantChoice != "" {
|
|
b.SetVariant(sys.VariantChoice)
|
|
} else {
|
|
b.SetVariant(currentPlatformSpecification.Variant)
|
|
}
|
|
// in case the arch string we started with was shorthand for a known arch+variant pair, normalize it
|
|
ps := internalUtil.NormalizePlatform(ociv1.Platform{OS: b.OS(), Architecture: b.Architecture(), Variant: b.Variant()})
|
|
b.SetArchitecture(ps.Architecture)
|
|
b.SetVariant(ps.Variant)
|
|
}
|
|
if b.Format == define.Dockerv2ImageManifest && b.Hostname() == "" {
|
|
b.SetHostname(stringid.TruncateID(stringid.GenerateRandomID()))
|
|
}
|
|
}
|
|
|
|
func (b *Builder) setupLogger() {
|
|
if b.Logger == nil {
|
|
b.Logger = logrus.New()
|
|
b.Logger.SetOutput(os.Stderr)
|
|
b.Logger.SetLevel(logrus.GetLevel())
|
|
}
|
|
}
|
|
|
|
// Annotations returns a set of key-value pairs from the image's manifest.
|
|
func (b *Builder) Annotations() map[string]string {
|
|
return maps.Clone(b.ImageAnnotations)
|
|
}
|
|
|
|
// SetAnnotation adds or overwrites a key's value from the image's manifest.
|
|
// Note: this setting is not present in the Docker v2 image format, so it is
|
|
// discarded when writing images using Docker v2 formats.
|
|
func (b *Builder) SetAnnotation(key, value string) {
|
|
if b.ImageAnnotations == nil {
|
|
b.ImageAnnotations = map[string]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 = nil
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// OSVersion returns a version 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) OSVersion() string {
|
|
return b.OCIv1.OSVersion
|
|
}
|
|
|
|
// SetOSVersion sets the version 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) SetOSVersion(version string) {
|
|
b.OCIv1.OSVersion = version
|
|
b.Docker.OSVersion = version
|
|
}
|
|
|
|
// OSFeatures returns a list of OS features which the container, or a container
|
|
// built using an image built from this container, depends on the OS supplying.
|
|
func (b *Builder) OSFeatures() []string {
|
|
return slices.Clone(b.OCIv1.OSFeatures)
|
|
}
|
|
|
|
// SetOSFeature adds a feature of the OS which the container, or a container
|
|
// built using an image built from this container, depends on the OS supplying.
|
|
func (b *Builder) SetOSFeature(feature string) {
|
|
if !slices.Contains(b.OCIv1.OSFeatures, feature) {
|
|
b.OCIv1.OSFeatures = append(b.OCIv1.OSFeatures, feature)
|
|
}
|
|
if !slices.Contains(b.Docker.OSFeatures, feature) {
|
|
b.Docker.OSFeatures = append(b.Docker.OSFeatures, feature)
|
|
}
|
|
}
|
|
|
|
// UnsetOSFeature removes a feature of the OS which the container, or a
|
|
// container built using an image built from this container, depends on the OS
|
|
// supplying.
|
|
func (b *Builder) UnsetOSFeature(feature string) {
|
|
if slices.Contains(b.OCIv1.OSFeatures, feature) {
|
|
features := make([]string, 0, len(b.OCIv1.OSFeatures))
|
|
for _, f := range b.OCIv1.OSFeatures {
|
|
if f != feature {
|
|
features = append(features, f)
|
|
}
|
|
}
|
|
b.OCIv1.OSFeatures = features
|
|
}
|
|
if slices.Contains(b.Docker.OSFeatures, feature) {
|
|
features := make([]string, 0, len(b.Docker.OSFeatures))
|
|
for _, f := range b.Docker.OSFeatures {
|
|
if f != feature {
|
|
features = append(features, f)
|
|
}
|
|
}
|
|
b.Docker.OSFeatures = features
|
|
}
|
|
}
|
|
|
|
// ClearOSFeatures clears the list of features of the OS which the container,
|
|
// or a container built using an image built from this container, depends on
|
|
// the OS supplying.
|
|
func (b *Builder) ClearOSFeatures() {
|
|
b.OCIv1.OSFeatures = []string{}
|
|
b.Docker.OSFeatures = []string{}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Variant returns a name of the architecture variant on which the container,
|
|
// or a container built using an image built from this container, is intended
|
|
// to be run.
|
|
func (b *Builder) Variant() string {
|
|
return b.OCIv1.Variant
|
|
}
|
|
|
|
// SetVariant sets the name of the architecture variant on which the container,
|
|
// or a container built using an image built from this container, is intended
|
|
// to be run.
|
|
func (b *Builder) SetVariant(variant string) {
|
|
b.Docker.Variant = variant
|
|
b.OCIv1.Variant = variant
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// OnBuild returns the OnBuild value from the container.
|
|
func (b *Builder) OnBuild() []string {
|
|
return slices.Clone(b.Docker.Config.OnBuild)
|
|
}
|
|
|
|
// ClearOnBuild removes all values from the OnBuild structure
|
|
func (b *Builder) ClearOnBuild() {
|
|
b.Docker.Config.OnBuild = []string{}
|
|
}
|
|
|
|
// SetOnBuild sets a trigger instruction to be executed when the image is used
|
|
// as the base of another image.
|
|
// Note: this setting is not present in the OCIv1 image format, so it is
|
|
// discarded when writing images using OCIv1 formats.
|
|
func (b *Builder) SetOnBuild(onBuild string) {
|
|
if onBuild != "" && b.Format != define.Dockerv2ImageManifest {
|
|
b.Logger.Warnf("ONBUILD is not supported for OCI image format, %s will be ignored. Must use `docker` format", onBuild)
|
|
}
|
|
b.Docker.Config.OnBuild = append(b.Docker.Config.OnBuild, onBuild)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Shell returns the default shell for running commands in the
|
|
// container, or in a container built using an image built from this container.
|
|
func (b *Builder) Shell() []string {
|
|
return slices.Clone(b.Docker.Config.Shell)
|
|
}
|
|
|
|
// SetShell sets the default shell for running
|
|
// commands in the container, or in a container built using an image built from
|
|
// this 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) SetShell(shell []string) {
|
|
if len(shell) > 0 && b.Format != define.Dockerv2ImageManifest {
|
|
b.Logger.Warnf("SHELL is not supported for OCI image format, %s will be ignored. Must use `docker` format", shell)
|
|
}
|
|
|
|
b.Docker.Config.Shell = slices.Clone(shell)
|
|
}
|
|
|
|
// 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 slices.Clone(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 slices.Clone(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 = slices.Clone(cmd)
|
|
b.Docker.Config.Cmd = slices.Clone(cmd)
|
|
}
|
|
|
|
// Entrypoint returns the command to be run for containers built from images
|
|
// built from this container.
|
|
func (b *Builder) Entrypoint() []string {
|
|
if len(b.OCIv1.Config.Entrypoint) > 0 {
|
|
return slices.Clone(b.OCIv1.Config.Entrypoint)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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 = slices.Clone(ep)
|
|
b.Docker.Config.Entrypoint = slices.Clone(ep)
|
|
}
|
|
|
|
// Labels returns a set of key-value pairs from the image's runtime
|
|
// configuration.
|
|
func (b *Builder) Labels() map[string]string {
|
|
return maps.Clone(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) {
|
|
if b.OCIv1.Config.Labels == nil {
|
|
b.OCIv1.Config.Labels = map[string]string{}
|
|
}
|
|
b.OCIv1.Config.Labels[k] = v
|
|
if b.Docker.Config.Labels == nil {
|
|
b.Docker.Config.Labels = map[string]string{}
|
|
}
|
|
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) {
|
|
if b.OCIv1.Config.ExposedPorts == nil {
|
|
b.OCIv1.Config.ExposedPorts = map[string]struct{}{}
|
|
}
|
|
b.OCIv1.Config.ExposedPorts[p] = struct{}{}
|
|
if b.Docker.Config.ExposedPorts == nil {
|
|
b.Docker.Config.ExposedPorts = make(docker.PortSet)
|
|
}
|
|
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)
|
|
}
|
|
if len(v) > 0 {
|
|
return v
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckVolume returns True if the location exists in 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) CheckVolume(v string) bool {
|
|
_, OCIv1Volume := b.OCIv1.Config.Volumes[v]
|
|
_, DockerVolume := b.Docker.Config.Volumes[v]
|
|
return OCIv1Volume || DockerVolume
|
|
}
|
|
|
|
// 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) {
|
|
if b.OCIv1.Config.Volumes == nil {
|
|
b.OCIv1.Config.Volumes = map[string]struct{}{}
|
|
}
|
|
b.OCIv1.Config.Volumes[v] = struct{}{}
|
|
if b.Docker.Config.Volumes == nil {
|
|
b.Docker.Config.Volumes = map[string]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) {
|
|
if name != "" && b.Format != define.Dockerv2ImageManifest {
|
|
b.Logger.Warnf("DOMAINNAME is not supported for OCI image format, domainname %s will be ignored. Must use `docker` format", name)
|
|
}
|
|
b.Docker.Config.Domainname = name
|
|
}
|
|
|
|
// SetDefaultMountsFilePath sets the mounts file path for testing purposes
|
|
func (b *Builder) SetDefaultMountsFilePath(path string) {
|
|
b.DefaultMountsFilePath = path
|
|
}
|
|
|
|
// Comment returns the comment which will be set in the container and in
|
|
// containers built using images built from the container
|
|
func (b *Builder) Comment() string {
|
|
return b.Docker.Comment
|
|
}
|
|
|
|
// SetComment sets the comment 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) SetComment(comment string) {
|
|
if comment != "" && b.Format != define.Dockerv2ImageManifest {
|
|
logrus.Warnf("COMMENT is not supported for OCI image format, comment %s will be ignored. Must use `docker` format", comment)
|
|
}
|
|
b.Docker.Comment = comment
|
|
}
|
|
|
|
// HistoryComment returns the comment which will be used in the history item
|
|
// which will describe the latest layer when we commit an image.
|
|
func (b *Builder) HistoryComment() string {
|
|
return b.ImageHistoryComment
|
|
}
|
|
|
|
// SetHistoryComment sets the comment which will be used in the history item
|
|
// which will describe the latest layer when we commit an image.
|
|
func (b *Builder) SetHistoryComment(comment string) {
|
|
b.ImageHistoryComment = comment
|
|
}
|
|
|
|
// StopSignal returns the signal which will be set in the container and in
|
|
// containers built using images built from the container
|
|
func (b *Builder) StopSignal() string {
|
|
return b.Docker.Config.StopSignal
|
|
}
|
|
|
|
// SetStopSignal sets the signal which will be set in the container and in
|
|
// containers built using images built from the container.
|
|
func (b *Builder) SetStopSignal(stopSignal string) {
|
|
b.OCIv1.Config.StopSignal = stopSignal
|
|
b.Docker.Config.StopSignal = stopSignal
|
|
}
|
|
|
|
// Healthcheck returns information that recommends how a container engine
|
|
// should check if a running container is "healthy".
|
|
func (b *Builder) Healthcheck() *docker.HealthConfig {
|
|
if b.Docker.Config.Healthcheck == nil {
|
|
return nil
|
|
}
|
|
return &docker.HealthConfig{
|
|
Test: slices.Clone(b.Docker.Config.Healthcheck.Test),
|
|
Interval: b.Docker.Config.Healthcheck.Interval,
|
|
Timeout: b.Docker.Config.Healthcheck.Timeout,
|
|
StartPeriod: b.Docker.Config.Healthcheck.StartPeriod,
|
|
StartInterval: b.Docker.Config.Healthcheck.StartInterval,
|
|
Retries: b.Docker.Config.Healthcheck.Retries,
|
|
}
|
|
}
|
|
|
|
// SetHealthcheck sets recommended commands to run in order to verify that a
|
|
// running container based on this image is "healthy", along with information
|
|
// specifying how often that test should be run, and how many times the test
|
|
// should fail before the container should be considered unhealthy.
|
|
// Note: this setting is not present in the OCIv1 image format, so it is
|
|
// discarded when writing images using OCIv1 formats.
|
|
func (b *Builder) SetHealthcheck(config *docker.HealthConfig) {
|
|
b.Docker.Config.Healthcheck = nil
|
|
if config != nil {
|
|
if b.Format != define.Dockerv2ImageManifest {
|
|
b.Logger.Warnf("HEALTHCHECK is not supported for OCI image format and will be ignored. Must use `docker` format")
|
|
}
|
|
b.Docker.Config.Healthcheck = &docker.HealthConfig{
|
|
Test: slices.Clone(config.Test),
|
|
Interval: config.Interval,
|
|
Timeout: config.Timeout,
|
|
StartPeriod: config.StartPeriod,
|
|
StartInterval: config.StartInterval,
|
|
Retries: config.Retries,
|
|
}
|
|
}
|
|
}
|
|
|
|
// AddPrependedEmptyLayer adds an item to the history that we'll create when
|
|
// committing 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
|
|
// committing 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
|
|
}
|
|
|
|
// AddPrependedLinkedLayer adds an item to the history that we'll create when
|
|
// committing the image, optionally with a layer, 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.
|
|
// The blobPath can be either the location of an uncompressed archive, or a
|
|
// directory whose contents will be archived to use as a layer blob. Leaving
|
|
// blobPath empty is functionally similar to calling AddPrependedEmptyLayer().
|
|
func (b *Builder) AddPrependedLinkedLayer(created *time.Time, createdBy, author, comment, blobPath string) {
|
|
if created != nil {
|
|
copiedTimestamp := *created
|
|
created = &copiedTimestamp
|
|
}
|
|
b.PrependedLinkedLayers = append(b.PrependedLinkedLayers, LinkedLayer{
|
|
BlobPath: blobPath,
|
|
History: ociv1.History{
|
|
Created: created,
|
|
CreatedBy: createdBy,
|
|
Author: author,
|
|
Comment: comment,
|
|
EmptyLayer: blobPath == "",
|
|
},
|
|
})
|
|
}
|
|
|
|
// ClearPrependedLinkedLayers clears the list of history entries that we'll add
|
|
// the committed image before the layer that we're adding (if we're adding it).
|
|
func (b *Builder) ClearPrependedLinkedLayers() {
|
|
b.PrependedLinkedLayers = nil
|
|
}
|
|
|
|
// AddAppendedLinkedLayer adds an item to the history that we'll create when
|
|
// committing the image, optionally with a layer, after the history item that
|
|
// we'll use to describe the new layer that we're adding.
|
|
// The blobPath can be either the location of an uncompressed archive, or a
|
|
// directory whose contents will be archived to use as a layer blob. Leaving
|
|
// blobPath empty is functionally similar to calling AddAppendedEmptyLayer().
|
|
func (b *Builder) AddAppendedLinkedLayer(created *time.Time, createdBy, author, comment, blobPath string) {
|
|
if created != nil {
|
|
copiedTimestamp := *created
|
|
created = &copiedTimestamp
|
|
}
|
|
b.AppendedLinkedLayers = append(b.AppendedLinkedLayers, LinkedLayer{
|
|
BlobPath: blobPath,
|
|
History: ociv1.History{
|
|
Created: created,
|
|
CreatedBy: createdBy,
|
|
Author: author,
|
|
Comment: comment,
|
|
EmptyLayer: blobPath == "",
|
|
},
|
|
})
|
|
}
|
|
|
|
// ClearAppendedLinkedLayers clears the list of linked layers that we'll add to
|
|
// the committed image after the layer that we're adding (if we're adding it).
|
|
func (b *Builder) ClearAppendedLinkedLayers() {
|
|
b.AppendedLinkedLayers = nil
|
|
}
|