mirror of https://github.com/containers/podman.git
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:
commit
7156f60fee
9
go.mod
9
go.mod
|
@ -59,7 +59,7 @@ require (
|
|||
github.com/opencontainers/runtime-spec v1.2.0
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc
|
||||
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/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
|
@ -97,9 +97,10 @@ require (
|
|||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // 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/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/typeurl/v2 v2.1.1 // 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/felixge/httpsnoop v1.0.4 // 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/gin-contrib/sse v0.1.0 // 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/mistifyio/go-zfs/v3 v3.0.1 // 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/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.2 // indirect
|
||||
|
|
18
go.sum
18
go.sum
|
@ -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/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/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao=
|
||||
github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4=
|
||||
github.com/containerd/containerd v1.7.20 h1:Sl6jQYk3TRavaU83h66QMbI2Nqg9Jm6qzwX57Vsn1SQ=
|
||||
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/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
|
||||
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/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/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
|
||||
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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
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.1/go.mod h1:UfjOOaspAq+RGh7GX1aZ0HeWWGHQWWsh+H5BgEWB3Pk=
|
||||
github.com/fsouza/go-dockerclient v1.11.2 h1:Wos4OMUwIjOW2rt8Z10TZSJHxgQH0KcYyf3O86dqFII=
|
||||
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/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
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.12.5/go.mod h1:YGwjA2loqyiYfZeEo8FtI7z4x5XponAaIWsWcSjWwso=
|
||||
github.com/moby/buildkit v0.15.1 h1:J6wrew7hphKqlq1wuu6yaUb/1Ra7gEzDAovylGztAKM=
|
||||
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/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
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/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
||||
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.14/go.mod h1:KkkXOyRjJlZEXWQtHNBNzVHqh4vf/0xX5cDIQ2gr+5I=
|
||||
github.com/openshift/imagebuilder v1.2.15 h1:MNn1OztEE/l8pSEDPYAQ71Ys6rpXA2P00UFhdY9p/yk=
|
||||
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/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqwnHagNDPGOlts35QkhAZ8by3DR7nMih7M=
|
||||
|
|
|
@ -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)
|
||||
}
|
176
vendor/github.com/containerd/containerd/platforms/platforms_deprecated.go
generated
vendored
Normal file
176
vendor/github.com/containerd/containerd/platforms/platforms_deprecated.go
generated
vendored
Normal 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()
|
||||
}
|
23
vendor/github.com/containerd/containerd/platforms/platforms_deprecated_other.go
generated
vendored
Normal file
23
vendor/github.com/containerd/containerd/platforms/platforms_deprecated_other.go
generated
vendored
Normal 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 ""
|
||||
}
|
49
vendor/github.com/containerd/containerd/platforms/platforms_deprecated_windows.go
generated
vendored
Normal file
49
vendor/github.com/containerd/containerd/platforms/platforms_deprecated_windows.go
generated
vendored
Normal 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),
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
*.go text eol=lf
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -19,12 +19,12 @@ package platforms
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
@ -70,7 +70,7 @@ func getCPUInfo(pattern string) (info string, err error) {
|
|||
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
|
||||
|
@ -83,7 +83,7 @@ func getCPUVariantFromArch(arch string) (string, error) {
|
|||
if arch == "aarch64" {
|
||||
variant = "8"
|
||||
} 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] {
|
||||
case "v8":
|
||||
variant = "8"
|
||||
|
@ -101,7 +101,7 @@ func getCPUVariantFromArch(arch string) (string, error) {
|
|||
variant = "unknown"
|
||||
}
|
||||
} 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
|
||||
}
|
||||
|
@ -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
|
||||
// was not present.
|
||||
func getCPUVariant() (string, error) {
|
||||
|
||||
variant, err := getCPUInfo("Cpu architecture")
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
//Let's try getting CPU variant from machine architecture
|
||||
if errors.Is(err, errNotFound) {
|
||||
// Let's try getting CPU variant from machine architecture
|
||||
arch, err := getMachineArch()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failure getting machine architecture: %v", err)
|
|
@ -21,8 +21,6 @@ package platforms
|
|||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
)
|
||||
|
||||
func getCPUVariant() (string, error) {
|
||||
|
@ -49,10 +47,8 @@ func getCPUVariant() (string, error) {
|
|||
default:
|
||||
variant = "unknown"
|
||||
}
|
||||
|
||||
} 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
|
|
@ -16,9 +16,11 @@
|
|||
|
||||
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 {
|
||||
return Format(DefaultSpec())
|
||||
return FormatAll(DefaultSpec())
|
||||
}
|
||||
|
||||
// DefaultStrict returns strict form of Default.
|
|
@ -22,7 +22,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/hcsshim/osversion"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
@ -52,29 +51,29 @@ func (m windowsmatcher) Match(p specs.Platform) bool {
|
|||
|
||||
if match && m.OS == "windows" {
|
||||
// HPC containers do not have OS version filled
|
||||
if p.OSVersion == "" {
|
||||
if m.OSVersion == "" || p.OSVersion == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
hostOsVersion := GetOsVersion(m.osVersionPrefix)
|
||||
ctrOsVersion := GetOsVersion(p.OSVersion)
|
||||
return osversion.CheckHostAndContainerCompat(hostOsVersion, ctrOsVersion)
|
||||
hostOsVersion := getOSVersion(m.osVersionPrefix)
|
||||
ctrOsVersion := getOSVersion(p.OSVersion)
|
||||
return checkHostAndContainerCompat(hostOsVersion, ctrOsVersion)
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
func GetOsVersion(osVersionPrefix string) osversion.OSVersion {
|
||||
func getOSVersion(osVersionPrefix string) osVersion {
|
||||
parts := strings.Split(osVersionPrefix, ".")
|
||||
if len(parts) < 3 {
|
||||
return osversion.OSVersion{}
|
||||
return osVersion{}
|
||||
}
|
||||
|
||||
majorVersion, _ := strconv.Atoi(parts[0])
|
||||
minorVersion, _ := strconv.Atoi(parts[1])
|
||||
buildNumber, _ := strconv.Atoi(parts[2])
|
||||
|
||||
return osversion.OSVersion{
|
||||
return osVersion{
|
||||
MajorVersion: uint8(majorVersion),
|
||||
MinorVersion: uint8(minorVersion),
|
||||
Build: uint16(buildNumber),
|
|
@ -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")
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -102,6 +102,9 @@
|
|||
// unless it is explicitly provided. This is treated as equivalent to armhf. A
|
||||
// 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
|
||||
// not yet been fully implemented and tested.
|
||||
package platforms
|
||||
|
@ -115,14 +118,15 @@ import (
|
|||
"strings"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
)
|
||||
|
||||
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.
|
||||
type Platform = specs.Platform
|
||||
|
||||
|
@ -155,40 +159,68 @@ func (m *matcher) Match(platform specs.Platform) bool {
|
|||
}
|
||||
|
||||
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.
|
||||
//
|
||||
// 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
|
||||
// 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
|
||||
// back to the known set of architectures. The missing component will be
|
||||
// inferred based on the local environment.
|
||||
func Parse(specifier string) (specs.Platform, error) {
|
||||
if strings.Contains(specifier, "*") {
|
||||
// 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 {
|
||||
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(), errdefs.ErrInvalidArgument)
|
||||
var p specs.Platform
|
||||
for i, part := range parts {
|
||||
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) {
|
||||
case 1:
|
||||
// in this case, we will test that the value might be an OS, then look
|
||||
// it up. If it is not known, we'll treat it as an architecture. Since
|
||||
// in this case, we will test that the value might be an OS (with or
|
||||
// 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
|
||||
// going to be a little more strict if we don't know about the argument
|
||||
// value.
|
||||
p.OS = normalizeOS(parts[0])
|
||||
if isKnownOS(p.OS) {
|
||||
// picks a default architecture
|
||||
p.Architecture = runtime.GOARCH
|
||||
|
@ -196,10 +228,6 @@ func Parse(specifier string) (specs.Platform, error) {
|
|||
p.Variant = cpuVariant()
|
||||
}
|
||||
|
||||
if p.OS == "windows" {
|
||||
p.OSVersion = GetWindowsOsVersion()
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -212,37 +240,27 @@ func Parse(specifier string) (specs.Platform, error) {
|
|||
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:
|
||||
// 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.
|
||||
p.OS = normalizeOS(parts[0])
|
||||
p.Architecture, p.Variant = normalizeArch(parts[1], "")
|
||||
if p.Architecture == "arm" && p.Variant == "v7" {
|
||||
p.Variant = ""
|
||||
}
|
||||
|
||||
if p.OS == "windows" {
|
||||
p.OSVersion = GetWindowsOsVersion()
|
||||
}
|
||||
|
||||
return p, nil
|
||||
case 3:
|
||||
// we have a fully specified variant, this is rare
|
||||
p.OS = normalizeOS(parts[0])
|
||||
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
|
||||
if p.Architecture == "arm64" && p.Variant == "" {
|
||||
p.Variant = "v8"
|
||||
}
|
||||
|
||||
if p.OS == "windows" {
|
||||
p.OSVersion = GetWindowsOsVersion()
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -264,6 +282,20 @@ func Format(platform specs.Platform) string {
|
|||
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.
|
||||
//
|
||||
// For example, if "Aarch64" is encountered, we change it to "arm64" or if
|
|
@ -28,7 +28,3 @@ func newDefaultMatcher(platform specs.Platform) Matcher {
|
|||
Platform: Normalize(platform),
|
||||
}
|
||||
}
|
||||
|
||||
func GetWindowsOsVersion() string {
|
||||
return ""
|
||||
}
|
|
@ -17,10 +17,7 @@
|
|||
package platforms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
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
|
||||
|
@ -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)
|
||||
}
|
|
@ -1,66 +1,284 @@
|
|||
# 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 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>
|
||||
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>
|
||||
Alexis Murzeau <amubtdx@gmail.com>
|
||||
Alice Frosi <afrosi@de.ibm.com>
|
||||
Allen Sun <allen.sun@daocloud.io>
|
||||
Amen Belayneh <amenbelayneh@gmail.com>
|
||||
Anca Iordache <anca.iordache@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>
|
||||
Anurag Goel <anurag@render.com>
|
||||
Anusha Ragunathan <anusha@docker.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>
|
||||
Brandon Mitchell <git@bmitch.net>
|
||||
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>
|
||||
Darren Shepherd <darren@rancher.com>
|
||||
Dave Chen <dave.chen@arm.com>
|
||||
Dave Henderson <dhenderson@gmail.com>
|
||||
Dave Tucker <dt@docker.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>
|
||||
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
||||
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>
|
||||
Edgar Lee <edgarl@netflix.com>
|
||||
Edgar Lee <edgarhinshunlee@gmail.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>
|
||||
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>
|
||||
Hector S <hfsam88@gmail.com>
|
||||
Helen Xie <chenjg@harmonycloud.cn>
|
||||
Himanshu Pandey <hpandey@pivotal.io>
|
||||
Hiromu Nakamura <abctail30@gmail.com>
|
||||
HowJMay <vulxj0j8j8@gmail.com>
|
||||
Hugo Santos <hugo@namespacelabs.com>
|
||||
Ian Campbell <ijc@docker.com>
|
||||
Ilya Dmitrichenko <errordeveloper@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>
|
||||
Jeffrey Huang <jeffreyhuang23@gmail.com>
|
||||
Jesse Rittner <rittneje@gmail.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 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>
|
||||
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>
|
||||
Justin Chadwell <me@jedevc.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>
|
||||
Kyle <Kylemit@gmail.com>
|
||||
l00397676 <lujingxiao@huawei.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>
|
||||
Maxime Lagresle <maxime@angel.co>
|
||||
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>
|
||||
Morgan Bauer <mbauer@us.ibm.com>
|
||||
Morlay <morlay.null@gmail.com>
|
||||
msg <msg@clinc.com>
|
||||
Nao YONASHIRO <yonashiro@r.recruit.co.jp>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
Seiya Miyata <odradek38@gmail.com>
|
||||
Serhat Gülçiçek <serhat25@gmail.com>
|
||||
Sertac Ozercan <sozercan@gmail.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>
|
||||
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>
|
||||
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 Riccardi <riccardi@systran.fr>
|
||||
Thomas Shaw <tomwillfixit@users.noreply.github.com>
|
||||
Tianon Gravi <admwiggin@gmail.com>
|
||||
Tibor Vass <tibor@docker.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>
|
||||
Tobias Klauser <tklauser@distanz.ch>
|
||||
Tomas Tomecek <ttomecek@redhat.com>
|
||||
Tomasz Kopczynski <tomek@kopczynski.net.pl>
|
||||
Tomohiro Kusumoto <zabio1192@gmail.com>
|
||||
Troels Liebe Bentsen <tlb@nversion.dk>
|
||||
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>
|
||||
Vlad A. Ionescu <vladaionescu@users.noreply.github.com>
|
||||
Vladislav Ivanov <vlad@ivanov.email>
|
||||
Wang Yumu <37442693@qq.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>
|
||||
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>
|
||||
岁丰 <genglu.gl@antfin.com>
|
||||
沈陵 <shenling.yyb@alibaba-inc.com>
|
||||
郑泽宇 <perhapszzy@sina.com>
|
||||
|
|
|
@ -13,12 +13,14 @@ import (
|
|||
|
||||
const (
|
||||
keySyntax = "syntax"
|
||||
keyCheck = "check"
|
||||
keyEscape = "escape"
|
||||
)
|
||||
|
||||
var validDirectives = map[string]struct{}{
|
||||
keySyntax: {},
|
||||
keyEscape: {},
|
||||
keyCheck: {},
|
||||
}
|
||||
|
||||
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
|
||||
// selection.
|
||||
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)
|
||||
if err != nil {
|
||||
return "", "", nil, false
|
||||
|
@ -119,42 +125,38 @@ func DetectSyntax(dt []byte) (string, string, []Range, bool) {
|
|||
line++
|
||||
}
|
||||
|
||||
// use default directive parser, and search for #syntax=
|
||||
// use default directive parser, and search for #key=
|
||||
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
|
||||
}
|
||||
|
||||
// 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.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
|
||||
}
|
||||
|
||||
// search for possible json directives
|
||||
var directive struct {
|
||||
Syntax string `json:"syntax"`
|
||||
}
|
||||
if err := json.Unmarshal(dt, &directive); err == nil {
|
||||
if directive.Syntax != "" {
|
||||
// use json directive, and search for { "key": "..." }
|
||||
jsonDirective := map[string]string{}
|
||||
if err := json.Unmarshal(dt, &jsonDirective); err == nil {
|
||||
if v, ok := jsonDirective[key]; ok {
|
||||
loc := []Range{{
|
||||
Start: Position{Line: line},
|
||||
End: Position{Line: line},
|
||||
}}
|
||||
return directive.Syntax, directive.Syntax, loc, true
|
||||
return v, v, loc, true
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
for _, d := range directives {
|
||||
// check for syntax directive before erroring out, since the error
|
||||
// might have occurred *after* the syntax directive
|
||||
if d.Name == keySyntax {
|
||||
if d.Name == key {
|
||||
p, _, _ := strings.Cut(d.Value, " ")
|
||||
return p, d.Value, d.Location, true
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
// ErrorLocation gives a location in source code that caused the error
|
||||
type ErrorLocation struct {
|
||||
Location []Range
|
||||
Locations [][]Range
|
||||
error
|
||||
}
|
||||
|
||||
|
@ -39,11 +39,12 @@ func WithLocation(err error, location []Range) error {
|
|||
}
|
||||
var el *ErrorLocation
|
||||
if errors.As(err, &el) {
|
||||
el.Locations = append(el.Locations, location)
|
||||
return err
|
||||
}
|
||||
return stack.Enable(&ErrorLocation{
|
||||
error: err,
|
||||
Location: location,
|
||||
error: err,
|
||||
Locations: [][]Range{location},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
|
||||
var (
|
||||
errDockerfileNotStringArray = errors.New("when using JSON array syntax, arrays must be comprised of strings only")
|
||||
errDockerfileNotJSONArray = errors.New("not a JSON array")
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -58,11 +59,11 @@ func parseWords(rest string, d *directives) []string {
|
|||
|
||||
words := []string{}
|
||||
phase := inSpaces
|
||||
word := ""
|
||||
quote := '\000'
|
||||
blankOK := false
|
||||
var ch rune
|
||||
var chWidth int
|
||||
var sbuilder strings.Builder
|
||||
|
||||
for pos := 0; pos <= len(rest); pos += chWidth {
|
||||
if pos != len(rest) {
|
||||
|
@ -79,18 +80,18 @@ func parseWords(rest string, d *directives) []string {
|
|||
phase = inWord // found it, fall through
|
||||
}
|
||||
if (phase == inWord || phase == inQuote) && (pos == len(rest)) {
|
||||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
if blankOK || sbuilder.Len() > 0 {
|
||||
words = append(words, sbuilder.String())
|
||||
}
|
||||
break
|
||||
}
|
||||
if phase == inWord {
|
||||
if unicode.IsSpace(ch) {
|
||||
phase = inSpaces
|
||||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
if blankOK || sbuilder.Len() > 0 {
|
||||
words = append(words, sbuilder.String())
|
||||
}
|
||||
word = ""
|
||||
sbuilder.Reset()
|
||||
blankOK = false
|
||||
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
|
||||
// add the escape token plus the char to the word, even if the char
|
||||
// is a quote.
|
||||
word += string(ch)
|
||||
sbuilder.WriteRune(ch)
|
||||
pos += chWidth
|
||||
ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
|
||||
}
|
||||
word += string(ch)
|
||||
sbuilder.WriteRune(ch)
|
||||
continue
|
||||
}
|
||||
if phase == inQuote {
|
||||
|
@ -124,10 +125,10 @@ func parseWords(rest string, d *directives) []string {
|
|||
continue // just skip the escape token at end
|
||||
}
|
||||
pos += chWidth
|
||||
word += string(ch)
|
||||
sbuilder.WriteRune(ch)
|
||||
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 {
|
||||
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
|
||||
|
@ -165,17 +166,20 @@ func parseNameVal(rest string, key string, d *directives) (*Node, error) {
|
|||
}
|
||||
|
||||
parts := strings.SplitN(word, "=", 2)
|
||||
node := newKeyValueNode(parts[0], parts[1])
|
||||
node := newKeyValueNode(parts[0], parts[1], "=")
|
||||
rootNode, prevNode = appendKeyValueNode(node, rootNode, prevNode)
|
||||
}
|
||||
|
||||
return rootNode, nil
|
||||
}
|
||||
|
||||
func newKeyValueNode(key, value string) *Node {
|
||||
func newKeyValueNode(key, value, sep string) *Node {
|
||||
return &Node{
|
||||
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 = node.Next
|
||||
for prevNode = node.Next; prevNode.Next != nil; {
|
||||
prevNode = prevNode.Next
|
||||
}
|
||||
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.
|
||||
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)
|
||||
if !strings.HasPrefix(rest, "[") {
|
||||
return nil, nil, errors.Errorf("Error parsing %q as a JSON array", rest)
|
||||
return nil, nil, errDockerfileNotJSONArray
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -307,7 +313,7 @@ func parseMaybeJSON(rest string, d *directives) (*Node, map[string]bool, error)
|
|||
return nil, nil, nil
|
||||
}
|
||||
|
||||
node, attrs, err := parseJSON(rest, d)
|
||||
node, attrs, err := parseJSON(rest)
|
||||
|
||||
if err == 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
|
||||
// delimited string.
|
||||
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 {
|
||||
return node, attrs, nil
|
||||
|
|
|
@ -114,7 +114,6 @@ type Heredoc struct {
|
|||
var (
|
||||
dispatch map[string]func(string, *directives) (*Node, map[string]bool, error)
|
||||
reWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`)
|
||||
reComment = regexp.MustCompile(`^#.*$`)
|
||||
reHeredoc = regexp.MustCompile(`^(\d*)<<(-?)([^<]*)$`)
|
||||
reLeadingTabs = regexp.MustCompile(`(?m)^\t+`)
|
||||
)
|
||||
|
@ -169,8 +168,8 @@ func (d *directives) setEscapeToken(s string) error {
|
|||
// possibleParserDirective looks for parser directives, eg '# escapeToken=<char>'.
|
||||
// Parser directives must precede any builder instruction or other comments,
|
||||
// and cannot be repeated.
|
||||
func (d *directives) possibleParserDirective(line string) error {
|
||||
directive, err := d.parser.ParseLine([]byte(line))
|
||||
func (d *directives) possibleParserDirective(line []byte) error {
|
||||
directive, err := d.parser.ParseLine(line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -284,6 +283,7 @@ func Parse(rwc io.Reader) (*Result, error) {
|
|||
scanner.Split(scanLines)
|
||||
warnings := []Warning{}
|
||||
var comments []string
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
var err error
|
||||
for scanner.Scan() {
|
||||
|
@ -307,10 +307,12 @@ func Parse(rwc io.Reader) (*Result, error) {
|
|||
currentLine++
|
||||
|
||||
startLine := currentLine
|
||||
line, isEndOfLine := trimContinuationCharacter(string(bytesRead), d)
|
||||
if isEndOfLine && line == "" {
|
||||
bytesRead, isEndOfLine := trimContinuationCharacter(bytesRead, d)
|
||||
if isEndOfLine && len(bytesRead) == 0 {
|
||||
continue
|
||||
}
|
||||
buf.Reset()
|
||||
buf.Write(bytesRead)
|
||||
|
||||
var hasEmptyContinuationLine bool
|
||||
for !isEndOfLine && scanner.Scan() {
|
||||
|
@ -329,16 +331,17 @@ func Parse(rwc io.Reader) (*Result, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
continuationLine := string(bytesRead)
|
||||
continuationLine, isEndOfLine = trimContinuationCharacter(continuationLine, d)
|
||||
line += continuationLine
|
||||
bytesRead, isEndOfLine = trimContinuationCharacter(bytesRead, d)
|
||||
buf.Write(bytesRead)
|
||||
}
|
||||
|
||||
line := buf.String()
|
||||
|
||||
if hasEmptyContinuationLine {
|
||||
warnings = append(warnings, Warning{
|
||||
Short: "Empty continuation line found in: " + line,
|
||||
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}},
|
||||
})
|
||||
}
|
||||
|
@ -348,7 +351,7 @@ func Parse(rwc io.Reader) (*Result, error) {
|
|||
return nil, withLocation(err, startLine, currentLine)
|
||||
}
|
||||
|
||||
if child.canContainHeredoc() {
|
||||
if child.canContainHeredoc() && strings.Contains(line, "<<") {
|
||||
heredocs, err := heredocsFromLine(line)
|
||||
if err != nil {
|
||||
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
|
||||
// part of the heredoc word is quoted, so we shouldn't expand the content.
|
||||
shlex.RawQuotes = false
|
||||
words, err := shlex.ProcessWords(rest, []string{})
|
||||
words, err := shlex.ProcessWords(rest, emptyEnvs{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -425,7 +428,7 @@ func heredocFromMatch(match []string) (*Heredoc, error) {
|
|||
}
|
||||
|
||||
shlex.RawQuotes = true
|
||||
wordsRaw, err := shlex.ProcessWords(rest, []string{})
|
||||
wordsRaw, err := shlex.ProcessWords(rest, emptyEnvs{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -466,7 +469,7 @@ func heredocsFromLine(line string) ([]Heredoc, error) {
|
|||
shlex.RawQuotes = true
|
||||
shlex.RawEscapes = true
|
||||
shlex.SkipUnsetEnv = true
|
||||
words, _ := shlex.ProcessWords(line, []string{})
|
||||
words, _ := shlex.ProcessWords(line, emptyEnvs{})
|
||||
|
||||
var docs []Heredoc
|
||||
for _, word := range words {
|
||||
|
@ -487,7 +490,10 @@ func ChompHeredocContent(src string) string {
|
|||
}
|
||||
|
||||
func trimComments(src []byte) []byte {
|
||||
return reComment.ReplaceAll(src, []byte{})
|
||||
if !isComment(src) {
|
||||
return src
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func trimLeadingWhitespace(src []byte) []byte {
|
||||
|
@ -501,7 +507,8 @@ func trimNewline(src []byte) []byte {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -510,9 +517,9 @@ func isEmptyContinuationLine(line []byte) bool {
|
|||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
||||
func trimContinuationCharacter(line string, d *directives) (string, bool) {
|
||||
if d.lineContinuationRegex.MatchString(line) {
|
||||
line = d.lineContinuationRegex.ReplaceAllString(line, "$1")
|
||||
func trimContinuationCharacter(line []byte, d *directives) ([]byte, bool) {
|
||||
if d.lineContinuationRegex.Match(line) {
|
||||
line = d.lineContinuationRegex.ReplaceAll(line, []byte("$1"))
|
||||
return line, false
|
||||
}
|
||||
return line, true
|
||||
|
@ -525,7 +532,7 @@ func processLine(d *directives, token []byte, stripLeftWhitespace bool) ([]byte,
|
|||
if stripLeftWhitespace {
|
||||
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
|
||||
|
@ -550,3 +557,13 @@ func handleScannerError(err error) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
type emptyEnvs struct{}
|
||||
|
||||
func (emptyEnvs) Get(string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (emptyEnvs) Keys() []string {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func extractBuilderFlags(line string) (string, []string, error) {
|
|||
|
||||
words := []string{}
|
||||
phase := inSpaces
|
||||
word := ""
|
||||
sbuilder := &strings.Builder{}
|
||||
quote := '\000'
|
||||
blankOK := false
|
||||
var ch rune
|
||||
|
@ -62,13 +62,14 @@ func extractBuilderFlags(line string) (string, []string, error) {
|
|||
phase = inWord // found something with "--", fall through
|
||||
}
|
||||
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)
|
||||
}
|
||||
break
|
||||
}
|
||||
if phase == inWord {
|
||||
if unicode.IsSpace(ch) {
|
||||
word := sbuilder.String()
|
||||
phase = inSpaces
|
||||
if word == "--" {
|
||||
return line[pos:], words, nil
|
||||
|
@ -76,7 +77,7 @@ func extractBuilderFlags(line string) (string, []string, error) {
|
|||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
}
|
||||
word = ""
|
||||
sbuilder.Reset()
|
||||
blankOK = false
|
||||
continue
|
||||
}
|
||||
|
@ -93,7 +94,9 @@ func extractBuilderFlags(line string) (string, []string, error) {
|
|||
pos++
|
||||
ch = rune(line[pos])
|
||||
}
|
||||
word += string(ch)
|
||||
if _, err := sbuilder.WriteRune(ch); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if phase == inQuote {
|
||||
|
@ -109,7 +112,9 @@ func extractBuilderFlags(line string) (string, []string, error) {
|
|||
pos++
|
||||
ch = rune(line[pos])
|
||||
}
|
||||
word += string(ch)
|
||||
if _, err := sbuilder.WriteRune(ch); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,3 +9,10 @@ package shell
|
|||
func EqualEnvKeys(from, to string) bool {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -8,3 +8,10 @@ import "strings"
|
|||
func EqualEnvKeys(from, to string) bool {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package shell
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
|
@ -10,6 +12,11 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type EnvGetter interface {
|
||||
Get(string) (string, bool)
|
||||
Keys() []string
|
||||
}
|
||||
|
||||
// Lex performs shell word splitting and variable expansion.
|
||||
//
|
||||
// Lex takes a string and an array of env variables and
|
||||
|
@ -17,12 +24,15 @@ import (
|
|||
// tokens. Tries to mimic bash shell process.
|
||||
// It doesn't support all flavors of ${xx:...} formats but new ones can
|
||||
// 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 {
|
||||
escapeToken rune
|
||||
RawQuotes bool
|
||||
RawEscapes bool
|
||||
SkipProcessQuotes bool
|
||||
SkipUnsetEnv bool
|
||||
shellWord shellWord
|
||||
}
|
||||
|
||||
// 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,
|
||||
// and replace any env var references in 'word'.
|
||||
func (s *Lex) ProcessWord(word string, env []string) (string, error) {
|
||||
word, _, err := s.process(word, BuildEnvs(env))
|
||||
return word, err
|
||||
// and replace any env var references in 'word'. It will also
|
||||
// return variables in word which were not found in the 'env' list,
|
||||
// which is useful in later linting.
|
||||
// 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,
|
||||
|
@ -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.
|
||||
// Note, each one is trimmed to remove leading and trailing spaces (unless
|
||||
// they are quoted", but ProcessWord retains spaces between words.
|
||||
func (s *Lex) ProcessWords(word string, env []string) ([]string, error) {
|
||||
_, words, err := s.process(word, BuildEnvs(env))
|
||||
return words, err
|
||||
func (s *Lex) ProcessWords(word string, env EnvGetter) ([]string, error) {
|
||||
result, err := s.process(word, env, false)
|
||||
return result.Words, err
|
||||
}
|
||||
|
||||
// ProcessWordWithMap will use the 'env' list of environment variables,
|
||||
// and replace any env var references in 'word'.
|
||||
func (s *Lex) ProcessWordWithMap(word string, env map[string]string) (string, error) {
|
||||
word, _, err := s.process(word, env)
|
||||
return word, err
|
||||
type ProcessWordResult struct {
|
||||
Result string
|
||||
Words []string
|
||||
Matched map[string]struct{}
|
||||
Unmatched map[string]struct{}
|
||||
}
|
||||
|
||||
// ProcessWordWithMatches will use the 'env' list of environment variables,
|
||||
// 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) {
|
||||
sw := s.init(word, env)
|
||||
word, _, err := sw.process(word)
|
||||
return word, sw.matches, err
|
||||
func (s *Lex) ProcessWordWithMatches(word string, env EnvGetter) (ProcessWordResult, error) {
|
||||
return s.process(word, env, true)
|
||||
}
|
||||
|
||||
func (s *Lex) ProcessWordsWithMap(word string, env map[string]string) ([]string, error) {
|
||||
_, words, err := s.process(word, env)
|
||||
return words, err
|
||||
}
|
||||
|
||||
func (s *Lex) init(word string, env map[string]string) *shellWord {
|
||||
sw := &shellWord{
|
||||
envs: env,
|
||||
escapeToken: s.escapeToken,
|
||||
skipUnsetEnv: s.SkipUnsetEnv,
|
||||
skipProcessQuotes: s.SkipProcessQuotes,
|
||||
rawQuotes: s.RawQuotes,
|
||||
rawEscapes: s.RawEscapes,
|
||||
matches: make(map[string]struct{}),
|
||||
func (s *Lex) initWord(word string, env EnvGetter, capture bool) *shellWord {
|
||||
sw := &s.shellWord
|
||||
sw.Lex = s
|
||||
sw.envs = env
|
||||
sw.capture = capture
|
||||
sw.rawEscapes = s.RawEscapes
|
||||
if capture {
|
||||
sw.matches = nil
|
||||
sw.nonmatches = nil
|
||||
}
|
||||
sw.scanner.Init(strings.NewReader(word))
|
||||
return sw
|
||||
}
|
||||
|
||||
func (s *Lex) process(word string, env map[string]string) (string, []string, error) {
|
||||
sw := s.init(word, env)
|
||||
return sw.process(word)
|
||||
func (s *Lex) process(word string, env EnvGetter, capture bool) (ProcessWordResult, error) {
|
||||
sw := s.initWord(word, env, capture)
|
||||
word, words, err := sw.process(word)
|
||||
return ProcessWordResult{
|
||||
Result: word,
|
||||
Words: words,
|
||||
Matched: sw.matches,
|
||||
Unmatched: sw.nonmatches,
|
||||
}, err
|
||||
}
|
||||
|
||||
type shellWord struct {
|
||||
scanner scanner.Scanner
|
||||
envs map[string]string
|
||||
escapeToken rune
|
||||
rawQuotes bool
|
||||
rawEscapes bool
|
||||
skipUnsetEnv bool
|
||||
skipProcessQuotes bool
|
||||
matches map[string]struct{}
|
||||
*Lex
|
||||
wordsBuffer strings.Builder
|
||||
scanner scanner.Scanner
|
||||
envs EnvGetter
|
||||
rawEscapes bool
|
||||
capture bool // capture matches and nonmatches
|
||||
matches map[string]struct{}
|
||||
nonmatches map[string]struct{}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
word string
|
||||
buf *strings.Builder
|
||||
words []string
|
||||
inWord bool
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addChar(ch rune) {
|
||||
if unicode.IsSpace(ch) && w.inWord {
|
||||
if len(w.word) != 0 {
|
||||
w.words = append(w.words, w.word)
|
||||
w.word = ""
|
||||
if w.buf.Len() != 0 {
|
||||
w.words = append(w.words, w.buf.String())
|
||||
w.buf.Reset()
|
||||
w.inWord = false
|
||||
}
|
||||
} else if !unicode.IsSpace(ch) {
|
||||
|
@ -126,7 +138,7 @@ func (w *wordsStruct) addChar(ch rune) {
|
|||
}
|
||||
|
||||
func (w *wordsStruct) addRawChar(ch rune) {
|
||||
w.word += string(ch)
|
||||
w.buf.WriteRune(ch)
|
||||
w.inWord = true
|
||||
}
|
||||
|
||||
|
@ -137,16 +149,16 @@ func (w *wordsStruct) addString(str string) {
|
|||
}
|
||||
|
||||
func (w *wordsStruct) addRawString(str string) {
|
||||
w.word += str
|
||||
w.buf.WriteString(str)
|
||||
w.inWord = true
|
||||
}
|
||||
|
||||
func (w *wordsStruct) getWords() []string {
|
||||
if len(w.word) > 0 {
|
||||
w.words = append(w.words, w.word)
|
||||
if w.buf.Len() > 0 {
|
||||
w.words = append(w.words, w.buf.String())
|
||||
|
||||
// Just in case we're called again by mistake
|
||||
w.word = ""
|
||||
w.buf.Reset()
|
||||
w.inWord = false
|
||||
}
|
||||
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
|
||||
// end of the word or the 'stopChar' character
|
||||
func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
var result bytes.Buffer
|
||||
func (sw *shellWord) processStopOn(stopChar rune, rawEscapes bool) (string, []string, error) {
|
||||
// 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
|
||||
words.buf = &sw.wordsBuffer
|
||||
|
||||
// no need to initialize all the time
|
||||
var charFuncMapping = map[rune]func() (string, error){
|
||||
'$': sw.processDollar,
|
||||
}
|
||||
if !sw.skipProcessQuotes {
|
||||
if !sw.SkipProcessQuotes {
|
||||
charFuncMapping['\''] = sw.processSingleQuote
|
||||
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 {
|
||||
ch := sw.scanner.Peek()
|
||||
|
||||
|
@ -230,7 +255,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
|
|||
var result bytes.Buffer
|
||||
|
||||
ch := sw.scanner.Next()
|
||||
if sw.rawQuotes {
|
||||
if sw.RawQuotes {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
|
||||
|
@ -240,7 +265,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
|
|||
case scanner.EOF:
|
||||
return "", errors.New("unexpected end of statement while looking for matching single-quote")
|
||||
case '\'':
|
||||
if sw.rawQuotes {
|
||||
if sw.RawQuotes {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
return result.String(), nil
|
||||
|
@ -265,7 +290,7 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
|
|||
var result bytes.Buffer
|
||||
|
||||
ch := sw.scanner.Next()
|
||||
if sw.rawQuotes {
|
||||
if sw.RawQuotes {
|
||||
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")
|
||||
case '"':
|
||||
ch := sw.scanner.Next()
|
||||
if sw.rawQuotes {
|
||||
if sw.RawQuotes {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
return result.String(), nil
|
||||
|
@ -319,7 +344,7 @@ func (sw *shellWord) processDollar() (string, error) {
|
|||
return "$", nil
|
||||
}
|
||||
value, found := sw.getEnv(name)
|
||||
if !found && sw.skipUnsetEnv {
|
||||
if !found && sw.SkipUnsetEnv {
|
||||
return "$" + name, nil
|
||||
}
|
||||
return value, nil
|
||||
|
@ -342,7 +367,7 @@ func (sw *shellWord) processDollar() (string, error) {
|
|||
case '}':
|
||||
// Normal ${xx} case
|
||||
value, set := sw.getEnv(name)
|
||||
if !set && sw.skipUnsetEnv {
|
||||
if !set && sw.SkipUnsetEnv {
|
||||
return fmt.Sprintf("${%s}", name), nil
|
||||
}
|
||||
return value, nil
|
||||
|
@ -351,8 +376,9 @@ func (sw *shellWord) processDollar() (string, error) {
|
|||
ch = sw.scanner.Next()
|
||||
chs += string(ch)
|
||||
fallthrough
|
||||
case '+', '-', '?':
|
||||
word, _, err := sw.processStopOn('}')
|
||||
case '+', '-', '?', '#', '%':
|
||||
rawEscapes := ch == '#' || ch == '%'
|
||||
word, _, err := sw.processStopOn('}', rawEscapes)
|
||||
if err != nil {
|
||||
if sw.scanner.Peek() == scanner.EOF {
|
||||
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
|
||||
// can use it to determine what to do based on the modifier
|
||||
value, set := sw.getEnv(name)
|
||||
if sw.skipUnsetEnv && !set {
|
||||
if sw.SkipUnsetEnv && !set {
|
||||
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 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:
|
||||
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:
|
||||
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) {
|
||||
for key, value := range sw.envs {
|
||||
if EqualEnvKeys(name, key) {
|
||||
v, ok := sw.envs.Get(name)
|
||||
if ok {
|
||||
if sw.capture {
|
||||
if sw.matches == nil {
|
||||
sw.matches = make(map[string]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
|
||||
}
|
||||
|
||||
func BuildEnvs(env []string) map[string]string {
|
||||
func EnvsFromSlice(env []string) EnvGetter {
|
||||
envs := map[string]string{}
|
||||
|
||||
keys := make([]string, 0, len(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 {
|
||||
envs[e] = ""
|
||||
} else {
|
||||
k := e[:i]
|
||||
v := e[i+1:]
|
||||
type envGetter struct {
|
||||
env map[string]string
|
||||
keys []string
|
||||
}
|
||||
|
||||
// overwrite value if key already exists
|
||||
envs[k] = v
|
||||
}
|
||||
var _ EnvGetter = &envGetter{}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -46,22 +46,23 @@ func Helper() {
|
|||
func Traces(err error) []*Stack {
|
||||
var st []*Stack
|
||||
|
||||
wrapped, ok := err.(interface {
|
||||
Unwrap() error
|
||||
})
|
||||
if ok {
|
||||
st = Traces(wrapped.Unwrap())
|
||||
switch e := err.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
st = Traces(e.Unwrap())
|
||||
case interface{ Unwrap() []error }:
|
||||
for _, ue := range e.Unwrap() {
|
||||
st = Traces(ue)
|
||||
// Only take first stack
|
||||
if len(st) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ste, ok := err.(interface {
|
||||
StackTrace() errors.StackTrace
|
||||
}); ok {
|
||||
switch ste := err.(type) {
|
||||
case interface{ StackTrace() errors.StackTrace }:
|
||||
st = append(st, convertStack(ste.StackTrace()))
|
||||
}
|
||||
|
||||
if ste, ok := err.(interface {
|
||||
StackTrace() *Stack
|
||||
}); ok {
|
||||
case interface{ StackTrace() *Stack }:
|
||||
st = append(st, ste.StackTrace())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.30.0
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v3.11.4
|
||||
// source: stack.proto
|
||||
|
||||
|
|
|
@ -3,15 +3,15 @@ syntax = "proto3";
|
|||
package stack;
|
||||
|
||||
message Stack {
|
||||
repeated Frame frames = 1;
|
||||
repeated string cmdline = 2;
|
||||
int32 pid = 3;
|
||||
string version = 4;
|
||||
string revision = 5;
|
||||
repeated Frame frames = 1;
|
||||
repeated string cmdline = 2;
|
||||
int32 pid = 3;
|
||||
string version = 4;
|
||||
string revision = 5;
|
||||
}
|
||||
|
||||
message Frame {
|
||||
string Name = 1;
|
||||
string File = 2;
|
||||
int32 Line = 3;
|
||||
string Name = 1;
|
||||
string File = 2;
|
||||
int32 Line = 3;
|
||||
}
|
|
@ -6,7 +6,7 @@ services:
|
|||
- docker
|
||||
|
||||
go:
|
||||
- "1.20"
|
||||
- "1.21.13"
|
||||
|
||||
before_install:
|
||||
- sudo systemctl stop docker.service && sudo systemctl stop docker.socket
|
||||
|
|
|
@ -18,9 +18,10 @@ import (
|
|||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/containers/storage/pkg/regexp"
|
||||
"github.com/openshift/imagebuilder/internal"
|
||||
"github.com/openshift/imagebuilder/signal"
|
||||
"github.com/openshift/imagebuilder/strslice"
|
||||
|
||||
|
@ -143,7 +144,7 @@ func processHereDocs(instruction, originalInstruction string, heredocs []buildki
|
|||
shlex := buildkitshell.NewLex('\\')
|
||||
shlex.RawQuotes = true
|
||||
shlex.RawEscapes = true
|
||||
content, err = shlex.ProcessWord(content, args)
|
||||
content, _, err = shlex.ProcessWord(content, internal.EnvironmentSlice(args))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
buildkitparser "github.com/moby/buildkit/frontend/dockerfile/parser"
|
||||
buildkitshell "github.com/moby/buildkit/frontend/dockerfile/shell"
|
||||
"github.com/openshift/imagebuilder/dockerfile/command"
|
||||
"github.com/openshift/imagebuilder/internal"
|
||||
)
|
||||
|
||||
// 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.RawEscapes = true
|
||||
shlex.SkipUnsetEnv = true
|
||||
words, _ := shlex.ProcessWords(line, []string{})
|
||||
words, _ := shlex.ProcessWords(line, internal.EnvironmentSlice([]string{}))
|
||||
|
||||
var docs []buildkitparser.Heredoc
|
||||
for _, word := range words {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#
|
||||
|
||||
%global golang_version 1.19
|
||||
%{!?version: %global version 1.2.14}
|
||||
%{!?version: %global version 1.2.15}
|
||||
%{!?release: %global release 1}
|
||||
%global package_name imagebuilder
|
||||
%global product_name Container Image Builder
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -107,9 +107,8 @@ github.com/chzyer/readline
|
|||
# github.com/containerd/cgroups/v3 v3.0.3
|
||||
## explicit; go 1.18
|
||||
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
|
||||
github.com/containerd/containerd/errdefs
|
||||
github.com/containerd/containerd/platforms
|
||||
# github.com/containerd/errdefs v0.1.0
|
||||
## explicit; go 1.20
|
||||
|
@ -117,6 +116,9 @@ github.com/containerd/errdefs
|
|||
# github.com/containerd/log v0.1.0
|
||||
## explicit; go 1.20
|
||||
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
|
||||
## explicit; go 1.19
|
||||
github.com/containerd/stargz-snapshotter/estargz
|
||||
|
@ -525,7 +527,7 @@ github.com/felixge/httpsnoop
|
|||
# github.com/fsnotify/fsnotify v1.7.0
|
||||
## explicit; go 1.17
|
||||
github.com/fsnotify/fsnotify
|
||||
# github.com/fsouza/go-dockerclient v1.11.1
|
||||
# github.com/fsouza/go-dockerclient v1.11.2
|
||||
## explicit; go 1.21
|
||||
github.com/fsouza/go-dockerclient
|
||||
# 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
|
||||
## explicit; go 1.14
|
||||
github.com/mitchellh/mapstructure
|
||||
# github.com/moby/buildkit v0.12.5
|
||||
## explicit; go 1.20
|
||||
# github.com/moby/buildkit v0.15.1
|
||||
## explicit; go 1.21.0
|
||||
github.com/moby/buildkit/frontend/dockerfile/command
|
||||
github.com/moby/buildkit/frontend/dockerfile/parser
|
||||
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/label
|
||||
github.com/opencontainers/selinux/pkg/pwalkdir
|
||||
# github.com/openshift/imagebuilder v1.2.14
|
||||
## explicit; go 1.19
|
||||
# github.com/openshift/imagebuilder v1.2.15
|
||||
## explicit; go 1.21.0
|
||||
github.com/openshift/imagebuilder
|
||||
github.com/openshift/imagebuilder/dockerfile/command
|
||||
github.com/openshift/imagebuilder/dockerfile/parser
|
||||
github.com/openshift/imagebuilder/internal
|
||||
github.com/openshift/imagebuilder/signal
|
||||
github.com/openshift/imagebuilder/strslice
|
||||
# github.com/opentracing/opentracing-go v1.2.0
|
||||
|
|
Loading…
Reference in New Issue