Vendor in latest containers(common, storage,image, buildah)

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2024-07-15 11:23:50 -04:00
parent 1d7439eb06
commit eb750f61f6
77 changed files with 1984 additions and 1339 deletions

View File

@ -32,7 +32,7 @@ env:
DEBIAN_NAME: "debian-13"
# Image identifiers
IMAGE_SUFFIX: "c20240411t124913z-f39f38d13"
IMAGE_SUFFIX: "c20240620t153000z-f40f39d13"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}"
@ -137,15 +137,9 @@ cross_build_task:
alias: cross_build
only_if: >-
$CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*'
osx_instance:
image: ghcr.io/cirruslabs/macos-ventura-base:latest
env:
HOME: /root
script:
- brew update
- brew install go
- brew install go-md2man
- brew install gpgme
- go version
- make cross CGO_ENABLED=0
@ -181,7 +175,7 @@ unit_task:
conformance_task:
name: 'Build Conformance w/ $STORAGE_DRIVER'
name: 'Debian Conformance w/ $STORAGE_DRIVER'
alias: conformance
only_if: *not_build_docs
depends_on: *smoke_vendor_cross
@ -194,6 +188,7 @@ conformance_task:
matrix:
- env:
STORAGE_DRIVER: 'vfs'
TMPDIR: '/var/tmp'
- env:
STORAGE_DRIVER: 'overlay'

View File

@ -2,45 +2,88 @@
# See the documentation for more information:
# https://packit.dev/docs/configuration/
specfile_path: rpm/buildah.spec
downstream_package_name: buildah
upstream_tag_template: v{version}
packages:
buildah-fedora:
pkg_tool: fedpkg
specfile_path: rpm/buildah.spec
buildah-centos:
pkg_tool: centpkg
specfile_path: rpm/buildah.spec
buildah-rhel:
specfile_path: rpm/buildah.spec
srpm_build_deps:
- make
jobs:
- job: copr_build
trigger: pull_request
notifications:
packages: [buildah-fedora]
notifications: &copr_build_failure_notification
failure_comment:
message: "Ephemeral COPR build failed. @containers/packit-build please check."
enable_net: true
targets:
- fedora-all-x86_64
- fedora-all-aarch64
- fedora-eln-x86_64
- fedora-eln-aarch64
fedora-all-x86_64: {}
fedora-all-aarch64: {}
fedora-eln-x86_64:
additional_repos:
- "https://kojipkgs.fedoraproject.org/repos/eln-build/latest/x86_64/"
fedora-eln-aarch64:
additional_repos:
- "https://kojipkgs.fedoraproject.org/repos/eln-build/latest/aarch64/"
enable_net: true
- job: copr_build
trigger: pull_request
packages: [buildah-centos]
notifications: *copr_build_failure_notification
targets:
- centos-stream-9-x86_64
- centos-stream-9-aarch64
- centos-stream-10-x86_64
- centos-stream-10-aarch64
enable_net: true
- job: copr_build
trigger: pull_request
packages: [buildah-rhel]
notifications: *copr_build_failure_notification
targets:
- epel-9-x86_64
- epel-9-aarch64
additional_repos:
- "copr://rhcontainerbot/podman-next"
enable_net: true
# Run on commit to main branch
- job: copr_build
trigger: commit
packages: [buildah-fedora]
notifications:
failure_comment:
message: "podman-next COPR build failed. @containers/packit-build please check."
branch: main
owner: rhcontainerbot
project: podman-next
enable_net: true
# Sync to Fedora
- job: propose_downstream
trigger: release
packages: [buildah-fedora]
update_release: false
dist_git_branches:
- fedora-all
# Sync to CentOS Stream
- job: propose_downstream
trigger: release
packages: [buildah-centos]
update_release: false
dist_git_branches:
- c10s
- job: koji_build
trigger: commit
dist_git_branches:

View File

@ -691,8 +691,8 @@ func (b *Builder) userForCopy(mountPoint string, userspec string) (uint32, uint3
return owner.UID, owner.GID, nil
}
// EnsureContainerPathAs creates the specified directory owned by USER
// with the file mode set to MODE.
// EnsureContainerPathAs creates the specified directory if it doesn't exist,
// setting a newly-created directory's owner to USER and its permissions to MODE.
func (b *Builder) EnsureContainerPathAs(path, user string, mode *os.FileMode) error {
mountPoint, err := b.Mount(b.MountLabel)
if err != nil {
@ -722,5 +722,4 @@ func (b *Builder) EnsureContainerPathAs(path, user string, mode *os.FileMode) er
GIDMap: destGIDMap,
}
return copier.Mkdir(mountPoint, filepath.Join(mountPoint, path), opts)
}

View File

@ -518,7 +518,12 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
if effectiveImportantFlags != expectedImportantFlags {
// Do a remount to try to get the desired flags to stick.
effectiveUnimportantFlags := uintptr(fs.Flags) & ^possibleImportantFlags
if err = unix.Mount(target, target, m.Type, unix.MS_REMOUNT|bindFlags|requestFlags|mountFlagsForFSFlags(effectiveUnimportantFlags), ""); err != nil {
remountFlags := unix.MS_REMOUNT | bindFlags | requestFlags | mountFlagsForFSFlags(effectiveUnimportantFlags)
// If we are requesting a read-only mount, add any possibleImportantFlags present in fs.Flags to remountFlags.
if requestFlags&unix.ST_RDONLY == unix.ST_RDONLY {
remountFlags |= uintptr(fs.Flags) & possibleImportantFlags
}
if err = unix.Mount(target, target, m.Type, remountFlags, ""); err != nil {
return undoBinds, fmt.Errorf("remounting %q in mount namespace with flags %#x instead of %#x: %w", target, requestFlags, effectiveImportantFlags, err)
}
// Check if the desired flags stuck.

View File

@ -25,6 +25,7 @@ import (
"github.com/containers/storage/pkg/stringid"
digest "github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
)
const (
@ -127,6 +128,10 @@ type CommitOptions struct {
// SBOMScanOptions encapsulates options which control whether or not we
// run scanners on the rootfs that we're about to commit, and how.
SBOMScanOptions []SBOMScanOptions
// CompatSetParent causes the "parent" field to be set when committing
// the image in Docker format. Newer BuildKit-based builds don't set
// this field.
CompatSetParent types.OptionalBool
}
var (
@ -325,7 +330,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
logrus.Debugf("committing image with reference %q is allowed by policy", transports.ImageName(dest))
// If we need to scan the rootfs, do it now.
options.ExtraImageContent = copyStringStringMap(options.ExtraImageContent)
options.ExtraImageContent = maps.Clone(options.ExtraImageContent)
var extraImageContent, extraLocalContent map[string]string
if len(options.SBOMScanOptions) != 0 {
var scansDirectory string
@ -339,9 +344,14 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
}
}()
}
for k, v := range extraImageContent {
if _, set := options.ExtraImageContent[k]; !set {
options.ExtraImageContent[k] = v
if len(extraImageContent) > 0 {
if options.ExtraImageContent == nil {
options.ExtraImageContent = make(map[string]string, len(extraImageContent))
}
for k, v := range extraImageContent {
if _, set := options.ExtraImageContent[k]; !set {
options.ExtraImageContent[k] = v
}
}
}
}

View File

@ -19,6 +19,7 @@ import (
"github.com/containers/storage/pkg/stringid"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
@ -91,8 +92,13 @@ func (b *Builder) initConfig(ctx context.Context, img types.Image, sys *types.Sy
if err := json.Unmarshal(b.Manifest, &v1Manifest); err != nil {
return fmt.Errorf("parsing OCI manifest %q: %w", string(b.Manifest), err)
}
for k, v := range v1Manifest.Annotations {
b.ImageAnnotations[k] = v
if len(v1Manifest.Annotations) > 0 {
if b.ImageAnnotations == nil {
b.ImageAnnotations = make(map[string]string, len(v1Manifest.Annotations))
}
for k, v := range v1Manifest.Annotations {
b.ImageAnnotations[k] = v
}
}
}
}
@ -158,7 +164,7 @@ func (b *Builder) setupLogger() {
// Annotations returns a set of key-value pairs from the image's manifest.
func (b *Builder) Annotations() map[string]string {
return copyStringStringMap(b.ImageAnnotations)
return maps.Clone(b.ImageAnnotations)
}
// SetAnnotation adds or overwrites a key's value from the image's manifest.
@ -180,7 +186,7 @@ func (b *Builder) UnsetAnnotation(key string) {
// ClearAnnotations removes all keys and their values from the image's
// manifest.
func (b *Builder) ClearAnnotations() {
b.ImageAnnotations = map[string]string{}
b.ImageAnnotations = nil
}
// CreatedBy returns a description of how this image was built.
@ -223,7 +229,7 @@ func (b *Builder) SetOSVersion(version string) {
// 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 copyStringSlice(b.OCIv1.OSFeatures)
return slices.Clone(b.OCIv1.OSFeatures)
}
// SetOSFeature adds a feature of the OS which the container, or a container
@ -327,7 +333,7 @@ func (b *Builder) SetUser(spec string) {
// OnBuild returns the OnBuild value from the container.
func (b *Builder) OnBuild() []string {
return copyStringSlice(b.Docker.Config.OnBuild)
return slices.Clone(b.Docker.Config.OnBuild)
}
// ClearOnBuild removes all values from the OnBuild structure
@ -363,7 +369,7 @@ func (b *Builder) SetWorkDir(there string) {
// 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 copyStringSlice(b.Docker.Config.Shell)
return slices.Clone(b.Docker.Config.Shell)
}
// SetShell sets the default shell for running
@ -376,13 +382,13 @@ func (b *Builder) SetShell(shell []string) {
b.Logger.Warnf("SHELL is not supported for OCI image format, %s will be ignored. Must use `docker` format", shell)
}
b.Docker.Config.Shell = copyStringSlice(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 copyStringSlice(b.OCIv1.Config.Env)
return slices.Clone(b.OCIv1.Config.Env)
}
// SetEnv adds or overwrites a value to the set of environment strings which
@ -432,22 +438,22 @@ func (b *Builder) ClearEnv() {
// 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)
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 = copyStringSlice(cmd)
b.Docker.Config.Cmd = copyStringSlice(cmd)
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 copyStringSlice(b.OCIv1.Config.Entrypoint)
return slices.Clone(b.OCIv1.Config.Entrypoint)
}
return nil
}
@ -455,14 +461,14 @@ func (b *Builder) Entrypoint() []string {
// 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)
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 copyStringStringMap(b.OCIv1.Config.Labels)
return maps.Clone(b.OCIv1.Config.Labels)
}
// SetLabel adds or overwrites a key's value from the image's runtime
@ -669,11 +675,12 @@ func (b *Builder) Healthcheck() *docker.HealthConfig {
return nil
}
return &docker.HealthConfig{
Test: copyStringSlice(b.Docker.Config.Healthcheck.Test),
Interval: b.Docker.Config.Healthcheck.Interval,
Timeout: b.Docker.Config.Healthcheck.Timeout,
StartPeriod: b.Docker.Config.Healthcheck.StartPeriod,
Retries: b.Docker.Config.Healthcheck.Retries,
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,
}
}
@ -690,11 +697,12 @@ func (b *Builder) SetHealthcheck(config *docker.HealthConfig) {
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: copyStringSlice(config.Test),
Interval: config.Interval,
Timeout: config.Timeout,
StartPeriod: config.StartPeriod,
Retries: config.Retries,
Test: slices.Clone(config.Test),
Interval: config.Interval,
Timeout: config.Timeout,
StartPeriod: config.StartPeriod,
StartInterval: config.StartInterval,
Retries: config.Retries,
}
}
}

View File

@ -1,5 +1,5 @@
//go:build (linux && !mips && !mipsle && !mips64 && !mips64le) || freebsd
// +build linux,!mips,!mipsle,!mips64,!mips64le freebsd
//go:build (linux && !mips && !mipsle && !mips64 && !mips64le) || freebsd || netbsd
// +build linux,!mips,!mipsle,!mips64,!mips64le freebsd netbsd
package copier

View File

@ -1,5 +1,5 @@
//go:build linux || darwin || freebsd
// +build linux darwin freebsd
//go:build !windows
// +build !windows
package copier

View File

@ -4,6 +4,7 @@ import (
"io"
"time"
"github.com/containers/common/libimage"
nettypes "github.com/containers/common/libnetwork/types"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
@ -272,6 +273,10 @@ type BuildOptions struct {
// the build was unsuccessful.
ForceRmIntermediateCtrs bool
// BlobDirectory is a directory which we'll use for caching layer blobs.
//
// This option will be overridden for cache pulls if
// CachePullDestinationLookupReferenceFunc is set, and overridden for cache pushes if
// CachePushSourceLookupReferenceFunc is set.
BlobDirectory string
// Target the targeted FROM in the Dockerfile to build.
Target string
@ -342,4 +347,27 @@ type BuildOptions struct {
// CDIConfigDir is the location of CDI configuration files, if the files in
// the default configuration locations shouldn't be used.
CDIConfigDir string
// CachePullSourceLookupReferenceFunc is an optional LookupReferenceFunc
// used to look up source references for cache pulls.
CachePullSourceLookupReferenceFunc libimage.LookupReferenceFunc
// CachePullDestinationLookupReferenceFunc is an optional generator
// function which provides a LookupReferenceFunc used to look up
// destination references for cache pulls.
//
// BlobDirectory will be ignored for cache pulls if this option is set.
CachePullDestinationLookupReferenceFunc func(srcRef types.ImageReference) libimage.LookupReferenceFunc
// CachePushSourceLookupReferenceFunc is an optional generator function
// which provides a LookupReferenceFunc used to look up source
// references for cache pushes.
//
// BlobDirectory will be ignored for cache pushes if this option is set.
CachePushSourceLookupReferenceFunc func(dest types.ImageReference) libimage.LookupReferenceFunc
// CachePushDestinationLookupReferenceFunc is an optional
// LookupReferenceFunc used to look up destination references for cache
// pushes
CachePushDestinationLookupReferenceFunc libimage.LookupReferenceFunc
// CompatSetParent causes the "parent" field to be set in the image's
// configuration when committing in Docker format. Newer
// BuildKit-based docker build doesn't set this field.
CompatSetParent types.OptionalBool
}

View File

@ -1,5 +1,5 @@
//go:build darwin || windows
// +build darwin windows
//go:build darwin || windows || netbsd
// +build darwin windows netbsd
package define

View File

@ -5,6 +5,9 @@ import (
)
// PullPolicy takes the value PullIfMissing, PullAlways, PullIfNewer, or PullNever.
// N.B.: the enumeration values for this type differ from those used by
// github.com/containers/common/pkg/config.PullPolicy (their zero values
// indicate different policies), so they are not interchangeable.
type PullPolicy int
const (

View File

@ -29,7 +29,7 @@ const (
// identify working containers.
Package = "buildah"
// Version for the Package. Also used by .packit.sh for Packit builds.
Version = "1.36.0"
Version = "1.37.0-dev"
// DefaultRuntime if containers.conf fails.
DefaultRuntime = "runc"

View File

@ -32,6 +32,7 @@ import (
specs "github.com/opencontainers/image-spec/specs-go"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
)
const (
@ -83,6 +84,7 @@ type containerImageRef struct {
overrideChanges []string
overrideConfig *manifest.Schema2Config
extraImageContent map[string]string
compatSetParent types.OptionalBool
}
type blobLayerInfo struct {
@ -321,7 +323,11 @@ func (i *containerImageRef) createConfigsAndManifests() (v1.Image, v1.Manifest,
if err := json.Unmarshal(i.dconfig, &dimage); err != nil {
return v1.Image{}, v1.Manifest{}, docker.V2Image{}, docker.V2S2Manifest{}, err
}
dimage.Parent = docker.ID(i.parent)
// Set the parent, but only if we want to be compatible with "classic" docker build.
if i.compatSetParent == types.OptionalBoolTrue {
dimage.Parent = docker.ID(i.parent)
}
// Set the container ID and containerConfig in the docker format.
dimage.Container = i.containerID
if dimage.Config != nil {
dimage.ContainerConfig = *dimage.Config
@ -721,18 +727,18 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
Created: &created,
CreatedBy: i.createdBy,
Author: oimage.Author,
Comment: comment,
EmptyLayer: i.emptyLayer,
}
oimage.History = append(oimage.History, onews)
oimage.History[baseImageHistoryLen].Comment = comment
dnews := docker.V2S2History{
Created: created,
CreatedBy: i.createdBy,
Author: dimage.Author,
Comment: comment,
EmptyLayer: i.emptyLayer,
}
dimage.History = append(dimage.History, dnews)
dimage.History[baseImageHistoryLen].Comment = comment
appendHistory(i.postEmptyLayers)
// Add a history entry for the extra image content if we added a layer for it.
@ -1102,7 +1108,8 @@ func (b *Builder) makeContainerImageRef(options CommitOptions) (*containerImageR
postEmptyLayers: b.AppendedEmptyLayers,
overrideChanges: options.OverrideChanges,
overrideConfig: options.OverrideConfig,
extraImageContent: copyStringStringMap(options.ExtraImageContent),
extraImageContent: maps.Clone(options.ExtraImageContent),
compatSetParent: options.CompatSetParent,
}
return ref, nil
}

View File

@ -70,6 +70,9 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B
if options.CommonBuildOpts == nil {
options.CommonBuildOpts = &define.CommonBuildOptions{}
}
if options.Args == nil {
options.Args = make(map[string]string)
}
if err := parse.Volumes(options.CommonBuildOpts.Volumes); err != nil {
return "", nil, fmt.Errorf("validating volumes: %w", err)
}

View File

@ -35,6 +35,7 @@ import (
"github.com/openshift/imagebuilder"
"github.com/openshift/imagebuilder/dockerfile/parser"
"github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
"golang.org/x/sync/semaphore"
)
@ -94,67 +95,72 @@ type Executor struct {
cniPluginPath string
cniConfigDir string
// NetworkInterface is the libnetwork network interface used to setup CNI or netavark networks.
networkInterface nettypes.ContainerNetwork
idmappingOptions *define.IDMappingOptions
commonBuildOptions *define.CommonBuildOptions
defaultMountsFilePath string
iidfile string
squash bool
labels []string
layerLabels []string
annotations []string
layers bool
noHostname bool
noHosts bool
useCache bool
removeIntermediateCtrs bool
forceRmIntermediateCtrs bool
imageMap map[string]string // Used to map images that we create to handle the AS construct.
containerMap map[string]*buildah.Builder // Used to map from image names to only-created-for-the-rootfs containers.
baseMap map[string]struct{} // Holds the names of every base image, as given.
rootfsMap map[string]struct{} // Holds the names of every stage whose rootfs is referenced in a COPY or ADD instruction.
blobDirectory string
excludes []string
groupAdd []string
ignoreFile string
args map[string]string
globalArgs map[string]string
unusedArgs map[string]struct{}
capabilities []string
devices define.ContainerDevices
deviceSpecs []string
signBy string
architecture string
timestamp *time.Time
os string
maxPullPushRetries int
retryPullPushDelay time.Duration
ociDecryptConfig *encconfig.DecryptConfig
lastError error
terminatedStage map[string]error
stagesLock sync.Mutex
stagesSemaphore *semaphore.Weighted
logRusage bool
rusageLogFile io.Writer
imageInfoLock sync.Mutex
imageInfoCache map[string]imageTypeAndHistoryAndDiffIDs
fromOverride string
additionalBuildContexts map[string]*define.AdditionalBuildContext
manifest string
secrets map[string]define.Secret
sshsources map[string]*sshagent.Source
logPrefix string
unsetEnvs []string
unsetLabels []string
processLabel string // Shares processLabel of first stage container with containers of other stages in same build
mountLabel string // Shares mountLabel of first stage container with containers of other stages in same build
buildOutput string // Specifies instructions for any custom build output
osVersion string
osFeatures []string
envs []string
confidentialWorkload define.ConfidentialWorkloadOptions
sbomScanOptions []define.SBOMScanOptions
cdiConfigDir string
networkInterface nettypes.ContainerNetwork
idmappingOptions *define.IDMappingOptions
commonBuildOptions *define.CommonBuildOptions
defaultMountsFilePath string
iidfile string
squash bool
labels []string
layerLabels []string
annotations []string
layers bool
noHostname bool
noHosts bool
useCache bool
removeIntermediateCtrs bool
forceRmIntermediateCtrs bool
imageMap map[string]string // Used to map images that we create to handle the AS construct.
containerMap map[string]*buildah.Builder // Used to map from image names to only-created-for-the-rootfs containers.
baseMap map[string]struct{} // Holds the names of every base image, as given.
rootfsMap map[string]struct{} // Holds the names of every stage whose rootfs is referenced in a COPY or ADD instruction.
blobDirectory string
excludes []string
groupAdd []string
ignoreFile string
args map[string]string
globalArgs map[string]string
unusedArgs map[string]struct{}
capabilities []string
devices define.ContainerDevices
deviceSpecs []string
signBy string
architecture string
timestamp *time.Time
os string
maxPullPushRetries int
retryPullPushDelay time.Duration
cachePullSourceLookupReferenceFunc libimage.LookupReferenceFunc
cachePullDestinationLookupReferenceFunc func(srcRef types.ImageReference) libimage.LookupReferenceFunc
cachePushSourceLookupReferenceFunc func(dest types.ImageReference) libimage.LookupReferenceFunc
cachePushDestinationLookupReferenceFunc libimage.LookupReferenceFunc
ociDecryptConfig *encconfig.DecryptConfig
lastError error
terminatedStage map[string]error
stagesLock sync.Mutex
stagesSemaphore *semaphore.Weighted
logRusage bool
rusageLogFile io.Writer
imageInfoLock sync.Mutex
imageInfoCache map[string]imageTypeAndHistoryAndDiffIDs
fromOverride string
additionalBuildContexts map[string]*define.AdditionalBuildContext
manifest string
secrets map[string]define.Secret
sshsources map[string]*sshagent.Source
logPrefix string
unsetEnvs []string
unsetLabels []string
processLabel string // Shares processLabel of first stage container with containers of other stages in same build
mountLabel string // Shares mountLabel of first stage container with containers of other stages in same build
buildOutput string // Specifies instructions for any custom build output
osVersion string
osFeatures []string
envs []string
confidentialWorkload define.ConfidentialWorkloadOptions
sbomScanOptions []define.SBOMScanOptions
cdiConfigDir string
compatSetParent types.OptionalBool
}
type imageTypeAndHistoryAndDiffIDs struct {
@ -221,92 +227,97 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o
}
exec := Executor{
args: options.Args,
cacheFrom: options.CacheFrom,
cacheTo: options.CacheTo,
cacheTTL: options.CacheTTL,
containerSuffix: options.ContainerSuffix,
logger: logger,
stages: make(map[string]*StageExecutor),
store: store,
contextDir: options.ContextDirectory,
excludes: excludes,
groupAdd: options.GroupAdd,
ignoreFile: options.IgnoreFile,
pullPolicy: options.PullPolicy,
registry: options.Registry,
ignoreUnrecognizedInstructions: options.IgnoreUnrecognizedInstructions,
quiet: options.Quiet,
runtime: options.Runtime,
runtimeArgs: options.RuntimeArgs,
transientMounts: transientMounts,
compression: options.Compression,
output: options.Output,
outputFormat: options.OutputFormat,
additionalTags: options.AdditionalTags,
signaturePolicyPath: options.SignaturePolicyPath,
skipUnusedStages: options.SkipUnusedStages,
systemContext: options.SystemContext,
log: options.Log,
in: options.In,
out: options.Out,
err: options.Err,
reportWriter: writer,
isolation: options.Isolation,
namespaceOptions: options.NamespaceOptions,
configureNetwork: options.ConfigureNetwork,
cniPluginPath: options.CNIPluginPath,
cniConfigDir: options.CNIConfigDir,
networkInterface: options.NetworkInterface,
idmappingOptions: options.IDMappingOptions,
commonBuildOptions: options.CommonBuildOpts,
defaultMountsFilePath: options.DefaultMountsFilePath,
iidfile: options.IIDFile,
squash: options.Squash,
labels: append([]string{}, options.Labels...),
layerLabels: append([]string{}, options.LayerLabels...),
annotations: append([]string{}, options.Annotations...),
layers: options.Layers,
noHostname: options.CommonBuildOpts.NoHostname,
noHosts: options.CommonBuildOpts.NoHosts,
useCache: !options.NoCache,
removeIntermediateCtrs: options.RemoveIntermediateCtrs,
forceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
imageMap: make(map[string]string),
containerMap: make(map[string]*buildah.Builder),
baseMap: make(map[string]struct{}),
rootfsMap: make(map[string]struct{}),
blobDirectory: options.BlobDirectory,
unusedArgs: make(map[string]struct{}),
capabilities: capabilities,
deviceSpecs: options.Devices,
signBy: options.SignBy,
architecture: options.Architecture,
timestamp: options.Timestamp,
os: options.OS,
maxPullPushRetries: options.MaxPullPushRetries,
retryPullPushDelay: options.PullPushRetryDelay,
ociDecryptConfig: options.OciDecryptConfig,
terminatedStage: make(map[string]error),
stagesSemaphore: options.JobSemaphore,
logRusage: options.LogRusage,
rusageLogFile: rusageLogFile,
imageInfoCache: make(map[string]imageTypeAndHistoryAndDiffIDs),
fromOverride: options.From,
additionalBuildContexts: options.AdditionalBuildContexts,
manifest: options.Manifest,
secrets: secrets,
sshsources: sshsources,
logPrefix: logPrefix,
unsetEnvs: append([]string{}, options.UnsetEnvs...),
unsetLabels: append([]string{}, options.UnsetLabels...),
buildOutput: options.BuildOutput,
osVersion: options.OSVersion,
osFeatures: append([]string{}, options.OSFeatures...),
envs: append([]string{}, options.Envs...),
confidentialWorkload: options.ConfidentialWorkload,
sbomScanOptions: options.SBOMScanOptions,
cdiConfigDir: options.CDIConfigDir,
args: options.Args,
cacheFrom: options.CacheFrom,
cacheTo: options.CacheTo,
cacheTTL: options.CacheTTL,
containerSuffix: options.ContainerSuffix,
logger: logger,
stages: make(map[string]*StageExecutor),
store: store,
contextDir: options.ContextDirectory,
excludes: excludes,
groupAdd: options.GroupAdd,
ignoreFile: options.IgnoreFile,
pullPolicy: options.PullPolicy,
registry: options.Registry,
ignoreUnrecognizedInstructions: options.IgnoreUnrecognizedInstructions,
quiet: options.Quiet,
runtime: options.Runtime,
runtimeArgs: options.RuntimeArgs,
transientMounts: transientMounts,
compression: options.Compression,
output: options.Output,
outputFormat: options.OutputFormat,
additionalTags: options.AdditionalTags,
signaturePolicyPath: options.SignaturePolicyPath,
skipUnusedStages: options.SkipUnusedStages,
systemContext: options.SystemContext,
log: options.Log,
in: options.In,
out: options.Out,
err: options.Err,
reportWriter: writer,
isolation: options.Isolation,
namespaceOptions: options.NamespaceOptions,
configureNetwork: options.ConfigureNetwork,
cniPluginPath: options.CNIPluginPath,
cniConfigDir: options.CNIConfigDir,
networkInterface: options.NetworkInterface,
idmappingOptions: options.IDMappingOptions,
commonBuildOptions: options.CommonBuildOpts,
defaultMountsFilePath: options.DefaultMountsFilePath,
iidfile: options.IIDFile,
squash: options.Squash,
labels: slices.Clone(options.Labels),
layerLabels: slices.Clone(options.LayerLabels),
annotations: slices.Clone(options.Annotations),
layers: options.Layers,
noHostname: options.CommonBuildOpts.NoHostname,
noHosts: options.CommonBuildOpts.NoHosts,
useCache: !options.NoCache,
removeIntermediateCtrs: options.RemoveIntermediateCtrs,
forceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
imageMap: make(map[string]string),
containerMap: make(map[string]*buildah.Builder),
baseMap: make(map[string]struct{}),
rootfsMap: make(map[string]struct{}),
blobDirectory: options.BlobDirectory,
unusedArgs: make(map[string]struct{}),
capabilities: capabilities,
deviceSpecs: options.Devices,
signBy: options.SignBy,
architecture: options.Architecture,
timestamp: options.Timestamp,
os: options.OS,
maxPullPushRetries: options.MaxPullPushRetries,
retryPullPushDelay: options.PullPushRetryDelay,
cachePullSourceLookupReferenceFunc: options.CachePullSourceLookupReferenceFunc,
cachePullDestinationLookupReferenceFunc: options.CachePullDestinationLookupReferenceFunc,
cachePushSourceLookupReferenceFunc: options.CachePushSourceLookupReferenceFunc,
cachePushDestinationLookupReferenceFunc: options.CachePushDestinationLookupReferenceFunc,
ociDecryptConfig: options.OciDecryptConfig,
terminatedStage: make(map[string]error),
stagesSemaphore: options.JobSemaphore,
logRusage: options.LogRusage,
rusageLogFile: rusageLogFile,
imageInfoCache: make(map[string]imageTypeAndHistoryAndDiffIDs),
fromOverride: options.From,
additionalBuildContexts: options.AdditionalBuildContexts,
manifest: options.Manifest,
secrets: secrets,
sshsources: sshsources,
logPrefix: logPrefix,
unsetEnvs: slices.Clone(options.UnsetEnvs),
unsetLabels: slices.Clone(options.UnsetLabels),
buildOutput: options.BuildOutput,
osVersion: options.OSVersion,
osFeatures: slices.Clone(options.OSFeatures),
envs: slices.Clone(options.Envs),
confidentialWorkload: options.ConfidentialWorkload,
sbomScanOptions: options.SBOMScanOptions,
cdiConfigDir: options.CDIConfigDir,
compatSetParent: options.CompatSetParent,
}
if exec.err == nil {
exec.err = os.Stderr

View File

@ -349,6 +349,26 @@ func (s *StageExecutor) volumeCacheRestore() error {
// Copy copies data into the working tree. The "Download" field is how
// imagebuilder tells us the instruction was "ADD" and not "COPY".
func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) error {
for _, cp := range copies {
if cp.KeepGitDir {
if cp.Download {
return errors.New("ADD --keep-git-dir is not supported")
}
return errors.New("COPY --keep-git-dir is not supported")
}
if cp.Link {
return errors.New("COPY --link is not supported")
}
if cp.Parents {
return errors.New("COPY --parents is not supported")
}
if len(cp.Excludes) > 0 {
if cp.Download {
return errors.New("ADD --excludes is not supported")
}
return errors.New("COPY --excludes is not supported")
}
}
s.builder.ContentDigester.Restart()
return s.performCopy(excludes, copies...)
}
@ -860,12 +880,15 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo
from = base
}
displayFrom := from
if ib.Platform != "" {
displayFrom = "--platform=" + ib.Platform + " " + displayFrom
}
// stage.Name will be a numeric string for all stages without an "AS" clause
asImageName := stage.Name
if asImageName != "" {
if _, err := strconv.Atoi(asImageName); err != nil {
displayFrom = from + " AS " + asImageName
displayFrom += " AS " + asImageName
}
}
@ -1992,6 +2015,12 @@ func (s *StageExecutor) pushCache(ctx context.Context, src, cacheKey string) err
MaxRetries: s.executor.maxPullPushRetries,
RetryDelay: s.executor.retryPullPushDelay,
}
if s.executor.cachePushSourceLookupReferenceFunc != nil {
options.SourceLookupReferenceFunc = s.executor.cachePushSourceLookupReferenceFunc(dest)
}
if s.executor.cachePushDestinationLookupReferenceFunc != nil {
options.DestinationLookupReferenceFunc = s.executor.cachePushDestinationLookupReferenceFunc
}
ref, digest, err := buildah.Push(ctx, src, dest, options)
if err != nil {
return fmt.Errorf("failed pushing cache to %q: %w", dest, err)
@ -2013,7 +2042,8 @@ func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (referen
return nil, "", err
}
for _, src := range srcList {
logrus.Debugf("trying to pull cache from remote repo: %+v", src.DockerReference())
srcDockerRef := src.DockerReference()
logrus.Debugf("trying to pull cache from remote repo: %+v", srcDockerRef)
options := buildah.PullOptions{
SignaturePolicyPath: s.executor.signaturePolicyPath,
Store: s.executor.store,
@ -2025,7 +2055,14 @@ func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (referen
ReportWriter: nil,
PullPolicy: define.PullIfNewer,
}
id, err := buildah.Pull(ctx, src.DockerReference().String(), options)
if s.executor.cachePullSourceLookupReferenceFunc != nil {
options.SourceLookupReferenceFunc = s.executor.cachePullSourceLookupReferenceFunc
}
if s.executor.cachePullDestinationLookupReferenceFunc != nil {
options.DestinationLookupReferenceFunc = s.executor.cachePullDestinationLookupReferenceFunc(src)
}
id, err := buildah.Pull(ctx, srcDockerRef.String(), options)
if err != nil {
logrus.Debugf("failed pulling cache from source %s: %v", src, err)
continue // failed pulling this one try next
@ -2179,11 +2216,12 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
s.builder.SetStopSignal(config.StopSignal)
if config.Healthcheck != nil {
s.builder.SetHealthcheck(&buildahdocker.HealthConfig{
Test: append([]string{}, config.Healthcheck.Test...),
Interval: config.Healthcheck.Interval,
Timeout: config.Healthcheck.Timeout,
StartPeriod: config.Healthcheck.StartPeriod,
Retries: config.Healthcheck.Retries,
Test: append([]string{}, config.Healthcheck.Test...),
Interval: config.Healthcheck.Interval,
Timeout: config.Healthcheck.Timeout,
StartPeriod: config.Healthcheck.StartPeriod,
StartInterval: config.Healthcheck.StartInterval,
Retries: config.Healthcheck.Retries,
})
} else {
s.builder.SetHealthcheck(nil)
@ -2237,6 +2275,7 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
RetryDelay: s.executor.retryPullPushDelay,
HistoryTimestamp: s.executor.timestamp,
Manifest: s.executor.manifest,
CompatSetParent: s.executor.compatSetParent,
}
if finalInstruction {
options.ConfidentialWorkloadOptions = s.executor.confidentialWorkload
@ -2291,9 +2330,11 @@ func (s *StageExecutor) generateBuildOutput(buildOutputOpts define.BuildOutputOp
}
func (s *StageExecutor) EnsureContainerPath(path string) error {
logrus.Debugf("EnsureContainerPath %q in %q", path, s.builder.ContainerID)
return s.builder.EnsureContainerPathAs(path, "", nil)
}
func (s *StageExecutor) EnsureContainerPathAs(path, user string, mode *os.FileMode) error {
logrus.Debugf("EnsureContainerPath %q (owner %q, mode %o) in %q", path, user, mode, s.builder.ContainerID)
return s.builder.EnsureContainerPathAs(path, user, mode)
}

View File

@ -97,7 +97,6 @@ func importBuilderDataFromImage(ctx context.Context, store storage.Store, system
FromImageDigest: imageDigest,
Container: containerName,
ContainerID: containerID,
ImageAnnotations: map[string]string{},
ImageCreatedBy: "",
NamespaceOptions: defaultNamespaceOptions,
IDMappingOptions: define.IDMappingOptions{

View File

@ -1,6 +1,8 @@
package config
import (
"slices"
"github.com/containers/image/v5/manifest"
dockerclient "github.com/fsouza/go-dockerclient"
)
@ -41,17 +43,17 @@ func Schema2ConfigFromGoDockerclientConfig(config *dockerclient.Config) *manifes
Tty: config.Tty,
OpenStdin: config.OpenStdin,
StdinOnce: config.StdinOnce,
Env: append([]string{}, config.Env...),
Cmd: append([]string{}, config.Cmd...),
Env: slices.Clone(config.Env),
Cmd: slices.Clone(config.Cmd),
Healthcheck: overrideHealthCheck,
ArgsEscaped: config.ArgsEscaped,
Image: config.Image,
Volumes: volumes,
WorkingDir: config.WorkingDir,
Entrypoint: append([]string{}, config.Entrypoint...),
Entrypoint: slices.Clone(config.Entrypoint),
NetworkDisabled: config.NetworkDisabled,
MacAddress: config.MacAddress,
OnBuild: append([]string{}, config.OnBuild...),
OnBuild: slices.Clone(config.OnBuild),
Labels: labels,
StopSignal: config.StopSignal,
Shell: config.Shell,

View File

@ -21,6 +21,8 @@ import (
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/openshift/imagebuilder"
"github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
const (
@ -313,10 +315,10 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions
UIDMap: uidmap,
GIDMap: gidmap,
},
Capabilities: copyStringSlice(options.Capabilities),
Capabilities: slices.Clone(options.Capabilities),
CommonBuildOpts: options.CommonBuildOpts,
TopLayer: topLayer,
Args: copyStringStringMap(options.Args),
Args: maps.Clone(options.Args),
Format: options.Format,
TempVolumes: map[string]bool{},
Devices: options.Devices,

View File

@ -263,8 +263,12 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.String("os", runtime.GOOS, "set the OS to the provided value instead of the current operating system of the host")
fs.StringArrayVar(&flags.OSFeatures, "os-feature", []string{}, "set required OS `feature` for the target image in addition to values from the base image")
fs.StringVar(&flags.OSVersion, "os-version", "", "set required OS `version` for the target image instead of the value from the base image")
fs.StringVar(&flags.Pull, "pull", "true", "pull base and SBOM scanner images from the registry if newer or not present in store, if false, only pull base and SBOM scanner images if not present, if always, pull base and SBOM scanner images even if the named images are present in store, if never, only use images present in store if available")
fs.Lookup("pull").NoOptDefVal = "true" //allow `--pull ` to be set to `true` as expected.
fs.StringVar(&flags.Pull, "pull", "missing", `pull base and SBOM scanner images from the registry. Values:
always: pull base and SBOM scanner images even if the named images are present in store.
missing: pull base and SBOM scanner images if the named images are not present in store.
never: only use images present in store if available.
newer: only pull base and SBOM scanner images when newer images exist on the registry than those in the store.`)
fs.Lookup("pull").NoOptDefVal = "missing" //treat a --pull with no argument like --pull=missing
fs.BoolVar(&flags.PullAlways, "pull-always", false, "pull the image even if the named image is present in store")
if err := fs.MarkHidden("pull-always"); err != nil {
panic(fmt.Sprintf("error marking the pull-always flag as hidden: %v", err))

View File

@ -449,6 +449,42 @@ func SystemContextFromFlagSet(flags *pflag.FlagSet, findFlagFunc func(name strin
return ctx, nil
}
// pullPolicyWithFlags parses a string value of a pull policy, evaluating it in
// combination with "always" and "never" boolean flags.
// Allow for:
// * --pull
// * --pull=""
// * --pull=true
// * --pull=false
// * --pull=never
// * --pull=always
// * --pull=ifmissing
// * --pull=missing
// * --pull=notpresent
// * --pull=newer
// * --pull=ifnewer
// and --pull-always and --pull-never as boolean flags.
func pullPolicyWithFlags(policySpec string, always, never bool) (define.PullPolicy, error) {
if always {
return define.PullAlways, nil
}
if never {
return define.PullNever, nil
}
policy := strings.ToLower(policySpec)
switch policy {
case "true", "missing", "ifmissing", "notpresent":
return define.PullIfMissing, nil
case "always":
return define.PullAlways, nil
case "false", "never":
return define.PullNever, nil
case "ifnewer", "newer":
return define.PullIfNewer, nil
}
return 0, fmt.Errorf("unrecognized pull policy %q", policySpec)
}
// PullPolicyFromOptions returns a PullPolicy that reflects the combination of
// the specified "pull" and undocumented "pull-always" and "pull-never" flags.
func PullPolicyFromOptions(c *cobra.Command) (define.PullPolicy, error) {
@ -474,30 +510,23 @@ func PullPolicyFromFlagSet(flags *pflag.FlagSet, findFlagFunc func(name string)
return 0, errors.New("can only set one of 'pull' or 'pull-always' or 'pull-never'")
}
// Allow for --pull, --pull=true, --pull=false, --pull=never, --pull=always
// --pull-always and --pull-never. The --pull-never and --pull-always options
// will not be documented.
pullPolicy := define.PullIfMissing
pullFlagValue := findFlagFunc("pull").Value.String()
if strings.EqualFold(pullFlagValue, "true") || strings.EqualFold(pullFlagValue, "ifnewer") {
pullPolicy = define.PullIfNewer
}
// The --pull-never and --pull-always options will not be documented.
pullAlwaysFlagValue, err := flags.GetBool("pull-always")
if err != nil {
return 0, err
}
if pullAlwaysFlagValue || strings.EqualFold(pullFlagValue, "always") {
pullPolicy = define.PullAlways
return 0, fmt.Errorf("checking the --pull-always flag value: %w", err)
}
pullNeverFlagValue, err := flags.GetBool("pull-never")
if err != nil {
return 0, fmt.Errorf("checking the --pull-never flag value: %w", err)
}
// The --pull[=...] flag is the one we really care about.
pullFlagValue := findFlagFunc("pull").Value.String()
pullPolicy, err := pullPolicyWithFlags(pullFlagValue, pullAlwaysFlagValue, pullNeverFlagValue)
if err != nil {
return 0, err
}
if pullNeverFlagValue ||
strings.EqualFold(pullFlagValue, "never") ||
strings.EqualFold(pullFlagValue, "false") {
pullPolicy = define.PullNever
}
logrus.Debugf("Pull Policy for pull [%v]", pullPolicy)
return pullPolicy, nil

View File

@ -0,0 +1,25 @@
package util
import (
"time"
"unsafe"
"golang.org/x/sys/unix"
)
func clockGettime(clockid int32, time *unix.Timespec) (err error) {
_, _, e1 := unix.Syscall(unix.SYS_CLOCK_GETTIME, uintptr(clockid), uintptr(unsafe.Pointer(time)), 0)
if e1 != 0 {
return e1
}
return nil
}
func ReadUptime() (time.Duration, error) {
tv, err := unix.SysctlTimeval("kern.boottime")
if err != nil {
return 0, err
}
sec, nsec := tv.Unix()
return time.Now().Sub(time.Unix(sec, nsec)), nil
}

View File

@ -1,5 +1,5 @@
//go:build linux || freebsd || darwin
// +build linux freebsd darwin
//go:build !windows
// +build !windows
package util

View File

@ -33,6 +33,8 @@ type PullOptions struct {
// BlobDirectory is the name of a directory in which we'll attempt to
// store copies of layer blobs that we pull down, if any. It should
// already exist.
//
// Not applicable if DestinationLookupReferenceFunc is set.
BlobDirectory string
// AllTags is a boolean value that determines if all tagged images
// will be downloaded from the repository. The default is false.
@ -50,6 +52,12 @@ type PullOptions struct {
OciDecryptConfig *encconfig.DecryptConfig
// PullPolicy takes the value PullIfMissing, PullAlways, PullIfNewer, or PullNever.
PullPolicy define.PullPolicy
// SourceLookupReference provides a function to look up source
// references.
SourceLookupReferenceFunc libimage.LookupReferenceFunc
// DestinationLookupReference provides a function to look up destination
// references. Overrides BlobDirectory, if set.
DestinationLookupReferenceFunc libimage.LookupReferenceFunc
}
// Pull copies the contents of the image from somewhere else to local storage. Returns the
@ -62,7 +70,12 @@ func Pull(ctx context.Context, imageName string, options PullOptions) (imageID s
libimageOptions.OciDecryptConfig = options.OciDecryptConfig
libimageOptions.AllTags = options.AllTags
libimageOptions.RetryDelay = &options.RetryDelay
libimageOptions.DestinationLookupReferenceFunc = cacheLookupReferenceFunc(options.BlobDirectory, types.PreserveOriginal)
libimageOptions.SourceLookupReferenceFunc = options.SourceLookupReferenceFunc
if options.DestinationLookupReferenceFunc != nil {
libimageOptions.DestinationLookupReferenceFunc = options.DestinationLookupReferenceFunc
} else {
libimageOptions.DestinationLookupReferenceFunc = cacheLookupReferenceFunc(options.BlobDirectory, types.PreserveOriginal)
}
if options.MaxRetries > 0 {
retries := uint(options.MaxRetries)

View File

@ -66,6 +66,8 @@ type PushOptions struct {
// prebuilt copies of layer blobs that we might otherwise need to
// regenerate from on-disk layers, substituting them in the list of
// blobs to copy whenever possible.
//
// Not applicable if SourceLookupReferenceFunc is set.
BlobDirectory string
// Quiet is a boolean value that determines if minimal output to
// the user will be displayed, this is best used for logging.
@ -90,6 +92,12 @@ type PushOptions struct {
// integers in the slice represent 0-indexed layer indices, with support for negative
// indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer.
OciEncryptLayers *[]int
// SourceLookupReference provides a function to look up source
// references. Overrides BlobDirectory, if set.
SourceLookupReferenceFunc libimage.LookupReferenceFunc
// DestinationLookupReference provides a function to look up destination
// references.
DestinationLookupReferenceFunc libimage.LookupReferenceFunc
// CompressionFormat is the format to use for the compression of the blobs
CompressionFormat *compression.Algorithm
@ -125,7 +133,12 @@ func Push(ctx context.Context, image string, dest types.ImageReference, options
if options.Compression == archive.Gzip {
compress = types.Compress
}
libimageOptions.SourceLookupReferenceFunc = cacheLookupReferenceFunc(options.BlobDirectory, compress)
if options.SourceLookupReferenceFunc != nil {
libimageOptions.SourceLookupReferenceFunc = options.SourceLookupReferenceFunc
} else {
libimageOptions.SourceLookupReferenceFunc = cacheLookupReferenceFunc(options.BlobDirectory, compress)
}
libimageOptions.DestinationLookupReferenceFunc = options.DestinationLookupReferenceFunc
runtime, err := libimage.RuntimeFromStore(options.Store, &libimage.RuntimeOptions{SystemContext: options.SystemContext})
if err != nil {

View File

@ -53,6 +53,7 @@ import (
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
"golang.org/x/sys/unix"
"golang.org/x/term"
)
@ -83,12 +84,17 @@ func (b *Builder) createResolvConf(rdir string, chownOpts *idtools.IDPair) (stri
// addResolvConf copies files from host and sets them up to bind mount into container
func (b *Builder) addResolvConfEntries(file string, networkNameServer []string,
namespaces []specs.LinuxNamespace, keepHostServers, ipv6 bool) error {
spec *specs.Spec, keepHostServers, ipv6 bool) error {
defaultConfig, err := config.Default()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}
var namespaces []specs.LinuxNamespace
if spec.Linux != nil {
namespaces = spec.Linux.Namespaces
}
dnsServers, dnsSearch, dnsOptions := b.CommonBuildOpts.DNSServers, b.CommonBuildOpts.DNSSearch, b.CommonBuildOpts.DNSOptions
nameservers := make([]string, 0, len(defaultConfig.Containers.DNSServers.Get())+len(dnsServers))
nameservers = append(nameservers, defaultConfig.Containers.DNSServers.Get()...)
@ -540,7 +546,7 @@ func runUsingRuntime(options RunOptions, configureNetwork bool, moreCreateArgs [
}
}
runtimeArgs := options.Args[:]
runtimeArgs := slices.Clone(options.Args)
if options.CgroupManager == config.SystemdCgroupsManager {
runtimeArgs = append(runtimeArgs, "--systemd-cgroup")
}
@ -1253,7 +1259,7 @@ func (b *Builder) runUsingRuntimeSubproc(isolation define.Isolation, options Run
}
if resolvFile != "" {
err = b.addResolvConfEntries(resolvFile, netResult.dnsServers, spec.Linux.Namespaces, netResult.keepHostResolvers, netResult.ipv6)
err = b.addResolvConfEntries(resolvFile, netResult.dnsServers, spec, netResult.keepHostResolvers, netResult.ipv6)
if err != nil {
return err
}

View File

@ -423,7 +423,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
// Only add entries here if we do not have to do setup network,
// if we do we have to do it much later after the network setup.
if !configureNetwork {
err = b.addResolvConfEntries(resolvFile, nil, spec.Linux.Namespaces, false, true)
err = b.addResolvConfEntries(resolvFile, nil, spec, false, true)
if err != nil {
return err
}

View File

@ -28,20 +28,6 @@ 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
}
func copyHistory(history []v1.History) []v1.History {
if len(history) == 0 {
return nil

View File

@ -1,5 +1,5 @@
//go:build linux || darwin || freebsd
// +build linux darwin freebsd
//go:build linux || darwin || freebsd || netbsd
// +build linux darwin freebsd netbsd
package util

View File

@ -12,7 +12,6 @@ import (
"strings"
"time"
"github.com/containers/common/libimage/manifests"
"github.com/containers/common/libimage/platform"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/retry"
@ -32,12 +31,6 @@ const (
defaultRetryDelay = time.Second
)
// LookupReferenceFunc return an image reference based on the specified one.
// The returned reference can return custom ImageSource or ImageDestination
// objects which intercept or filter blobs, manifests, and signatures as
// they are read and written.
type LookupReferenceFunc = manifests.LookupReferenceFunc
// CopyOptions allow for customizing image-copy operations.
type CopyOptions struct {
// If set, will be used for copying the image. Fields below may

View File

@ -6,6 +6,7 @@ import (
"context"
"errors"
"fmt"
"maps"
"slices"
"time"
@ -20,7 +21,6 @@ import (
structcopier "github.com/jinzhu/copier"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/exp/maps"
)
// NOTE: the abstractions and APIs here are a first step to further merge
@ -238,9 +238,7 @@ func (m *ManifestList) Inspect() (*define.ManifestListData, error) {
for i, manifest := range ociFormat.Manifests {
inspectList.Manifests[i].Annotations = manifest.Annotations
inspectList.Manifests[i].ArtifactType = manifest.ArtifactType
if manifest.URLs != nil {
inspectList.Manifests[i].URLs = slices.Clone(manifest.URLs)
}
inspectList.Manifests[i].URLs = slices.Clone(manifest.URLs)
inspectList.Manifests[i].Data = manifest.Data
inspectList.Manifests[i].Files, err = m.list.Files(manifest.Digest)
if err != nil {
@ -252,10 +250,7 @@ func (m *ManifestList) Inspect() (*define.ManifestListData, error) {
if platform == nil {
platform = &imgspecv1.Platform{}
}
var osFeatures []string
if platform.OSFeatures != nil {
osFeatures = slices.Clone(platform.OSFeatures)
}
osFeatures := slices.Clone(platform.OSFeatures)
inspectList.Subject = &define.ManifestListDescriptor{
Platform: manifest.Schema2PlatformSpec{
OS: platform.OS,
@ -483,23 +478,23 @@ func (m *ManifestList) AddArtifact(ctx context.Context, options *ManifestListAdd
// Options for annotating a manifest list.
type ManifestListAnnotateOptions struct {
// Add the specified annotations to the added image.
// Add the specified annotations to the added image. Empty values are ignored.
Annotations map[string]string
// Add the specified architecture to the added image.
// Add the specified architecture to the added image. Empty values are ignored.
Architecture string
// Add the specified features to the added image.
// Add the specified features to the added image. Empty values are ignored.
Features []string
// Add the specified OS to the added image.
// Add the specified OS to the added image. Empty values are ignored.
OS string
// Add the specified OS features to the added image.
// Add the specified OS features to the added image. Empty values are ignored.
OSFeatures []string
// Add the specified OS version to the added image.
// Add the specified OS version to the added image. Empty values are ignored.
OSVersion string
// Add the specified variant to the added image.
// Add the specified variant to the added image. Empty values are ignored unless Architecture is set to a non-empty value.
Variant string
// Add the specified annotations to the index itself.
// Add the specified annotations to the index itself. Empty values are ignored.
IndexAnnotations map[string]string
// Set the subject to which the index refers.
// Set the subject to which the index refers. Empty values are ignored.
Subject string
}
@ -536,7 +531,7 @@ func (m *ManifestList) AnnotateInstance(d digest.Digest, options *ManifestListAn
return err
}
}
if len(options.Variant) > 0 {
if len(options.Architecture) != 0 || len(options.Variant) > 0 {
if err := m.list.SetVariant(d, options.Variant); err != nil {
return err
}

View File

@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"maps"
"mime"
"net/http"
"os"
@ -40,7 +41,6 @@ import (
imgspec "github.com/opencontainers/image-spec/specs-go"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
)
const (
@ -236,11 +236,7 @@ func (l *list) SaveToImage(store storage.Store, imageID string, names []string,
// Files returns the list of files associated with a particular artifact
// instance in the image index, primarily for display purposes.
func (l *list) Files(instanceDigest digest.Digest) ([]string, error) {
filesList, ok := l.artifacts.Files[instanceDigest]
if ok {
return slices.Clone(filesList), nil
}
return nil, nil
return slices.Clone(l.artifacts.Files[instanceDigest]), nil
}
// instanceByFile returns the instanceDigest of the first manifest in the index
@ -640,9 +636,7 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag
if instanceInfo.OS == "" {
instanceInfo.OS = config.OS
instanceInfo.OSVersion = config.OSVersion
if config.OSFeatures != nil {
instanceInfo.OSFeatures = slices.Clone(config.OSFeatures)
}
instanceInfo.OSFeatures = slices.Clone(config.OSFeatures)
}
if instanceInfo.Architecture == "" {
instanceInfo.Architecture = config.Architecture
@ -906,9 +900,7 @@ func (l *list) AddArtifact(ctx context.Context, sys *types.SystemContext, option
Subject: subject,
}
// Add in annotations, more or less exactly as specified.
if options.Annotations != nil {
artifactManifest.Annotations = maps.Clone(options.Annotations)
}
artifactManifest.Annotations = maps.Clone(options.Annotations)
// Encode and save the data we care about.
artifactManifestBytes, err := json.Marshal(artifactManifest)

View File

@ -0,0 +1,9 @@
package libimage
import "github.com/containers/common/libimage/manifests"
// LookupReferenceFunc return an image reference based on the specified one.
// The returned reference can return custom ImageSource or ImageDestination
// objects which intercept or filter blobs, manifests, and signatures as
// they are read and written.
type LookupReferenceFunc = manifests.LookupReferenceFunc

View File

@ -462,6 +462,10 @@ func (n *Netns) setupMounts() error {
// 5. Mount the new prepared run dir to /run, it has to be recursive to keep the other bind mounts.
runDir := n.getPath("run")
err = os.MkdirAll(runDir, 0o700)
if err != nil {
return wrapError("create run directory", err)
}
// relabel the new run directory to the iptables /run label
// this is important, otherwise the iptables command will fail
err = label.Relabel(runDir, "system_u:object_r:iptables_var_run_t:s0", false)

View File

@ -326,6 +326,11 @@ func createIpvlanOrMacvlan(network *types.Network) error {
return fmt.Errorf("unknown ipvlan mode %q", value)
}
}
case types.MetricOption:
_, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return err
}
case types.MTUOption:
_, err := internalutil.ParseMTU(value)
if err != nil {

View File

@ -130,6 +130,7 @@ func createPastaArgs(opts *SetupOptions) ([]string, []string, error) {
noTCPNamespacePorts := true
noUDPNamespacePorts := true
noMapGW := true
quiet := true
cmdArgs := []string{"--config-net"}
// first append options set in the config
@ -158,6 +159,8 @@ func createPastaArgs(opts *SetupOptions) ([]string, []string, error) {
noTCPNamespacePorts = false
case "-U", "--udp-ns":
noUDPNamespacePorts = false
case "-d", "--debug", "--trace":
quiet = false
case dnsForwardOpt:
// if there is no arg after it pasta will likely error out anyway due invalid cli args
if len(cmdArgs) > i+1 {
@ -216,9 +219,12 @@ func createPastaArgs(opts *SetupOptions) ([]string, []string, error) {
if noMapGW {
cmdArgs = append(cmdArgs, "--no-map-gw")
}
if quiet {
// pass --quiet to silence the info output from pasta if verbose/trace pasta is not required
cmdArgs = append(cmdArgs, "--quiet")
}
// always pass --quiet to silence the info output from pasta
cmdArgs = append(cmdArgs, "--quiet", "--netns", opts.Netns)
cmdArgs = append(cmdArgs, "--netns", opts.Netns)
return cmdArgs, dnsForwardIPs, nil
}

View File

@ -51,9 +51,16 @@ func DefaultProfile() *Seccomp {
{
Names: []string{
"bdflush",
"cachestat",
"futex_requeue",
"futex_wait",
"futex_waitv",
"futex_wake",
"io_pgetevents",
"io_pgetevents_time64",
"kexec_file_load",
"kexec_load",
"map_shadow_stack",
"migrate_pages",
"move_pages",
"nfsservctl",
@ -68,9 +75,9 @@ func DefaultProfile() *Seccomp {
"pciconfig_write",
"sgetmask",
"ssetmask",
"swapcontext",
"swapoff",
"swapon",
"syscall",
"sysfs",
"uselib",
"userfaultfd",
@ -310,7 +317,6 @@ func DefaultProfile() *Seccomp {
"pwritev2",
"read",
"readahead",
"readdir",
"readlink",
"readlinkat",
"readv",
@ -398,15 +404,12 @@ func DefaultProfile() *Seccomp {
"shmdt",
"shmget",
"shutdown",
"sigaction",
"sigaltstack",
"signal",
"signalfd",
"signalfd4",
"sigpending",
"sigprocmask",
"sigreturn",
"sigsuspend",
"socketcall",
"socketpair",
"splice",
@ -420,7 +423,6 @@ func DefaultProfile() *Seccomp {
"sync",
"sync_file_range",
"syncfs",
"syscall",
"sysinfo",
"syslog",
"tee",
@ -433,7 +435,6 @@ func DefaultProfile() *Seccomp {
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
@ -523,6 +524,7 @@ func DefaultProfile() *Seccomp {
{
Names: []string{
"sync_file_range2",
"swapcontext",
},
Action: ActAllow,
Args: []*Arg{},
@ -577,6 +579,16 @@ func DefaultProfile() *Seccomp {
Arches: []string{"s390", "s390x"},
},
},
{
Names: []string{
"riscv_flush_icache",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"riscv64"},
},
},
{
Names: []string{
"open_by_handle_at",
@ -604,8 +616,8 @@ func DefaultProfile() *Seccomp {
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
@ -618,11 +630,11 @@ func DefaultProfile() *Seccomp {
},
{
Names: []string{
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
@ -885,6 +897,50 @@ func DefaultProfile() *Seccomp {
Caps: []string{"CAP_AUDIT_WRITE"},
},
},
{
Names: []string{
"bpf",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_ADMIN", "CAP_BPF"},
},
},
{
Names: []string{
"bpf",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_BPF"},
},
},
{
Names: []string{
"perf_event_open",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_ADMIN", "CAP_BPF"},
},
},
{
Names: []string{
"perf_event_open",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_PERFMON"},
},
},
}
return &Seccomp{

View File

@ -55,9 +55,16 @@
{
"names": [
"bdflush",
"cachestat",
"futex_requeue",
"futex_wait",
"futex_waitv",
"futex_wake",
"io_pgetevents",
"io_pgetevents_time64",
"kexec_file_load",
"kexec_load",
"map_shadow_stack",
"migrate_pages",
"move_pages",
"nfsservctl",
@ -72,9 +79,9 @@
"pciconfig_write",
"sgetmask",
"ssetmask",
"swapcontext",
"swapoff",
"swapon",
"syscall",
"sysfs",
"uselib",
"userfaultfd",
@ -317,7 +324,6 @@
"pwritev2",
"read",
"readahead",
"readdir",
"readlink",
"readlinkat",
"readv",
@ -405,15 +411,12 @@
"shmdt",
"shmget",
"shutdown",
"sigaction",
"sigaltstack",
"signal",
"signalfd",
"signalfd4",
"sigpending",
"sigprocmask",
"sigreturn",
"sigsuspend",
"socketcall",
"socketpair",
"splice",
@ -427,7 +430,6 @@
"sync",
"sync_file_range",
"syncfs",
"syscall",
"sysinfo",
"syslog",
"tee",
@ -440,7 +442,6 @@
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
@ -562,7 +563,8 @@
},
{
"names": [
"sync_file_range2"
"sync_file_range2",
"swapcontext"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@ -642,6 +644,20 @@
},
"excludes": {}
},
{
"names": [
"riscv_flush_icache"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"arches": [
"riscv64"
]
},
"excludes": {}
},
{
"names": [
"open_by_handle_at"
@ -677,8 +693,8 @@
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns"
@ -695,11 +711,11 @@
},
{
"names": [
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns"
@ -1047,6 +1063,68 @@
]
},
"excludes": {}
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_ADMIN",
"CAP_BPF"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"caps": [
"CAP_BPF"
]
},
"excludes": {}
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_ADMIN",
"CAP_BPF"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"caps": [
"CAP_PERFMON"
]
},
"excludes": {}
}
]
}

View File

@ -409,7 +409,6 @@ func (ic *imageCopier) compareImageDestinationManifestEqual(ctx context.Context,
// copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.cannotModifyManifestReason == "".
func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algorithm, error) {
srcInfos := ic.src.LayerInfos()
numLayers := len(srcInfos)
updatedSrcInfos, err := ic.src.LayerInfosForCopy(ctx)
if err != nil {
return nil, err
@ -440,7 +439,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor
// copyGroup is used to determine if all layers are copied
copyGroup := sync.WaitGroup{}
data := make([]copyLayerData, numLayers)
data := make([]copyLayerData, len(srcInfos))
copyLayerHelper := func(index int, srcLayer types.BlobInfo, toEncrypt bool, pool *mpb.Progress, srcRef reference.Named) {
defer ic.c.concurrentBlobCopiesSemaphore.Release(1)
defer copyGroup.Done()
@ -463,9 +462,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor
// Decide which layers to encrypt
layersToEncrypt := set.New[int]()
var encryptAll bool
if ic.c.options.OciEncryptLayers != nil {
encryptAll = len(*ic.c.options.OciEncryptLayers) == 0
totalLayers := len(srcInfos)
for _, l := range *ic.c.options.OciEncryptLayers {
switch {
@ -478,7 +475,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor
}
}
if encryptAll {
if len(*ic.c.options.OciEncryptLayers) == 0 { // “encrypt all layers”
for i := 0; i < len(srcInfos); i++ {
layersToEncrypt.Add(i)
}
@ -493,8 +490,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor
defer copyGroup.Wait()
for i, srcLayer := range srcInfos {
err = ic.c.concurrentBlobCopiesSemaphore.Acquire(ctx, 1)
if err != nil {
if err := ic.c.concurrentBlobCopiesSemaphore.Acquire(ctx, 1); err != nil {
// This can only fail with ctx.Err(), so no need to blame acquiring the semaphore.
return fmt.Errorf("copying layer: %w", err)
}
@ -509,8 +505,8 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor
}
compressionAlgos := set.New[string]()
destInfos := make([]types.BlobInfo, numLayers)
diffIDs := make([]digest.Digest, numLayers)
destInfos := make([]types.BlobInfo, len(srcInfos))
diffIDs := make([]digest.Digest, len(srcInfos))
for i, cld := range data {
if cld.err != nil {
return nil, cld.err

View File

@ -86,11 +86,9 @@ type extensionSignatureList struct {
Signatures []extensionSignature `json:"signatures"`
}
// bearerToken records a cached token we can use to authenticate.
type bearerToken struct {
Token string `json:"token"`
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
IssuedAt time.Time `json:"issued_at"`
token string
expirationTime time.Time
}
@ -147,25 +145,6 @@ const (
noAuth
)
func newBearerTokenFromJSONBlob(blob []byte) (*bearerToken, error) {
token := new(bearerToken)
if err := json.Unmarshal(blob, &token); err != nil {
return nil, err
}
if token.Token == "" {
token.Token = token.AccessToken
}
if token.ExpiresIn < minimumTokenLifetimeSeconds {
token.ExpiresIn = minimumTokenLifetimeSeconds
logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
}
if token.IssuedAt.IsZero() {
token.IssuedAt = time.Now().UTC()
}
token.expirationTime = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second)
return token, nil
}
// dockerCertDir returns a path to a directory to be consumed by tlsclientconfig.SetupCertificates() depending on ctx and hostPort.
func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) {
if sys != nil && sys.DockerCertPath != "" {
@ -774,7 +753,7 @@ func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope
token = *t
c.tokenCache.Store(cacheKey, token)
}
registryToken = token.Token
registryToken = token.token
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", registryToken))
return nil
@ -827,12 +806,7 @@ func (c *dockerClient) getBearerTokenOAuth2(ctx context.Context, challenge chall
return nil, err
}
tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
if err != nil {
return nil, err
}
return newBearerTokenFromJSONBlob(tokenBlob)
return newBearerTokenFromHTTPResponseBody(res)
}
func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
@ -878,12 +852,50 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
if err := httpResponseToError(res, "Requesting bearer token"); err != nil {
return nil, err
}
tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
return newBearerTokenFromHTTPResponseBody(res)
}
// newBearerTokenFromHTTPResponseBody parses a http.Response to obtain a bearerToken.
// The caller is still responsible for ensuring res.Body is closed.
func newBearerTokenFromHTTPResponseBody(res *http.Response) (*bearerToken, error) {
blob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
if err != nil {
return nil, err
}
return newBearerTokenFromJSONBlob(tokenBlob)
var token struct {
Token string `json:"token"`
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
IssuedAt time.Time `json:"issued_at"`
expirationTime time.Time
}
if err := json.Unmarshal(blob, &token); err != nil {
const bodySampleLength = 50
bodySample := blob
if len(bodySample) > bodySampleLength {
bodySample = bodySample[:bodySampleLength]
}
return nil, fmt.Errorf("decoding bearer token (last URL %q, body start %q): %w", res.Request.URL.Redacted(), string(bodySample), err)
}
bt := &bearerToken{
token: token.Token,
}
if bt.token == "" {
bt.token = token.AccessToken
}
if token.ExpiresIn < minimumTokenLifetimeSeconds {
token.ExpiresIn = minimumTokenLifetimeSeconds
logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
}
if token.IssuedAt.IsZero() {
token.IssuedAt = time.Now().UTC()
}
bt.expirationTime = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second)
return bt, nil
}
// detectPropertiesHelper performs the work of detectProperties which executes

View File

@ -14,6 +14,7 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
)
// Image is a Docker-specific implementation of types.ImageCloser with a few extra methods
@ -90,6 +91,14 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
}
for _, tag := range tagsHolder.Tags {
if _, err := reference.WithTag(dr.ref, tag); err != nil { // Ensure the tag does not contain unexpected values
// Per https://github.com/containers/skopeo/issues/2346 , unknown versions of JFrog Artifactory,
// contrary to the tag format specified in
// https://github.com/opencontainers/distribution-spec/blob/8a871c8234977df058f1a14e299fe0a673853da2/spec.md?plain=1#L160 ,
// include digests in the list.
if _, err := digest.Parse(tag); err == nil {
logrus.Debugf("Ignoring invalid tag %q matching a digest format", tag)
continue
}
return nil, fmt.Errorf("registry returned invalid tag %q: %w", tag, err)
}
tags = append(tags, tag)

View File

@ -1,7 +1,9 @@
package docker
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
@ -11,6 +13,7 @@ import (
"net/http"
"net/url"
"os"
"os/exec"
"strings"
"sync"
@ -162,6 +165,34 @@ func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logica
client.Close()
return nil, err
}
if h, err := sysregistriesv2.AdditionalLayerStoreAuthHelper(endpointSys); err == nil && h != "" {
acf := map[string]struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
IdentityToken string `json:"identityToken,omitempty"`
}{
physicalRef.ref.String(): {
Username: client.auth.Username,
Password: client.auth.Password,
IdentityToken: client.auth.IdentityToken,
},
}
acfD, err := json.Marshal(acf)
if err != nil {
logrus.Warnf("failed to marshal auth config: %v", err)
} else {
cmd := exec.Command(h)
cmd.Stdin = bytes.NewReader(acfD)
if err := cmd.Run(); err != nil {
var stderr string
if ee, ok := err.(*exec.ExitError); ok {
stderr = string(ee.Stderr)
}
logrus.Warnf("Failed to call additional-layer-store-auth-helper (stderr:%s): %v", stderr, err)
}
}
}
return s, nil
}

View File

@ -1,6 +1,7 @@
package manifest
import (
"bytes"
"encoding/json"
"fmt"
"maps"
@ -296,29 +297,51 @@ func OCI1IndexPublicFromComponents(components []imgspecv1.Descriptor, annotation
},
}
for i, component := range components {
var platform *imgspecv1.Platform
if component.Platform != nil {
platformCopy := ociPlatformClone(*component.Platform)
platform = &platformCopy
}
m := imgspecv1.Descriptor{
MediaType: component.MediaType,
ArtifactType: component.ArtifactType,
Size: component.Size,
Digest: component.Digest,
URLs: slices.Clone(component.URLs),
Annotations: maps.Clone(component.Annotations),
Platform: platform,
}
index.Manifests[i] = m
index.Manifests[i] = oci1DescriptorClone(component)
}
return &index
}
func oci1DescriptorClone(d imgspecv1.Descriptor) imgspecv1.Descriptor {
var platform *imgspecv1.Platform
if d.Platform != nil {
platformCopy := ociPlatformClone(*d.Platform)
platform = &platformCopy
}
return imgspecv1.Descriptor{
MediaType: d.MediaType,
Digest: d.Digest,
Size: d.Size,
URLs: slices.Clone(d.URLs),
Annotations: maps.Clone(d.Annotations),
Data: bytes.Clone(d.Data),
Platform: platform,
ArtifactType: d.ArtifactType,
}
}
// OCI1IndexPublicClone creates a deep copy of the passed-in index.
// This is publicly visible as c/image/manifest.OCI1IndexClone.
func OCI1IndexPublicClone(index *OCI1IndexPublic) *OCI1IndexPublic {
return OCI1IndexPublicFromComponents(index.Manifests, index.Annotations)
var subject *imgspecv1.Descriptor
if index.Subject != nil {
s := oci1DescriptorClone(*index.Subject)
subject = &s
}
manifests := make([]imgspecv1.Descriptor, len(index.Manifests))
for i, m := range index.Manifests {
manifests[i] = oci1DescriptorClone(m)
}
return &OCI1IndexPublic{
Index: imgspecv1.Index{
Versioned: index.Versioned,
MediaType: index.MediaType,
ArtifactType: index.ArtifactType,
Manifests: manifests,
Subject: subject,
Annotations: maps.Clone(index.Annotations),
},
}
}
// ToOCI1Index returns the index encoded as an OCI1 index.

View File

@ -248,6 +248,11 @@ type V2RegistriesConf struct {
// potentially use all unqualified-search registries
ShortNameMode string `toml:"short-name-mode"`
// AdditionalLayerStoreAuthHelper is a helper binary that receives
// registry credentials pass them to Additional Layer Store for
// registry authentication. These credentials are only collected when pulling (not pushing).
AdditionalLayerStoreAuthHelper string `toml:"additional-layer-store-auth-helper"`
shortNameAliasConf
// If you add any field, make sure to update Nonempty() below.
@ -825,6 +830,16 @@ func CredentialHelpers(sys *types.SystemContext) ([]string, error) {
return config.partialV2.CredentialHelpers, nil
}
// AdditionalLayerStoreAuthHelper returns the helper for passing registry
// credentials to Additional Layer Store.
func AdditionalLayerStoreAuthHelper(sys *types.SystemContext) (string, error) {
config, err := getConfig(sys)
if err != nil {
return "", err
}
return config.partialV2.AdditionalLayerStoreAuthHelper, nil
}
// refMatchingSubdomainPrefix returns the length of ref
// iff ref, which is a registry, repository namespace, repository or image reference (as formatted by
// reference.Domain(), reference.Named.Name() or reference.Reference.String()
@ -1051,6 +1066,11 @@ func (c *parsedConfig) updateWithConfigurationFrom(updates *parsedConfig) {
c.shortNameMode = updates.shortNameMode
}
// == Merge AdditionalLayerStoreAuthHelper:
if updates.partialV2.AdditionalLayerStoreAuthHelper != "" {
c.partialV2.AdditionalLayerStoreAuthHelper = updates.partialV2.AdditionalLayerStoreAuthHelper
}
// == Merge aliasCache:
// We dont maintain (in fact we actively clear) c.partialV2.shortNameAliasConf.
c.aliasCache.updateWithConfigurationFrom(updates.aliasCache)

View File

@ -15,6 +15,7 @@ import (
"github.com/containers/image/v5/signature/internal"
"github.com/containers/storage/pkg/homedir"
// This is a fallback code; the primary recommendation is to use the gpgme mechanism
// implementation, which is out-of-process and more appropriate for handling long-term private key material
// than any Go implementation.
@ -150,7 +151,7 @@ func (m *openpgpSigningMechanism) Verify(unverifiedSignature []byte) (contents [
return nil, "", fmt.Errorf("signature error: %v", md.SignatureError)
}
if md.SignedBy == nil {
return nil, "", internal.NewInvalidSignatureError(fmt.Sprintf("Invalid GPG signature: %#v", md.Signature))
return nil, "", internal.NewInvalidSignatureError(fmt.Sprintf("Key not found for key ID %x in signature", md.SignedByKeyId))
}
if md.Signature != nil {
if md.Signature.SigLifetimeSecs != nil {

View File

@ -325,7 +325,13 @@ func (s *storageImageDestination) PutBlobPartial(ctx context.Context, chunkAcces
if out.UncompressedDigest != "" {
// The computation of UncompressedDigest means the whole layer has been consumed; while doing that, chunked.GetDiffer is
// responsible for ensuring blobDigest has been validated.
if out.CompressedDigest != blobDigest {
return private.UploadedBlob{}, fmt.Errorf("internal error: ApplyDiffWithDiffer returned CompressedDigest %q not matching expected %q",
out.CompressedDigest, blobDigest)
}
s.lockProtected.blobDiffIDs[blobDigest] = out.UncompressedDigest
// We trust ApplyDiffWithDiffer to validate or create both values correctly.
options.Cache.RecordDigestUncompressedPair(out.CompressedDigest, out.UncompressedDigest)
} else {
// Dont identify layers by TOC if UncompressedDigest is available.
// - Using UncompressedDigest allows image reuse with non-partially-pulled layers

View File

@ -46,7 +46,14 @@ func tryProcFilter(args []string, input io.Reader, cleanup func()) (io.ReadClose
go func() {
err := cmd.Run()
if err != nil && stderrBuf.Len() > 0 {
err = fmt.Errorf("%s: %w", strings.TrimRight(stderrBuf.String(), "\n"), err)
b := make([]byte, 1)
// if there is an error reading from input, prefer to return that error
_, errRead := input.Read(b)
if errRead != nil && errRead != io.EOF {
err = errRead
} else {
err = fmt.Errorf("%s: %w", strings.TrimRight(stderrBuf.String(), "\n"), err)
}
}
w.CloseWithError(err) // CloseWithErr(nil) == Close()
cleanup()