Merge pull request #3252 from mtrmac/registries.conf-mirrors
Update containers/image to v2.0.0, and buildah to v1.8.4
This commit is contained in:
commit
5341ce802e
|
@ -44,17 +44,7 @@ func getRegistries() ([]sysregistriesv2.Registry, error) {
|
||||||
|
|
||||||
// GetRegistries obtains the list of search registries defined in the global registries file.
|
// GetRegistries obtains the list of search registries defined in the global registries file.
|
||||||
func GetRegistries() ([]string, error) {
|
func GetRegistries() ([]string, error) {
|
||||||
var searchRegistries []string
|
return sysregistriesv2.UnqualifiedSearchRegistries(&types.SystemContext{SystemRegistriesConfPath: SystemRegistriesConfPath()})
|
||||||
registries, err := getRegistries()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, reg := range registries {
|
|
||||||
if reg.Search {
|
|
||||||
searchRegistries = append(searchRegistries, reg.Location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return searchRegistries, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockedRegistries obtains the list of blocked registries defined in the global registries file.
|
// GetBlockedRegistries obtains the list of blocked registries defined in the global registries file.
|
||||||
|
@ -66,7 +56,7 @@ func GetBlockedRegistries() ([]string, error) {
|
||||||
}
|
}
|
||||||
for _, reg := range registries {
|
for _, reg := range registries {
|
||||||
if reg.Blocked {
|
if reg.Blocked {
|
||||||
blockedRegistries = append(blockedRegistries, reg.Location)
|
blockedRegistries = append(blockedRegistries, reg.Prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return blockedRegistries, nil
|
return blockedRegistries, nil
|
||||||
|
@ -81,7 +71,7 @@ func GetInsecureRegistries() ([]string, error) {
|
||||||
}
|
}
|
||||||
for _, reg := range registries {
|
for _, reg := range registries {
|
||||||
if reg.Insecure {
|
if reg.Insecure {
|
||||||
insecureRegistries = append(insecureRegistries, reg.Location)
|
insecureRegistries = append(insecureRegistries, reg.Prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return insecureRegistries, nil
|
return insecureRegistries, nil
|
||||||
|
|
|
@ -15,7 +15,7 @@ github.com/containerd/cgroups 4994991857f9b0ae8dc439551e8bebdbb4bf66c1
|
||||||
github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d
|
github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d
|
||||||
github.com/containernetworking/cni v0.7.0-rc2
|
github.com/containernetworking/cni v0.7.0-rc2
|
||||||
github.com/containernetworking/plugins v0.7.4
|
github.com/containernetworking/plugins v0.7.4
|
||||||
github.com/containers/image 2c0349c99af7d90694b3faa0e9bde404d407b145
|
github.com/containers/image v2.0.0
|
||||||
github.com/vbauerster/mpb v3.3.4
|
github.com/vbauerster/mpb v3.3.4
|
||||||
github.com/mattn/go-isatty v0.0.4
|
github.com/mattn/go-isatty v0.0.4
|
||||||
github.com/VividCortex/ewma v1.1.1
|
github.com/VividCortex/ewma v1.1.1
|
||||||
|
@ -93,7 +93,7 @@ k8s.io/api kubernetes-1.10.13-beta.0 https://github.com/kubernetes/api
|
||||||
k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apimachinery
|
k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apimachinery
|
||||||
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
|
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
|
||||||
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
|
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
|
||||||
github.com/containers/buildah v1.8.3
|
github.com/containers/buildah v1.8.4
|
||||||
github.com/varlink/go 0f1d566d194b9d6d48e0d47c5e4d822628919066
|
github.com/varlink/go 0f1d566d194b9d6d48e0d47c5e4d822628919066
|
||||||
# TODO: Gotty has not been updated since 2012. Can we find replacement?
|
# TODO: Gotty has not been updated since 2012. Can we find replacement?
|
||||||
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512
|
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512
|
||||||
|
|
|
@ -34,10 +34,14 @@ type AddAndCopyOptions struct {
|
||||||
// If the sources include directory trees, Hasher will be passed
|
// If the sources include directory trees, Hasher will be passed
|
||||||
// tar-format archives of the directory trees.
|
// tar-format archives of the directory trees.
|
||||||
Hasher io.Writer
|
Hasher io.Writer
|
||||||
// Exludes contents in the .dockerignore file
|
// Excludes is the contents of the .dockerignore file
|
||||||
Excludes []string
|
Excludes []string
|
||||||
// current directory on host
|
// The base directory for Excludes and data to copy in
|
||||||
ContextDir string
|
ContextDir string
|
||||||
|
// ID mapping options to use when contents to be copied are part of
|
||||||
|
// another container, and need ownerships to be mapped from the host to
|
||||||
|
// that container's values before copying them into the container.
|
||||||
|
IDMappingOptions *IDMappingOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// addURL copies the contents of the source URL to the destination. This is
|
// addURL copies the contents of the source URL to the destination. This is
|
||||||
|
@ -116,6 +120,12 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
|
hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
|
||||||
dest := mountPoint
|
dest := mountPoint
|
||||||
if destination != "" && filepath.IsAbs(destination) {
|
if destination != "" && filepath.IsAbs(destination) {
|
||||||
|
dir := filepath.Dir(destination)
|
||||||
|
if dir != "." && dir != "/" {
|
||||||
|
if err = idtools.MkdirAllAndChownNew(filepath.Join(dest, dir), 0755, hostOwner); err != nil {
|
||||||
|
return errors.Wrapf(err, "error creating directory %q", filepath.Join(dest, dir))
|
||||||
|
}
|
||||||
|
}
|
||||||
dest = filepath.Join(dest, destination)
|
dest = filepath.Join(dest, destination)
|
||||||
} else {
|
} else {
|
||||||
if err = idtools.MkdirAllAndChownNew(filepath.Join(dest, b.WorkDir()), 0755, hostOwner); err != nil {
|
if err = idtools.MkdirAllAndChownNew(filepath.Join(dest, b.WorkDir()), 0755, hostOwner); err != nil {
|
||||||
|
@ -146,8 +156,8 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
if len(source) > 1 && (destfi == nil || !destfi.IsDir()) {
|
if len(source) > 1 && (destfi == nil || !destfi.IsDir()) {
|
||||||
return errors.Errorf("destination %q is not a directory", dest)
|
return errors.Errorf("destination %q is not a directory", dest)
|
||||||
}
|
}
|
||||||
copyFileWithTar := b.copyFileWithTar(&containerOwner, options.Hasher)
|
copyFileWithTar := b.copyFileWithTar(options.IDMappingOptions, &containerOwner, options.Hasher)
|
||||||
copyWithTar := b.copyWithTar(&containerOwner, options.Hasher)
|
copyWithTar := b.copyWithTar(options.IDMappingOptions, &containerOwner, options.Hasher)
|
||||||
untarPath := b.untarPath(nil, options.Hasher)
|
untarPath := b.untarPath(nil, options.Hasher)
|
||||||
err = addHelper(excludes, extract, dest, destfi, hostOwner, options, copyFileWithTar, copyWithTar, untarPath, source...)
|
err = addHelper(excludes, extract, dest, destfi, hostOwner, options, copyFileWithTar, copyWithTar, untarPath, source...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -26,7 +26,7 @@ const (
|
||||||
Package = "buildah"
|
Package = "buildah"
|
||||||
// Version for the Package. Bump version in contrib/rpm/buildah.spec
|
// Version for the Package. Bump version in contrib/rpm/buildah.spec
|
||||||
// too.
|
// too.
|
||||||
Version = "1.8.3"
|
Version = "1.8.4"
|
||||||
// The value we use to identify what type of information, currently a
|
// The value we use to identify what type of information, currently a
|
||||||
// serialized Builder structure, we are using as per-container state.
|
// serialized Builder structure, we are using as per-container state.
|
||||||
// This should only be changed when we make incompatible changes to
|
// This should only be changed when we make incompatible changes to
|
||||||
|
|
|
@ -220,7 +220,6 @@ func runUsingChrootMain() {
|
||||||
var stdout io.Writer
|
var stdout io.Writer
|
||||||
var stderr io.Writer
|
var stderr io.Writer
|
||||||
fdDesc := make(map[int]string)
|
fdDesc := make(map[int]string)
|
||||||
deferred := func() {}
|
|
||||||
if options.Spec.Process.Terminal {
|
if options.Spec.Process.Terminal {
|
||||||
// Create a pseudo-terminal -- open a copy of the master side.
|
// Create a pseudo-terminal -- open a copy of the master side.
|
||||||
ptyMasterFd, err := unix.Open("/dev/ptmx", os.O_RDWR, 0600)
|
ptyMasterFd, err := unix.Open("/dev/ptmx", os.O_RDWR, 0600)
|
||||||
|
@ -370,12 +369,16 @@ func runUsingChrootMain() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := unix.SetNonblock(relays[unix.Stdin], true); err != nil {
|
||||||
|
logrus.Errorf("error setting %d to nonblocking: %v", relays[unix.Stdin], err)
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
buffers := make(map[int]*bytes.Buffer)
|
buffers := make(map[int]*bytes.Buffer)
|
||||||
for _, writeFd := range relays {
|
for _, writeFd := range relays {
|
||||||
buffers[writeFd] = new(bytes.Buffer)
|
buffers[writeFd] = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
pollTimeout := -1
|
pollTimeout := -1
|
||||||
|
stdinClose := false
|
||||||
for len(relays) > 0 {
|
for len(relays) > 0 {
|
||||||
fds := make([]unix.PollFd, 0, len(relays))
|
fds := make([]unix.PollFd, 0, len(relays))
|
||||||
for fd := range relays {
|
for fd := range relays {
|
||||||
|
@ -395,6 +398,9 @@ func runUsingChrootMain() {
|
||||||
removeFds[int(rfd.Fd)] = struct{}{}
|
removeFds[int(rfd.Fd)] = struct{}{}
|
||||||
}
|
}
|
||||||
if rfd.Revents&unix.POLLIN == 0 {
|
if rfd.Revents&unix.POLLIN == 0 {
|
||||||
|
if stdinClose && stdinCopy == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b := make([]byte, 8192)
|
b := make([]byte, 8192)
|
||||||
|
@ -449,8 +455,19 @@ func runUsingChrootMain() {
|
||||||
if buffer.Len() > 0 {
|
if buffer.Len() > 0 {
|
||||||
pollTimeout = 100
|
pollTimeout = 100
|
||||||
}
|
}
|
||||||
|
if wfd == relays[unix.Stdin] && stdinClose && buffer.Len() == 0 {
|
||||||
|
stdinCopy.Close()
|
||||||
|
delete(relays, unix.Stdin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for rfd := range removeFds {
|
for rfd := range removeFds {
|
||||||
|
if rfd == unix.Stdin {
|
||||||
|
buffer, found := buffers[relays[unix.Stdin]]
|
||||||
|
if found && buffer.Len() > 0 {
|
||||||
|
stdinClose = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
if !options.Spec.Process.Terminal && rfd == unix.Stdin {
|
if !options.Spec.Process.Terminal && rfd == unix.Stdin {
|
||||||
stdinCopy.Close()
|
stdinCopy.Close()
|
||||||
}
|
}
|
||||||
|
@ -461,7 +478,6 @@ func runUsingChrootMain() {
|
||||||
|
|
||||||
// Set up mounts and namespaces, and run the parent subprocess.
|
// Set up mounts and namespaces, and run the parent subprocess.
|
||||||
status, err := runUsingChroot(options.Spec, options.BundlePath, ctty, stdin, stdout, stderr, closeOnceRunning)
|
status, err := runUsingChroot(options.Spec, options.BundlePath, ctty, stdin, stdout, stderr, closeOnceRunning)
|
||||||
deferred()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error running subprocess: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error running subprocess: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -707,7 +707,7 @@ func (b *Builder) makeImageRef(options CommitOptions, exporting bool) (types.Ima
|
||||||
exporting: exporting,
|
exporting: exporting,
|
||||||
squash: options.Squash,
|
squash: options.Squash,
|
||||||
emptyLayer: options.EmptyLayer,
|
emptyLayer: options.EmptyLayer,
|
||||||
tarPath: b.tarPath(),
|
tarPath: b.tarPath(&b.IDMappingOptions),
|
||||||
parent: parent,
|
parent: parent,
|
||||||
blobDirectory: options.BlobDirectory,
|
blobDirectory: options.BlobDirectory,
|
||||||
preEmptyLayers: b.PrependedEmptyLayers,
|
preEmptyLayers: b.PrependedEmptyLayers,
|
||||||
|
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
"github.com/cyphar/filepath-securejoin"
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
@ -210,7 +210,6 @@ type Executor struct {
|
||||||
annotations []string
|
annotations []string
|
||||||
onbuild []string
|
onbuild []string
|
||||||
layers bool
|
layers bool
|
||||||
topLayers []string
|
|
||||||
useCache bool
|
useCache bool
|
||||||
removeIntermediateCtrs bool
|
removeIntermediateCtrs bool
|
||||||
forceRmIntermediateCtrs bool
|
forceRmIntermediateCtrs bool
|
||||||
|
@ -515,26 +514,55 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err
|
||||||
for _, src := range copy.Src {
|
for _, src := range copy.Src {
|
||||||
contextDir := s.executor.contextDir
|
contextDir := s.executor.contextDir
|
||||||
copyExcludes := excludes
|
copyExcludes := excludes
|
||||||
|
var idMappingOptions *buildah.IDMappingOptions
|
||||||
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
|
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
|
||||||
sources = append(sources, src)
|
sources = append(sources, src)
|
||||||
} else if len(copy.From) > 0 {
|
} else if len(copy.From) > 0 {
|
||||||
|
var srcRoot string
|
||||||
if other, ok := s.executor.stages[copy.From]; ok && other.index < s.index {
|
if other, ok := s.executor.stages[copy.From]; ok && other.index < s.index {
|
||||||
sources = append(sources, filepath.Join(other.mountPoint, src))
|
srcRoot = other.mountPoint
|
||||||
contextDir = other.mountPoint
|
contextDir = other.mountPoint
|
||||||
|
idMappingOptions = &other.builder.IDMappingOptions
|
||||||
} else if builder, ok := s.executor.containerMap[copy.From]; ok {
|
} else if builder, ok := s.executor.containerMap[copy.From]; ok {
|
||||||
sources = append(sources, filepath.Join(builder.MountPoint, src))
|
srcRoot = builder.MountPoint
|
||||||
contextDir = builder.MountPoint
|
contextDir = builder.MountPoint
|
||||||
|
idMappingOptions = &builder.IDMappingOptions
|
||||||
} else {
|
} else {
|
||||||
return errors.Errorf("the stage %q has not been built", copy.From)
|
return errors.Errorf("the stage %q has not been built", copy.From)
|
||||||
}
|
}
|
||||||
|
srcSecure, err := securejoin.SecureJoin(srcRoot, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// If destination is a folder, we need to take extra care to
|
||||||
|
// ensure that files are copied with correct names (since
|
||||||
|
// resolving a symlink may result in a different name).
|
||||||
|
if hadFinalPathSeparator {
|
||||||
|
_, srcName := filepath.Split(src)
|
||||||
|
_, srcNameSecure := filepath.Split(srcSecure)
|
||||||
|
if srcName != srcNameSecure {
|
||||||
|
options := buildah.AddAndCopyOptions{
|
||||||
|
Chown: copy.Chown,
|
||||||
|
ContextDir: contextDir,
|
||||||
|
Excludes: copyExcludes,
|
||||||
|
}
|
||||||
|
if err := s.builder.Add(filepath.Join(copy.Dest, srcName), copy.Download, options, srcSecure); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources = append(sources, srcSecure)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
sources = append(sources, filepath.Join(s.executor.contextDir, src))
|
sources = append(sources, filepath.Join(s.executor.contextDir, src))
|
||||||
copyExcludes = append(s.executor.excludes, excludes...)
|
copyExcludes = append(s.executor.excludes, excludes...)
|
||||||
}
|
}
|
||||||
options := buildah.AddAndCopyOptions{
|
options := buildah.AddAndCopyOptions{
|
||||||
Chown: copy.Chown,
|
Chown: copy.Chown,
|
||||||
ContextDir: contextDir,
|
ContextDir: contextDir,
|
||||||
Excludes: copyExcludes,
|
Excludes: copyExcludes,
|
||||||
|
IDMappingOptions: idMappingOptions,
|
||||||
}
|
}
|
||||||
if err := s.builder.Add(copy.Dest, copy.Download, options, sources...); err != nil {
|
if err := s.builder.Add(copy.Dest, copy.Download, options, sources...); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -860,9 +888,6 @@ func (s *StageExecutor) prepare(ctx context.Context, stage imagebuilder.Stage, f
|
||||||
// Make this our "current" working container.
|
// Make this our "current" working container.
|
||||||
s.mountPoint = mountPoint
|
s.mountPoint = mountPoint
|
||||||
s.builder = builder
|
s.builder = builder
|
||||||
// Add the top layer of this image to b.topLayers so we can
|
|
||||||
// keep track of them when building with cached images.
|
|
||||||
s.executor.topLayers = append(s.executor.topLayers, builder.TopLayer)
|
|
||||||
}
|
}
|
||||||
logrus.Debugln("Container ID:", builder.ContainerID)
|
logrus.Debugln("Container ID:", builder.ContainerID)
|
||||||
return builder, nil
|
return builder, nil
|
||||||
|
@ -967,7 +992,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
|
||||||
}
|
}
|
||||||
logImageID := func(imgID string) {
|
logImageID := func(imgID string) {
|
||||||
if s.executor.iidfile == "" {
|
if s.executor.iidfile == "" {
|
||||||
fmt.Fprintf(s.executor.out, "--> %s\n", imgID)
|
fmt.Fprintf(s.executor.out, "%s\n", imgID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -985,7 +1010,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
|
||||||
// We don't need to squash the base image, so just
|
// We don't need to squash the base image, so just
|
||||||
// reuse the base image.
|
// reuse the base image.
|
||||||
logCommit(s.output, -1)
|
logCommit(s.output, -1)
|
||||||
if imgID, ref, err = s.copyExistingImage(ctx, s.builder.FromImageID, s.output); err != nil {
|
if imgID, ref, err = s.tagExistingImage(ctx, s.builder.FromImageID, s.output); err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1110,7 +1135,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
|
||||||
imgID = cacheID
|
imgID = cacheID
|
||||||
if commitName != "" {
|
if commitName != "" {
|
||||||
logCommit(commitName, i)
|
logCommit(commitName, i)
|
||||||
if imgID, ref, err = s.copyExistingImage(ctx, cacheID, commitName); err != nil {
|
if imgID, ref, err = s.tagExistingImage(ctx, cacheID, commitName); err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
logImageID(imgID)
|
logImageID(imgID)
|
||||||
|
@ -1179,8 +1204,8 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
|
||||||
return imgID, ref, nil
|
return imgID, ref, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyExistingImage creates a copy of an image already in the store
|
// tagExistingImage adds names to an image already in the store
|
||||||
func (s *StageExecutor) copyExistingImage(ctx context.Context, cacheID, output string) (string, reference.Canonical, error) {
|
func (s *StageExecutor) tagExistingImage(ctx context.Context, cacheID, output string) (string, reference.Canonical, error) {
|
||||||
// If we don't need to attach a name to the image, just return the cache ID.
|
// If we don't need to attach a name to the image, just return the cache ID.
|
||||||
if output == "" {
|
if output == "" {
|
||||||
return cacheID, nil, nil
|
return cacheID, nil, nil
|
||||||
|
@ -1247,11 +1272,11 @@ func (s *StageExecutor) layerExists(ctx context.Context, currNode *parser.Node,
|
||||||
return "", errors.Wrapf(err, "error getting top layer info")
|
return "", errors.Wrapf(err, "error getting top layer info")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the parent of the top layer of an image is equal to the last entry in b.topLayers
|
// If the parent of the top layer of an image is equal to the current build image's top layer,
|
||||||
// it means that this image is potentially a cached intermediate image from a previous
|
// it means that this image is potentially a cached intermediate image from a previous
|
||||||
// build. Next we double check that the history of this image is equivalent to the previous
|
// build. Next we double check that the history of this image is equivalent to the previous
|
||||||
// lines in the Dockerfile up till the point we are at in the build.
|
// lines in the Dockerfile up till the point we are at in the build.
|
||||||
if imageTopLayer == nil || imageTopLayer.Parent == s.executor.topLayers[len(s.executor.topLayers)-1] || imageTopLayer.ID == s.executor.topLayers[len(s.executor.topLayers)-1] {
|
if imageTopLayer == nil || (s.builder.TopLayer != "" && (imageTopLayer.Parent == s.builder.TopLayer || imageTopLayer.ID == s.builder.TopLayer)) {
|
||||||
history, err := s.executor.getImageHistory(ctx, image.ID)
|
history, err := s.executor.getImageHistory(ctx, image.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrapf(err, "error getting history of %q", image.ID)
|
return "", errors.Wrapf(err, "error getting history of %q", image.ID)
|
||||||
|
@ -1340,26 +1365,8 @@ func (b *Executor) historyMatches(baseHistory []v1.History, child *parser.Node,
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instruction := child.Original
|
if history[len(baseHistory)].CreatedBy != b.getCreatedBy(child) {
|
||||||
switch strings.ToUpper(child.Value) {
|
return false
|
||||||
case "RUN":
|
|
||||||
instruction = instruction[4:]
|
|
||||||
buildArgs := b.getBuildArgs()
|
|
||||||
// If a previous image was built with some build-args but the new build process doesn't have any build-args
|
|
||||||
// specified, the command might be expanded differently, so compare the lengths of the old instruction with
|
|
||||||
// the current one. 11 is the length of "/bin/sh -c " that is used to run the run commands.
|
|
||||||
if buildArgs == "" && len(history[len(baseHistory)].CreatedBy) > len(instruction)+11 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// There are build-args, so check if anything with the build-args has changed
|
|
||||||
if buildArgs != "" && !strings.Contains(history[len(baseHistory)].CreatedBy, buildArgs) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
if !strings.Contains(history[len(baseHistory)].CreatedBy, instruction) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1373,6 +1380,7 @@ func (b *Executor) getBuildArgs() string {
|
||||||
buildArgs = append(buildArgs, k+"="+v)
|
buildArgs = append(buildArgs, k+"="+v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sort.Strings(buildArgs)
|
||||||
return strings.Join(buildArgs, " ")
|
return strings.Join(buildArgs, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1545,7 +1553,6 @@ func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, cr
|
||||||
options := buildah.CommitOptions{
|
options := buildah.CommitOptions{
|
||||||
Compression: s.executor.compression,
|
Compression: s.executor.compression,
|
||||||
SignaturePolicyPath: s.executor.signaturePolicyPath,
|
SignaturePolicyPath: s.executor.signaturePolicyPath,
|
||||||
AdditionalTags: s.executor.additionalTags,
|
|
||||||
ReportWriter: writer,
|
ReportWriter: writer,
|
||||||
PreferredManifestType: s.executor.outputFormat,
|
PreferredManifestType: s.executor.outputFormat,
|
||||||
SystemContext: s.executor.systemContext,
|
SystemContext: s.executor.systemContext,
|
||||||
|
@ -1731,6 +1738,24 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
|
||||||
fmt.Fprintf(b.out, "[Warning] one or more build args were not consumed: %v\n", unusedList)
|
fmt.Fprintf(b.out, "[Warning] one or more build args were not consumed: %v\n", unusedList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(b.additionalTags) > 0 {
|
||||||
|
if dest, err := b.resolveNameToImageRef(b.output); err == nil {
|
||||||
|
switch dest.Transport().Name() {
|
||||||
|
case is.Transport.Name():
|
||||||
|
img, err := is.Transport.GetStoreImage(b.store, dest)
|
||||||
|
if err != nil {
|
||||||
|
return imageID, ref, errors.Wrapf(err, "error locating just-written image %q", transports.ImageName(dest))
|
||||||
|
}
|
||||||
|
if err = util.AddImageNames(b.store, "", b.systemContext, img, b.additionalTags); err != nil {
|
||||||
|
return imageID, ref, errors.Wrapf(err, "error setting image names to %v", append(img.Names, b.additionalTags...))
|
||||||
|
}
|
||||||
|
logrus.Debugf("assigned names %v to image %q", img.Names, img.ID)
|
||||||
|
default:
|
||||||
|
logrus.Warnf("don't know how to add tags to images stored in %q transport", dest.Transport().Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := cleanup(); err != nil {
|
if err := cleanup(); err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package overlay
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -15,13 +16,27 @@ import (
|
||||||
// MountTemp creates a subdir of the contentDir based on the source directory
|
// MountTemp creates a subdir of the contentDir based on the source directory
|
||||||
// from the source system. It then mounds up the source directory on to the
|
// from the source system. It then mounds up the source directory on to the
|
||||||
// generated mount point and returns the mount point to the caller.
|
// generated mount point and returns the mount point to the caller.
|
||||||
func MountTemp(store storage.Store, containerId, source, dest string, rootUID, rootGID int) (specs.Mount, string, error) {
|
func MountTemp(store storage.Store, containerId, source, dest string, rootUID, rootGID int) (mount specs.Mount, contentDir string, Err error) {
|
||||||
mount := specs.Mount{}
|
|
||||||
|
|
||||||
contentDir, err := store.ContainerDirectory(containerId)
|
containerDir, err := store.ContainerDirectory(containerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mount, "", err
|
return mount, "", err
|
||||||
}
|
}
|
||||||
|
contentDir = filepath.Join(containerDir, "overlay")
|
||||||
|
if err := idtools.MkdirAllAs(contentDir, 0700, rootUID, rootGID); err != nil {
|
||||||
|
return mount, "", errors.Wrapf(err, "failed to create the overlay %s directory", contentDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentDir, err = ioutil.TempDir(contentDir, "")
|
||||||
|
if err != nil {
|
||||||
|
return mount, "", errors.Wrapf(err, "failed to create TempDir in the overlay %s directory", contentDir)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if Err != nil {
|
||||||
|
os.RemoveAll(contentDir)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
upperDir := filepath.Join(contentDir, "upper")
|
upperDir := filepath.Join(contentDir, "upper")
|
||||||
workDir := filepath.Join(contentDir, "work")
|
workDir := filepath.Join(contentDir, "work")
|
||||||
if err := idtools.MkdirAllAs(upperDir, 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAs(upperDir, 0700, rootUID, rootGID); err != nil {
|
||||||
|
@ -44,3 +59,13 @@ func MountTemp(store storage.Store, containerId, source, dest string, rootUID, r
|
||||||
func RemoveTemp(contentDir string) error {
|
func RemoveTemp(contentDir string) error {
|
||||||
return os.RemoveAll(contentDir)
|
return os.RemoveAll(contentDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CleanupContent removes all temporary mountpoint and all content from
|
||||||
|
// directory
|
||||||
|
func CleanupContent(containerDir string) (Err error) {
|
||||||
|
contentDir := filepath.Join(containerDir, "overlay")
|
||||||
|
if err := os.RemoveAll(contentDir); err != nil && !os.IsNotExist(err) {
|
||||||
|
return errors.Wrapf(err, "failed to cleanup overlay %s directory", contentDir)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
|
||||||
var (
|
var (
|
||||||
memoryLimit int64
|
memoryLimit int64
|
||||||
memorySwap int64
|
memorySwap int64
|
||||||
|
noDNS bool
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,9 +68,26 @@ func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noDNS = false
|
||||||
dnsServers, _ := c.Flags().GetStringSlice("dns")
|
dnsServers, _ := c.Flags().GetStringSlice("dns")
|
||||||
|
for _, server := range dnsServers {
|
||||||
|
if strings.ToLower(server) == "none" {
|
||||||
|
noDNS = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if noDNS && len(dnsServers) > 1 {
|
||||||
|
return nil, errors.Errorf("invalid --dns, --dns=none may not be used with any other --dns options")
|
||||||
|
}
|
||||||
|
|
||||||
dnsSearch, _ := c.Flags().GetStringSlice("dns-search")
|
dnsSearch, _ := c.Flags().GetStringSlice("dns-search")
|
||||||
|
if noDNS && len(dnsSearch) > 0 {
|
||||||
|
return nil, errors.Errorf("invalid --dns-search, --dns-search may not be used with --dns=none")
|
||||||
|
}
|
||||||
|
|
||||||
dnsOptions, _ := c.Flags().GetStringSlice("dns-option")
|
dnsOptions, _ := c.Flags().GetStringSlice("dns-option")
|
||||||
|
if noDNS && len(dnsOptions) > 0 {
|
||||||
|
return nil, errors.Errorf("invalid --dns-option, --dns-option may not be used with --dns=none")
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := units.FromHumanSize(c.Flag("shm-size").Value.String()); err != nil {
|
if _, err := units.FromHumanSize(c.Flag("shm-size").Value.String()); err != nil {
|
||||||
return nil, errors.Wrapf(err, "invalid --shm-size")
|
return nil, errors.Wrapf(err, "invalid --shm-size")
|
||||||
|
@ -80,7 +98,7 @@ func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
|
||||||
}
|
}
|
||||||
cpuPeriod, _ := c.Flags().GetUint64("cpu-period")
|
cpuPeriod, _ := c.Flags().GetUint64("cpu-period")
|
||||||
cpuQuota, _ := c.Flags().GetInt64("cpu-quota")
|
cpuQuota, _ := c.Flags().GetInt64("cpu-quota")
|
||||||
cpuShares, _ := c.Flags().GetUint64("cpu-shared")
|
cpuShares, _ := c.Flags().GetUint64("cpu-shares")
|
||||||
httpProxy, _ := c.Flags().GetBool("http-proxy")
|
httpProxy, _ := c.Flags().GetBool("http-proxy")
|
||||||
ulimit, _ := c.Flags().GetStringSlice("ulimit")
|
ulimit, _ := c.Flags().GetStringSlice("ulimit")
|
||||||
commonOpts := &buildah.CommonBuildOptions{
|
commonOpts := &buildah.CommonBuildOptions{
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <linux/memfd.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
@ -14,6 +14,17 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/* Open Source projects like conda-forge, want to package podman and are based
|
||||||
|
off of centos:6, Conda-force has minimal libc requirements and is lacking
|
||||||
|
the memfd.h file, so we use mmam.h
|
||||||
|
*/
|
||||||
|
#ifndef MFD_ALLOW_SEALING
|
||||||
|
#define MFD_ALLOW_SEALING 2U
|
||||||
|
#endif
|
||||||
|
#ifndef MFD_CLOEXEC
|
||||||
|
#define MFD_CLOEXEC 1U
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef F_LINUX_SPECIFIC_BASE
|
#ifndef F_LINUX_SPECIFIC_BASE
|
||||||
#define F_LINUX_SPECIFIC_BASE 1024
|
#define F_LINUX_SPECIFIC_BASE 1024
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -195,13 +195,25 @@ func (c *Cmd) Start() error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
gidmapSet = true
|
gidmapSet = true
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(continueWrite, "error running newgidmap: %v: %s", err, g.String())
|
logrus.Warnf("error running newgidmap: %v: %s", err, g.String())
|
||||||
fmt.Fprintf(continueWrite, "falling back to single mapping\n")
|
logrus.Warnf("falling back to single mapping")
|
||||||
g.Reset()
|
g.Reset()
|
||||||
g.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Getegid())))
|
g.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Getegid())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !gidmapSet {
|
if !gidmapSet {
|
||||||
|
if c.UseNewgidmap {
|
||||||
|
setgroups, err := os.OpenFile(fmt.Sprintf("/proc/%s/setgroups", pidString), os.O_TRUNC|os.O_WRONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(continueWrite, "error opening /proc/%s/setgroups: %v", pidString, err)
|
||||||
|
return errors.Wrapf(err, "error opening /proc/%s/setgroups", pidString)
|
||||||
|
}
|
||||||
|
defer setgroups.Close()
|
||||||
|
if _, err := fmt.Fprintf(setgroups, "deny"); err != nil {
|
||||||
|
fmt.Fprintf(continueWrite, "error writing 'deny' to /proc/%s/setgroups: %v", pidString, err)
|
||||||
|
return errors.Wrapf(err, "error writing 'deny' to /proc/%s/setgroups", pidString)
|
||||||
|
}
|
||||||
|
}
|
||||||
gidmap, err := os.OpenFile(fmt.Sprintf("/proc/%s/gid_map", pidString), os.O_TRUNC|os.O_WRONLY, 0)
|
gidmap, err := os.OpenFile(fmt.Sprintf("/proc/%s/gid_map", pidString), os.O_TRUNC|os.O_WRONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(continueWrite, "error opening /proc/%s/gid_map: %v", pidString, err)
|
fmt.Fprintf(continueWrite, "error opening /proc/%s/gid_map: %v", pidString, err)
|
||||||
|
@ -232,8 +244,8 @@ func (c *Cmd) Start() error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
uidmapSet = true
|
uidmapSet = true
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(continueWrite, "error running newuidmap: %v: %s", err, u.String())
|
logrus.Warnf("error running newuidmap: %v: %s", err, u.String())
|
||||||
fmt.Fprintf(continueWrite, "falling back to single mapping\n")
|
logrus.Warnf("falling back to single mapping")
|
||||||
u.Reset()
|
u.Reset()
|
||||||
u.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Geteuid())))
|
u.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Geteuid())))
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,7 +174,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
||||||
bindFiles["/etc/hosts"] = hostFile
|
bindFiles["/etc/hosts"] = hostFile
|
||||||
}
|
}
|
||||||
|
|
||||||
if !contains(volumes, "/etc/resolv.conf") {
|
if !(contains(volumes, "/etc/resolv.conf") || (len(b.CommonBuildOpts.DNSServers) == 1 && strings.ToLower(b.CommonBuildOpts.DNSServers[0]) == "none")) {
|
||||||
resolvFile, err := b.addNetworkConfig(path, "/etc/resolv.conf", rootIDPair, b.CommonBuildOpts.DNSServers, b.CommonBuildOpts.DNSSearch, b.CommonBuildOpts.DNSOptions)
|
resolvFile, err := b.addNetworkConfig(path, "/etc/resolv.conf", rootIDPair, b.CommonBuildOpts.DNSServers, b.CommonBuildOpts.DNSSearch, b.CommonBuildOpts.DNSOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -434,7 +434,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st
|
||||||
|
|
||||||
// Add temporary copies of the contents of volume locations at the
|
// Add temporary copies of the contents of volume locations at the
|
||||||
// volume locations, unless we already have something there.
|
// volume locations, unless we already have something there.
|
||||||
copyWithTar := b.copyWithTar(nil, nil)
|
copyWithTar := b.copyWithTar(nil, nil, nil)
|
||||||
builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, copyWithTar, builtinVolumes, int(rootUID), int(rootGID))
|
builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, copyWithTar, builtinVolumes, int(rootUID), int(rootGID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1049,6 +1049,18 @@ func runConfigureNetwork(isolation Isolation, options RunOptions, configureNetwo
|
||||||
return teardown, nil
|
return teardown, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setNonblock(fd int, description string, nonblocking bool) error {
|
||||||
|
err := unix.SetNonblock(fd, nonblocking)
|
||||||
|
if err != nil {
|
||||||
|
if nonblocking {
|
||||||
|
logrus.Errorf("error setting %s to nonblocking: %v", description, err)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("error setting descriptor %s blocking: %v", description, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copyConsole bool, consoleListener *net.UnixListener, finishCopy []int, finishedCopy chan struct{}, spec *specs.Spec) {
|
func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copyConsole bool, consoleListener *net.UnixListener, finishCopy []int, finishedCopy chan struct{}, spec *specs.Spec) {
|
||||||
defer func() {
|
defer func() {
|
||||||
unix.Close(finishCopy[0])
|
unix.Close(finishCopy[0])
|
||||||
|
@ -1116,14 +1128,16 @@ func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copy
|
||||||
}
|
}
|
||||||
// Set our reading descriptors to non-blocking.
|
// Set our reading descriptors to non-blocking.
|
||||||
for rfd, wfd := range relayMap {
|
for rfd, wfd := range relayMap {
|
||||||
if err := unix.SetNonblock(rfd, true); err != nil {
|
if err := setNonblock(rfd, readDesc[rfd], true); err != nil {
|
||||||
logrus.Errorf("error setting %s to nonblocking: %v", readDesc[rfd], err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := unix.SetNonblock(wfd, false); err != nil {
|
setNonblock(wfd, writeDesc[wfd], false)
|
||||||
logrus.Errorf("error setting descriptor %d (%s) blocking: %v", wfd, writeDesc[wfd], err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setNonblock(stdioPipe[unix.Stdin][1], writeDesc[stdioPipe[unix.Stdin][1]], true)
|
||||||
|
|
||||||
|
closeStdin := false
|
||||||
|
|
||||||
// Pass data back and forth.
|
// Pass data back and forth.
|
||||||
pollTimeout := -1
|
pollTimeout := -1
|
||||||
for len(relayMap) > 0 {
|
for len(relayMap) > 0 {
|
||||||
|
@ -1155,12 +1169,6 @@ func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copy
|
||||||
}
|
}
|
||||||
// If the POLLIN flag isn't set, then there's no data to be read from this descriptor.
|
// If the POLLIN flag isn't set, then there's no data to be read from this descriptor.
|
||||||
if pollFd.Revents&unix.POLLIN == 0 {
|
if pollFd.Revents&unix.POLLIN == 0 {
|
||||||
// If we're using pipes and it's our stdin and it's closed, close the writing
|
|
||||||
// end of the corresponding pipe.
|
|
||||||
if copyPipes && int(pollFd.Fd) == unix.Stdin && pollFd.Revents&unix.POLLHUP != 0 {
|
|
||||||
unix.Close(stdioPipe[unix.Stdin][1])
|
|
||||||
stdioPipe[unix.Stdin][1] = -1
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Read whatever there is to be read.
|
// Read whatever there is to be read.
|
||||||
|
@ -1175,10 +1183,8 @@ func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copy
|
||||||
// using pipes, it's an EOF, so close the stdin
|
// using pipes, it's an EOF, so close the stdin
|
||||||
// pipe's writing end.
|
// pipe's writing end.
|
||||||
if n == 0 && copyPipes && int(pollFd.Fd) == unix.Stdin {
|
if n == 0 && copyPipes && int(pollFd.Fd) == unix.Stdin {
|
||||||
unix.Close(stdioPipe[unix.Stdin][1])
|
removes[int(pollFd.Fd)] = struct{}{}
|
||||||
stdioPipe[unix.Stdin][1] = -1
|
} else if n > 0 {
|
||||||
}
|
|
||||||
if n > 0 {
|
|
||||||
// Buffer the data in case we get blocked on where they need to go.
|
// Buffer the data in case we get blocked on where they need to go.
|
||||||
nwritten, err := relayBuffer[writeFD].Write(buf[:n])
|
nwritten, err := relayBuffer[writeFD].Write(buf[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1222,6 +1228,11 @@ func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copy
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
relayBuffer[writeFD].Next(n)
|
relayBuffer[writeFD].Next(n)
|
||||||
}
|
}
|
||||||
|
if closeStdin && writeFD == stdioPipe[unix.Stdin][1] && stdioPipe[unix.Stdin][1] >= 0 && relayBuffer[stdioPipe[unix.Stdin][1]].Len() == 0 {
|
||||||
|
logrus.Debugf("closing stdin")
|
||||||
|
unix.Close(stdioPipe[unix.Stdin][1])
|
||||||
|
stdioPipe[unix.Stdin][1] = -1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if relayBuffer[writeFD].Len() > 0 {
|
if relayBuffer[writeFD].Len() > 0 {
|
||||||
pollTimeout = 100
|
pollTimeout = 100
|
||||||
|
@ -1229,6 +1240,14 @@ func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copy
|
||||||
}
|
}
|
||||||
// Remove any descriptors which we don't need to poll any more from the poll descriptor list.
|
// Remove any descriptors which we don't need to poll any more from the poll descriptor list.
|
||||||
for remove := range removes {
|
for remove := range removes {
|
||||||
|
if copyPipes && remove == unix.Stdin {
|
||||||
|
closeStdin = true
|
||||||
|
if relayBuffer[stdioPipe[unix.Stdin][1]].Len() == 0 {
|
||||||
|
logrus.Debugf("closing stdin")
|
||||||
|
unix.Close(stdioPipe[unix.Stdin][1])
|
||||||
|
stdioPipe[unix.Stdin][1] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
delete(relayMap, remove)
|
delete(relayMap, remove)
|
||||||
}
|
}
|
||||||
// If the we-can-return pipe had anything for us, we're done.
|
// If the we-can-return pipe had anything for us, we're done.
|
||||||
|
@ -1453,7 +1472,7 @@ func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if configureNetwork {
|
if configureNetwork && !unshare.IsRootless() {
|
||||||
for name, val := range util.DefaultNetworkSysctl {
|
for name, val := range util.DefaultNetworkSysctl {
|
||||||
// Check that the sysctl we are adding is actually supported
|
// Check that the sysctl we are adding is actually supported
|
||||||
// by the kernel
|
// by the kernel
|
||||||
|
@ -1564,6 +1583,15 @@ func (b *Builder) cleanupTempVolumes() {
|
||||||
|
|
||||||
func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, rootUID, rootGID int) (mounts []specs.Mount, Err error) {
|
func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, rootUID, rootGID int) (mounts []specs.Mount, Err error) {
|
||||||
|
|
||||||
|
// Make sure the overlay directory is clean before running
|
||||||
|
containerDir, err := b.store.ContainerDirectory(b.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error looking up container directory for %s", b.ContainerID)
|
||||||
|
}
|
||||||
|
if err := overlay.CleanupContent(containerDir); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error cleaning up overlay content for %s", b.ContainerID)
|
||||||
|
}
|
||||||
|
|
||||||
parseMount := func(host, container string, options []string) (specs.Mount, error) {
|
parseMount := func(host, container string, options []string) (specs.Mount, error) {
|
||||||
var foundrw, foundro, foundz, foundZ, foundO bool
|
var foundrw, foundro, foundz, foundZ, foundO bool
|
||||||
var rootProp string
|
var rootProp string
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package buildah
|
package buildah
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containers/buildah/util"
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/pkg/sysregistries"
|
"github.com/containers/image/pkg/sysregistries"
|
||||||
"github.com/containers/image/pkg/sysregistriesv2"
|
"github.com/containers/image/pkg/sysregistriesv2"
|
||||||
|
@ -12,7 +15,9 @@ import (
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
"github.com/containers/storage/pkg/chrootarchive"
|
"github.com/containers/storage/pkg/chrootarchive"
|
||||||
"github.com/containers/storage/pkg/idtools"
|
"github.com/containers/storage/pkg/idtools"
|
||||||
|
"github.com/containers/storage/pkg/pools"
|
||||||
"github.com/containers/storage/pkg/reexec"
|
"github.com/containers/storage/pkg/reexec"
|
||||||
|
"github.com/containers/storage/pkg/system"
|
||||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/opencontainers/selinux/go-selinux"
|
"github.com/opencontainers/selinux/go-selinux"
|
||||||
|
@ -105,19 +110,108 @@ func convertRuntimeIDMaps(UIDMap, GIDMap []rspec.LinuxIDMapping) ([]idtools.IDMa
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyFileWithTar returns a function which copies a single file from outside
|
// copyFileWithTar returns a function which copies a single file from outside
|
||||||
// of any container into our working container, mapping permissions using the
|
// of any container, or another container, into our working container, mapping
|
||||||
// container's ID maps, possibly overridden using the passed-in chownOpts
|
// read permissions using the passed-in ID maps, writing using the container's
|
||||||
func (b *Builder) copyFileWithTar(chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {
|
// ID mappings, possibly overridden using the passed-in chownOpts
|
||||||
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
|
func (b *Builder) copyFileWithTar(tarIDMappingOptions *IDMappingOptions, chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {
|
||||||
return chrootarchive.CopyFileWithTarAndChown(chownOpts, hasher, convertedUIDMap, convertedGIDMap)
|
if tarIDMappingOptions == nil {
|
||||||
|
tarIDMappingOptions = &IDMappingOptions{
|
||||||
|
HostUIDMapping: true,
|
||||||
|
HostGIDMapping: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(src, dest string) error {
|
||||||
|
logrus.Debugf("copyFileWithTar(%s, %s)", src, dest)
|
||||||
|
f, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error opening %q to copy its contents", src)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sysfi, err := system.Lstat(src)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error reading attributes of %q", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostUID := sysfi.UID()
|
||||||
|
hostGID := sysfi.GID()
|
||||||
|
containerUID, containerGID, err := util.GetContainerIDs(tarIDMappingOptions.UIDMap, tarIDMappingOptions.GIDMap, hostUID, hostGID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error mapping owner IDs of %q: %d/%d", src, hostUID, hostGID)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Lstat(src)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error reading attributes of %q", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr, err := tar.FileInfoHeader(fi, filepath.Base(src))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error generating tar header for: %q", src)
|
||||||
|
}
|
||||||
|
hdr.Name = filepath.Base(dest)
|
||||||
|
hdr.Uid = int(containerUID)
|
||||||
|
hdr.Gid = int(containerGID)
|
||||||
|
|
||||||
|
pipeReader, pipeWriter := io.Pipe()
|
||||||
|
writer := tar.NewWriter(pipeWriter)
|
||||||
|
var copyErr error
|
||||||
|
go func(srcFile *os.File) {
|
||||||
|
err := writer.WriteHeader(hdr)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("error writing header for %s: %v", srcFile.Name(), err)
|
||||||
|
copyErr = err
|
||||||
|
}
|
||||||
|
n, err := pools.Copy(writer, srcFile)
|
||||||
|
if n != hdr.Size {
|
||||||
|
logrus.Debugf("expected to write %d bytes for %s, wrote %d instead", hdr.Size, srcFile.Name(), n)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("error reading %s: %v", srcFile.Name(), err)
|
||||||
|
copyErr = err
|
||||||
|
}
|
||||||
|
if err = writer.Close(); err != nil {
|
||||||
|
logrus.Debugf("error closing write pipe for %s: %v", srcFile.Name(), err)
|
||||||
|
}
|
||||||
|
if err = srcFile.Close(); err != nil {
|
||||||
|
logrus.Debugf("error closing %s: %v", srcFile.Name(), err)
|
||||||
|
}
|
||||||
|
pipeWriter.Close()
|
||||||
|
pipeWriter = nil
|
||||||
|
return
|
||||||
|
}(f)
|
||||||
|
|
||||||
|
untar := b.untar(chownOpts, hasher)
|
||||||
|
err = untar(pipeReader, filepath.Dir(dest))
|
||||||
|
if err == nil {
|
||||||
|
err = copyErr
|
||||||
|
}
|
||||||
|
f = nil
|
||||||
|
if pipeWriter != nil {
|
||||||
|
pipeWriter.Close()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyWithTar returns a function which copies a directory tree from outside of
|
// copyWithTar returns a function which copies a directory tree from outside of
|
||||||
// any container into our working container, mapping permissions using the
|
// our container or from another container, into our working container, mapping
|
||||||
// container's ID maps, possibly overridden using the passed-in chownOpts
|
// permissions at read-time using the container's ID maps, with ownership at
|
||||||
func (b *Builder) copyWithTar(chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {
|
// write-time possibly overridden using the passed-in chownOpts
|
||||||
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
|
func (b *Builder) copyWithTar(tarIDMappingOptions *IDMappingOptions, chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {
|
||||||
return chrootarchive.CopyWithTarAndChown(chownOpts, hasher, convertedUIDMap, convertedGIDMap)
|
tar := b.tarPath(tarIDMappingOptions)
|
||||||
|
untar := b.untar(chownOpts, hasher)
|
||||||
|
return func(src, dest string) error {
|
||||||
|
rc, err := tar(src)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error archiving %q for copy", src)
|
||||||
|
}
|
||||||
|
return untar(rc, dest)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// untarPath returns a function which extracts an archive in a specified
|
// untarPath returns a function which extracts an archive in a specified
|
||||||
|
@ -128,12 +222,58 @@ func (b *Builder) untarPath(chownOpts *idtools.IDPair, hasher io.Writer) func(sr
|
||||||
return chrootarchive.UntarPathAndChown(chownOpts, hasher, convertedUIDMap, convertedGIDMap)
|
return chrootarchive.UntarPathAndChown(chownOpts, hasher, convertedUIDMap, convertedGIDMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tarPath returns a function which creates an archive of a specified
|
// tarPath returns a function which creates an archive of a specified location,
|
||||||
|
// which is often somewhere in the container's filesystem, mapping permissions
|
||||||
|
// using the container's ID maps, or the passed-in maps if specified
|
||||||
|
func (b *Builder) tarPath(idMappingOptions *IDMappingOptions) func(path string) (io.ReadCloser, error) {
|
||||||
|
var uidmap, gidmap []idtools.IDMap
|
||||||
|
if idMappingOptions == nil {
|
||||||
|
idMappingOptions = &IDMappingOptions{
|
||||||
|
HostUIDMapping: true,
|
||||||
|
HostGIDMapping: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(idMappingOptions.UIDMap, idMappingOptions.GIDMap)
|
||||||
|
tarMappings := idtools.NewIDMappingsFromMaps(convertedUIDMap, convertedGIDMap)
|
||||||
|
uidmap = tarMappings.UIDs()
|
||||||
|
gidmap = tarMappings.GIDs()
|
||||||
|
options := &archive.TarOptions{
|
||||||
|
Compression: archive.Uncompressed,
|
||||||
|
UIDMaps: uidmap,
|
||||||
|
GIDMaps: gidmap,
|
||||||
|
}
|
||||||
|
return func(path string) (io.ReadCloser, error) {
|
||||||
|
return archive.TarWithOptions(path, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// untar returns a function which extracts an archive stream to a specified
|
||||||
// location in the container's filesystem, mapping permissions using the
|
// location in the container's filesystem, mapping permissions using the
|
||||||
// container's ID maps
|
// container's ID maps, possibly overridden using the passed-in chownOpts
|
||||||
func (b *Builder) tarPath() func(path string) (io.ReadCloser, error) {
|
func (b *Builder) untar(chownOpts *idtools.IDPair, hasher io.Writer) func(tarArchive io.ReadCloser, dest string) error {
|
||||||
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
|
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
|
||||||
return archive.TarPath(convertedUIDMap, convertedGIDMap)
|
untarMappings := idtools.NewIDMappingsFromMaps(convertedUIDMap, convertedGIDMap)
|
||||||
|
options := &archive.TarOptions{
|
||||||
|
UIDMaps: untarMappings.UIDs(),
|
||||||
|
GIDMaps: untarMappings.GIDs(),
|
||||||
|
ChownOpts: chownOpts,
|
||||||
|
}
|
||||||
|
untar := chrootarchive.Untar
|
||||||
|
if hasher != nil {
|
||||||
|
originalUntar := untar
|
||||||
|
untar = func(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
|
||||||
|
return originalUntar(io.TeeReader(tarArchive, hasher), dest, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(tarArchive io.ReadCloser, dest string) error {
|
||||||
|
err := untar(tarArchive, dest, options)
|
||||||
|
if err2 := tarArchive.Close(); err2 != nil {
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isRegistryBlocked checks if the named registry is marked as blocked
|
// isRegistryBlocked checks if the named registry is marked as blocked
|
||||||
|
|
|
@ -106,13 +106,19 @@ func ResolveName(name string, firstRegistry string, sc *types.SystemContext, sto
|
||||||
|
|
||||||
// Figure out the list of registries.
|
// Figure out the list of registries.
|
||||||
var registries []string
|
var registries []string
|
||||||
searchRegistries, err := sysregistriesv2.FindUnqualifiedSearchRegistries(sc)
|
searchRegistries, err := sysregistriesv2.UnqualifiedSearchRegistries(sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("unable to read configured registries to complete %q: %v", name, err)
|
logrus.Debugf("unable to read configured registries to complete %q: %v", name, err)
|
||||||
|
searchRegistries = nil
|
||||||
}
|
}
|
||||||
for _, registry := range searchRegistries {
|
for _, registry := range searchRegistries {
|
||||||
if !registry.Blocked {
|
reg, err := sysregistriesv2.FindRegistry(sc, registry)
|
||||||
registries = append(registries, registry.Location)
|
if err != nil {
|
||||||
|
logrus.Debugf("unable to read registry configuraitno for %#v: %v", registry, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if reg == nil || !reg.Blocked {
|
||||||
|
registries = append(registries, registry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchRegistriesAreEmpty := len(registries) == 0
|
searchRegistriesAreEmpty := len(registries) == 0
|
||||||
|
@ -257,6 +263,36 @@ func StringInSlice(s string, slice []string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContainerIDs uses ID mappings to compute the container-level IDs that will
|
||||||
|
// correspond to a UID/GID pair on the host.
|
||||||
|
func GetContainerIDs(uidmap, gidmap []specs.LinuxIDMapping, uid, gid uint32) (uint32, uint32, error) {
|
||||||
|
uidMapped := true
|
||||||
|
for _, m := range uidmap {
|
||||||
|
uidMapped = false
|
||||||
|
if uid >= m.HostID && uid < m.HostID+m.Size {
|
||||||
|
uid = (uid - m.HostID) + m.ContainerID
|
||||||
|
uidMapped = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !uidMapped {
|
||||||
|
return 0, 0, errors.Errorf("container uses ID mappings (%#v), but doesn't map UID %d", uidmap, uid)
|
||||||
|
}
|
||||||
|
gidMapped := true
|
||||||
|
for _, m := range gidmap {
|
||||||
|
gidMapped = false
|
||||||
|
if gid >= m.HostID && gid < m.HostID+m.Size {
|
||||||
|
gid = (gid - m.HostID) + m.ContainerID
|
||||||
|
gidMapped = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !gidMapped {
|
||||||
|
return 0, 0, errors.Errorf("container uses ID mappings (%#v), but doesn't map GID %d", gidmap, gid)
|
||||||
|
}
|
||||||
|
return uid, gid, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetHostIDs uses ID mappings to compute the host-level IDs that will
|
// GetHostIDs uses ID mappings to compute the host-level IDs that will
|
||||||
// correspond to a UID/GID pair in the container.
|
// correspond to a UID/GID pair in the container.
|
||||||
func GetHostIDs(uidmap, gidmap []specs.LinuxIDMapping, uid, gid uint32) (uint32, uint32, error) {
|
func GetHostIDs(uidmap, gidmap []specs.LinuxIDMapping, uid, gid uint32) (uint32, uint32, error) {
|
||||||
|
@ -270,7 +306,7 @@ func GetHostIDs(uidmap, gidmap []specs.LinuxIDMapping, uid, gid uint32) (uint32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !uidMapped {
|
if !uidMapped {
|
||||||
return 0, 0, errors.Errorf("container uses ID mappings, but doesn't map UID %d", uid)
|
return 0, 0, errors.Errorf("container uses ID mappings (%#v), but doesn't map UID %d", uidmap, uid)
|
||||||
}
|
}
|
||||||
gidMapped := true
|
gidMapped := true
|
||||||
for _, m := range gidmap {
|
for _, m := range gidmap {
|
||||||
|
@ -282,7 +318,7 @@ func GetHostIDs(uidmap, gidmap []specs.LinuxIDMapping, uid, gid uint32) (uint32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !gidMapped {
|
if !gidMapped {
|
||||||
return 0, 0, errors.Errorf("container uses ID mappings, but doesn't map GID %d", gid)
|
return 0, 0, errors.Errorf("container uses ID mappings (%#v), but doesn't map GID %d", gidmap, gid)
|
||||||
}
|
}
|
||||||
return uid, gid, nil
|
return uid, gid, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ github.com/blang/semver v3.5.0
|
||||||
github.com/BurntSushi/toml v0.2.0
|
github.com/BurntSushi/toml v0.2.0
|
||||||
github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d
|
github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d
|
||||||
github.com/containernetworking/cni v0.7.0-rc2
|
github.com/containernetworking/cni v0.7.0-rc2
|
||||||
github.com/containers/image 9467ac9cfd92c545aa389f22f27e552de053c0f2
|
github.com/containers/image v2.0.0
|
||||||
github.com/cyphar/filepath-securejoin v0.2.1
|
github.com/cyphar/filepath-securejoin v0.2.1
|
||||||
github.com/vbauerster/mpb v3.3.4
|
github.com/vbauerster/mpb v3.3.4
|
||||||
github.com/mattn/go-isatty v0.0.4
|
github.com/mattn/go-isatty v0.0.4
|
||||||
github.com/VividCortex/ewma v1.1.1
|
github.com/VividCortex/ewma v1.1.1
|
||||||
github.com/containers/storage v1.12.7
|
github.com/containers/storage v1.12.10
|
||||||
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
|
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
|
||||||
github.com/docker/docker 54dddadc7d5d89fe0be88f76979f6f6ab0dede83
|
github.com/docker/docker 54dddadc7d5d89fe0be88f76979f6f6ab0dede83
|
||||||
github.com/docker/docker-credential-helpers v0.6.1
|
github.com/docker/docker-credential-helpers v0.6.1
|
||||||
|
|
|
@ -29,44 +29,16 @@ type dockerImageSource struct {
|
||||||
cachedManifestMIMEType string // Only valid if cachedManifest != nil
|
cachedManifestMIMEType string // Only valid if cachedManifest != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newImageSource creates a new `ImageSource` for the specified image reference
|
// newImageSource creates a new ImageSource for the specified image reference.
|
||||||
// `ref`.
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
//
|
|
||||||
// The following steps will be done during the instance creation:
|
|
||||||
//
|
|
||||||
// - Lookup the registry within the configured location in
|
|
||||||
// `sys.SystemRegistriesConfPath`. If there is no configured registry available,
|
|
||||||
// we fallback to the provided docker reference `ref`.
|
|
||||||
//
|
|
||||||
// - References which contain a configured prefix will be automatically rewritten
|
|
||||||
// to the correct target reference. For example, if the configured
|
|
||||||
// `prefix = "example.com/foo"`, `location = "example.com"` and the image will be
|
|
||||||
// pulled from the ref `example.com/foo/image`, then the resulting pull will
|
|
||||||
// effectively point to `example.com/image`.
|
|
||||||
//
|
|
||||||
// - If the rewritten reference succeeds, it will be used as the `dockerRef`
|
|
||||||
// in the client. If the rewrite fails, the function immediately returns an error.
|
|
||||||
//
|
|
||||||
// - Each mirror will be used (in the configured order) to test the
|
|
||||||
// availability of the image manifest on the remote location. For example,
|
|
||||||
// if the manifest is not reachable due to connectivity issues, then the next
|
|
||||||
// mirror will be tested instead. If no mirror is configured or contains the
|
|
||||||
// target manifest, then the initial `ref` will be tested as fallback. The
|
|
||||||
// creation of the new `dockerImageSource` only succeeds if a remote
|
|
||||||
// location with the available manifest was found.
|
|
||||||
//
|
|
||||||
// A cleanup call to `.Close()` is needed if the caller is done using the returned
|
|
||||||
// `ImageSource`.
|
|
||||||
func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerReference) (*dockerImageSource, error) {
|
func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerReference) (*dockerImageSource, error) {
|
||||||
registry, err := sysregistriesv2.FindRegistry(sys, ref.ref.Name())
|
registry, err := sysregistriesv2.FindRegistry(sys, ref.ref.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error loading registries configuration")
|
return nil, errors.Wrapf(err, "error loading registries configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
if registry == nil {
|
if registry == nil {
|
||||||
// No configuration was found for the provided reference, so we create
|
// No configuration was found for the provided reference, so use the
|
||||||
// a fallback registry by hand to make the client creation below work
|
// equivalent of a default configuration.
|
||||||
// as intended.
|
|
||||||
registry = &sysregistriesv2.Registry{
|
registry = &sysregistriesv2.Registry{
|
||||||
Endpoint: sysregistriesv2.Endpoint{
|
Endpoint: sysregistriesv2.Endpoint{
|
||||||
Location: ref.ref.String(),
|
Location: ref.ref.String(),
|
||||||
|
@ -76,18 +48,19 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef
|
||||||
}
|
}
|
||||||
|
|
||||||
primaryDomain := reference.Domain(ref.ref)
|
primaryDomain := reference.Domain(ref.ref)
|
||||||
// Found the registry within the sysregistriesv2 configuration. Now we test
|
// Check all endpoints for the manifest availability. If we find one that does
|
||||||
// all endpoints for the manifest availability. If a working image source
|
// contain the image, it will be used for all future pull actions. Always try the
|
||||||
// was found, it will be used for all future pull actions.
|
// non-mirror original location last; this both transparently handles the case
|
||||||
|
// of no mirrors configured, and ensures we return the error encountered when
|
||||||
|
// acessing the upstream location if all endpoints fail.
|
||||||
manifestLoadErr := errors.New("Internal error: newImageSource returned without trying any endpoint")
|
manifestLoadErr := errors.New("Internal error: newImageSource returned without trying any endpoint")
|
||||||
for _, endpoint := range append(registry.Mirrors, registry.Endpoint) {
|
pullSources, err := registry.PullSourcesFromReference(ref.ref)
|
||||||
logrus.Debugf("Trying to pull %q from endpoint %q", ref.ref, endpoint.Location)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
newRef, err := endpoint.RewriteReference(ref.ref, registry.Prefix)
|
}
|
||||||
if err != nil {
|
for _, pullSource := range pullSources {
|
||||||
return nil, err
|
logrus.Debugf("Trying to pull %q", pullSource.Reference)
|
||||||
}
|
dockerRef, err := newReference(pullSource.Reference)
|
||||||
dockerRef, err := newReference(newRef)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -104,7 +77,7 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client.tlsClientConfig.InsecureSkipVerify = endpoint.Insecure
|
client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure
|
||||||
|
|
||||||
testImageSource := &dockerImageSource{
|
testImageSource := &dockerImageSource{
|
||||||
ref: dockerRef,
|
ref: dockerRef,
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
This is a copy of github.com/docker/distribution/reference as of commit fb0bebc4b64e3881cc52a2478d749845ed76d2a8,
|
This is a copy of github.com/docker/distribution/reference as of commit 3226863cbcba6dbc2f6c83a37b28126c934af3f8,
|
||||||
except that ParseAnyReferenceWithSet has been removed to drop the dependency on github.com/docker/distribution/digestset.
|
except that ParseAnyReferenceWithSet has been removed to drop the dependency on github.com/docker/distribution/digestset.
|
|
@ -55,6 +55,35 @@ func ParseNormalizedNamed(s string) (Named, error) {
|
||||||
return named, nil
|
return named, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseDockerRef normalizes the image reference following the docker convention. This is added
|
||||||
|
// mainly for backward compatibility.
|
||||||
|
// The reference returned can only be either tagged or digested. For reference contains both tag
|
||||||
|
// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@
|
||||||
|
// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as
|
||||||
|
// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa.
|
||||||
|
func ParseDockerRef(ref string) (Named, error) {
|
||||||
|
named, err := ParseNormalizedNamed(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, ok := named.(NamedTagged); ok {
|
||||||
|
if canonical, ok := named.(Canonical); ok {
|
||||||
|
// The reference is both tagged and digested, only
|
||||||
|
// return digested.
|
||||||
|
newNamed, err := WithName(canonical.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newCanonical, err := WithDigest(newNamed, canonical.Digest())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newCanonical, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TagNameOnly(named), nil
|
||||||
|
}
|
||||||
|
|
||||||
// splitDockerDomain splits a repository name to domain and remotename string.
|
// splitDockerDomain splits a repository name to domain and remotename string.
|
||||||
// If no valid domain is found, the default domain is used. Repository name
|
// If no valid domain is found, the default domain is used. Repository name
|
||||||
// needs to be already validated before.
|
// needs to be already validated before.
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
// tag := /[\w][\w.-]{0,127}/
|
// tag := /[\w][\w.-]{0,127}/
|
||||||
//
|
//
|
||||||
// digest := digest-algorithm ":" digest-hex
|
// digest := digest-algorithm ":" digest-hex
|
||||||
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]
|
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]*
|
||||||
// digest-algorithm-separator := /[+.-_]/
|
// digest-algorithm-separator := /[+.-_]/
|
||||||
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
||||||
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
||||||
|
@ -205,7 +205,7 @@ func Parse(s string) (Reference, error) {
|
||||||
var repo repository
|
var repo repository
|
||||||
|
|
||||||
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
|
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
|
||||||
if nameMatch != nil && len(nameMatch) == 3 {
|
if len(nameMatch) == 3 {
|
||||||
repo.domain = nameMatch[1]
|
repo.domain = nameMatch[1]
|
||||||
repo.path = nameMatch[2]
|
repo.path = nameMatch[2]
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,15 +20,15 @@ var (
|
||||||
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
||||||
|
|
||||||
// domainComponentRegexp restricts the registry domain component of a
|
// domainComponentRegexp restricts the registry domain component of a
|
||||||
// repository name to start with a component as defined by domainRegexp
|
// repository name to start with a component as defined by DomainRegexp
|
||||||
// and followed by an optional port.
|
// and followed by an optional port.
|
||||||
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||||
|
|
||||||
// domainRegexp defines the structure of potential domain components
|
// DomainRegexp defines the structure of potential domain components
|
||||||
// that may be part of image names. This is purposely a subset of what is
|
// that may be part of image names. This is purposely a subset of what is
|
||||||
// allowed by DNS to ensure backwards compatibility with Docker image
|
// allowed by DNS to ensure backwards compatibility with Docker image
|
||||||
// names.
|
// names.
|
||||||
domainRegexp = expression(
|
DomainRegexp = expression(
|
||||||
domainComponentRegexp,
|
domainComponentRegexp,
|
||||||
optional(repeated(literal(`.`), domainComponentRegexp)),
|
optional(repeated(literal(`.`), domainComponentRegexp)),
|
||||||
optional(literal(`:`), match(`[0-9]+`)))
|
optional(literal(`:`), match(`[0-9]+`)))
|
||||||
|
@ -51,14 +51,14 @@ var (
|
||||||
// regexp has capturing groups for the domain and name part omitting
|
// regexp has capturing groups for the domain and name part omitting
|
||||||
// the separating forward slash from either.
|
// the separating forward slash from either.
|
||||||
NameRegexp = expression(
|
NameRegexp = expression(
|
||||||
optional(domainRegexp, literal(`/`)),
|
optional(DomainRegexp, literal(`/`)),
|
||||||
nameComponentRegexp,
|
nameComponentRegexp,
|
||||||
optional(repeated(literal(`/`), nameComponentRegexp)))
|
optional(repeated(literal(`/`), nameComponentRegexp)))
|
||||||
|
|
||||||
// anchoredNameRegexp is used to parse a name value, capturing the
|
// anchoredNameRegexp is used to parse a name value, capturing the
|
||||||
// domain and trailing components.
|
// domain and trailing components.
|
||||||
anchoredNameRegexp = anchored(
|
anchoredNameRegexp = anchored(
|
||||||
optional(capture(domainRegexp), literal(`/`)),
|
optional(capture(DomainRegexp), literal(`/`)),
|
||||||
capture(nameComponentRegexp,
|
capture(nameComponentRegexp,
|
||||||
optional(repeated(literal(`/`), nameComponentRegexp))))
|
optional(repeated(literal(`/`), nameComponentRegexp))))
|
||||||
|
|
||||||
|
|
224
vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
generated
vendored
224
vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
generated
vendored
|
@ -5,6 +5,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -35,10 +36,10 @@ type Endpoint struct {
|
||||||
Insecure bool `toml:"insecure"`
|
Insecure bool `toml:"insecure"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RewriteReference will substitute the provided reference `prefix` to the
|
// rewriteReference will substitute the provided reference `prefix` to the
|
||||||
// endpoints `location` from the `ref` and creates a new named reference from it.
|
// endpoints `location` from the `ref` and creates a new named reference from it.
|
||||||
// The function errors if the newly created reference is not parsable.
|
// The function errors if the newly created reference is not parsable.
|
||||||
func (e *Endpoint) RewriteReference(ref reference.Named, prefix string) (reference.Named, error) {
|
func (e *Endpoint) rewriteReference(ref reference.Named, prefix string) (reference.Named, error) {
|
||||||
refString := ref.String()
|
refString := ref.String()
|
||||||
if !refMatchesPrefix(refString, prefix) {
|
if !refMatchesPrefix(refString, prefix) {
|
||||||
return nil, fmt.Errorf("invalid prefix '%v' for reference '%v'", prefix, refString)
|
return nil, fmt.Errorf("invalid prefix '%v' for reference '%v'", prefix, refString)
|
||||||
|
@ -61,8 +62,10 @@ type Registry struct {
|
||||||
Mirrors []Endpoint `toml:"mirror"`
|
Mirrors []Endpoint `toml:"mirror"`
|
||||||
// If true, pulling from the registry will be blocked.
|
// If true, pulling from the registry will be blocked.
|
||||||
Blocked bool `toml:"blocked"`
|
Blocked bool `toml:"blocked"`
|
||||||
// If true, the registry can be used when pulling an unqualified image.
|
// If true, mirrors will only be used for digest pulls. Pulling images by
|
||||||
Search bool `toml:"unqualified-search"`
|
// tag can potentially yield different images, depending on which endpoint
|
||||||
|
// we pull from. Forcing digest-pulls for mirrors avoids that issue.
|
||||||
|
MirrorByDigestOnly bool `toml:"mirror-by-digest-only"`
|
||||||
// Prefix is used for matching images, and to translate one namespace to
|
// Prefix is used for matching images, and to translate one namespace to
|
||||||
// another. If `Prefix="example.com/bar"`, `location="example.com/foo/bar"`
|
// another. If `Prefix="example.com/bar"`, `location="example.com/foo/bar"`
|
||||||
// and we pull from "example.com/bar/myimage:latest", the image will
|
// and we pull from "example.com/bar/myimage:latest", the image will
|
||||||
|
@ -71,6 +74,41 @@ type Registry struct {
|
||||||
Prefix string `toml:"prefix"`
|
Prefix string `toml:"prefix"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PullSource consists of an Endpoint and a Reference. Note that the reference is
|
||||||
|
// rewritten according to the registries prefix and the Endpoint's location.
|
||||||
|
type PullSource struct {
|
||||||
|
Endpoint Endpoint
|
||||||
|
Reference reference.Named
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullSourcesFromReference returns a slice of PullSource's based on the passed
|
||||||
|
// reference.
|
||||||
|
func (r *Registry) PullSourcesFromReference(ref reference.Named) ([]PullSource, error) {
|
||||||
|
var endpoints []Endpoint
|
||||||
|
|
||||||
|
if r.MirrorByDigestOnly {
|
||||||
|
// Only use mirrors when the reference is a digest one.
|
||||||
|
if _, isDigested := ref.(reference.Canonical); isDigested {
|
||||||
|
endpoints = append(r.Mirrors, r.Endpoint)
|
||||||
|
} else {
|
||||||
|
endpoints = []Endpoint{r.Endpoint}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
endpoints = append(r.Mirrors, r.Endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
sources := []PullSource{}
|
||||||
|
for _, ep := range endpoints {
|
||||||
|
rewritten, err := ep.rewriteReference(ref, r.Prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sources = append(sources, PullSource{Endpoint: ep, Reference: rewritten})
|
||||||
|
}
|
||||||
|
|
||||||
|
return sources, nil
|
||||||
|
}
|
||||||
|
|
||||||
// V1TOMLregistries is for backwards compatibility to sysregistries v1
|
// V1TOMLregistries is for backwards compatibility to sysregistries v1
|
||||||
type V1TOMLregistries struct {
|
type V1TOMLregistries struct {
|
||||||
Registries []string `toml:"registries"`
|
Registries []string `toml:"registries"`
|
||||||
|
@ -83,11 +121,35 @@ type V1TOMLConfig struct {
|
||||||
Block V1TOMLregistries `toml:"block"`
|
Block V1TOMLregistries `toml:"block"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// V1RegistriesConf is the sysregistries v1 configuration format.
|
||||||
|
type V1RegistriesConf struct {
|
||||||
|
V1TOMLConfig `toml:"registries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nonempty returns true if config contains at least one configuration entry.
|
||||||
|
func (config *V1RegistriesConf) Nonempty() bool {
|
||||||
|
return (len(config.V1TOMLConfig.Search.Registries) != 0 ||
|
||||||
|
len(config.V1TOMLConfig.Insecure.Registries) != 0 ||
|
||||||
|
len(config.V1TOMLConfig.Block.Registries) != 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2RegistriesConf is the sysregistries v2 configuration format.
|
||||||
|
type V2RegistriesConf struct {
|
||||||
|
Registries []Registry `toml:"registry"`
|
||||||
|
// An array of host[:port] (not prefix!) entries to use for resolving unqualified image references
|
||||||
|
UnqualifiedSearchRegistries []string `toml:"unqualified-search-registries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nonempty returns true if config contains at least one configuration entry.
|
||||||
|
func (config *V2RegistriesConf) Nonempty() bool {
|
||||||
|
return (len(config.Registries) != 0 ||
|
||||||
|
len(config.UnqualifiedSearchRegistries) != 0)
|
||||||
|
}
|
||||||
|
|
||||||
// tomlConfig is the data type used to unmarshal the toml config.
|
// tomlConfig is the data type used to unmarshal the toml config.
|
||||||
type tomlConfig struct {
|
type tomlConfig struct {
|
||||||
Registries []Registry `toml:"registry"`
|
V2RegistriesConf
|
||||||
// backwards compatability to sysregistries v1
|
V1RegistriesConf // for backwards compatibility with sysregistries v1
|
||||||
V1TOMLConfig `toml:"registries"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvalidRegistries represents an invalid registry configurations. An example
|
// InvalidRegistries represents an invalid registry configurations. An example
|
||||||
|
@ -120,12 +182,10 @@ func parseLocation(input string) (string, error) {
|
||||||
return trimmed, nil
|
return trimmed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getV1Registries transforms v1 registries in the config into an array of v2
|
// ConvertToV2 returns a v2 config corresponding to a v1 one.
|
||||||
// registries of type Registry.
|
func (config *V1RegistriesConf) ConvertToV2() (*V2RegistriesConf, error) {
|
||||||
func getV1Registries(config *tomlConfig) ([]Registry, error) {
|
|
||||||
regMap := make(map[string]*Registry)
|
regMap := make(map[string]*Registry)
|
||||||
// We must preserve the order of config.V1Registries.Search.Registries at least. The order of the
|
// The order of the registries is not really important, but make it deterministic (the same for the same config file)
|
||||||
// other registries is not really important, but make it deterministic (the same for the same config file)
|
|
||||||
// to minimize behavior inconsistency and not contribute to difficult-to-reproduce situations.
|
// to minimize behavior inconsistency and not contribute to difficult-to-reproduce situations.
|
||||||
registryOrder := []string{}
|
registryOrder := []string{}
|
||||||
|
|
||||||
|
@ -148,15 +208,6 @@ func getV1Registries(config *tomlConfig) ([]Registry, error) {
|
||||||
return reg, nil
|
return reg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: config.V1Registries.Search needs to be processed first to ensure registryOrder is populated in the right order
|
|
||||||
// if one of the search registries is also in one of the other lists.
|
|
||||||
for _, search := range config.V1TOMLConfig.Search.Registries {
|
|
||||||
reg, err := getRegistry(search)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
reg.Search = true
|
|
||||||
}
|
|
||||||
for _, blocked := range config.V1TOMLConfig.Block.Registries {
|
for _, blocked := range config.V1TOMLConfig.Block.Registries {
|
||||||
reg, err := getRegistry(blocked)
|
reg, err := getRegistry(blocked)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -172,28 +223,31 @@ func getV1Registries(config *tomlConfig) ([]Registry, error) {
|
||||||
reg.Insecure = true
|
reg.Insecure = true
|
||||||
}
|
}
|
||||||
|
|
||||||
registries := []Registry{}
|
res := &V2RegistriesConf{
|
||||||
|
UnqualifiedSearchRegistries: config.V1TOMLConfig.Search.Registries,
|
||||||
|
}
|
||||||
for _, location := range registryOrder {
|
for _, location := range registryOrder {
|
||||||
reg := regMap[location]
|
reg := regMap[location]
|
||||||
registries = append(registries, *reg)
|
res.Registries = append(res.Registries, *reg)
|
||||||
}
|
}
|
||||||
return registries, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// postProcessRegistries checks the consistency of all registries (e.g., set
|
// anchoredDomainRegexp is an internal implementation detail of postProcess, defining the valid values of elements of UnqualifiedSearchRegistries.
|
||||||
// the Prefix to Location if not set) and applies conflict checks. It returns an
|
var anchoredDomainRegexp = regexp.MustCompile("^" + reference.DomainRegexp.String() + "$")
|
||||||
// array of cleaned registries and error in case of conflicts.
|
|
||||||
func postProcessRegistries(regs []Registry) ([]Registry, error) {
|
|
||||||
var registries []Registry
|
|
||||||
regMap := make(map[string][]Registry)
|
|
||||||
|
|
||||||
for _, reg := range regs {
|
// postProcess checks the consistency of all the configuration, looks for conflicts,
|
||||||
var err error
|
// and normalizes the configuration (e.g., sets the Prefix to Location if not set).
|
||||||
|
func (config *V2RegistriesConf) postProcess() error {
|
||||||
|
regMap := make(map[string][]*Registry)
|
||||||
|
|
||||||
|
for i := range config.Registries {
|
||||||
|
reg := &config.Registries[i]
|
||||||
// make sure Location and Prefix are valid
|
// make sure Location and Prefix are valid
|
||||||
|
var err error
|
||||||
reg.Location, err = parseLocation(reg.Location)
|
reg.Location, err = parseLocation(reg.Location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if reg.Prefix == "" {
|
if reg.Prefix == "" {
|
||||||
|
@ -201,7 +255,7 @@ func postProcessRegistries(regs []Registry) ([]Registry, error) {
|
||||||
} else {
|
} else {
|
||||||
reg.Prefix, err = parseLocation(reg.Prefix)
|
reg.Prefix, err = parseLocation(reg.Prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,10 +263,9 @@ func postProcessRegistries(regs []Registry) ([]Registry, error) {
|
||||||
for _, mir := range reg.Mirrors {
|
for _, mir := range reg.Mirrors {
|
||||||
mir.Location, err = parseLocation(mir.Location)
|
mir.Location, err = parseLocation(mir.Location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
registries = append(registries, reg)
|
|
||||||
regMap[reg.Location] = append(regMap[reg.Location], reg)
|
regMap[reg.Location] = append(regMap[reg.Location], reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,22 +275,32 @@ func postProcessRegistries(regs []Registry) ([]Registry, error) {
|
||||||
//
|
//
|
||||||
// Note: we need to iterate over the registries array to ensure a
|
// Note: we need to iterate over the registries array to ensure a
|
||||||
// deterministic behavior which is not guaranteed by maps.
|
// deterministic behavior which is not guaranteed by maps.
|
||||||
for _, reg := range registries {
|
for _, reg := range config.Registries {
|
||||||
others, _ := regMap[reg.Location]
|
others, _ := regMap[reg.Location]
|
||||||
for _, other := range others {
|
for _, other := range others {
|
||||||
if reg.Insecure != other.Insecure {
|
if reg.Insecure != other.Insecure {
|
||||||
msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'insecure' setting", reg.Location)
|
msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'insecure' setting", reg.Location)
|
||||||
|
return &InvalidRegistries{s: msg}
|
||||||
return nil, &InvalidRegistries{s: msg}
|
|
||||||
}
|
}
|
||||||
if reg.Blocked != other.Blocked {
|
if reg.Blocked != other.Blocked {
|
||||||
msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'blocked' setting", reg.Location)
|
msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'blocked' setting", reg.Location)
|
||||||
return nil, &InvalidRegistries{s: msg}
|
return &InvalidRegistries{s: msg}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return registries, nil
|
for i := range config.UnqualifiedSearchRegistries {
|
||||||
|
registry, err := parseLocation(config.UnqualifiedSearchRegistries[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !anchoredDomainRegexp.MatchString(registry) {
|
||||||
|
return &InvalidRegistries{fmt.Sprintf("Invalid unqualified-search-registries entry %#v", registry)}
|
||||||
|
}
|
||||||
|
config.UnqualifiedSearchRegistries[i] = registry
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getConfigPath returns the system-registries config path if specified.
|
// getConfigPath returns the system-registries config path if specified.
|
||||||
|
@ -260,7 +323,7 @@ var configMutex = sync.Mutex{}
|
||||||
// configCache caches already loaded configs with config paths as keys and is
|
// configCache caches already loaded configs with config paths as keys and is
|
||||||
// used to avoid redudantly parsing configs. Concurrent accesses to the cache
|
// used to avoid redudantly parsing configs. Concurrent accesses to the cache
|
||||||
// are synchronized via configMutex.
|
// are synchronized via configMutex.
|
||||||
var configCache = make(map[string][]Registry)
|
var configCache = make(map[string]*V2RegistriesConf)
|
||||||
|
|
||||||
// InvalidateCache invalidates the registry cache. This function is meant to be
|
// InvalidateCache invalidates the registry cache. This function is meant to be
|
||||||
// used for long-running processes that need to reload potential changes made to
|
// used for long-running processes that need to reload potential changes made to
|
||||||
|
@ -268,20 +331,18 @@ var configCache = make(map[string][]Registry)
|
||||||
func InvalidateCache() {
|
func InvalidateCache() {
|
||||||
configMutex.Lock()
|
configMutex.Lock()
|
||||||
defer configMutex.Unlock()
|
defer configMutex.Unlock()
|
||||||
configCache = make(map[string][]Registry)
|
configCache = make(map[string]*V2RegistriesConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRegistries loads and returns the registries specified in the config.
|
// getConfig returns the config object corresponding to ctx, loading it if it is not yet cached.
|
||||||
// Note the parsed content of registry config files is cached. For reloading,
|
func getConfig(ctx *types.SystemContext) (*V2RegistriesConf, error) {
|
||||||
// use `InvalidateCache` and re-call `GetRegistries`.
|
|
||||||
func GetRegistries(ctx *types.SystemContext) ([]Registry, error) {
|
|
||||||
configPath := getConfigPath(ctx)
|
configPath := getConfigPath(ctx)
|
||||||
|
|
||||||
configMutex.Lock()
|
configMutex.Lock()
|
||||||
defer configMutex.Unlock()
|
defer configMutex.Unlock()
|
||||||
// if the config has already been loaded, return the cached registries
|
// if the config has already been loaded, return the cached registries
|
||||||
if registries, inCache := configCache[configPath]; inCache {
|
if config, inCache := configCache[configPath]; inCache {
|
||||||
return registries, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// load the config
|
// load the config
|
||||||
|
@ -292,51 +353,53 @@ func GetRegistries(ctx *types.SystemContext) ([]Registry, error) {
|
||||||
// isn't set. Note: if ctx.SystemRegistriesConfPath points to
|
// isn't set. Note: if ctx.SystemRegistriesConfPath points to
|
||||||
// the default config, we will still return an error.
|
// the default config, we will still return an error.
|
||||||
if os.IsNotExist(err) && (ctx == nil || ctx.SystemRegistriesConfPath == "") {
|
if os.IsNotExist(err) && (ctx == nil || ctx.SystemRegistriesConfPath == "") {
|
||||||
return []Registry{}, nil
|
return &V2RegistriesConf{Registries: []Registry{}}, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
registries := config.Registries
|
v2Config := &config.V2RegistriesConf
|
||||||
|
|
||||||
// backwards compatibility for v1 configs
|
// backwards compatibility for v1 configs
|
||||||
v1Registries, err := getV1Registries(config)
|
if config.V1RegistriesConf.Nonempty() {
|
||||||
if err != nil {
|
if config.V2RegistriesConf.Nonempty() {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(v1Registries) > 0 {
|
|
||||||
if len(registries) > 0 {
|
|
||||||
return nil, &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"}
|
return nil, &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"}
|
||||||
}
|
}
|
||||||
registries = v1Registries
|
v2, err := config.V1RegistriesConf.ConvertToV2()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v2Config = v2
|
||||||
}
|
}
|
||||||
|
|
||||||
registries, err = postProcessRegistries(registries)
|
if err := v2Config.postProcess(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate the cache
|
// populate the cache
|
||||||
configCache[configPath] = registries
|
configCache[configPath] = v2Config
|
||||||
|
return v2Config, nil
|
||||||
return registries, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindUnqualifiedSearchRegistries returns all registries that are configured
|
// GetRegistries loads and returns the registries specified in the config.
|
||||||
// for unqualified image search (i.e., with Registry.Search == true).
|
// Note the parsed content of registry config files is cached. For reloading,
|
||||||
func FindUnqualifiedSearchRegistries(ctx *types.SystemContext) ([]Registry, error) {
|
// use `InvalidateCache` and re-call `GetRegistries`.
|
||||||
registries, err := GetRegistries(ctx)
|
func GetRegistries(ctx *types.SystemContext) ([]Registry, error) {
|
||||||
|
config, err := getConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return config.Registries, nil
|
||||||
|
}
|
||||||
|
|
||||||
unqualified := []Registry{}
|
// UnqualifiedSearchRegistries returns a list of host[:port] entries to try
|
||||||
for _, reg := range registries {
|
// for unqualified image search, in the returned order)
|
||||||
if reg.Search {
|
func UnqualifiedSearchRegistries(ctx *types.SystemContext) ([]string, error) {
|
||||||
unqualified = append(unqualified, reg)
|
config, err := getConfig(ctx)
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return unqualified, nil
|
return config.UnqualifiedSearchRegistries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// refMatchesPrefix returns true iff ref,
|
// refMatchesPrefix returns true iff ref,
|
||||||
|
@ -371,14 +434,14 @@ func refMatchesPrefix(ref, prefix string) bool {
|
||||||
// — note that this requires the name to start with an explicit hostname!).
|
// — note that this requires the name to start with an explicit hostname!).
|
||||||
// If no Registry prefixes the image, nil is returned.
|
// If no Registry prefixes the image, nil is returned.
|
||||||
func FindRegistry(ctx *types.SystemContext, ref string) (*Registry, error) {
|
func FindRegistry(ctx *types.SystemContext, ref string) (*Registry, error) {
|
||||||
registries, err := GetRegistries(ctx)
|
config, err := getConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
reg := Registry{}
|
reg := Registry{}
|
||||||
prefixLen := 0
|
prefixLen := 0
|
||||||
for _, r := range registries {
|
for _, r := range config.Registries {
|
||||||
if refMatchesPrefix(ref, r.Prefix) {
|
if refMatchesPrefix(ref, r.Prefix) {
|
||||||
length := len(r.Prefix)
|
length := len(r.Prefix)
|
||||||
if length > prefixLen {
|
if length > prefixLen {
|
||||||
|
@ -393,21 +456,12 @@ func FindRegistry(ctx *types.SystemContext, ref string) (*Registry, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads the global registry file from the filesystem. Returns a byte array.
|
|
||||||
func readRegistryConf(configPath string) ([]byte, error) {
|
|
||||||
configBytes, err := ioutil.ReadFile(configPath)
|
|
||||||
return configBytes, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used in unittests to parse custom configs without a types.SystemContext.
|
|
||||||
var readConf = readRegistryConf
|
|
||||||
|
|
||||||
// Loads the registry configuration file from the filesystem and then unmarshals
|
// Loads the registry configuration file from the filesystem and then unmarshals
|
||||||
// it. Returns the unmarshalled object.
|
// it. Returns the unmarshalled object.
|
||||||
func loadRegistryConf(configPath string) (*tomlConfig, error) {
|
func loadRegistryConf(configPath string) (*tomlConfig, error) {
|
||||||
config := &tomlConfig{}
|
config := &tomlConfig{}
|
||||||
|
|
||||||
configBytes, err := readConf(configPath)
|
configBytes, err := ioutil.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,14 @@ import "fmt"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// VersionMajor is for an API incompatible changes
|
// VersionMajor is for an API incompatible changes
|
||||||
VersionMajor = 1
|
VersionMajor = 2
|
||||||
// VersionMinor is for functionality in a backwards-compatible manner
|
// VersionMinor is for functionality in a backwards-compatible manner
|
||||||
VersionMinor = 7
|
VersionMinor = 0
|
||||||
// VersionPatch is for backwards-compatible bug fixes
|
// VersionPatch is for backwards-compatible bug fixes
|
||||||
VersionPatch = 0
|
VersionPatch = 0
|
||||||
|
|
||||||
// VersionDev indicates development branch. Releases will be empty string.
|
// VersionDev indicates development branch. Releases will be empty string.
|
||||||
VersionDev = "-dev"
|
VersionDev = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the specification version that the package types support.
|
// Version is the specification version that the package types support.
|
||||||
|
|
Loading…
Reference in New Issue