Merge pull request #23721 from containers/renovate/github.com-openshift-imagebuilder-1.x

Update module github.com/openshift/imagebuilder to v1.2.15
This commit is contained in:
openshift-merge-bot[bot] 2024-08-22 16:58:04 +00:00 committed by GitHub
commit 7156f60fee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 1378 additions and 371 deletions

9
go.mod
View File

@ -59,7 +59,7 @@ require (
github.com/opencontainers/runtime-spec v1.2.0 github.com/opencontainers/runtime-spec v1.2.0
github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc
github.com/opencontainers/selinux v1.11.0 github.com/opencontainers/selinux v1.11.0
github.com/openshift/imagebuilder v1.2.14 github.com/openshift/imagebuilder v1.2.15
github.com/rootless-containers/rootlesskit/v2 v2.2.0 github.com/rootless-containers/rootlesskit/v2 v2.2.0
github.com/shirou/gopsutil/v3 v3.24.5 github.com/shirou/gopsutil/v3 v3.24.5
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
@ -97,9 +97,10 @@ require (
github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/chzyer/readline v1.5.1 // indirect github.com/chzyer/readline v1.5.1 // indirect
github.com/containerd/cgroups/v3 v3.0.3 // indirect github.com/containerd/cgroups/v3 v3.0.3 // indirect
github.com/containerd/containerd v1.7.18 // indirect github.com/containerd/containerd v1.7.20 // indirect
github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/errdefs v0.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/containerd/typeurl/v2 v2.1.1 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/containernetworking/cni v1.2.3 // indirect github.com/containernetworking/cni v1.2.3 // indirect
@ -115,7 +116,7 @@ require (
github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fsouza/go-dockerclient v1.11.1 // indirect github.com/fsouza/go-dockerclient v1.11.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect github.com/gin-gonic/gin v1.9.1 // indirect
@ -167,7 +168,7 @@ require (
github.com/miekg/pkcs11 v1.1.1 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/buildkit v0.12.5 // indirect github.com/moby/buildkit v0.15.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect

18
go.sum
View File

@ -63,12 +63,14 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= github.com/containerd/containerd v1.7.20 h1:Sl6jQYk3TRavaU83h66QMbI2Nqg9Jm6qzwX57Vsn1SQ=
github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= github.com/containerd/containerd v1.7.20/go.mod h1:52GsS5CwquuqPuLncsXwG0t2CiUce+KsNHJZQJvAgR0=
github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
@ -161,8 +163,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsouza/go-dockerclient v1.11.1 h1:i5Vk9riDxW2uP9pVS5FYkpquMTFT5lsx2pt7oErRTjI= github.com/fsouza/go-dockerclient v1.11.2 h1:Wos4OMUwIjOW2rt8Z10TZSJHxgQH0KcYyf3O86dqFII=
github.com/fsouza/go-dockerclient v1.11.1/go.mod h1:UfjOOaspAq+RGh7GX1aZ0HeWWGHQWWsh+H5BgEWB3Pk= github.com/fsouza/go-dockerclient v1.11.2/go.mod h1:HZN6ky2Mg5mfZO/WZBFDe6XCricqTnDJntfXHZTYnQQ=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@ -357,8 +359,8 @@ github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPn
github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k= github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/buildkit v0.12.5 h1:RNHH1l3HDhYyZafr5EgstEu8aGNCwyfvMtrQDtjH9T0= github.com/moby/buildkit v0.15.1 h1:J6wrew7hphKqlq1wuu6yaUb/1Ra7gEzDAovylGztAKM=
github.com/moby/buildkit v0.12.5/go.mod h1:YGwjA2loqyiYfZeEo8FtI7z4x5XponAaIWsWcSjWwso= github.com/moby/buildkit v0.15.1/go.mod h1:Yis8ZMUJTHX9XhH9zVyK2igqSHV3sxi3UN0uztZocZk=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
@ -400,8 +402,8 @@ github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc h1:
github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc/go.mod h1:8tx1helyqhUC65McMm3x7HmOex8lO2/v9zPuxmKHurs= github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc/go.mod h1:8tx1helyqhUC65McMm3x7HmOex8lO2/v9zPuxmKHurs=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/openshift/imagebuilder v1.2.14 h1:l4gUw0KIsjZrX7otfS4WoKxzGBrxYldU3pF4+5W/ud8= github.com/openshift/imagebuilder v1.2.15 h1:MNn1OztEE/l8pSEDPYAQ71Ys6rpXA2P00UFhdY9p/yk=
github.com/openshift/imagebuilder v1.2.14/go.mod h1:KkkXOyRjJlZEXWQtHNBNzVHqh4vf/0xX5cDIQ2gr+5I= github.com/openshift/imagebuilder v1.2.15/go.mod h1:cK6MLyBl1IHmIYGLY/2SLOG6p0PtEDUOC7khxsFYUXE=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqwnHagNDPGOlts35QkhAZ8by3DR7nMih7M= github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqwnHagNDPGOlts35QkhAZ8by3DR7nMih7M=

View File

@ -1,116 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package errdefs defines the common errors used throughout containerd
// packages.
//
// Use with fmt.Errorf to add context to an error.
//
// To detect an error class, use the IsXXX functions to tell whether an error
// is of a certain type.
//
// The functions ToGRPC and FromGRPC can be used to map server-side and
// client-side errors to the correct types.
package errdefs
import (
"github.com/containerd/errdefs"
)
// Definitions of common error types used throughout containerd. All containerd
// errors returned by most packages will map into one of these errors classes.
// Packages should return errors of these types when they want to instruct a
// client to take a particular action.
//
// For the most part, we just try to provide local grpc errors. Most conditions
// map very well to those defined by grpc.
var (
ErrUnknown = errdefs.ErrUnknown
ErrInvalidArgument = errdefs.ErrInvalidArgument
ErrNotFound = errdefs.ErrNotFound
ErrAlreadyExists = errdefs.ErrAlreadyExists
ErrFailedPrecondition = errdefs.ErrFailedPrecondition
ErrUnavailable = errdefs.ErrUnavailable
ErrNotImplemented = errdefs.ErrNotImplemented
)
// IsInvalidArgument returns true if the error is due to an invalid argument
func IsInvalidArgument(err error) bool {
return errdefs.IsInvalidArgument(err)
}
// IsNotFound returns true if the error is due to a missing object
func IsNotFound(err error) bool {
return errdefs.IsNotFound(err)
}
// IsAlreadyExists returns true if the error is due to an already existing
// metadata item
func IsAlreadyExists(err error) bool {
return errdefs.IsAlreadyExists(err)
}
// IsFailedPrecondition returns true if an operation could not proceed to the
// lack of a particular condition
func IsFailedPrecondition(err error) bool {
return errdefs.IsFailedPrecondition(err)
}
// IsUnavailable returns true if the error is due to a resource being unavailable
func IsUnavailable(err error) bool {
return errdefs.IsUnavailable(err)
}
// IsNotImplemented returns true if the error is due to not being implemented
func IsNotImplemented(err error) bool {
return errdefs.IsNotImplemented(err)
}
// IsCanceled returns true if the error is due to `context.Canceled`.
func IsCanceled(err error) bool {
return errdefs.IsCanceled(err)
}
// IsDeadlineExceeded returns true if the error is due to
// `context.DeadlineExceeded`.
func IsDeadlineExceeded(err error) bool {
return errdefs.IsDeadlineExceeded(err)
}
// ToGRPC will attempt to map the backend containerd error into a grpc error,
// using the original error message as a description.
//
// Further information may be extracted from certain errors depending on their
// type.
//
// If the error is unmapped, the original error will be returned to be handled
// by the regular grpc error handling stack.
func ToGRPC(err error) error {
return errdefs.ToGRPC(err)
}
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
// and combining it with the target error string.
//
// This is equivalent to errdefs.ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
func ToGRPCf(err error, format string, args ...interface{}) error {
return errdefs.ToGRPCf(err, format, args...)
}
// FromGRPC returns the underlying error from a grpc service based on the grpc error code
func FromGRPC(err error) error {
return errdefs.FromGRPC(err)
}

View File

@ -0,0 +1,176 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package platforms
import (
"github.com/containerd/platforms"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)
// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
//
// Deprecated: use [specs.Platform].
type Platform = specs.Platform
// DefaultSpec returns the current platform's default platform specification.
//
// Deprecated: use [platforms.DefaultSpec].
func DefaultSpec() specs.Platform {
return platforms.DefaultSpec()
}
// Default returns the default matcher for the platform.
//
// Deprecated: use [platforms.Default].
func Default() platforms.MatchComparer {
return platforms.Default()
}
// DefaultString returns the default string specifier for the platform.
//
// Deprecated: use [platforms.DefaultString].
func DefaultString() string {
return platforms.Format(platforms.DefaultSpec()) // For 1.7 continue using the old format without os-version included.
}
// DefaultStrict returns strict form of Default.
//
// Deprecated: use [platforms.DefaultStrict].
func DefaultStrict() MatchComparer {
return platforms.DefaultStrict()
}
// MatchComparer is able to match and compare platforms to
// filter and sort platforms.
//
// Deprecated: use [platforms.MatchComparer].
type MatchComparer = platforms.MatchComparer
// Matcher matches platforms specifications, provided by an image or runtime.
//
// Deprecated: use [platforms.Matcher].
type Matcher = platforms.Matcher
// NewMatcher returns a simple matcher based on the provided platform
// specification. The returned matcher only looks for equality based on os,
// architecture and variant.
//
// One may implement their own matcher if this doesn't provide the required
// functionality.
//
// Applications should opt to use `Match` over directly parsing specifiers.
//
// Deprecated: use [platforms.NewMatcher].
func NewMatcher(platform specs.Platform) platforms.Matcher {
return platforms.NewMatcher(platform)
}
// Parse parses the platform specifier syntax into a platform declaration.
//
// Platform specifiers are in the format `<os>|<arch>|<os>/<arch>[/<variant>]`.
// The minimum required information for a platform specifier is the operating
// system or architecture. If there is only a single string (no slashes), the
// value will be matched against the known set of operating systems, then fall
// back to the known set of architectures. The missing component will be
// inferred based on the local environment.
//
// Deprecated: use [platforms.Parse].
func Parse(specifier string) (specs.Platform, error) {
return platforms.Parse(specifier)
}
// MustParse is like Parses but panics if the specifier cannot be parsed.
// Simplifies initialization of global variables.
//
// Deprecated: use [platforms.MustParse].
func MustParse(specifier string) specs.Platform {
return platforms.MustParse(specifier)
}
// Format returns a string specifier from the provided platform specification.
//
// Deprecated: use [platforms.Format].
func Format(platform specs.Platform) string {
return platforms.Format(platform)
}
// Normalize validates and translate the platform to the canonical value.
//
// For example, if "Aarch64" is encountered, we change it to "arm64" or if
// "x86_64" is encountered, it becomes "amd64".
//
// Deprecated: use [platforms.Normalize].
func Normalize(platform specs.Platform) specs.Platform {
return platforms.Normalize(platform)
}
// Only returns a match comparer for a single platform
// using default resolution logic for the platform.
//
// For arm/v8, will also match arm/v7, arm/v6 and arm/v5
// For arm/v7, will also match arm/v6 and arm/v5
// For arm/v6, will also match arm/v5
// For amd64, will also match 386
//
// Deprecated: use [platforms.Only].
func Only(platform specs.Platform) platforms.MatchComparer {
return platforms.Only(platform)
}
// OnlyStrict returns a match comparer for a single platform.
//
// Unlike Only, OnlyStrict does not match sub platforms.
// So, "arm/vN" will not match "arm/vM" where M < N,
// and "amd64" will not also match "386".
//
// OnlyStrict matches non-canonical forms.
// So, "arm64" matches "arm/64/v8".
//
// Deprecated: use [platforms.OnlyStrict].
func OnlyStrict(platform specs.Platform) platforms.MatchComparer {
return platforms.OnlyStrict(platform)
}
// Ordered returns a platform MatchComparer which matches any of the platforms
// but orders them in order they are provided.
//
// Deprecated: use [platforms.Ordered].
func Ordered(platform ...specs.Platform) platforms.MatchComparer {
return platforms.Ordered(platform...)
}
// Any returns a platform MatchComparer which matches any of the platforms
// with no preference for ordering.
//
// Deprecated: use [platforms.Any].
func Any(platform ...specs.Platform) platforms.MatchComparer {
return platforms.Any(platform...)
}
// All is a platform MatchComparer which matches all platforms
// with preference for ordering.
//
// Deprecated: use [platforms.All].
var All = platforms.All
// GetWindowsOsVersion returns the version of Windows of the local system,
// it returns an empty string on other platforms.
//
// Deprecated: this function is deprecated, and removed in github.com/containerd/platforms
func GetWindowsOsVersion() string {
return getWindowsOsVersion()
}

View File

@ -0,0 +1,23 @@
//go:build !windows
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package platforms
func getWindowsOsVersion() string {
return ""
}

View File

@ -0,0 +1,49 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package platforms
import (
"fmt"
"strconv"
"strings"
"github.com/Microsoft/hcsshim/osversion"
"golang.org/x/sys/windows"
)
func getWindowsOsVersion() string {
major, minor, build := windows.RtlGetNtVersionNumbers()
return fmt.Sprintf("%d.%d.%d", major, minor, build)
}
// Deprecated: this function is deprecated, and removed in github.com/containerd/platforms
func GetOsVersion(osVersionPrefix string) osversion.OSVersion {
parts := strings.Split(osVersionPrefix, ".")
if len(parts) < 3 {
return osversion.OSVersion{}
}
majorVersion, _ := strconv.Atoi(parts[0])
minorVersion, _ := strconv.Atoi(parts[1])
buildNumber, _ := strconv.Atoi(parts[2])
return osversion.OSVersion{
MajorVersion: uint8(majorVersion),
MinorVersion: uint8(minorVersion),
Build: uint16(buildNumber),
}
}

View File

@ -0,0 +1 @@
*.go text eol=lf

30
vendor/github.com/containerd/platforms/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,30 @@
linters:
enable:
- exportloopref # Checks for pointers to enclosing loop variables
- gofmt
- goimports
- gosec
- ineffassign
- misspell
- nolintlint
- revive
- staticcheck
- tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17
- unconvert
- unused
- vet
- dupword # Checks for duplicate words in the source code
disable:
- errcheck
run:
timeout: 5m
skip-dirs:
- api
- cluster
- design
- docs
- docs/man
- releases
- reports
- test # e2e scripts

191
vendor/github.com/containerd/platforms/LICENSE generated vendored Normal file
View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright The containerd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

32
vendor/github.com/containerd/platforms/README.md generated vendored Normal file
View File

@ -0,0 +1,32 @@
# platforms
A Go package for formatting, normalizing and matching container platforms.
This package is based on the Open Containers Image Spec definition of a [platform](https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/descriptor.go#L52).
## Platform Specifier
While the OCI platform specifications provide a tool for components to
specify structured information, user input typically doesn't need the full
context and much can be inferred. To solve this problem, this package introduces
"specifiers". A specifier has the format
`<os>|<arch>|<os>/<arch>[/<variant>]`. The user can provide either the
operating system or the architecture or both.
An example of a common specifier is `linux/amd64`. If the host has a default
runtime that matches this, the user can simply provide the component that
matters. For example, if an image provides `amd64` and `arm64` support, the
operating system, `linux` can be inferred, so they only have to provide
`arm64` or `amd64`. Similar behavior is implemented for operating systems,
where the architecture may be known but a runtime may support images from
different operating systems.
## Project details
**platforms** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

View File

@ -19,12 +19,12 @@ package platforms
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"errors"
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
"strings" "strings"
"github.com/containerd/errdefs"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -70,7 +70,7 @@ func getCPUInfo(pattern string) (info string, err error) {
return "", err return "", err
} }
return "", fmt.Errorf("getCPUInfo for pattern %s: %w", pattern, errdefs.ErrNotFound) return "", fmt.Errorf("getCPUInfo for pattern %s: %w", pattern, errNotFound)
} }
// getCPUVariantFromArch get CPU variant from arch through a system call // getCPUVariantFromArch get CPU variant from arch through a system call
@ -83,7 +83,7 @@ func getCPUVariantFromArch(arch string) (string, error) {
if arch == "aarch64" { if arch == "aarch64" {
variant = "8" variant = "8"
} else if arch[0:4] == "armv" && len(arch) >= 5 { } else if arch[0:4] == "armv" && len(arch) >= 5 {
//Valid arch format is in form of armvXx // Valid arch format is in form of armvXx
switch arch[3:5] { switch arch[3:5] {
case "v8": case "v8":
variant = "8" variant = "8"
@ -101,7 +101,7 @@ func getCPUVariantFromArch(arch string) (string, error) {
variant = "unknown" variant = "unknown"
} }
} else { } else {
return "", fmt.Errorf("getCPUVariantFromArch invalid arch: %s, %w", arch, errdefs.ErrInvalidArgument) return "", fmt.Errorf("getCPUVariantFromArch invalid arch: %s, %w", arch, errInvalidArgument)
} }
return variant, nil return variant, nil
} }
@ -112,11 +112,10 @@ func getCPUVariantFromArch(arch string) (string, error) {
// This is to cover running ARM in emulated environment on x86 host as this field in /proc/cpuinfo // This is to cover running ARM in emulated environment on x86 host as this field in /proc/cpuinfo
// was not present. // was not present.
func getCPUVariant() (string, error) { func getCPUVariant() (string, error) {
variant, err := getCPUInfo("Cpu architecture") variant, err := getCPUInfo("Cpu architecture")
if err != nil { if err != nil {
if errdefs.IsNotFound(err) { if errors.Is(err, errNotFound) {
//Let's try getting CPU variant from machine architecture // Let's try getting CPU variant from machine architecture
arch, err := getMachineArch() arch, err := getMachineArch()
if err != nil { if err != nil {
return "", fmt.Errorf("failure getting machine architecture: %v", err) return "", fmt.Errorf("failure getting machine architecture: %v", err)

View File

@ -21,8 +21,6 @@ package platforms
import ( import (
"fmt" "fmt"
"runtime" "runtime"
"github.com/containerd/errdefs"
) )
func getCPUVariant() (string, error) { func getCPUVariant() (string, error) {
@ -49,10 +47,8 @@ func getCPUVariant() (string, error) {
default: default:
variant = "unknown" variant = "unknown"
} }
} else { } else {
return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errdefs.ErrNotImplemented) return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
} }
return variant, nil return variant, nil

View File

@ -16,9 +16,11 @@
package platforms package platforms
// DefaultString returns the default string specifier for the platform. // DefaultString returns the default string specifier for the platform,
// with [PR#6](https://github.com/containerd/platforms/pull/6) the result
// may now also include the OSVersion from the provided platform specification.
func DefaultString() string { func DefaultString() string {
return Format(DefaultSpec()) return FormatAll(DefaultSpec())
} }
// DefaultStrict returns strict form of Default. // DefaultStrict returns strict form of Default.

View File

@ -22,7 +22,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/Microsoft/hcsshim/osversion"
specs "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -52,29 +51,29 @@ func (m windowsmatcher) Match(p specs.Platform) bool {
if match && m.OS == "windows" { if match && m.OS == "windows" {
// HPC containers do not have OS version filled // HPC containers do not have OS version filled
if p.OSVersion == "" { if m.OSVersion == "" || p.OSVersion == "" {
return true return true
} }
hostOsVersion := GetOsVersion(m.osVersionPrefix) hostOsVersion := getOSVersion(m.osVersionPrefix)
ctrOsVersion := GetOsVersion(p.OSVersion) ctrOsVersion := getOSVersion(p.OSVersion)
return osversion.CheckHostAndContainerCompat(hostOsVersion, ctrOsVersion) return checkHostAndContainerCompat(hostOsVersion, ctrOsVersion)
} }
return match return match
} }
func GetOsVersion(osVersionPrefix string) osversion.OSVersion { func getOSVersion(osVersionPrefix string) osVersion {
parts := strings.Split(osVersionPrefix, ".") parts := strings.Split(osVersionPrefix, ".")
if len(parts) < 3 { if len(parts) < 3 {
return osversion.OSVersion{} return osVersion{}
} }
majorVersion, _ := strconv.Atoi(parts[0]) majorVersion, _ := strconv.Atoi(parts[0])
minorVersion, _ := strconv.Atoi(parts[1]) minorVersion, _ := strconv.Atoi(parts[1])
buildNumber, _ := strconv.Atoi(parts[2]) buildNumber, _ := strconv.Atoi(parts[2])
return osversion.OSVersion{ return osVersion{
MajorVersion: uint8(majorVersion), MajorVersion: uint8(majorVersion),
MinorVersion: uint8(minorVersion), MinorVersion: uint8(minorVersion),
Build: uint16(buildNumber), Build: uint16(buildNumber),

30
vendor/github.com/containerd/platforms/errors.go generated vendored Normal file
View File

@ -0,0 +1,30 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package platforms
import "errors"
// These errors mirror the errors defined in [github.com/containerd/containerd/errdefs],
// however, they are not exported as they are not expected to be used as sentinel
// errors by consumers of this package.
//
//nolint:unused // not all errors are used on all platforms.
var (
errNotFound = errors.New("not found")
errInvalidArgument = errors.New("invalid argument")
errNotImplemented = errors.New("not implemented")
)

View File

@ -0,0 +1,78 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package platforms
// osVersion is a wrapper for Windows version information
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
type osVersion struct {
Version uint32
MajorVersion uint8
MinorVersion uint8
Build uint16
}
// Windows Client and Server build numbers.
//
// See:
// https://learn.microsoft.com/en-us/windows/release-health/release-information
// https://learn.microsoft.com/en-us/windows/release-health/windows-server-release-info
// https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information
const (
// rs5 (version 1809, codename "Redstone 5") corresponds to Windows Server
// 2019 (ltsc2019), and Windows 10 (October 2018 Update).
rs5 = 17763
// v21H2Server corresponds to Windows Server 2022 (ltsc2022).
v21H2Server = 20348
// v22H2Win11 corresponds to Windows 11 (2022 Update).
v22H2Win11 = 22621
)
// List of stable ABI compliant ltsc releases
// Note: List must be sorted in ascending order
var compatLTSCReleases = []uint16{
v21H2Server,
}
// CheckHostAndContainerCompat checks if given host and container
// OS versions are compatible.
// It includes support for stable ABI compliant versions as well.
// Every release after WS 2022 will support the previous ltsc
// container image. Stable ABI is in preview mode for windows 11 client.
// Refer: https://learn.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/version-compatibility?tabs=windows-server-2022%2Cwindows-10#windows-server-host-os-compatibility
func checkHostAndContainerCompat(host, ctr osVersion) bool {
// check major minor versions of host and guest
if host.MajorVersion != ctr.MajorVersion ||
host.MinorVersion != ctr.MinorVersion {
return false
}
// If host is < WS 2022, exact version match is required
if host.Build < v21H2Server {
return host.Build == ctr.Build
}
var supportedLtscRelease uint16
for i := len(compatLTSCReleases) - 1; i >= 0; i-- {
if host.Build >= compatLTSCReleases[i] {
supportedLtscRelease = compatLTSCReleases[i]
break
}
}
return ctr.Build >= supportedLtscRelease && ctr.Build <= host.Build
}

View File

@ -102,6 +102,9 @@
// unless it is explicitly provided. This is treated as equivalent to armhf. A // unless it is explicitly provided. This is treated as equivalent to armhf. A
// previous architecture, armel, will be normalized to arm/v6. // previous architecture, armel, will be normalized to arm/v6.
// //
// Similarly, the most common arm64 version v8, and most common amd64 version v1
// are represented without the variant.
//
// While these normalizations are provided, their support on arm platforms has // While these normalizations are provided, their support on arm platforms has
// not yet been fully implemented and tested. // not yet been fully implemented and tested.
package platforms package platforms
@ -115,14 +118,15 @@ import (
"strings" "strings"
specs "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/containerd/errdefs"
) )
var ( var (
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`) specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
osAndVersionRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.-]*)\))?$`)
) )
const osAndVersionFormat = "%s(%s)"
// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere. // Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
type Platform = specs.Platform type Platform = specs.Platform
@ -155,40 +159,68 @@ func (m *matcher) Match(platform specs.Platform) bool {
} }
func (m *matcher) String() string { func (m *matcher) String() string {
return Format(m.Platform) return FormatAll(m.Platform)
}
// ParseAll parses a list of platform specifiers into a list of platform.
func ParseAll(specifiers []string) ([]specs.Platform, error) {
platforms := make([]specs.Platform, len(specifiers))
for i, s := range specifiers {
p, err := Parse(s)
if err != nil {
return nil, fmt.Errorf("invalid platform %s: %w", s, err)
}
platforms[i] = p
}
return platforms, nil
} }
// Parse parses the platform specifier syntax into a platform declaration. // Parse parses the platform specifier syntax into a platform declaration.
// //
// Platform specifiers are in the format `<os>|<arch>|<os>/<arch>[/<variant>]`. // Platform specifiers are in the format `<os>[(<OSVersion>)]|<arch>|<os>[(<OSVersion>)]/<arch>[/<variant>]`.
// The minimum required information for a platform specifier is the operating // The minimum required information for a platform specifier is the operating
// system or architecture. If there is only a single string (no slashes), the // system or architecture. The OSVersion can be part of the OS like `windows(10.0.17763)`
// When an OSVersion is specified, then specs.Platform.OSVersion is populated with that value,
// and an empty string otherwise.
// If there is only a single string (no slashes), the
// value will be matched against the known set of operating systems, then fall // value will be matched against the known set of operating systems, then fall
// back to the known set of architectures. The missing component will be // back to the known set of architectures. The missing component will be
// inferred based on the local environment. // inferred based on the local environment.
func Parse(specifier string) (specs.Platform, error) { func Parse(specifier string) (specs.Platform, error) {
if strings.Contains(specifier, "*") { if strings.Contains(specifier, "*") {
// TODO(stevvooe): need to work out exact wildcard handling // TODO(stevvooe): need to work out exact wildcard handling
return specs.Platform{}, fmt.Errorf("%q: wildcards not yet supported: %w", specifier, errdefs.ErrInvalidArgument) return specs.Platform{}, fmt.Errorf("%q: wildcards not yet supported: %w", specifier, errInvalidArgument)
} }
parts := strings.Split(specifier, "/") // Limit to 4 elements to prevent unbounded split
parts := strings.SplitN(specifier, "/", 4)
for _, part := range parts { var p specs.Platform
if !specifierRe.MatchString(part) { for i, part := range parts {
return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errdefs.ErrInvalidArgument) if i == 0 {
// First element is <os>[(<OSVersion>)]
osVer := osAndVersionRe.FindStringSubmatch(part)
if osVer == nil {
return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osAndVersionRe.String(), errInvalidArgument)
}
p.OS = normalizeOS(osVer[1])
p.OSVersion = osVer[2]
} else {
if !specifierRe.MatchString(part) {
return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errInvalidArgument)
}
} }
} }
var p specs.Platform
switch len(parts) { switch len(parts) {
case 1: case 1:
// in this case, we will test that the value might be an OS, then look // in this case, we will test that the value might be an OS (with or
// it up. If it is not known, we'll treat it as an architecture. Since // without the optional OSVersion specified) and look it up.
// If it is not known, we'll treat it as an architecture. Since
// we have very little information about the platform here, we are // we have very little information about the platform here, we are
// going to be a little more strict if we don't know about the argument // going to be a little more strict if we don't know about the argument
// value. // value.
p.OS = normalizeOS(parts[0])
if isKnownOS(p.OS) { if isKnownOS(p.OS) {
// picks a default architecture // picks a default architecture
p.Architecture = runtime.GOARCH p.Architecture = runtime.GOARCH
@ -196,10 +228,6 @@ func Parse(specifier string) (specs.Platform, error) {
p.Variant = cpuVariant() p.Variant = cpuVariant()
} }
if p.OS == "windows" {
p.OSVersion = GetWindowsOsVersion()
}
return p, nil return p, nil
} }
@ -212,37 +240,27 @@ func Parse(specifier string) (specs.Platform, error) {
return p, nil return p, nil
} }
return specs.Platform{}, fmt.Errorf("%q: unknown operating system or architecture: %w", specifier, errdefs.ErrInvalidArgument) return specs.Platform{}, fmt.Errorf("%q: unknown operating system or architecture: %w", specifier, errInvalidArgument)
case 2: case 2:
// In this case, we treat as a regular os/arch pair. We don't care // In this case, we treat as a regular OS[(OSVersion)]/arch pair. We don't care
// about whether or not we know of the platform. // about whether or not we know of the platform.
p.OS = normalizeOS(parts[0])
p.Architecture, p.Variant = normalizeArch(parts[1], "") p.Architecture, p.Variant = normalizeArch(parts[1], "")
if p.Architecture == "arm" && p.Variant == "v7" { if p.Architecture == "arm" && p.Variant == "v7" {
p.Variant = "" p.Variant = ""
} }
if p.OS == "windows" {
p.OSVersion = GetWindowsOsVersion()
}
return p, nil return p, nil
case 3: case 3:
// we have a fully specified variant, this is rare // we have a fully specified variant, this is rare
p.OS = normalizeOS(parts[0])
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2]) p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
if p.Architecture == "arm64" && p.Variant == "" { if p.Architecture == "arm64" && p.Variant == "" {
p.Variant = "v8" p.Variant = "v8"
} }
if p.OS == "windows" {
p.OSVersion = GetWindowsOsVersion()
}
return p, nil return p, nil
} }
return specs.Platform{}, fmt.Errorf("%q: cannot parse platform specifier: %w", specifier, errdefs.ErrInvalidArgument) return specs.Platform{}, fmt.Errorf("%q: cannot parse platform specifier: %w", specifier, errInvalidArgument)
} }
// MustParse is like Parses but panics if the specifier cannot be parsed. // MustParse is like Parses but panics if the specifier cannot be parsed.
@ -264,6 +282,20 @@ func Format(platform specs.Platform) string {
return path.Join(platform.OS, platform.Architecture, platform.Variant) return path.Join(platform.OS, platform.Architecture, platform.Variant)
} }
// FormatAll returns a string specifier that also includes the OSVersion from the
// provided platform specification.
func FormatAll(platform specs.Platform) string {
if platform.OS == "" {
return "unknown"
}
if platform.OSVersion != "" {
OSAndVersion := fmt.Sprintf(osAndVersionFormat, platform.OS, platform.OSVersion)
return path.Join(OSAndVersion, platform.Architecture, platform.Variant)
}
return path.Join(platform.OS, platform.Architecture, platform.Variant)
}
// Normalize validates and translate the platform to the canonical value. // Normalize validates and translate the platform to the canonical value.
// //
// For example, if "Aarch64" is encountered, we change it to "arm64" or if // For example, if "Aarch64" is encountered, we change it to "arm64" or if

View File

@ -28,7 +28,3 @@ func newDefaultMatcher(platform specs.Platform) Matcher {
Platform: Normalize(platform), Platform: Normalize(platform),
} }
} }
func GetWindowsOsVersion() string {
return ""
}

View File

@ -17,10 +17,7 @@
package platforms package platforms
import ( import (
"fmt"
specs "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/sys/windows"
) )
// NewMatcher returns a Windows matcher that will match on osVersionPrefix if // NewMatcher returns a Windows matcher that will match on osVersionPrefix if
@ -35,8 +32,3 @@ func newDefaultMatcher(platform specs.Platform) Matcher {
}, },
} }
} }
func GetWindowsOsVersion() string {
major, minor, build := windows.RtlGetNtVersionNumbers()
return fmt.Sprintf("%d.%d.%d", major, minor, build)
}

View File

@ -1,66 +1,284 @@
# This file lists all individuals having contributed content to the repository. # This file lists all individuals having contributed content to the repository.
# For how it is generated, see `scripts/generate-authors.sh`. # For how it is generated, see hack/dockerfiles/authors.Dockerfile.
a-palchikov <deemok@gmail.com>
Aaron L. Xu <likexu@harmonycloud.cn> Aaron L. Xu <likexu@harmonycloud.cn>
Aaron Lehmann <aaron.lehmann@docker.com> Aaron Lehmann <aaron.lehmann@docker.com>
Aaron Lehmann <alehmann@netflix.com>
Abdur Rehman <abdur_rehman@mentor.com>
Addam Hardy <addam.hardy@gmail.com>
Adrian Plata <adrian.plata@docker.com>
Aidan Hobson Sayers <aidanhs@cantab.net>
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp> Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
Alan Fregtman <941331+darkvertex@users.noreply.github.com>
Alex Couture-Beil <alex@earthly.dev>
Alex Mayer <amayer5125@gmail.com>
Alex Suraci <suraci.alex@gmail.com>
Alexander Morozov <lk4d4@docker.com> Alexander Morozov <lk4d4@docker.com>
Alexis Murzeau <amubtdx@gmail.com>
Alice Frosi <afrosi@de.ibm.com> Alice Frosi <afrosi@de.ibm.com>
Allen Sun <allen.sun@daocloud.io> Allen Sun <allen.sun@daocloud.io>
Amen Belayneh <amenbelayneh@gmail.com>
Anca Iordache <anca.iordache@docker.com>
Anda Xu <anda.xu@docker.com> Anda Xu <anda.xu@docker.com>
Anders F Björklund <anders.f.bjorklund@gmail.com>
Andrea Bolognani <abologna@redhat.com>
Andrea Luzzardi <aluzzardi@gmail.com>
Andrew Chang <chang331006@gmail.com>
Andrey Smirnov <smirnov.andrey@gmail.com>
Andy Alt <andy5995@users.noreply.github.com>
Andy Caldwell <andrew.caldwell@metaswitch.com>
Ankush Agarwal <ankushagarwal11@gmail.com>
Anthony Sottile <asottile@umich.edu> Anthony Sottile <asottile@umich.edu>
Anurag Goel <anurag@render.com>
Anusha Ragunathan <anusha@docker.com>
Arnaud Bailly <arnaud.oqube@gmail.com> Arnaud Bailly <arnaud.oqube@gmail.com>
Avi Deitcher <avi@deitcher.net>
Bastiaan Bakker <bbakker@xebia.com>
Ben Longo <benlongo9807@gmail.com>
Bertrand Paquet <bertrand.paquet@gmail.com>
Bin Liu <liubin0329@gmail.com> Bin Liu <liubin0329@gmail.com>
Brandon Mitchell <git@bmitch.net>
Brian Goff <cpuguy83@gmail.com> Brian Goff <cpuguy83@gmail.com>
Ce Gao <ce.gao@outlook.com>
Chaerim Yeo <yeochaerim@gmail.com>
Changwei Ge <gechangwei@bytedance.com>
Chanhun Jeong <chanhun.jeong@navercorp.com>
ChaosGramer <ChaosGramer@users.noreply.github.com>
Charles Chan <charleswhchan@users.noreply.github.com>
Charles Korn <me@charleskorn.com>
Charles Law <claw@conduce.com>
Chenbin <chen.bin11@zte.com.cn>
Chris Goller <goller@gmail.com>
Chris McKinnel <chrismckinnel@gmail.com>
Christian Höltje <docwhat@gerf.org>
Christian Weichel <chris@gitpod.io>
Ciro S. Costa <cscosta@pivotal.io>
Claudiu Belu <cbelu@cloudbasesolutions.com>
Colin Chartier <colin.n.chartier@gmail.com>
Corey Larson <corey@earthly.dev>
Cory Bennett <cbennett@netflix.com>
Cory Snider <csnider@mirantis.com>
coryb <cbennett@netflix.com>
CrazyMax <github@crazymax.dev>
Csaba Apagyi <csaba.apagyi@gmail.com>
Dan Duvall <dduvall@wikimedia.org>
Daniel Cassidy <mail@danielcassidy.me.uk>
Daniel Nephin <dnephin@gmail.com> Daniel Nephin <dnephin@gmail.com>
Darren Shepherd <darren@rancher.com>
Dave Chen <dave.chen@arm.com> Dave Chen <dave.chen@arm.com>
Dave Henderson <dhenderson@gmail.com>
Dave Tucker <dt@docker.com>
David Calavera <david.calavera@gmail.com> David Calavera <david.calavera@gmail.com>
David Dooling <dooling@gmail.com>
David Gageot <david.gageot@docker.com>
David Karlsson <david.karlsson@docker.com>
Davis Schirmer <djds@bghost.xyz>
Dennis Chen <dennis.chen@arm.com> Dennis Chen <dennis.chen@arm.com>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Derek McGowan <derek@mcgstyle.net> Derek McGowan <derek@mcgstyle.net>
Dharmit Shah <shahdharmit@gmail.com>
Ding Fei <dingfei@stars.org.cn>
dito <itodaisuke00@gmail.com>
Doug Davis <dug@us.ibm.com> Doug Davis <dug@us.ibm.com>
Edgar Lee <edgarl@netflix.com> Edgar Lee <edgarhinshunlee@gmail.com>
Eli Uriegas <eli.uriegas@docker.com> Eli Uriegas <eli.uriegas@docker.com>
Elias Faxö <elias.faxo@tre.se>
Eng Zer Jun <engzerjun@gmail.com>
Eric Engestrom <eric@engestrom.ch>
Erik Sipsma <erik@sipsma.dev>
eyherabh <hugogabriel.eyherabide@gmail.com>
f0 <f0@users.noreply.github.com> f0 <f0@users.noreply.github.com>
Fernando Miguel <github@FernandoMiguel.net> Fernando Miguel <github@FernandoMiguel.net>
Fiona Klute <fiona.klute@gmx.de>
Foysal Iqbal <foysal.iqbal.fb@gmail.com>
Fred Cox <mcfedr@gmail.com>
Frieder Bluemle <frieder.bluemle@gmail.com>
Gabriel <samfiragabriel@gmail.com>
Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Gaetan de Villele <gdevillele@gmail.com>
Gahl Saraf <saraf.gahl@gmail.com>
genglu.gl <luzigeng32@163.com>
George <george@betterde.com>
ggjulio <juligonz@student.42.fr>
Govind Rai <raigovind93@gmail.com>
Grant Reaber <grant.reaber@gmail.com>
Guilhem C <guilhem.charles@gmail.com>
Hans van den Bogert <hansbogert@gmail.com>
Hao Hu <hao.hu.fr@gmail.com> Hao Hu <hao.hu.fr@gmail.com>
Hector S <hfsam88@gmail.com>
Helen Xie <chenjg@harmonycloud.cn> Helen Xie <chenjg@harmonycloud.cn>
Himanshu Pandey <hpandey@pivotal.io> Himanshu Pandey <hpandey@pivotal.io>
Hiromu Nakamura <abctail30@gmail.com> Hiromu Nakamura <abctail30@gmail.com>
HowJMay <vulxj0j8j8@gmail.com>
Hugo Santos <hugo@namespacelabs.com>
Ian Campbell <ijc@docker.com> Ian Campbell <ijc@docker.com>
Ilya Dmitrichenko <errordeveloper@gmail.com>
Iskander (Alex) Sharipov <quasilyte@gmail.com> Iskander (Alex) Sharipov <quasilyte@gmail.com>
Jacob Gillespie <jacobwgillespie@gmail.com>
Jacob MacElroy <jacob@okteto.com>
Jean-Pierre Huynh <jean-pierre.huynh@ounet.fr> Jean-Pierre Huynh <jean-pierre.huynh@ounet.fr>
Jeffrey Huang <jeffreyhuang23@gmail.com>
Jesse Rittner <rittneje@gmail.com>
Jessica Frazelle <acidburn@microsoft.com> Jessica Frazelle <acidburn@microsoft.com>
jgeiger <jgeiger@gmail.com>
Jitender Kumar <jitender.kumar@intel.com>
jlecordier <jeanlecordier@hotmail.fr>
joey <zchengjoey@gmail.com>
John Howard <jhoward@microsoft.com> John Howard <jhoward@microsoft.com>
John Maguire <jmaguire@duosecurity.com>
John Mulhausen <john@docker.com>
John Tims <john.k.tims@gmail.com>
Jon Zeolla <zeolla@gmail.com>
Jonathan Azoff <azoff@users.noreply.github.com>
Jonathan Giannuzzi <jonathan@giannuzzi.me>
Jonathan Stoppani <jonathan.stoppani@divio.com> Jonathan Stoppani <jonathan.stoppani@divio.com>
Jonny Stoten <jonny.stoten@docker.com>
JordanGoasdoue <jordan.goasdoue@dailymotion.com>
jroenf <jeroenfranse@gmail.com>
Julian Goede <julian.goede@pm.me>
Justas Brazauskas <brazauskasjustas@gmail.com> Justas Brazauskas <brazauskasjustas@gmail.com>
Justin Chadwell <me@jedevc.com>
Justin Cormack <justin.cormack@docker.com> Justin Cormack <justin.cormack@docker.com>
Justin Garrison <justin@linux.com>
Jörg Franke <359489+NewJorg@users.noreply.github.com>
Kang, Matthew <impulsecss@gmail.com>
Kees Cook <keescook@chromium.org>
Kevin Burke <kev@inburke.com>
kevinmeredith <kevin.m.meredith@gmail.com>
Kir Kolyshkin <kolyshkin@gmail.com>
Kohei Tokunaga <ktokunaga.mail@gmail.com>
Koichi Shiraishi <zchee.io@gmail.com>
Kris-Mikael Krister <krismikael@protonmail.com>
Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp>
Kyle <Kylemit@gmail.com>
l00397676 <lujingxiao@huawei.com>
Lajos Papp <lalyos@yahoo.com> Lajos Papp <lalyos@yahoo.com>
lalyos <lalyos@yahoo.com>
Levi Harrison <levisamuelharrison@gmail.com>
liwenqi <vikilwq@zju.edu.cn>
lixiaobing10051267 <li.xiaobing1@zte.com.cn>
lomot <lomot@qq.com>
Lu Jingxiao <lujingxiao@huawei.com>
Luca Visentin <luck.visentin@gmail.com>
Maciej Kalisz <mdkalish@users.noreply.github.com>
Madhav Puri <madhav.puri@gmail.com>
Manu Gupta <manugupt1@gmail.com>
Marcus Comstedt <marcus@mc.pp.se>
Mark Gordon <msg555@gmail.com>
Marko Kohtala <marko.kohtala@gmail.com>
Mary Anthony <mary@docker.com>
masibw <masi19bw@gmail.com>
Matias Insaurralde <matias@insaurral.de>
Matt Kang <impulsecss@gmail.com>
Matt Rickard <mrick@google.com> Matt Rickard <mrick@google.com>
Maxime Lagresle <maxime@angel.co>
Michael Crosby <crosbymichael@gmail.com> Michael Crosby <crosbymichael@gmail.com>
Michael Friis <friism@gmail.com>
Michael Irwin <mikesir87@gmail.com>
Miguel Ángel Jimeno <miguelangel4b@gmail.com>
Mihai Borobocea <MihaiBorob@gmail.com>
Mike Brown <brownwm@us.ibm.com>
mikelinjie <294893458@qq.com>
Mikhail Vasin <vasin@cloud-tv.ru>
Misty Stanley-Jones <misty@docker.com>
Miyachi Katsuya <miyachi_katsuya@r.recruit.co.jp> Miyachi Katsuya <miyachi_katsuya@r.recruit.co.jp>
Morgan Bauer <mbauer@us.ibm.com>
Morlay <morlay.null@gmail.com>
msg <msg@clinc.com>
Nao YONASHIRO <yonashiro@r.recruit.co.jp> Nao YONASHIRO <yonashiro@r.recruit.co.jp>
Natasha Jarus <linuxmercedes@gmail.com> Natasha Jarus <linuxmercedes@gmail.com>
Nathan Sullivan <nathan@nightsys.net>
Nick Miyake <nmiyake@users.noreply.github.com>
Nick Santos <nick.santos@docker.com>
Nikhil Pandeti <nikhil.pandeti@utexas.edu>
Noel Georgi <18496730+frezbo@users.noreply.github.com> Noel Georgi <18496730+frezbo@users.noreply.github.com>
Oliver Bristow <oliver.bristow@project-tracr.com>
Omer Duchovne <79370724+od-cyera@users.noreply.github.com>
Omer Mizrahi <ommizrah@microsoft.com>
Ondrej Fabry <ofabry@cisco.com> Ondrej Fabry <ofabry@cisco.com>
Otto Kekäläinen <otto@seravo.fi>
Pablo Chico de Guzman <pchico83@gmail.com>
Patrick Hemmer <patrick.hemmer@gmail.com>
Patrick Lang <plang@microsoft.com>
Patrick Van Stee <patrick@vanstee.me> Patrick Van Stee <patrick@vanstee.me>
Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
Paweł Gronowski <pawel.gronowski@docker.com>
Peter Dave Hello <hsu@peterdavehello.org>
Petr Fedchenkov <giggsoff@gmail.com>
Phil Estes <estesp@gmail.com>
Pierre Fenoll <pierrefenoll@gmail.com>
pieterdd <pieterdd@users.noreply.github.com>
Pranav Pandit <pranavp@microsoft.com>
Pratik Raj <rajpratik71@gmail.com>
Prayag Verma <prayag.verma@gmail.com>
Qiang Huang <h.huangqiang@huawei.com>
Remy Suen <remy.suen@gmail.com>
Ri Xu <xuri.me@gmail.com> Ri Xu <xuri.me@gmail.com>
Rob Taylor <rob@shape.build>
Robert Estelle <robertestelle@gmail.com>
Rubens Figueiredo <r.figueiredo.52@gmail.com>
Sam Whited <sam@samwhited.com>
Sascha Schwarze <schwarzs@de.ibm.com>
Sean P. Kane <spkane00@gmail.com>
Sebastiaan van Stijn <github@gone.nl> Sebastiaan van Stijn <github@gone.nl>
Seiya Miyata <odradek38@gmail.com>
Serhat Gülçiçek <serhat25@gmail.com>
Sertac Ozercan <sozercan@gmail.com>
Shev Yan <yandong_8212@163.com> Shev Yan <yandong_8212@163.com>
Shijiang Wei <mountkin@gmail.com>
Shingo Omura <everpeace@gmail.com>
Shiwei Zhang <shizh@microsoft.com>
Siebe Schaap <siebe@digibites.nl>
Silvin Lubecki <31478878+silvin-lubecki@users.noreply.github.com>
Simon Ferquel <simon.ferquel@docker.com> Simon Ferquel <simon.ferquel@docker.com>
Slava Semushin <semushin@redhat.com>
Solomon Hykes <sh.github.6811@hykes.org>
squeegels <1674195+squeegels@users.noreply.github.com>
Stefan Scherer <stefan.scherer@docker.com>
Stefan Weil <sw@weilnetz.de> Stefan Weil <sw@weilnetz.de>
StefanSchoof <Stefan.Schoof@direkt-gruppe.de>
Stepan Blyshchak <stepanblischak@gmail.com>
Steve Lohr <schdief.law@gmail.com>
sunchunming <sunchunming1@jd.com>
Sven Dowideit <SvenDowideit@home.org.au>
Takuya Noguchi <takninnovationresearch@gmail.com>
Thomas Leonard <thomas.leonard@docker.com> Thomas Leonard <thomas.leonard@docker.com>
Thomas Riccardi <riccardi@systran.fr>
Thomas Shaw <tomwillfixit@users.noreply.github.com> Thomas Shaw <tomwillfixit@users.noreply.github.com>
Tianon Gravi <admwiggin@gmail.com>
Tibor Vass <tibor@docker.com> Tibor Vass <tibor@docker.com>
Tiffany Jernigan <tiffany.f.j@gmail.com> Tiffany Jernigan <tiffany.f.j@gmail.com>
Tim Waugh <twaugh@redhat.com>
Tim Wraight <tim.wraight@tangentlabs.co.uk>
Tino Rusch <tino.rusch@gmail.com> Tino Rusch <tino.rusch@gmail.com>
Tobias Klauser <tklauser@distanz.ch> Tobias Klauser <tklauser@distanz.ch>
Tomas Tomecek <ttomecek@redhat.com> Tomas Tomecek <ttomecek@redhat.com>
Tomasz Kopczynski <tomek@kopczynski.net.pl>
Tomohiro Kusumoto <zabio1192@gmail.com> Tomohiro Kusumoto <zabio1192@gmail.com>
Troels Liebe Bentsen <tlb@nversion.dk>
Tõnis Tiigi <tonistiigi@gmail.com> Tõnis Tiigi <tonistiigi@gmail.com>
Valentin Lorentz <progval+git@progval.net>
Vasek - Tom C <tom.chauveau@epitech.eu>
Victor Vieux <victorvieux@gmail.com>
Victoria Bialas <victoria.bialas@docker.com>
Vincent Demeester <vincent.demeester@docker.com> Vincent Demeester <vincent.demeester@docker.com>
Vlad A. Ionescu <vladaionescu@users.noreply.github.com>
Vladislav Ivanov <vlad@ivanov.email>
Wang Yumu <37442693@qq.com>
Wei Fu <fuweid89@gmail.com> Wei Fu <fuweid89@gmail.com>
Wei Zhang <kweizh@gmail.com>
wingkwong <wingkwong.code@gmail.com>
Xiaofan Zhang <xiaofan.zhang@clinc.com>
Ximo Guanter <ximo.guanter@gmail.com>
Yamazaki Masashi <masi19bw@gmail.com>
Yan Song <imeoer@linux.alibaba.com>
Yong Tang <yong.tang.github@outlook.com> Yong Tang <yong.tang.github@outlook.com>
Yuichiro Kaneko <spiketeika@gmail.com> Yuichiro Kaneko <spiketeika@gmail.com>
Yurii Rashkovskii <yrashk@gmail.com>
Zach Badgett <zach.badgett@gmail.com>
zhangwenlong <zhangwenlong8911@163.com>
Ziv Tsarfati <digger18@gmail.com> Ziv Tsarfati <digger18@gmail.com>
岁丰 <genglu.gl@antfin.com>
沈陵 <shenling.yyb@alibaba-inc.com>
郑泽宇 <perhapszzy@sina.com> 郑泽宇 <perhapszzy@sina.com>

View File

@ -13,12 +13,14 @@ import (
const ( const (
keySyntax = "syntax" keySyntax = "syntax"
keyCheck = "check"
keyEscape = "escape" keyEscape = "escape"
) )
var validDirectives = map[string]struct{}{ var validDirectives = map[string]struct{}{
keySyntax: {}, keySyntax: {},
keyEscape: {}, keyEscape: {},
keyCheck: {},
} }
type Directive struct { type Directive struct {
@ -110,6 +112,10 @@ func (d *DirectiveParser) ParseAll(data []byte) ([]*Directive, error) {
// This allows for a flexible range of input formats, and appropriate syntax // This allows for a flexible range of input formats, and appropriate syntax
// selection. // selection.
func DetectSyntax(dt []byte) (string, string, []Range, bool) { func DetectSyntax(dt []byte) (string, string, []Range, bool) {
return ParseDirective(keySyntax, dt)
}
func ParseDirective(key string, dt []byte) (string, string, []Range, bool) {
dt, hadShebang, err := discardShebang(dt) dt, hadShebang, err := discardShebang(dt)
if err != nil { if err != nil {
return "", "", nil, false return "", "", nil, false
@ -119,42 +125,38 @@ func DetectSyntax(dt []byte) (string, string, []Range, bool) {
line++ line++
} }
// use default directive parser, and search for #syntax= // use default directive parser, and search for #key=
directiveParser := DirectiveParser{line: line} directiveParser := DirectiveParser{line: line}
if syntax, cmdline, loc, ok := detectSyntaxFromParser(dt, directiveParser); ok { if syntax, cmdline, loc, ok := detectDirectiveFromParser(key, dt, directiveParser); ok {
return syntax, cmdline, loc, true return syntax, cmdline, loc, true
} }
// use directive with different comment prefix, and search for //syntax= // use directive with different comment prefix, and search for //key=
directiveParser = DirectiveParser{line: line} directiveParser = DirectiveParser{line: line}
directiveParser.setComment("//") directiveParser.setComment("//")
if syntax, cmdline, loc, ok := detectSyntaxFromParser(dt, directiveParser); ok { if syntax, cmdline, loc, ok := detectDirectiveFromParser(key, dt, directiveParser); ok {
return syntax, cmdline, loc, true return syntax, cmdline, loc, true
} }
// search for possible json directives // use json directive, and search for { "key": "..." }
var directive struct { jsonDirective := map[string]string{}
Syntax string `json:"syntax"` if err := json.Unmarshal(dt, &jsonDirective); err == nil {
} if v, ok := jsonDirective[key]; ok {
if err := json.Unmarshal(dt, &directive); err == nil {
if directive.Syntax != "" {
loc := []Range{{ loc := []Range{{
Start: Position{Line: line}, Start: Position{Line: line},
End: Position{Line: line}, End: Position{Line: line},
}} }}
return directive.Syntax, directive.Syntax, loc, true return v, v, loc, true
} }
} }
return "", "", nil, false return "", "", nil, false
} }
func detectSyntaxFromParser(dt []byte, parser DirectiveParser) (string, string, []Range, bool) { func detectDirectiveFromParser(key string, dt []byte, parser DirectiveParser) (string, string, []Range, bool) {
directives, _ := parser.ParseAll(dt) directives, _ := parser.ParseAll(dt)
for _, d := range directives { for _, d := range directives {
// check for syntax directive before erroring out, since the error if d.Name == key {
// might have occurred *after* the syntax directive
if d.Name == keySyntax {
p, _, _ := strings.Cut(d.Value, " ") p, _, _ := strings.Cut(d.Value, " ")
return p, d.Value, d.Location, true return p, d.Value, d.Location, true
} }

View File

@ -7,7 +7,7 @@ import (
// ErrorLocation gives a location in source code that caused the error // ErrorLocation gives a location in source code that caused the error
type ErrorLocation struct { type ErrorLocation struct {
Location []Range Locations [][]Range
error error
} }
@ -39,11 +39,12 @@ func WithLocation(err error, location []Range) error {
} }
var el *ErrorLocation var el *ErrorLocation
if errors.As(err, &el) { if errors.As(err, &el) {
el.Locations = append(el.Locations, location)
return err return err
} }
return stack.Enable(&ErrorLocation{ return stack.Enable(&ErrorLocation{
error: err, error: err,
Location: location, Locations: [][]Range{location},
}) })
} }

View File

@ -17,6 +17,7 @@ import (
var ( var (
errDockerfileNotStringArray = errors.New("when using JSON array syntax, arrays must be comprised of strings only") errDockerfileNotStringArray = errors.New("when using JSON array syntax, arrays must be comprised of strings only")
errDockerfileNotJSONArray = errors.New("not a JSON array")
) )
const ( const (
@ -58,11 +59,11 @@ func parseWords(rest string, d *directives) []string {
words := []string{} words := []string{}
phase := inSpaces phase := inSpaces
word := ""
quote := '\000' quote := '\000'
blankOK := false blankOK := false
var ch rune var ch rune
var chWidth int var chWidth int
var sbuilder strings.Builder
for pos := 0; pos <= len(rest); pos += chWidth { for pos := 0; pos <= len(rest); pos += chWidth {
if pos != len(rest) { if pos != len(rest) {
@ -79,18 +80,18 @@ func parseWords(rest string, d *directives) []string {
phase = inWord // found it, fall through phase = inWord // found it, fall through
} }
if (phase == inWord || phase == inQuote) && (pos == len(rest)) { if (phase == inWord || phase == inQuote) && (pos == len(rest)) {
if blankOK || len(word) > 0 { if blankOK || sbuilder.Len() > 0 {
words = append(words, word) words = append(words, sbuilder.String())
} }
break break
} }
if phase == inWord { if phase == inWord {
if unicode.IsSpace(ch) { if unicode.IsSpace(ch) {
phase = inSpaces phase = inSpaces
if blankOK || len(word) > 0 { if blankOK || sbuilder.Len() > 0 {
words = append(words, word) words = append(words, sbuilder.String())
} }
word = "" sbuilder.Reset()
blankOK = false blankOK = false
continue continue
} }
@ -106,11 +107,11 @@ func parseWords(rest string, d *directives) []string {
// If we're not quoted and we see an escape token, then always just // If we're not quoted and we see an escape token, then always just
// add the escape token plus the char to the word, even if the char // add the escape token plus the char to the word, even if the char
// is a quote. // is a quote.
word += string(ch) sbuilder.WriteRune(ch)
pos += chWidth pos += chWidth
ch, chWidth = utf8.DecodeRuneInString(rest[pos:]) ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
} }
word += string(ch) sbuilder.WriteRune(ch)
continue continue
} }
if phase == inQuote { if phase == inQuote {
@ -124,10 +125,10 @@ func parseWords(rest string, d *directives) []string {
continue // just skip the escape token at end continue // just skip the escape token at end
} }
pos += chWidth pos += chWidth
word += string(ch) sbuilder.WriteRune(ch)
ch, chWidth = utf8.DecodeRuneInString(rest[pos:]) ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
} }
word += string(ch) sbuilder.WriteRune(ch)
} }
} }
@ -154,7 +155,7 @@ func parseNameVal(rest string, key string, d *directives) (*Node, error) {
if len(parts) < 2 { if len(parts) < 2 {
return nil, errors.Errorf("%s must have two arguments", key) return nil, errors.Errorf("%s must have two arguments", key)
} }
return newKeyValueNode(parts[0], parts[1]), nil return newKeyValueNode(parts[0], parts[1], ""), nil
} }
var rootNode *Node var rootNode *Node
@ -165,17 +166,20 @@ func parseNameVal(rest string, key string, d *directives) (*Node, error) {
} }
parts := strings.SplitN(word, "=", 2) parts := strings.SplitN(word, "=", 2)
node := newKeyValueNode(parts[0], parts[1]) node := newKeyValueNode(parts[0], parts[1], "=")
rootNode, prevNode = appendKeyValueNode(node, rootNode, prevNode) rootNode, prevNode = appendKeyValueNode(node, rootNode, prevNode)
} }
return rootNode, nil return rootNode, nil
} }
func newKeyValueNode(key, value string) *Node { func newKeyValueNode(key, value, sep string) *Node {
return &Node{ return &Node{
Value: key, Value: key,
Next: &Node{Value: value}, Next: &Node{
Value: value,
Next: &Node{Value: sep},
},
} }
} }
@ -187,7 +191,9 @@ func appendKeyValueNode(node, rootNode, prevNode *Node) (*Node, *Node) {
prevNode.Next = node prevNode.Next = node
} }
prevNode = node.Next for prevNode = node.Next; prevNode.Next != nil; {
prevNode = prevNode.Next
}
return rootNode, prevNode return rootNode, prevNode
} }
@ -269,14 +275,14 @@ func parseString(rest string, d *directives) (*Node, map[string]bool, error) {
} }
// parseJSON converts JSON arrays to an AST. // parseJSON converts JSON arrays to an AST.
func parseJSON(rest string, d *directives) (*Node, map[string]bool, error) { func parseJSON(rest string) (*Node, map[string]bool, error) {
rest = strings.TrimLeftFunc(rest, unicode.IsSpace) rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
if !strings.HasPrefix(rest, "[") { if !strings.HasPrefix(rest, "[") {
return nil, nil, errors.Errorf("Error parsing %q as a JSON array", rest) return nil, nil, errDockerfileNotJSONArray
} }
var myJSON []interface{} var myJSON []interface{}
if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil { if err := json.Unmarshal([]byte(rest), &myJSON); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -307,7 +313,7 @@ func parseMaybeJSON(rest string, d *directives) (*Node, map[string]bool, error)
return nil, nil, nil return nil, nil, nil
} }
node, attrs, err := parseJSON(rest, d) node, attrs, err := parseJSON(rest)
if err == nil { if err == nil {
return node, attrs, nil return node, attrs, nil
@ -325,7 +331,7 @@ func parseMaybeJSON(rest string, d *directives) (*Node, map[string]bool, error)
// so, passes to parseJSON; if not, attempts to parse it as a whitespace // so, passes to parseJSON; if not, attempts to parse it as a whitespace
// delimited string. // delimited string.
func parseMaybeJSONToList(rest string, d *directives) (*Node, map[string]bool, error) { func parseMaybeJSONToList(rest string, d *directives) (*Node, map[string]bool, error) {
node, attrs, err := parseJSON(rest, d) node, attrs, err := parseJSON(rest)
if err == nil { if err == nil {
return node, attrs, nil return node, attrs, nil

View File

@ -114,7 +114,6 @@ type Heredoc struct {
var ( var (
dispatch map[string]func(string, *directives) (*Node, map[string]bool, error) dispatch map[string]func(string, *directives) (*Node, map[string]bool, error)
reWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`) reWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`)
reComment = regexp.MustCompile(`^#.*$`)
reHeredoc = regexp.MustCompile(`^(\d*)<<(-?)([^<]*)$`) reHeredoc = regexp.MustCompile(`^(\d*)<<(-?)([^<]*)$`)
reLeadingTabs = regexp.MustCompile(`(?m)^\t+`) reLeadingTabs = regexp.MustCompile(`(?m)^\t+`)
) )
@ -169,8 +168,8 @@ func (d *directives) setEscapeToken(s string) error {
// possibleParserDirective looks for parser directives, eg '# escapeToken=<char>'. // possibleParserDirective looks for parser directives, eg '# escapeToken=<char>'.
// Parser directives must precede any builder instruction or other comments, // Parser directives must precede any builder instruction or other comments,
// and cannot be repeated. // and cannot be repeated.
func (d *directives) possibleParserDirective(line string) error { func (d *directives) possibleParserDirective(line []byte) error {
directive, err := d.parser.ParseLine([]byte(line)) directive, err := d.parser.ParseLine(line)
if err != nil { if err != nil {
return err return err
} }
@ -284,6 +283,7 @@ func Parse(rwc io.Reader) (*Result, error) {
scanner.Split(scanLines) scanner.Split(scanLines)
warnings := []Warning{} warnings := []Warning{}
var comments []string var comments []string
buf := &bytes.Buffer{}
var err error var err error
for scanner.Scan() { for scanner.Scan() {
@ -307,10 +307,12 @@ func Parse(rwc io.Reader) (*Result, error) {
currentLine++ currentLine++
startLine := currentLine startLine := currentLine
line, isEndOfLine := trimContinuationCharacter(string(bytesRead), d) bytesRead, isEndOfLine := trimContinuationCharacter(bytesRead, d)
if isEndOfLine && line == "" { if isEndOfLine && len(bytesRead) == 0 {
continue continue
} }
buf.Reset()
buf.Write(bytesRead)
var hasEmptyContinuationLine bool var hasEmptyContinuationLine bool
for !isEndOfLine && scanner.Scan() { for !isEndOfLine && scanner.Scan() {
@ -329,16 +331,17 @@ func Parse(rwc io.Reader) (*Result, error) {
continue continue
} }
continuationLine := string(bytesRead) bytesRead, isEndOfLine = trimContinuationCharacter(bytesRead, d)
continuationLine, isEndOfLine = trimContinuationCharacter(continuationLine, d) buf.Write(bytesRead)
line += continuationLine
} }
line := buf.String()
if hasEmptyContinuationLine { if hasEmptyContinuationLine {
warnings = append(warnings, Warning{ warnings = append(warnings, Warning{
Short: "Empty continuation line found in: " + line, Short: "Empty continuation line found in: " + line,
Detail: [][]byte{[]byte("Empty continuation lines will become errors in a future release")}, Detail: [][]byte{[]byte("Empty continuation lines will become errors in a future release")},
URL: "https://github.com/moby/moby/pull/33719", URL: "https://docs.docker.com/go/dockerfile/rule/no-empty-continuation/",
Location: &Range{Start: Position{Line: currentLine}, End: Position{Line: currentLine}}, Location: &Range{Start: Position{Line: currentLine}, End: Position{Line: currentLine}},
}) })
} }
@ -348,7 +351,7 @@ func Parse(rwc io.Reader) (*Result, error) {
return nil, withLocation(err, startLine, currentLine) return nil, withLocation(err, startLine, currentLine)
} }
if child.canContainHeredoc() { if child.canContainHeredoc() && strings.Contains(line, "<<") {
heredocs, err := heredocsFromLine(line) heredocs, err := heredocsFromLine(line)
if err != nil { if err != nil {
return nil, withLocation(err, startLine, currentLine) return nil, withLocation(err, startLine, currentLine)
@ -415,7 +418,7 @@ func heredocFromMatch(match []string) (*Heredoc, error) {
// If there are quotes in one but not the other, then we know that some // If there are quotes in one but not the other, then we know that some
// part of the heredoc word is quoted, so we shouldn't expand the content. // part of the heredoc word is quoted, so we shouldn't expand the content.
shlex.RawQuotes = false shlex.RawQuotes = false
words, err := shlex.ProcessWords(rest, []string{}) words, err := shlex.ProcessWords(rest, emptyEnvs{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -425,7 +428,7 @@ func heredocFromMatch(match []string) (*Heredoc, error) {
} }
shlex.RawQuotes = true shlex.RawQuotes = true
wordsRaw, err := shlex.ProcessWords(rest, []string{}) wordsRaw, err := shlex.ProcessWords(rest, emptyEnvs{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -466,7 +469,7 @@ func heredocsFromLine(line string) ([]Heredoc, error) {
shlex.RawQuotes = true shlex.RawQuotes = true
shlex.RawEscapes = true shlex.RawEscapes = true
shlex.SkipUnsetEnv = true shlex.SkipUnsetEnv = true
words, _ := shlex.ProcessWords(line, []string{}) words, _ := shlex.ProcessWords(line, emptyEnvs{})
var docs []Heredoc var docs []Heredoc
for _, word := range words { for _, word := range words {
@ -487,7 +490,10 @@ func ChompHeredocContent(src string) string {
} }
func trimComments(src []byte) []byte { func trimComments(src []byte) []byte {
return reComment.ReplaceAll(src, []byte{}) if !isComment(src) {
return src
}
return nil
} }
func trimLeadingWhitespace(src []byte) []byte { func trimLeadingWhitespace(src []byte) []byte {
@ -501,7 +507,8 @@ func trimNewline(src []byte) []byte {
} }
func isComment(line []byte) bool { func isComment(line []byte) bool {
return reComment.Match(trimLeadingWhitespace(trimNewline(line))) line = trimLeadingWhitespace(line)
return len(line) > 0 && line[0] == '#'
} }
func isEmptyContinuationLine(line []byte) bool { func isEmptyContinuationLine(line []byte) bool {
@ -510,9 +517,9 @@ func isEmptyContinuationLine(line []byte) bool {
var utf8bom = []byte{0xEF, 0xBB, 0xBF} var utf8bom = []byte{0xEF, 0xBB, 0xBF}
func trimContinuationCharacter(line string, d *directives) (string, bool) { func trimContinuationCharacter(line []byte, d *directives) ([]byte, bool) {
if d.lineContinuationRegex.MatchString(line) { if d.lineContinuationRegex.Match(line) {
line = d.lineContinuationRegex.ReplaceAllString(line, "$1") line = d.lineContinuationRegex.ReplaceAll(line, []byte("$1"))
return line, false return line, false
} }
return line, true return line, true
@ -525,7 +532,7 @@ func processLine(d *directives, token []byte, stripLeftWhitespace bool) ([]byte,
if stripLeftWhitespace { if stripLeftWhitespace {
token = trimLeadingWhitespace(token) token = trimLeadingWhitespace(token)
} }
return trimComments(token), d.possibleParserDirective(string(token)) return trimComments(token), d.possibleParserDirective(token)
} }
// Variation of bufio.ScanLines that preserves the line endings // Variation of bufio.ScanLines that preserves the line endings
@ -550,3 +557,13 @@ func handleScannerError(err error) error {
return err return err
} }
} }
type emptyEnvs struct{}
func (emptyEnvs) Get(string) (string, bool) {
return "", false
}
func (emptyEnvs) Keys() []string {
return nil
}

View File

@ -36,7 +36,7 @@ func extractBuilderFlags(line string) (string, []string, error) {
words := []string{} words := []string{}
phase := inSpaces phase := inSpaces
word := "" sbuilder := &strings.Builder{}
quote := '\000' quote := '\000'
blankOK := false blankOK := false
var ch rune var ch rune
@ -62,13 +62,14 @@ func extractBuilderFlags(line string) (string, []string, error) {
phase = inWord // found something with "--", fall through phase = inWord // found something with "--", fall through
} }
if (phase == inWord || phase == inQuote) && (pos == len(line)) { if (phase == inWord || phase == inQuote) && (pos == len(line)) {
if word != "--" && (blankOK || len(word) > 0) { if word := sbuilder.String(); word != "--" && (blankOK || len(word) > 0) {
words = append(words, word) words = append(words, word)
} }
break break
} }
if phase == inWord { if phase == inWord {
if unicode.IsSpace(ch) { if unicode.IsSpace(ch) {
word := sbuilder.String()
phase = inSpaces phase = inSpaces
if word == "--" { if word == "--" {
return line[pos:], words, nil return line[pos:], words, nil
@ -76,7 +77,7 @@ func extractBuilderFlags(line string) (string, []string, error) {
if blankOK || len(word) > 0 { if blankOK || len(word) > 0 {
words = append(words, word) words = append(words, word)
} }
word = "" sbuilder.Reset()
blankOK = false blankOK = false
continue continue
} }
@ -93,7 +94,9 @@ func extractBuilderFlags(line string) (string, []string, error) {
pos++ pos++
ch = rune(line[pos]) ch = rune(line[pos])
} }
word += string(ch) if _, err := sbuilder.WriteRune(ch); err != nil {
return "", nil, err
}
continue continue
} }
if phase == inQuote { if phase == inQuote {
@ -109,7 +112,9 @@ func extractBuilderFlags(line string) (string, []string, error) {
pos++ pos++
ch = rune(line[pos]) ch = rune(line[pos])
} }
word += string(ch) if _, err := sbuilder.WriteRune(ch); err != nil {
return "", nil, err
}
} }
} }

View File

@ -9,3 +9,10 @@ package shell
func EqualEnvKeys(from, to string) bool { func EqualEnvKeys(from, to string) bool {
return from == to return from == to
} }
// NormalizeEnvKey returns the key in a normalized form that can be used
// for comparison. On Unix this is a no-op. On Windows this converts the
// key to uppercase.
func NormalizeEnvKey(key string) string {
return key
}

View File

@ -8,3 +8,10 @@ import "strings"
func EqualEnvKeys(from, to string) bool { func EqualEnvKeys(from, to string) bool {
return strings.EqualFold(from, to) return strings.EqualFold(from, to)
} }
// NormalizeEnvKey returns the key in a normalized form that can be used
// for comparison. On Unix this is a no-op. On Windows this converts the
// key to uppercase.
func NormalizeEnvKey(key string) string {
return strings.ToUpper(key)
}

View File

@ -3,6 +3,8 @@ package shell
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"regexp"
"slices"
"strings" "strings"
"text/scanner" "text/scanner"
"unicode" "unicode"
@ -10,6 +12,11 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type EnvGetter interface {
Get(string) (string, bool)
Keys() []string
}
// Lex performs shell word splitting and variable expansion. // Lex performs shell word splitting and variable expansion.
// //
// Lex takes a string and an array of env variables and // Lex takes a string and an array of env variables and
@ -17,12 +24,15 @@ import (
// tokens. Tries to mimic bash shell process. // tokens. Tries to mimic bash shell process.
// It doesn't support all flavors of ${xx:...} formats but new ones can // It doesn't support all flavors of ${xx:...} formats but new ones can
// be added by adding code to the "special ${} format processing" section // be added by adding code to the "special ${} format processing" section
//
// It is not safe to call methods on a Lex instance concurrently.
type Lex struct { type Lex struct {
escapeToken rune escapeToken rune
RawQuotes bool RawQuotes bool
RawEscapes bool RawEscapes bool
SkipProcessQuotes bool SkipProcessQuotes bool
SkipUnsetEnv bool SkipUnsetEnv bool
shellWord shellWord
} }
// NewLex creates a new Lex which uses escapeToken to escape quotes. // NewLex creates a new Lex which uses escapeToken to escape quotes.
@ -31,10 +41,13 @@ func NewLex(escapeToken rune) *Lex {
} }
// ProcessWord will use the 'env' list of environment variables, // ProcessWord will use the 'env' list of environment variables,
// and replace any env var references in 'word'. // and replace any env var references in 'word'. It will also
func (s *Lex) ProcessWord(word string, env []string) (string, error) { // return variables in word which were not found in the 'env' list,
word, _, err := s.process(word, BuildEnvs(env)) // which is useful in later linting.
return word, err // TODO: rename
func (s *Lex) ProcessWord(word string, env EnvGetter) (string, map[string]struct{}, error) {
result, err := s.process(word, env, true)
return result.Result, result.Unmatched, err
} }
// ProcessWords will use the 'env' list of environment variables, // ProcessWords will use the 'env' list of environment variables,
@ -44,63 +57,62 @@ func (s *Lex) ProcessWord(word string, env []string) (string, error) {
// this splitting is done **after** the env var substitutions are done. // this splitting is done **after** the env var substitutions are done.
// Note, each one is trimmed to remove leading and trailing spaces (unless // Note, each one is trimmed to remove leading and trailing spaces (unless
// they are quoted", but ProcessWord retains spaces between words. // they are quoted", but ProcessWord retains spaces between words.
func (s *Lex) ProcessWords(word string, env []string) ([]string, error) { func (s *Lex) ProcessWords(word string, env EnvGetter) ([]string, error) {
_, words, err := s.process(word, BuildEnvs(env)) result, err := s.process(word, env, false)
return words, err return result.Words, err
} }
// ProcessWordWithMap will use the 'env' list of environment variables, type ProcessWordResult struct {
// and replace any env var references in 'word'. Result string
func (s *Lex) ProcessWordWithMap(word string, env map[string]string) (string, error) { Words []string
word, _, err := s.process(word, env) Matched map[string]struct{}
return word, err Unmatched map[string]struct{}
} }
// ProcessWordWithMatches will use the 'env' list of environment variables, // ProcessWordWithMatches will use the 'env' list of environment variables,
// replace any env var references in 'word' and return the env that were used. // replace any env var references in 'word' and return the env that were used.
func (s *Lex) ProcessWordWithMatches(word string, env map[string]string) (string, map[string]struct{}, error) { func (s *Lex) ProcessWordWithMatches(word string, env EnvGetter) (ProcessWordResult, error) {
sw := s.init(word, env) return s.process(word, env, true)
word, _, err := sw.process(word)
return word, sw.matches, err
} }
func (s *Lex) ProcessWordsWithMap(word string, env map[string]string) ([]string, error) { func (s *Lex) initWord(word string, env EnvGetter, capture bool) *shellWord {
_, words, err := s.process(word, env) sw := &s.shellWord
return words, err sw.Lex = s
} sw.envs = env
sw.capture = capture
func (s *Lex) init(word string, env map[string]string) *shellWord { sw.rawEscapes = s.RawEscapes
sw := &shellWord{ if capture {
envs: env, sw.matches = nil
escapeToken: s.escapeToken, sw.nonmatches = nil
skipUnsetEnv: s.SkipUnsetEnv,
skipProcessQuotes: s.SkipProcessQuotes,
rawQuotes: s.RawQuotes,
rawEscapes: s.RawEscapes,
matches: make(map[string]struct{}),
} }
sw.scanner.Init(strings.NewReader(word)) sw.scanner.Init(strings.NewReader(word))
return sw return sw
} }
func (s *Lex) process(word string, env map[string]string) (string, []string, error) { func (s *Lex) process(word string, env EnvGetter, capture bool) (ProcessWordResult, error) {
sw := s.init(word, env) sw := s.initWord(word, env, capture)
return sw.process(word) word, words, err := sw.process(word)
return ProcessWordResult{
Result: word,
Words: words,
Matched: sw.matches,
Unmatched: sw.nonmatches,
}, err
} }
type shellWord struct { type shellWord struct {
scanner scanner.Scanner *Lex
envs map[string]string wordsBuffer strings.Builder
escapeToken rune scanner scanner.Scanner
rawQuotes bool envs EnvGetter
rawEscapes bool rawEscapes bool
skipUnsetEnv bool capture bool // capture matches and nonmatches
skipProcessQuotes bool matches map[string]struct{}
matches map[string]struct{} nonmatches map[string]struct{}
} }
func (sw *shellWord) process(source string) (string, []string, error) { func (sw *shellWord) process(source string) (string, []string, error) {
word, words, err := sw.processStopOn(scanner.EOF) word, words, err := sw.processStopOn(scanner.EOF, sw.rawEscapes)
if err != nil { if err != nil {
err = errors.Wrapf(err, "failed to process %q", source) err = errors.Wrapf(err, "failed to process %q", source)
} }
@ -108,16 +120,16 @@ func (sw *shellWord) process(source string) (string, []string, error) {
} }
type wordsStruct struct { type wordsStruct struct {
word string buf *strings.Builder
words []string words []string
inWord bool inWord bool
} }
func (w *wordsStruct) addChar(ch rune) { func (w *wordsStruct) addChar(ch rune) {
if unicode.IsSpace(ch) && w.inWord { if unicode.IsSpace(ch) && w.inWord {
if len(w.word) != 0 { if w.buf.Len() != 0 {
w.words = append(w.words, w.word) w.words = append(w.words, w.buf.String())
w.word = "" w.buf.Reset()
w.inWord = false w.inWord = false
} }
} else if !unicode.IsSpace(ch) { } else if !unicode.IsSpace(ch) {
@ -126,7 +138,7 @@ func (w *wordsStruct) addChar(ch rune) {
} }
func (w *wordsStruct) addRawChar(ch rune) { func (w *wordsStruct) addRawChar(ch rune) {
w.word += string(ch) w.buf.WriteRune(ch)
w.inWord = true w.inWord = true
} }
@ -137,16 +149,16 @@ func (w *wordsStruct) addString(str string) {
} }
func (w *wordsStruct) addRawString(str string) { func (w *wordsStruct) addRawString(str string) {
w.word += str w.buf.WriteString(str)
w.inWord = true w.inWord = true
} }
func (w *wordsStruct) getWords() []string { func (w *wordsStruct) getWords() []string {
if len(w.word) > 0 { if w.buf.Len() > 0 {
w.words = append(w.words, w.word) w.words = append(w.words, w.buf.String())
// Just in case we're called again by mistake // Just in case we're called again by mistake
w.word = "" w.buf.Reset()
w.inWord = false w.inWord = false
} }
return w.words return w.words
@ -154,18 +166,31 @@ func (w *wordsStruct) getWords() []string {
// Process the word, starting at 'pos', and stop when we get to the // Process the word, starting at 'pos', and stop when we get to the
// end of the word or the 'stopChar' character // end of the word or the 'stopChar' character
func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) { func (sw *shellWord) processStopOn(stopChar rune, rawEscapes bool) (string, []string, error) {
var result bytes.Buffer // result buffer can't be currently shared for shellWord as it is called internally
// by processDollar
var result strings.Builder
sw.wordsBuffer.Reset()
var words wordsStruct var words wordsStruct
words.buf = &sw.wordsBuffer
// no need to initialize all the time
var charFuncMapping = map[rune]func() (string, error){ var charFuncMapping = map[rune]func() (string, error){
'$': sw.processDollar, '$': sw.processDollar,
} }
if !sw.skipProcessQuotes { if !sw.SkipProcessQuotes {
charFuncMapping['\''] = sw.processSingleQuote charFuncMapping['\''] = sw.processSingleQuote
charFuncMapping['"'] = sw.processDoubleQuote charFuncMapping['"'] = sw.processDoubleQuote
} }
// temporarily set sw.rawEscapes if needed
if rawEscapes != sw.rawEscapes {
sw.rawEscapes = rawEscapes
defer func() {
sw.rawEscapes = !rawEscapes
}()
}
for sw.scanner.Peek() != scanner.EOF { for sw.scanner.Peek() != scanner.EOF {
ch := sw.scanner.Peek() ch := sw.scanner.Peek()
@ -230,7 +255,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
var result bytes.Buffer var result bytes.Buffer
ch := sw.scanner.Next() ch := sw.scanner.Next()
if sw.rawQuotes { if sw.RawQuotes {
result.WriteRune(ch) result.WriteRune(ch)
} }
@ -240,7 +265,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
case scanner.EOF: case scanner.EOF:
return "", errors.New("unexpected end of statement while looking for matching single-quote") return "", errors.New("unexpected end of statement while looking for matching single-quote")
case '\'': case '\'':
if sw.rawQuotes { if sw.RawQuotes {
result.WriteRune(ch) result.WriteRune(ch)
} }
return result.String(), nil return result.String(), nil
@ -265,7 +290,7 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
var result bytes.Buffer var result bytes.Buffer
ch := sw.scanner.Next() ch := sw.scanner.Next()
if sw.rawQuotes { if sw.RawQuotes {
result.WriteRune(ch) result.WriteRune(ch)
} }
@ -275,7 +300,7 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
return "", errors.New("unexpected end of statement while looking for matching double-quote") return "", errors.New("unexpected end of statement while looking for matching double-quote")
case '"': case '"':
ch := sw.scanner.Next() ch := sw.scanner.Next()
if sw.rawQuotes { if sw.RawQuotes {
result.WriteRune(ch) result.WriteRune(ch)
} }
return result.String(), nil return result.String(), nil
@ -319,7 +344,7 @@ func (sw *shellWord) processDollar() (string, error) {
return "$", nil return "$", nil
} }
value, found := sw.getEnv(name) value, found := sw.getEnv(name)
if !found && sw.skipUnsetEnv { if !found && sw.SkipUnsetEnv {
return "$" + name, nil return "$" + name, nil
} }
return value, nil return value, nil
@ -342,7 +367,7 @@ func (sw *shellWord) processDollar() (string, error) {
case '}': case '}':
// Normal ${xx} case // Normal ${xx} case
value, set := sw.getEnv(name) value, set := sw.getEnv(name)
if !set && sw.skipUnsetEnv { if !set && sw.SkipUnsetEnv {
return fmt.Sprintf("${%s}", name), nil return fmt.Sprintf("${%s}", name), nil
} }
return value, nil return value, nil
@ -351,8 +376,9 @@ func (sw *shellWord) processDollar() (string, error) {
ch = sw.scanner.Next() ch = sw.scanner.Next()
chs += string(ch) chs += string(ch)
fallthrough fallthrough
case '+', '-', '?': case '+', '-', '?', '#', '%':
word, _, err := sw.processStopOn('}') rawEscapes := ch == '#' || ch == '%'
word, _, err := sw.processStopOn('}', rawEscapes)
if err != nil { if err != nil {
if sw.scanner.Peek() == scanner.EOF { if sw.scanner.Peek() == scanner.EOF {
return "", errors.New("syntax error: missing '}'") return "", errors.New("syntax error: missing '}'")
@ -363,7 +389,7 @@ func (sw *shellWord) processDollar() (string, error) {
// Grab the current value of the variable in question so we // Grab the current value of the variable in question so we
// can use it to determine what to do based on the modifier // can use it to determine what to do based on the modifier
value, set := sw.getEnv(name) value, set := sw.getEnv(name)
if sw.skipUnsetEnv && !set { if sw.SkipUnsetEnv && !set {
return fmt.Sprintf("${%s%s%s}", name, chs, word), nil return fmt.Sprintf("${%s%s%s}", name, chs, word), nil
} }
@ -394,9 +420,61 @@ func (sw *shellWord) processDollar() (string, error) {
return "", errors.Errorf("%s: %s", name, message) return "", errors.Errorf("%s: %s", name, message)
} }
return value, nil return value, nil
case '%', '#':
// %/# matches the shortest pattern expansion, %%/## the longest
greedy := false
if len(word) > 0 && word[0] == byte(ch) {
greedy = true
word = word[1:]
}
if ch == '%' {
return trimSuffix(word, value, greedy)
}
return trimPrefix(word, value, greedy)
default: default:
return "", errors.Errorf("unsupported modifier (%s) in substitution", chs) return "", errors.Errorf("unsupported modifier (%s) in substitution", chs)
} }
case '/':
replaceAll := sw.scanner.Peek() == '/'
if replaceAll {
sw.scanner.Next()
}
pattern, _, err := sw.processStopOn('/', true)
if err != nil {
if sw.scanner.Peek() == scanner.EOF {
return "", errors.New("syntax error: missing '/' in ${}")
}
return "", err
}
replacement, _, err := sw.processStopOn('}', true)
if err != nil {
if sw.scanner.Peek() == scanner.EOF {
return "", errors.New("syntax error: missing '}'")
}
return "", err
}
value, set := sw.getEnv(name)
if sw.SkipUnsetEnv && !set {
return fmt.Sprintf("${%s/%s/%s}", name, pattern, replacement), nil
}
re, err := convertShellPatternToRegex(pattern, true, false)
if err != nil {
return "", errors.Errorf("invalid pattern (%s) in substitution: %s", pattern, err)
}
if replaceAll {
value = re.ReplaceAllString(value, replacement)
} else {
if idx := re.FindStringIndex(value); idx != nil {
value = value[0:idx[0]] + replacement + value[idx[1]:]
}
}
return value, nil
default: default:
return "", errors.Errorf("unsupported modifier (%s) in substitution", chs) return "", errors.Errorf("unsupported modifier (%s) in substitution", chs)
} }
@ -444,31 +522,155 @@ func isSpecialParam(char rune) bool {
} }
func (sw *shellWord) getEnv(name string) (string, bool) { func (sw *shellWord) getEnv(name string) (string, bool) {
for key, value := range sw.envs { v, ok := sw.envs.Get(name)
if EqualEnvKeys(name, key) { if ok {
if sw.capture {
if sw.matches == nil {
sw.matches = make(map[string]struct{})
}
sw.matches[name] = struct{}{} sw.matches[name] = struct{}{}
return value, true
} }
return v, true
}
if sw.capture {
if sw.nonmatches == nil {
sw.nonmatches = make(map[string]struct{})
}
sw.nonmatches[name] = struct{}{}
} }
return "", false return "", false
} }
func BuildEnvs(env []string) map[string]string { func EnvsFromSlice(env []string) EnvGetter {
envs := map[string]string{} envs := map[string]string{}
keys := make([]string, 0, len(env))
for _, e := range env { for _, e := range env {
i := strings.Index(e, "=") k, v, _ := strings.Cut(e, "=")
keys = append(keys, k)
envs[NormalizeEnvKey(k)] = v
}
return &envGetter{env: envs, keys: keys}
}
if i < 0 { type envGetter struct {
envs[e] = "" env map[string]string
} else { keys []string
k := e[:i] }
v := e[i+1:]
// overwrite value if key already exists var _ EnvGetter = &envGetter{}
envs[k] = v
} func (e *envGetter) Get(key string) (string, bool) {
key = NormalizeEnvKey(key)
v, ok := e.env[key]
return v, ok
}
func (e *envGetter) Keys() []string {
return e.keys
}
// convertShellPatternToRegex converts a shell-like wildcard pattern
// (? is a single char, * either the shortest or longest (greedy) string)
// to an equivalent regular expression.
//
// Based on
// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
// but without the bracket expressions (`[]`)
func convertShellPatternToRegex(pattern string, greedy bool, anchored bool) (*regexp.Regexp, error) {
var s scanner.Scanner
s.Init(strings.NewReader(pattern))
var out strings.Builder
out.Grow(len(pattern) + 4)
// match only at the beginning of the string
if anchored {
out.WriteByte('^')
} }
return envs // default: non-greedy wildcards
starPattern := ".*?"
if greedy {
starPattern = ".*"
}
for tok := s.Next(); tok != scanner.EOF; tok = s.Next() {
switch tok {
case '*':
out.WriteString(starPattern)
continue
case '?':
out.WriteByte('.')
continue
case '\\':
// } and / as part of ${} need to be escaped, but the escape isn't part
// of the pattern
if s.Peek() == '}' || s.Peek() == '/' {
continue
}
out.WriteRune('\\')
tok = s.Next()
if tok != '*' && tok != '?' && tok != '\\' {
return nil, errors.Errorf("invalid escape '\\%c'", tok)
}
// regex characters that need to be escaped
// escaping closing is optional, but done for consistency
case '[', ']', '{', '}', '.', '+', '(', ')', '|', '^', '$':
out.WriteByte('\\')
}
out.WriteRune(tok)
}
return regexp.Compile(out.String())
}
func trimPrefix(word, value string, greedy bool) (string, error) {
re, err := convertShellPatternToRegex(word, greedy, true)
if err != nil {
return "", errors.Errorf("invalid pattern (%s) in substitution: %s", word, err)
}
if idx := re.FindStringIndex(value); idx != nil {
value = value[idx[1]:]
}
return value, nil
}
// reverse without avoid reversing escapes, i.e. a\*c -> c\*a
func reversePattern(pattern string) string {
patternRunes := []rune(pattern)
out := make([]rune, len(patternRunes))
lastIdx := len(patternRunes) - 1
for i := 0; i <= lastIdx; {
tok := patternRunes[i]
outIdx := lastIdx - i
if tok == '\\' && i != lastIdx {
out[outIdx-1] = tok
// the pattern is taken from a ${var#pattern}, so the last
// character can't be an escape character
out[outIdx] = patternRunes[i+1]
i += 2
} else {
out[outIdx] = tok
i++
}
}
return string(out)
}
func reverseString(str string) string {
out := []rune(str)
slices.Reverse(out)
return string(out)
}
func trimSuffix(pattern, word string, greedy bool) (string, error) {
// regular expressions can't handle finding the shortest rightmost
// string so we reverse both search space and pattern to convert it
// to a leftmost search in both cases
pattern = reversePattern(pattern)
word = reverseString(word)
str, err := trimPrefix(pattern, word, greedy)
if err != nil {
return "", err
}
return reverseString(str), nil
} }

View File

@ -46,22 +46,23 @@ func Helper() {
func Traces(err error) []*Stack { func Traces(err error) []*Stack {
var st []*Stack var st []*Stack
wrapped, ok := err.(interface { switch e := err.(type) {
Unwrap() error case interface{ Unwrap() error }:
}) st = Traces(e.Unwrap())
if ok { case interface{ Unwrap() []error }:
st = Traces(wrapped.Unwrap()) for _, ue := range e.Unwrap() {
st = Traces(ue)
// Only take first stack
if len(st) > 0 {
break
}
}
} }
if ste, ok := err.(interface { switch ste := err.(type) {
StackTrace() errors.StackTrace case interface{ StackTrace() errors.StackTrace }:
}); ok {
st = append(st, convertStack(ste.StackTrace())) st = append(st, convertStack(ste.StackTrace()))
} case interface{ StackTrace() *Stack }:
if ste, ok := err.(interface {
StackTrace() *Stack
}); ok {
st = append(st, ste.StackTrace()) st = append(st, ste.StackTrace())
} }

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.30.0 // protoc-gen-go v1.33.0
// protoc v3.11.4 // protoc v3.11.4
// source: stack.proto // source: stack.proto

View File

@ -3,15 +3,15 @@ syntax = "proto3";
package stack; package stack;
message Stack { message Stack {
repeated Frame frames = 1; repeated Frame frames = 1;
repeated string cmdline = 2; repeated string cmdline = 2;
int32 pid = 3; int32 pid = 3;
string version = 4; string version = 4;
string revision = 5; string revision = 5;
} }
message Frame { message Frame {
string Name = 1; string Name = 1;
string File = 2; string File = 2;
int32 Line = 3; int32 Line = 3;
} }

View File

@ -6,7 +6,7 @@ services:
- docker - docker
go: go:
- "1.20" - "1.21.13"
before_install: before_install:
- sudo systemctl stop docker.service && sudo systemctl stop docker.socket - sudo systemctl stop docker.service && sudo systemctl stop docker.socket

View File

@ -18,9 +18,10 @@ import (
docker "github.com/fsouza/go-dockerclient" docker "github.com/fsouza/go-dockerclient"
"github.com/containerd/containerd/errdefs" "github.com/containerd/errdefs"
"github.com/containerd/containerd/platforms" "github.com/containerd/platforms"
"github.com/containers/storage/pkg/regexp" "github.com/containers/storage/pkg/regexp"
"github.com/openshift/imagebuilder/internal"
"github.com/openshift/imagebuilder/signal" "github.com/openshift/imagebuilder/signal"
"github.com/openshift/imagebuilder/strslice" "github.com/openshift/imagebuilder/strslice"
@ -143,7 +144,7 @@ func processHereDocs(instruction, originalInstruction string, heredocs []buildki
shlex := buildkitshell.NewLex('\\') shlex := buildkitshell.NewLex('\\')
shlex.RawQuotes = true shlex.RawQuotes = true
shlex.RawEscapes = true shlex.RawEscapes = true
content, err = shlex.ProcessWord(content, args) content, _, err = shlex.ProcessWord(content, internal.EnvironmentSlice(args))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,6 +18,7 @@ import (
buildkitparser "github.com/moby/buildkit/frontend/dockerfile/parser" buildkitparser "github.com/moby/buildkit/frontend/dockerfile/parser"
buildkitshell "github.com/moby/buildkit/frontend/dockerfile/shell" buildkitshell "github.com/moby/buildkit/frontend/dockerfile/shell"
"github.com/openshift/imagebuilder/dockerfile/command" "github.com/openshift/imagebuilder/dockerfile/command"
"github.com/openshift/imagebuilder/internal"
) )
// Node is a structure used to represent a parse tree. // Node is a structure used to represent a parse tree.
@ -408,7 +409,7 @@ func heredocsFromLine(line string) ([]buildkitparser.Heredoc, error) {
shlex.RawQuotes = true shlex.RawQuotes = true
shlex.RawEscapes = true shlex.RawEscapes = true
shlex.SkipUnsetEnv = true shlex.SkipUnsetEnv = true
words, _ := shlex.ProcessWords(line, []string{}) words, _ := shlex.ProcessWords(line, internal.EnvironmentSlice([]string{}))
var docs []buildkitparser.Heredoc var docs []buildkitparser.Heredoc
for _, word := range words { for _, word := range words {

View File

@ -12,7 +12,7 @@
# #
%global golang_version 1.19 %global golang_version 1.19
%{!?version: %global version 1.2.14} %{!?version: %global version 1.2.15}
%{!?release: %global release 1} %{!?release: %global release 1}
%global package_name imagebuilder %global package_name imagebuilder
%global product_name Container Image Builder %global product_name Container Image Builder

View File

@ -0,0 +1,23 @@
package internal
import "strings"
type EnvironmentSlice []string
func (e EnvironmentSlice) Keys() []string {
keys := make([]string, 0, len(e))
for _, kv := range e {
k, _, _ := strings.Cut(kv, "=")
keys = append(keys, k)
}
return keys
}
func (e EnvironmentSlice) Get(key string) (string, bool) {
for _, kv := range e {
if k, v, ok := strings.Cut(kv, "="); ok && k == key {
return v, true
}
}
return "", false
}

17
vendor/modules.txt vendored
View File

@ -107,9 +107,8 @@ github.com/chzyer/readline
# github.com/containerd/cgroups/v3 v3.0.3 # github.com/containerd/cgroups/v3 v3.0.3
## explicit; go 1.18 ## explicit; go 1.18
github.com/containerd/cgroups/v3/cgroup1/stats github.com/containerd/cgroups/v3/cgroup1/stats
# github.com/containerd/containerd v1.7.18 # github.com/containerd/containerd v1.7.20
## explicit; go 1.21 ## explicit; go 1.21
github.com/containerd/containerd/errdefs
github.com/containerd/containerd/platforms github.com/containerd/containerd/platforms
# github.com/containerd/errdefs v0.1.0 # github.com/containerd/errdefs v0.1.0
## explicit; go 1.20 ## explicit; go 1.20
@ -117,6 +116,9 @@ github.com/containerd/errdefs
# github.com/containerd/log v0.1.0 # github.com/containerd/log v0.1.0
## explicit; go 1.20 ## explicit; go 1.20
github.com/containerd/log github.com/containerd/log
# github.com/containerd/platforms v0.2.1
## explicit; go 1.20
github.com/containerd/platforms
# github.com/containerd/stargz-snapshotter/estargz v0.15.1 # github.com/containerd/stargz-snapshotter/estargz v0.15.1
## explicit; go 1.19 ## explicit; go 1.19
github.com/containerd/stargz-snapshotter/estargz github.com/containerd/stargz-snapshotter/estargz
@ -525,7 +527,7 @@ github.com/felixge/httpsnoop
# github.com/fsnotify/fsnotify v1.7.0 # github.com/fsnotify/fsnotify v1.7.0
## explicit; go 1.17 ## explicit; go 1.17
github.com/fsnotify/fsnotify github.com/fsnotify/fsnotify
# github.com/fsouza/go-dockerclient v1.11.1 # github.com/fsouza/go-dockerclient v1.11.2
## explicit; go 1.21 ## explicit; go 1.21
github.com/fsouza/go-dockerclient github.com/fsouza/go-dockerclient
# github.com/gabriel-vasile/mimetype v1.4.3 # github.com/gabriel-vasile/mimetype v1.4.3
@ -792,8 +794,8 @@ github.com/mistifyio/go-zfs/v3
# github.com/mitchellh/mapstructure v1.5.0 # github.com/mitchellh/mapstructure v1.5.0
## explicit; go 1.14 ## explicit; go 1.14
github.com/mitchellh/mapstructure github.com/mitchellh/mapstructure
# github.com/moby/buildkit v0.12.5 # github.com/moby/buildkit v0.15.1
## explicit; go 1.20 ## explicit; go 1.21.0
github.com/moby/buildkit/frontend/dockerfile/command github.com/moby/buildkit/frontend/dockerfile/command
github.com/moby/buildkit/frontend/dockerfile/parser github.com/moby/buildkit/frontend/dockerfile/parser
github.com/moby/buildkit/frontend/dockerfile/shell github.com/moby/buildkit/frontend/dockerfile/shell
@ -909,11 +911,12 @@ github.com/opencontainers/runtime-tools/validate/capabilities
github.com/opencontainers/selinux/go-selinux github.com/opencontainers/selinux/go-selinux
github.com/opencontainers/selinux/go-selinux/label github.com/opencontainers/selinux/go-selinux/label
github.com/opencontainers/selinux/pkg/pwalkdir github.com/opencontainers/selinux/pkg/pwalkdir
# github.com/openshift/imagebuilder v1.2.14 # github.com/openshift/imagebuilder v1.2.15
## explicit; go 1.19 ## explicit; go 1.21.0
github.com/openshift/imagebuilder github.com/openshift/imagebuilder
github.com/openshift/imagebuilder/dockerfile/command github.com/openshift/imagebuilder/dockerfile/command
github.com/openshift/imagebuilder/dockerfile/parser github.com/openshift/imagebuilder/dockerfile/parser
github.com/openshift/imagebuilder/internal
github.com/openshift/imagebuilder/signal github.com/openshift/imagebuilder/signal
github.com/openshift/imagebuilder/strslice github.com/openshift/imagebuilder/strslice
# github.com/opentracing/opentracing-go v1.2.0 # github.com/opentracing/opentracing-go v1.2.0