diff --git a/go.mod b/go.mod index be45ed8e2ca9..cb7156102b55 100644 --- a/go.mod +++ b/go.mod @@ -28,8 +28,8 @@ require ( github.com/containernetworking/plugins v1.4.0 github.com/coreos/go-systemd/v22 v22.5.0 github.com/distribution/reference v0.5.0 - github.com/docker/cli v25.0.3+incompatible - github.com/docker/docker v25.0.3+incompatible + github.com/docker/cli v26.0.0-rc1+incompatible + github.com/docker/docker v26.0.0-rc1.0.20240229165828-b8aa8579cad7+incompatible github.com/docker/go-connections v0.5.0 github.com/docker/go-units v0.5.0 github.com/gofrs/flock v0.8.1 @@ -173,3 +173,5 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect kernel.org/pub/linux/libs/security/libcap/psx v1.2.67 // indirect ) + +replace github.com/docker/docker => github.com/robmry/moby v0.0.0-20240229231553-bcc0ad7b9776 diff --git a/go.sum b/go.sum index 544f6bd877f7..e301fe3c1aa5 100644 --- a/go.sum +++ b/go.sum @@ -135,10 +135,8 @@ github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/docker/cli v25.0.3+incompatible h1:KLeNs7zws74oFuVhgZQ5ONGZiXUUdgsdy6/EsX/6284= -github.com/docker/cli v25.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ= -github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/cli v26.0.0-rc1+incompatible h1:PVxv2ySd8iZHoNfoAoKcnWSC/hKP2qMb806PWM34v50= +github.com/docker/cli v26.0.0-rc1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -372,6 +370,8 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/robmry/moby v0.0.0-20240229231553-bcc0ad7b9776 h1:Fz+svPqOcrhKDpO5UHXasHYee5Ljrf9Vo02v18Uth3k= +github.com/robmry/moby v0.0.0-20240229231553-bcc0ad7b9776/go.mod h1:tK57zForIqPPqk4JYALsLA3tBLXqhVUsS8pzfzspDm0= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= diff --git a/vendor/github.com/docker/docker/api/common.go b/vendor/github.com/docker/docker/api/common.go index 37e553d4183d..b11c2fe02b12 100644 --- a/vendor/github.com/docker/docker/api/common.go +++ b/vendor/github.com/docker/docker/api/common.go @@ -2,8 +2,17 @@ package api // import "github.com/docker/docker/api" // Common constants for daemon and client. const ( - // DefaultVersion of Current REST API - DefaultVersion = "1.44" + // DefaultVersion of the current REST API. + DefaultVersion = "1.45" + + // MinSupportedAPIVersion is the minimum API version that can be supported + // by the API server, specified as "major.minor". Note that the daemon + // may be configured with a different minimum API version, as returned + // in [github.com/docker/docker/api/types.Version.MinAPIVersion]. + // + // API requests for API versions lower than the configured version produce + // an error. + MinSupportedAPIVersion = "1.24" // NoBaseImageSpecifier is the symbol used by the FROM // command to specify that no base image is to be used. diff --git a/vendor/github.com/docker/docker/api/swagger.yaml b/vendor/github.com/docker/docker/api/swagger.yaml index e55a76fc63c3..b58310f327d2 100644 --- a/vendor/github.com/docker/docker/api/swagger.yaml +++ b/vendor/github.com/docker/docker/api/swagger.yaml @@ -19,10 +19,10 @@ produces: consumes: - "application/json" - "text/plain" -basePath: "/v1.44" +basePath: "/v1.45" info: title: "Docker Engine API" - version: "1.44" + version: "1.45" x-logo: url: "https://docs.docker.com/assets/images/logo-docker-main.png" description: | @@ -55,8 +55,8 @@ info: the URL is not supported by the daemon, a HTTP `400 Bad Request` error message is returned. - If you omit the version-prefix, the current version of the API (v1.44) is used. - For example, calling `/info` is the same as calling `/v1.44/info`. Using the + If you omit the version-prefix, the current version of the API (v1.45) is used. + For example, calling `/info` is the same as calling `/v1.45/info`. Using the API without a version-prefix is deprecated and will be removed in a future release. Engine releases in the near future should support this version of the API, @@ -391,7 +391,11 @@ definitions: ReadOnlyNonRecursive: description: | Make the mount non-recursively read-only, but still leave the mount recursive - (unless NonRecursive is set to true in conjunction). + (unless NonRecursive is set to `true` in conjunction). + + Addded in v1.44, before that version all read-only mounts were + non-recursive by default. To match the previous behaviour this + will default to `true` for clients on versions prior to v1.44. type: "boolean" default: false ReadOnlyForceRecursive: @@ -423,6 +427,10 @@ definitions: type: "object" additionalProperties: type: "string" + Subpath: + description: "Source path inside the volume. Must be relative without any back traversals." + type: "string" + example: "dir-inside-volume/subdirectory" TmpfsOptions: description: "Optional configuration for the `tmpfs` type." type: "object" @@ -1743,8 +1751,12 @@ definitions: description: | Date and time at which the image was created, formatted in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + + This information is only available if present in the image, + and omitted otherwise. type: "string" - x-nullable: false + format: "dateTime" + x-nullable: true example: "2022-02-04T21:20:12.497794809Z" Container: description: | diff --git a/vendor/github.com/docker/docker/api/types/client.go b/vendor/github.com/docker/docker/api/types/client.go index 24b00a2759d9..882201f0eae5 100644 --- a/vendor/github.com/docker/docker/api/types/client.go +++ b/vendor/github.com/docker/docker/api/types/client.go @@ -157,42 +157,12 @@ type ImageBuildResponse struct { OSType string } -// ImageCreateOptions holds information to create images. -type ImageCreateOptions struct { - RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry. - Platform string // Platform is the target platform of the image if it needs to be pulled from the registry. -} - // ImageImportSource holds source information for ImageImport type ImageImportSource struct { Source io.Reader // Source is the data to send to the server to create this image from. You must set SourceName to "-" to leverage this. SourceName string // SourceName is the name of the image to pull. Set to "-" to leverage the Source attribute. } -// ImageImportOptions holds information to import images from the client host. -type ImageImportOptions struct { - Tag string // Tag is the name to tag this image with. This attribute is deprecated. - Message string // Message is the message to tag the image with - Changes []string // Changes are the raw changes to apply to this image - Platform string // Platform is the target platform of the image -} - -// ImageListOptions holds parameters to list images with. -type ImageListOptions struct { - // All controls whether all images in the graph are filtered, or just - // the heads. - All bool - - // Filters is a JSON-encoded set of filter arguments. - Filters filters.Args - - // SharedSize indicates whether the shared size of images should be computed. - SharedSize bool - - // ContainerCount indicates whether container count should be computed. - ContainerCount bool -} - // ImageLoadResponse returns information to the client about a load process. type ImageLoadResponse struct { // Body must be closed to avoid a resource leak @@ -200,14 +170,6 @@ type ImageLoadResponse struct { JSON bool } -// ImagePullOptions holds information to pull images. -type ImagePullOptions struct { - All bool - RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry - PrivilegeFunc RequestPrivilegeFunc - Platform string -} - // RequestPrivilegeFunc is a function interface that // clients can supply to retry operations after // getting an authorization error. @@ -216,15 +178,6 @@ type ImagePullOptions struct { // if the privilege request fails. type RequestPrivilegeFunc func() (string, error) -// ImagePushOptions holds information to push images. -type ImagePushOptions ImagePullOptions - -// ImageRemoveOptions holds parameters to remove images. -type ImageRemoveOptions struct { - Force bool - PruneChildren bool -} - // ImageSearchOptions holds parameters to search images with. type ImageSearchOptions struct { RegistryAuth string diff --git a/vendor/github.com/docker/docker/api/types/container/config.go b/vendor/github.com/docker/docker/api/types/container/config.go index be41d6315e5d..86f46b74afc4 100644 --- a/vendor/github.com/docker/docker/api/types/container/config.go +++ b/vendor/github.com/docker/docker/api/types/container/config.go @@ -5,8 +5,8 @@ import ( "time" "github.com/docker/docker/api/types/strslice" - dockerspec "github.com/docker/docker/image/spec/specs-go/v1" "github.com/docker/go-connections/nat" + dockerspec "github.com/moby/docker-image-spec/specs-go/v1" ) // MinimumDuration puts a minimum on user configured duration. diff --git a/vendor/github.com/docker/docker/api/types/image/opts.go b/vendor/github.com/docker/docker/api/types/image/opts.go index 3cefecb0da34..c6b1f351b45f 100644 --- a/vendor/github.com/docker/docker/api/types/image/opts.go +++ b/vendor/github.com/docker/docker/api/types/image/opts.go @@ -1,9 +1,57 @@ package image -import ocispec "github.com/opencontainers/image-spec/specs-go/v1" +import "github.com/docker/docker/api/types/filters" -// GetImageOpts holds parameters to inspect an image. -type GetImageOpts struct { - Platform *ocispec.Platform - Details bool +// ImportOptions holds information to import images from the client host. +type ImportOptions struct { + Tag string // Tag is the name to tag this image with. This attribute is deprecated. + Message string // Message is the message to tag the image with + Changes []string // Changes are the raw changes to apply to this image + Platform string // Platform is the target platform of the image +} + +// CreateOptions holds information to create images. +type CreateOptions struct { + RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry. + Platform string // Platform is the target platform of the image if it needs to be pulled from the registry. +} + +// PullOptions holds information to pull images. +type PullOptions struct { + All bool + RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry + + // PrivilegeFunc is a function that clients can supply to retry operations + // after getting an authorization error. This function returns the registry + // authentication header value in base64 encoded format, or an error if the + // privilege request fails. + // + // Also see [github.com/docker/docker/api/types.RequestPrivilegeFunc]. + PrivilegeFunc func() (string, error) + Platform string +} + +// PushOptions holds information to push images. +type PushOptions PullOptions + +// ListOptions holds parameters to list images with. +type ListOptions struct { + // All controls whether all images in the graph are filtered, or just + // the heads. + All bool + + // Filters is a JSON-encoded set of filter arguments. + Filters filters.Args + + // SharedSize indicates whether the shared size of images should be computed. + SharedSize bool + + // ContainerCount indicates whether container count should be computed. + ContainerCount bool +} + +// RemoveOptions holds parameters to remove images. +type RemoveOptions struct { + Force bool + PruneChildren bool } diff --git a/vendor/github.com/docker/docker/api/types/mount/mount.go b/vendor/github.com/docker/docker/api/types/mount/mount.go index 57edf2ef1830..6fe04da25794 100644 --- a/vendor/github.com/docker/docker/api/types/mount/mount.go +++ b/vendor/github.com/docker/docker/api/types/mount/mount.go @@ -96,6 +96,7 @@ type BindOptions struct { type VolumeOptions struct { NoCopy bool `json:",omitempty"` Labels map[string]string `json:",omitempty"` + Subpath string `json:",omitempty"` DriverConfig *Driver `json:",omitempty"` } diff --git a/vendor/github.com/docker/docker/api/types/types.go b/vendor/github.com/docker/docker/api/types/types.go index 5c56a0cafef1..56a8b77d45de 100644 --- a/vendor/github.com/docker/docker/api/types/types.go +++ b/vendor/github.com/docker/docker/api/types/types.go @@ -72,7 +72,10 @@ type ImageInspect struct { // Created is the date and time at which the image was created, formatted in // RFC 3339 nano-seconds (time.RFC3339Nano). - Created string + // + // This information is only available if present in the image, + // and omitted otherwise. + Created string `json:",omitempty"` // Container is the ID of the container that was used to create the image. // diff --git a/vendor/github.com/docker/docker/api/types/types_deprecated.go b/vendor/github.com/docker/docker/api/types/types_deprecated.go index e332a7bb6d9f..231a5cca4687 100644 --- a/vendor/github.com/docker/docker/api/types/types_deprecated.go +++ b/vendor/github.com/docker/docker/api/types/types_deprecated.go @@ -1,138 +1,35 @@ package types import ( - "github.com/docker/docker/api/types/checkpoint" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/api/types/system" ) -// CheckpointCreateOptions holds parameters to create a checkpoint from a container. +// ImageImportOptions holds information to import images from the client host. // -// Deprecated: use [checkpoint.CreateOptions]. -type CheckpointCreateOptions = checkpoint.CreateOptions +// Deprecated: use [image.ImportOptions]. +type ImageImportOptions = image.ImportOptions -// CheckpointListOptions holds parameters to list checkpoints for a container +// ImageCreateOptions holds information to create images. // -// Deprecated: use [checkpoint.ListOptions]. -type CheckpointListOptions = checkpoint.ListOptions +// Deprecated: use [image.CreateOptions]. +type ImageCreateOptions = image.CreateOptions -// CheckpointDeleteOptions holds parameters to delete a checkpoint from a container +// ImagePullOptions holds information to pull images. // -// Deprecated: use [checkpoint.DeleteOptions]. -type CheckpointDeleteOptions = checkpoint.DeleteOptions +// Deprecated: use [image.PullOptions]. +type ImagePullOptions = image.PullOptions -// Checkpoint represents the details of a checkpoint when listing endpoints. +// ImagePushOptions holds information to push images. // -// Deprecated: use [checkpoint.Summary]. -type Checkpoint = checkpoint.Summary +// Deprecated: use [image.PushOptions]. +type ImagePushOptions = image.PushOptions -// Info contains response of Engine API: -// GET "/info" +// ImageListOptions holds parameters to list images with. // -// Deprecated: use [system.Info]. -type Info = system.Info +// Deprecated: use [image.ListOptions]. +type ImageListOptions = image.ListOptions -// Commit holds the Git-commit (SHA1) that a binary was built from, as reported -// in the version-string of external tools, such as containerd, or runC. +// ImageRemoveOptions holds parameters to remove images. // -// Deprecated: use [system.Commit]. -type Commit = system.Commit - -// PluginsInfo is a temp struct holding Plugins name -// registered with docker daemon. It is used by [system.Info] struct -// -// Deprecated: use [system.PluginsInfo]. -type PluginsInfo = system.PluginsInfo - -// NetworkAddressPool is a temp struct used by [system.Info] struct. -// -// Deprecated: use [system.NetworkAddressPool]. -type NetworkAddressPool = system.NetworkAddressPool - -// Runtime describes an OCI runtime. -// -// Deprecated: use [system.Runtime]. -type Runtime = system.Runtime - -// SecurityOpt contains the name and options of a security option. -// -// Deprecated: use [system.SecurityOpt]. -type SecurityOpt = system.SecurityOpt - -// KeyValue holds a key/value pair. -// -// Deprecated: use [system.KeyValue]. -type KeyValue = system.KeyValue - -// ImageDeleteResponseItem image delete response item. -// -// Deprecated: use [image.DeleteResponse]. -type ImageDeleteResponseItem = image.DeleteResponse - -// ImageSummary image summary. -// -// Deprecated: use [image.Summary]. -type ImageSummary = image.Summary - -// ImageMetadata contains engine-local data about the image. -// -// Deprecated: use [image.Metadata]. -type ImageMetadata = image.Metadata - -// ServiceCreateResponse contains the information returned to a client -// on the creation of a new service. -// -// Deprecated: use [swarm.ServiceCreateResponse]. -type ServiceCreateResponse = swarm.ServiceCreateResponse - -// ServiceUpdateResponse service update response. -// -// Deprecated: use [swarm.ServiceUpdateResponse]. -type ServiceUpdateResponse = swarm.ServiceUpdateResponse - -// ContainerStartOptions holds parameters to start containers. -// -// Deprecated: use [container.StartOptions]. -type ContainerStartOptions = container.StartOptions - -// ResizeOptions holds parameters to resize a TTY. -// It can be used to resize container TTYs and -// exec process TTYs too. -// -// Deprecated: use [container.ResizeOptions]. -type ResizeOptions = container.ResizeOptions - -// ContainerAttachOptions holds parameters to attach to a container. -// -// Deprecated: use [container.AttachOptions]. -type ContainerAttachOptions = container.AttachOptions - -// ContainerCommitOptions holds parameters to commit changes into a container. -// -// Deprecated: use [container.CommitOptions]. -type ContainerCommitOptions = container.CommitOptions - -// ContainerListOptions holds parameters to list containers with. -// -// Deprecated: use [container.ListOptions]. -type ContainerListOptions = container.ListOptions - -// ContainerLogsOptions holds parameters to filter logs with. -// -// Deprecated: use [container.LogsOptions]. -type ContainerLogsOptions = container.LogsOptions - -// ContainerRemoveOptions holds parameters to remove containers. -// -// Deprecated: use [container.RemoveOptions]. -type ContainerRemoveOptions = container.RemoveOptions - -// DecodeSecurityOptions decodes a security options string slice to a type safe -// [system.SecurityOpt]. -// -// Deprecated: use [system.DecodeSecurityOptions]. -func DecodeSecurityOptions(opts []string) ([]system.SecurityOpt, error) { - return system.DecodeSecurityOptions(opts) -} +// Deprecated: use [image.RemoveOptions]. +type ImageRemoveOptions = image.RemoveOptions diff --git a/vendor/github.com/docker/docker/api/types/versions/README.md b/vendor/github.com/docker/docker/api/types/versions/README.md deleted file mode 100644 index 1ef911edb0f9..000000000000 --- a/vendor/github.com/docker/docker/api/types/versions/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Legacy API type versions - -This package includes types for legacy API versions. The stable version of the API types live in `api/types/*.go`. - -Consider moving a type here when you need to keep backwards compatibility in the API. This legacy types are organized by the latest API version they appear in. For instance, types in the `v1p19` package are valid for API versions below or equal `1.19`. Types in the `v1p20` package are valid for the API version `1.20`, since the versions below that will use the legacy types in `v1p19`. - -## Package name conventions - -The package name convention is to use `v` as a prefix for the version number and `p`(patch) as a separator. We use this nomenclature due to a few restrictions in the Go package name convention: - -1. We cannot use `.` because it's interpreted by the language, think of `v1.20.CallFunction`. -2. We cannot use `_` because golint complains about it. The code is actually valid, but it looks probably more weird: `v1_20.CallFunction`. - -For instance, if you want to modify a type that was available in the version `1.21` of the API but it will have different fields in the version `1.22`, you want to create a new package under `api/types/versions/v1p21`. diff --git a/vendor/github.com/docker/docker/client/client.go b/vendor/github.com/docker/docker/client/client.go index 0b496b0fa66f..f2eeb6c5702e 100644 --- a/vendor/github.com/docker/docker/client/client.go +++ b/vendor/github.com/docker/docker/client/client.go @@ -265,17 +265,22 @@ func (cli *Client) Close() error { // This allows for version-dependent code to use the same version as will // be negotiated when making the actual requests, and for which cases // we cannot do the negotiation lazily. -func (cli *Client) checkVersion(ctx context.Context) { - if cli.negotiateVersion && !cli.negotiated { - cli.NegotiateAPIVersion(ctx) +func (cli *Client) checkVersion(ctx context.Context) error { + if !cli.manualOverride && cli.negotiateVersion && !cli.negotiated { + ping, err := cli.Ping(ctx) + if err != nil { + return err + } + cli.negotiateAPIVersionPing(ping) } + return nil } // getAPIPath returns the versioned request path to call the API. // It appends the query parameters to the path if they are not empty. func (cli *Client) getAPIPath(ctx context.Context, p string, query url.Values) string { var apiPath string - cli.checkVersion(ctx) + _ = cli.checkVersion(ctx) if cli.version != "" { v := strings.TrimPrefix(cli.version, "v") apiPath = path.Join(cli.basePath, "/v"+v, p) @@ -307,7 +312,11 @@ func (cli *Client) ClientVersion() string { // added (1.24). func (cli *Client) NegotiateAPIVersion(ctx context.Context) { if !cli.manualOverride { - ping, _ := cli.Ping(ctx) + ping, err := cli.Ping(ctx) + if err != nil { + // FIXME(thaJeztah): Ping returns an error when failing to connect to the API; we should not swallow the error here, and instead returning it. + return + } cli.negotiateAPIVersionPing(ping) } } diff --git a/vendor/github.com/docker/docker/client/container_create.go b/vendor/github.com/docker/docker/client/container_create.go index 409f5b492a6e..5442d4267d09 100644 --- a/vendor/github.com/docker/docker/client/container_create.go +++ b/vendor/github.com/docker/docker/client/container_create.go @@ -28,7 +28,9 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return response, err + } if err := cli.NewVersionError(ctx, "1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil { return response, err diff --git a/vendor/github.com/docker/docker/client/container_exec.go b/vendor/github.com/docker/docker/client/container_exec.go index 3fff0c828897..526a3876a4a7 100644 --- a/vendor/github.com/docker/docker/client/container_exec.go +++ b/vendor/github.com/docker/docker/client/container_exec.go @@ -18,7 +18,9 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, container string, co // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return response, err + } if err := cli.NewVersionError(ctx, "1.25", "env"); len(config.Env) != 0 && err != nil { return response, err diff --git a/vendor/github.com/docker/docker/client/container_restart.go b/vendor/github.com/docker/docker/client/container_restart.go index 825d3e4e9d9b..02b5079bc463 100644 --- a/vendor/github.com/docker/docker/client/container_restart.go +++ b/vendor/github.com/docker/docker/client/container_restart.go @@ -23,7 +23,9 @@ func (cli *Client) ContainerRestart(ctx context.Context, containerID string, opt // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return err + } if versions.GreaterThanOrEqualTo(cli.version, "1.42") { query.Set("signal", options.Signal) } diff --git a/vendor/github.com/docker/docker/client/container_stop.go b/vendor/github.com/docker/docker/client/container_stop.go index ac0cab69de94..7c98a354b42e 100644 --- a/vendor/github.com/docker/docker/client/container_stop.go +++ b/vendor/github.com/docker/docker/client/container_stop.go @@ -27,7 +27,9 @@ func (cli *Client) ContainerStop(ctx context.Context, containerID string, option // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return err + } if versions.GreaterThanOrEqualTo(cli.version, "1.42") { query.Set("signal", options.Signal) } diff --git a/vendor/github.com/docker/docker/client/container_wait.go b/vendor/github.com/docker/docker/client/container_wait.go index b8d3bdef0db8..8bb6be0a18b2 100644 --- a/vendor/github.com/docker/docker/client/container_wait.go +++ b/vendor/github.com/docker/docker/client/container_wait.go @@ -30,19 +30,22 @@ const containerWaitErrorMsgLimit = 2 * 1024 /* Max: 2KiB */ // synchronize ContainerWait with other calls, such as specifying a // "next-exit" condition before issuing a ContainerStart request. func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error) { + resultC := make(chan container.WaitResponse) + errC := make(chan error, 1) + // Make sure we negotiated (if the client is configured to do so), // as code below contains API-version specific handling of options. // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + errC <- err + return resultC, errC + } if versions.LessThan(cli.ClientVersion(), "1.30") { return cli.legacyContainerWait(ctx, containerID) } - resultC := make(chan container.WaitResponse) - errC := make(chan error, 1) - query := url.Values{} if condition != "" { query.Set("condition", string(condition)) diff --git a/vendor/github.com/docker/docker/client/distribution_inspect.go b/vendor/github.com/docker/docker/client/distribution_inspect.go index 68ef31b78b07..68e6ec5ed6bf 100644 --- a/vendor/github.com/docker/docker/client/distribution_inspect.go +++ b/vendor/github.com/docker/docker/client/distribution_inspect.go @@ -10,11 +10,11 @@ import ( ) // DistributionInspect returns the image digest with the full manifest. -func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registry.DistributionInspect, error) { +func (cli *Client) DistributionInspect(ctx context.Context, imageRef, encodedRegistryAuth string) (registry.DistributionInspect, error) { // Contact the registry to retrieve digest and platform information var distributionInspect registry.DistributionInspect - if image == "" { - return distributionInspect, objectNotFoundError{object: "distribution", id: image} + if imageRef == "" { + return distributionInspect, objectNotFoundError{object: "distribution", id: imageRef} } if err := cli.NewVersionError(ctx, "1.30", "distribution inspect"); err != nil { @@ -28,7 +28,7 @@ func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegist } } - resp, err := cli.get(ctx, "/distribution/"+image+"/json", url.Values{}, headers) + resp, err := cli.get(ctx, "/distribution/"+imageRef+"/json", url.Values{}, headers) defer ensureReaderClosed(resp) if err != nil { return distributionInspect, err diff --git a/vendor/github.com/docker/docker/client/errors.go b/vendor/github.com/docker/docker/client/errors.go index 4b96b0208585..0d01e243fe0b 100644 --- a/vendor/github.com/docker/docker/client/errors.go +++ b/vendor/github.com/docker/docker/client/errors.go @@ -11,15 +11,16 @@ import ( // errConnectionFailed implements an error returned when connection failed. type errConnectionFailed struct { - host string + error } // Error returns a string representation of an errConnectionFailed -func (err errConnectionFailed) Error() string { - if err.host == "" { - return "Cannot connect to the Docker daemon. Is the docker daemon running on this host?" - } - return fmt.Sprintf("Cannot connect to the Docker daemon at %s. Is the docker daemon running?", err.host) +func (e errConnectionFailed) Error() string { + return e.error.Error() +} + +func (e errConnectionFailed) Unwrap() error { + return e.error } // IsErrConnectionFailed returns true if the error is caused by connection failed. @@ -29,7 +30,13 @@ func IsErrConnectionFailed(err error) bool { // ErrorConnectionFailed returns an error with host in the error message when connection to docker daemon failed. func ErrorConnectionFailed(host string) error { - return errConnectionFailed{host: host} + var err error + if host == "" { + err = fmt.Errorf("Cannot connect to the Docker daemon. Is the docker daemon running on this host?") + } else { + err = fmt.Errorf("Cannot connect to the Docker daemon at %s. Is the docker daemon running?", host) + } + return errConnectionFailed{error: err} } // IsErrNotFound returns true if the error is a NotFound error, which is returned @@ -60,7 +67,9 @@ func (cli *Client) NewVersionError(ctx context.Context, APIrequired, feature str // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return err + } if cli.version != "" && versions.LessThan(cli.version, APIrequired) { return fmt.Errorf("%q requires API version %s, but the Docker daemon API version is %s", feature, APIrequired, cli.version) } diff --git a/vendor/github.com/docker/docker/client/image_create.go b/vendor/github.com/docker/docker/client/image_create.go index 29cd0b437393..7c7873dca5aa 100644 --- a/vendor/github.com/docker/docker/client/image_create.go +++ b/vendor/github.com/docker/docker/client/image_create.go @@ -8,13 +8,13 @@ import ( "strings" "github.com/distribution/reference" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/registry" ) // ImageCreate creates a new image based on the parent options. // It returns the JSON content in the response body. -func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) { +func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options image.CreateOptions) (io.ReadCloser, error) { ref, err := reference.ParseNormalizedNamed(parentReference) if err != nil { return nil, err diff --git a/vendor/github.com/docker/docker/client/image_import.go b/vendor/github.com/docker/docker/client/image_import.go index cd376a14e581..5a890b0c59ef 100644 --- a/vendor/github.com/docker/docker/client/image_import.go +++ b/vendor/github.com/docker/docker/client/image_import.go @@ -8,11 +8,12 @@ import ( "github.com/distribution/reference" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/image" ) // ImageImport creates a new image based on the source options. // It returns the JSON content in the response body. -func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { +func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) { if ref != "" { // Check if the given image name can be resolved if _, err := reference.ParseNormalizedNamed(ref); err != nil { diff --git a/vendor/github.com/docker/docker/client/image_list.go b/vendor/github.com/docker/docker/client/image_list.go index f3f2280e3249..a9cc1e21e5dd 100644 --- a/vendor/github.com/docker/docker/client/image_list.go +++ b/vendor/github.com/docker/docker/client/image_list.go @@ -5,22 +5,24 @@ import ( "encoding/json" "net/url" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/versions" ) // ImageList returns a list of images in the docker host. -func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions) ([]image.Summary, error) { +func (cli *Client) ImageList(ctx context.Context, options image.ListOptions) ([]image.Summary, error) { + var images []image.Summary + // Make sure we negotiated (if the client is configured to do so), // as code below contains API-version specific handling of options. // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return images, err + } - var images []image.Summary query := url.Values{} optionFilters := options.Filters diff --git a/vendor/github.com/docker/docker/client/image_pull.go b/vendor/github.com/docker/docker/client/image_pull.go index d92049d58845..6438cf6a96b2 100644 --- a/vendor/github.com/docker/docker/client/image_pull.go +++ b/vendor/github.com/docker/docker/client/image_pull.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/distribution/reference" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/errdefs" ) @@ -19,7 +19,7 @@ import ( // FIXME(vdemeester): there is currently used in a few way in docker/docker // - if not in trusted content, ref is used to pass the whole reference, and tag is empty // - if in trusted content, ref is used to pass the reference name, and tag for the digest -func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.ImagePullOptions) (io.ReadCloser, error) { +func (cli *Client) ImagePull(ctx context.Context, refStr string, options image.PullOptions) (io.ReadCloser, error) { ref, err := reference.ParseNormalizedNamed(refStr) if err != nil { return nil, err diff --git a/vendor/github.com/docker/docker/client/image_push.go b/vendor/github.com/docker/docker/client/image_push.go index 6839a89e0785..e6a6b11eeadd 100644 --- a/vendor/github.com/docker/docker/client/image_push.go +++ b/vendor/github.com/docker/docker/client/image_push.go @@ -8,7 +8,7 @@ import ( "net/url" "github.com/distribution/reference" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/registry" "github.com/docker/docker/errdefs" ) @@ -17,7 +17,7 @@ import ( // It executes the privileged function if the operation is unauthorized // and it tries one more time. // It's up to the caller to handle the io.ReadCloser and close it properly. -func (cli *Client) ImagePush(ctx context.Context, image string, options types.ImagePushOptions) (io.ReadCloser, error) { +func (cli *Client) ImagePush(ctx context.Context, image string, options image.PushOptions) (io.ReadCloser, error) { ref, err := reference.ParseNormalizedNamed(image) if err != nil { return nil, err diff --git a/vendor/github.com/docker/docker/client/image_remove.go b/vendor/github.com/docker/docker/client/image_remove.go index b936d20830de..652d1bfa3ed6 100644 --- a/vendor/github.com/docker/docker/client/image_remove.go +++ b/vendor/github.com/docker/docker/client/image_remove.go @@ -5,12 +5,11 @@ import ( "encoding/json" "net/url" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/image" ) // ImageRemove removes an image from the docker host. -func (cli *Client) ImageRemove(ctx context.Context, imageID string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error) { +func (cli *Client) ImageRemove(ctx context.Context, imageID string, options image.RemoveOptions) ([]image.DeleteResponse, error) { query := url.Values{} if options.Force { diff --git a/vendor/github.com/docker/docker/client/interface.go b/vendor/github.com/docker/docker/client/interface.go index 302f5fb13e02..45d233f253eb 100644 --- a/vendor/github.com/docker/docker/client/interface.go +++ b/vendor/github.com/docker/docker/client/interface.go @@ -90,15 +90,15 @@ type ImageAPIClient interface { ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) BuildCancel(ctx context.Context, id string) error - ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) + ImageCreate(ctx context.Context, parentReference string, options image.CreateOptions) (io.ReadCloser, error) ImageHistory(ctx context.Context, image string) ([]image.HistoryResponseItem, error) - ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) + ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) ImageInspectWithRaw(ctx context.Context, image string) (types.ImageInspect, []byte, error) - ImageList(ctx context.Context, options types.ImageListOptions) ([]image.Summary, error) + ImageList(ctx context.Context, options image.ListOptions) ([]image.Summary, error) ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error) - ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) - ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) - ImageRemove(ctx context.Context, image string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error) + ImagePull(ctx context.Context, ref string, options image.PullOptions) (io.ReadCloser, error) + ImagePush(ctx context.Context, ref string, options image.PushOptions) (io.ReadCloser, error) + ImageRemove(ctx context.Context, image string, options image.RemoveOptions) ([]image.DeleteResponse, error) ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) ImageSave(ctx context.Context, images []string) (io.ReadCloser, error) ImageTag(ctx context.Context, image, ref string) error diff --git a/vendor/github.com/docker/docker/client/network_create.go b/vendor/github.com/docker/docker/client/network_create.go index 668e87d653b2..d510feb3db9b 100644 --- a/vendor/github.com/docker/docker/client/network_create.go +++ b/vendor/github.com/docker/docker/client/network_create.go @@ -10,12 +10,16 @@ import ( // NetworkCreate creates a new network in the docker host. func (cli *Client) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) { + var response types.NetworkCreateResponse + // Make sure we negotiated (if the client is configured to do so), // as code below contains API-version specific handling of options. // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return response, err + } networkCreateRequest := types.NetworkCreateRequest{ NetworkCreate: options, @@ -25,7 +29,6 @@ func (cli *Client) NetworkCreate(ctx context.Context, name string, options types networkCreateRequest.CheckDuplicate = true //nolint:staticcheck // ignore SA1019: CheckDuplicate is deprecated since API v1.44. } - var response types.NetworkCreateResponse serverResp, err := cli.post(ctx, "/networks/create", nil, networkCreateRequest, nil) defer ensureReaderClosed(serverResp) if err != nil { diff --git a/vendor/github.com/docker/docker/client/ping.go b/vendor/github.com/docker/docker/client/ping.go index dfd1042fab26..bf3e9b1cd6d5 100644 --- a/vendor/github.com/docker/docker/client/ping.go +++ b/vendor/github.com/docker/docker/client/ping.go @@ -14,7 +14,10 @@ import ( // Ping pings the server and returns the value of the "Docker-Experimental", // "Builder-Version", "OS-Type" & "API-Version" headers. It attempts to use // a HEAD request on the endpoint, but falls back to GET if HEAD is not supported -// by the daemon. +// by the daemon. It ignores internal server errors returned by the API, which +// may be returned if the daemon is in an unhealthy state, but returns errors +// for other non-success status codes, failing to connect to the API, or failing +// to parse the API response. func (cli *Client) Ping(ctx context.Context) (types.Ping, error) { var ping types.Ping diff --git a/vendor/github.com/docker/docker/client/request.go b/vendor/github.com/docker/docker/client/request.go index efe07bb9ea59..50e213b50a08 100644 --- a/vendor/github.com/docker/docker/client/request.go +++ b/vendor/github.com/docker/docker/client/request.go @@ -134,17 +134,18 @@ func (cli *Client) sendRequest(ctx context.Context, method, path string, query u return resp, errdefs.FromStatusCode(err, resp.statusCode) } +// FIXME(thaJeztah): Should this actually return a serverResp when a connection error occurred? func (cli *Client) doRequest(req *http.Request) (serverResponse, error) { serverResp := serverResponse{statusCode: -1, reqURL: req.URL} resp, err := cli.client.Do(req) if err != nil { if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") { - return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err) + return serverResp, errConnectionFailed{fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)} } if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") { - return serverResp, errors.Wrap(err, "the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings") + return serverResp, errConnectionFailed{errors.Wrap(err, "the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings")} } // Don't decorate context sentinel errors; users may be comparing to @@ -156,12 +157,13 @@ func (cli *Client) doRequest(req *http.Request) (serverResponse, error) { if uErr, ok := err.(*url.Error); ok { if nErr, ok := uErr.Err.(*net.OpError); ok { if os.IsPermission(nErr.Err) { - return serverResp, errors.Wrapf(err, "permission denied while trying to connect to the Docker daemon socket at %v", cli.host) + return serverResp, errConnectionFailed{errors.Wrapf(err, "permission denied while trying to connect to the Docker daemon socket at %v", cli.host)} } } } if nErr, ok := err.(net.Error); ok { + // FIXME(thaJeztah): any net.Error should be considered a connection error (but we should include the original error)? if nErr.Timeout() { return serverResp, ErrorConnectionFailed(cli.host) } @@ -190,7 +192,7 @@ func (cli *Client) doRequest(req *http.Request) (serverResponse, error) { } } - return serverResp, errors.Wrap(err, "error during connect") + return serverResp, errConnectionFailed{errors.Wrap(err, "error during connect")} } if resp != nil { diff --git a/vendor/github.com/docker/docker/client/service_create.go b/vendor/github.com/docker/docker/client/service_create.go index 2ebb5ee3a580..b72cb420d49e 100644 --- a/vendor/github.com/docker/docker/client/service_create.go +++ b/vendor/github.com/docker/docker/client/service_create.go @@ -25,7 +25,9 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return response, err + } // Make sure containerSpec is not nil when no runtime is set or the runtime is set to container if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) { diff --git a/vendor/github.com/docker/docker/client/service_update.go b/vendor/github.com/docker/docker/client/service_update.go index e05eebf56657..d2f03f02f07c 100644 --- a/vendor/github.com/docker/docker/client/service_update.go +++ b/vendor/github.com/docker/docker/client/service_update.go @@ -16,18 +16,18 @@ import ( // It should be the value as set *before* the update. You can find this value in the Meta field // of swarm.Service, which can be found using ServiceInspectWithRaw. func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error) { + response := swarm.ServiceUpdateResponse{} + // Make sure we negotiated (if the client is configured to do so), // as code below contains API-version specific handling of options. // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) - - var ( - query = url.Values{} - response = swarm.ServiceUpdateResponse{} - ) + if err := cli.checkVersion(ctx); err != nil { + return response, err + } + query := url.Values{} if options.RegistryAuthFrom != "" { query.Set("registryAuthFrom", options.RegistryAuthFrom) } diff --git a/vendor/github.com/docker/docker/client/volume_remove.go b/vendor/github.com/docker/docker/client/volume_remove.go index 31e08cb97597..b8bdc5ae8585 100644 --- a/vendor/github.com/docker/docker/client/volume_remove.go +++ b/vendor/github.com/docker/docker/client/volume_remove.go @@ -16,7 +16,9 @@ func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool // // Normally, version-negotiation (if enabled) would not happen until // the API request is made. - cli.checkVersion(ctx) + if err := cli.checkVersion(ctx); err != nil { + return err + } if versions.GreaterThanOrEqualTo(cli.version, "1.25") { query.Set("force", "1") } diff --git a/vendor/github.com/docker/docker/image/spec/specs-go/v1/image.go b/vendor/github.com/docker/docker/image/spec/specs-go/v1/image.go deleted file mode 100644 index 16726176350f..000000000000 --- a/vendor/github.com/docker/docker/image/spec/specs-go/v1/image.go +++ /dev/null @@ -1,54 +0,0 @@ -package v1 - -import ( - "time" - - ocispec "github.com/opencontainers/image-spec/specs-go/v1" -) - -const DockerOCIImageMediaType = "application/vnd.docker.container.image.v1+json" - -// DockerOCIImage is a ocispec.Image extended with Docker specific Config. -type DockerOCIImage struct { - ocispec.Image - - // Shadow ocispec.Image.Config - Config DockerOCIImageConfig `json:"config,omitempty"` -} - -// DockerOCIImageConfig is a ocispec.ImageConfig extended with Docker specific fields. -type DockerOCIImageConfig struct { - ocispec.ImageConfig - - DockerOCIImageConfigExt -} - -// DockerOCIImageConfigExt contains Docker-specific fields in DockerImageConfig. -type DockerOCIImageConfigExt struct { - Healthcheck *HealthcheckConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy - - OnBuild []string `json:",omitempty"` // ONBUILD metadata that were defined on the image Dockerfile - Shell []string `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT -} - -// HealthcheckConfig holds configuration settings for the HEALTHCHECK feature. -type HealthcheckConfig struct { - // Test is the test to perform to check that the container is healthy. - // An empty slice means to inherit the default. - // The options are: - // {} : inherit healthcheck - // {"NONE"} : disable healthcheck - // {"CMD", args...} : exec arguments directly - // {"CMD-SHELL", command} : run command with system's default shell - Test []string `json:",omitempty"` - - // Zero means to inherit. Durations are expressed as integer nanoseconds. - Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks. - Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung. - StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down. - StartInterval time.Duration `json:",omitempty"` // The interval to attempt healthchecks at during the start period - - // Retries is the number of consecutive failures needed to consider a container as unhealthy. - // Zero means inherit. - Retries int `json:",omitempty"` -} diff --git a/vendor/github.com/docker/docker/libnetwork/internal/resolvconf/resolvconf.go b/vendor/github.com/docker/docker/libnetwork/internal/resolvconf/resolvconf.go new file mode 100644 index 000000000000..e9f112275877 --- /dev/null +++ b/vendor/github.com/docker/docker/libnetwork/internal/resolvconf/resolvconf.go @@ -0,0 +1,512 @@ +// Package resolvconf is used to generate a container's /etc/resolv.conf file. +// +// Constructor Load and Parse read a resolv.conf file from the filesystem or +// a reader respectively, and return a ResolvConf object. +// +// The ResolvConf object can then be updated with overrides for nameserver, +// search domains, and DNS options. +// +// ResolvConf can then be transformed to make it suitable for legacy networking, +// a network with an internal nameserver, or used as-is for host networking. +// +// This package includes methods to write the file for the container, along with +// a hash that can be used to detect modifications made by the user to avoid +// overwriting those updates. + +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.19 + +package resolvconf + +import ( + "bufio" + "bytes" + "context" + "fmt" + "io" + "io/fs" + "net/netip" + "os" + "strconv" + "strings" + "text/template" + + "github.com/containerd/log" + "github.com/docker/docker/errdefs" + "github.com/docker/docker/pkg/ioutils" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +// Fallback nameservers, to use if none can be obtained from the host or command +// line options. +var ( + defaultIPv4NSs = []netip.Addr{ + netip.MustParseAddr("8.8.8.8"), + netip.MustParseAddr("8.8.4.4"), + } + defaultIPv6NSs = []netip.Addr{ + netip.MustParseAddr("2001:4860:4860::8888"), + netip.MustParseAddr("2001:4860:4860::8844"), + } +) + +// ResolvConf represents a resolv.conf file. It can be constructed by +// reading a resolv.conf file, using method Parse(). +type ResolvConf struct { + nameServers []netip.Addr + search []string + options []string + other []string // Unrecognised directives from the host's file, if any. + + md metadata +} + +// ExtDNSEntry represents a nameserver address that was removed from the +// container's resolv.conf when it was transformed by TransformForIntNS(). These +// are addresses read from the host's file, or applied via an override ('--dns'). +type ExtDNSEntry struct { + Addr netip.Addr + HostLoopback bool // The address is loopback, in the host's namespace. +} + +func (ed ExtDNSEntry) String() string { + if ed.HostLoopback { + return fmt.Sprintf("host(%s)", ed.Addr) + } + return ed.Addr.String() +} + +// metadata is used to track where components of the generated file have come +// from, in order to generate comments in the file for debug/info. Struct members +// are exported for use by 'text/template'. +type metadata struct { + SourcePath string + Header string + NSOverride bool + SearchOverride bool + OptionsOverride bool + NDotsFrom string + UsedDefaultNS bool + Transform string + InvalidNSs []string + ExtNameServers []ExtDNSEntry +} + +// Load opens a file at path and parses it as a resolv.conf file. +// On error, the returned ResolvConf will be zero-valued. +func Load(path string) (ResolvConf, error) { + f, err := os.Open(path) + if err != nil { + return ResolvConf{}, err + } + defer f.Close() + return Parse(f, path) +} + +// Parse parses a resolv.conf file from reader. +// path is optional if reader is an *os.File. +// On error, the returned ResolvConf will be zero-valued. +func Parse(reader io.Reader, path string) (ResolvConf, error) { + var rc ResolvConf + rc.md.SourcePath = path + if path == "" { + if namer, ok := reader.(interface{ Name() string }); ok { + rc.md.SourcePath = namer.Name() + } + } + + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + rc.processLine(scanner.Text()) + } + if err := scanner.Err(); err != nil { + return ResolvConf{}, errdefs.System(err) + } + if _, ok := rc.Option("ndots"); ok { + rc.md.NDotsFrom = "host" + } + return rc, nil +} + +// SetHeader sets the content to be included verbatim at the top of the +// generated resolv.conf file. No formatting or checking is done on the +// string. It must be valid resolv.conf syntax. (Comments must have '#' +// or ';' in the first column of each line). +// +// For example: +// +// SetHeader("# My resolv.conf\n# This file was generated.") +func (rc *ResolvConf) SetHeader(c string) { + rc.md.Header = c +} + +// NameServers returns addresses used in nameserver directives. +func (rc *ResolvConf) NameServers() []netip.Addr { + return append([]netip.Addr(nil), rc.nameServers...) +} + +// OverrideNameServers replaces the current set of nameservers. +func (rc *ResolvConf) OverrideNameServers(nameServers []netip.Addr) { + rc.nameServers = nameServers + rc.md.NSOverride = true +} + +// Search returns the current DNS search domains. +func (rc *ResolvConf) Search() []string { + return append([]string(nil), rc.search...) +} + +// OverrideSearch replaces the current DNS search domains. +func (rc *ResolvConf) OverrideSearch(search []string) { + var filtered []string + for _, s := range search { + if s != "." { + filtered = append(filtered, s) + } + } + rc.search = filtered + rc.md.SearchOverride = true +} + +// Options returns the current options. +func (rc *ResolvConf) Options() []string { + return append([]string(nil), rc.options...) +} + +// Option finds the last option named search, and returns (value, true) if +// found, else ("", false). Options are treated as "name:value", where the +// ":value" may be omitted. +// +// For example, for "ndots:1 edns0": +// +// Option("ndots") -> ("1", true) +// Option("edns0") -> ("", true) +func (rc *ResolvConf) Option(search string) (string, bool) { + for i := len(rc.options) - 1; i >= 0; i -= 1 { + k, v, _ := strings.Cut(rc.options[i], ":") + if k == search { + return v, true + } + } + return "", false +} + +// OverrideOptions replaces the current DNS options. +func (rc *ResolvConf) OverrideOptions(options []string) { + rc.options = append([]string(nil), options...) + rc.md.NDotsFrom = "" + if _, exists := rc.Option("ndots"); exists { + rc.md.NDotsFrom = "override" + } + rc.md.OptionsOverride = true +} + +// AddOption adds a single DNS option. +func (rc *ResolvConf) AddOption(option string) { + if len(option) > 6 && option[:6] == "ndots:" { + rc.md.NDotsFrom = "internal" + } + rc.options = append(rc.options, option) +} + +// TransformForLegacyNw makes sure the resolv.conf file will be suitable for +// use in a legacy network (one that has no internal resolver). +// - Remove loopback addresses inherited from the host's resolv.conf, because +// they'll only work in the host's namespace. +// - Remove IPv6 addresses if !ipv6. +// - Add default nameservers if there are no addresses left. +func (rc *ResolvConf) TransformForLegacyNw(ipv6 bool) { + rc.md.Transform = "legacy" + if rc.md.NSOverride { + return + } + var filtered []netip.Addr + for _, addr := range rc.nameServers { + if !addr.IsLoopback() && (!addr.Is6() || ipv6) { + filtered = append(filtered, addr) + } + } + rc.nameServers = filtered + if len(rc.nameServers) == 0 { + log.G(context.TODO()).Info("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers") + rc.nameServers = defaultNSAddrs(ipv6) + rc.md.UsedDefaultNS = true + } +} + +// TransformForIntNS makes sure the resolv.conf file will be suitable for +// use in a network sandbox that has an internal DNS resolver. +// - Add internalNS as a nameserver. +// - Remove other nameservers, stashing them as ExtNameServers for the +// internal resolver to use. (Apart from IPv6 nameservers, if keepIPv6.) +// - Mark ExtNameServers that must be used in the host namespace. +// - If no ExtNameServer addresses are found, use the defaults. +// - Return an error if an "ndots" option inherited from the host's config, or +// supplied in an override is not valid. +// - Ensure there's an 'options' value for each entry in reqdOptions. If the +// option includes a ':', and an option with a matching prefix exists, it +// is not modified. +func (rc *ResolvConf) TransformForIntNS( + keepIPv6 bool, + internalNS netip.Addr, + reqdOptions []string, +) ([]ExtDNSEntry, error) { + // The transformed config must list the internal nameserver. + newNSs := []netip.Addr{internalNS} + // Filter out other nameservers, keeping them for use as upstream nameservers by the + // internal nameserver. + rc.md.ExtNameServers = nil + for _, addr := range rc.nameServers { + // The internal resolver only uses IPv4 addresses so, keep IPv6 nameservers in + // the container's file if keepIPv6, else drop them. + if addr.Is6() { + if keepIPv6 { + newNSs = append(newNSs, addr) + } + } else { + // Extract this NS. Mark loopback addresses that did not come from an override as + // 'HostLoopback'. Upstream requests for these servers will be made in the host's + // network namespace. (So, '--dns 127.0.0.53' means use a nameserver listening on + // the container's loopback interface. But, if the host's resolv.conf contains + // 'nameserver 127.0.0.53', the host's resolver will be used.) + // + // TODO(robmry) - why only loopback addresses? + // Addresses from the host's resolv.conf must be usable in the host's namespace, + // and a lookup from the container's namespace is more expensive? And, for + // example, if the host has a nameserver with an IPv6 LL address with a zone-id, + // it won't work from the container's namespace (now, while the address is left in + // the container's resolv.conf, or in future for the internal resolver). + rc.md.ExtNameServers = append(rc.md.ExtNameServers, ExtDNSEntry{ + Addr: addr, + HostLoopback: addr.IsLoopback() && !rc.md.NSOverride, + }) + } + } + rc.nameServers = newNSs + + // If there are no external nameservers, and the only nameserver left is the + // internal resolver, use the defaults as ext nameservers. + if len(rc.md.ExtNameServers) == 0 && len(rc.nameServers) == 1 { + log.G(context.TODO()).Info("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers") + for _, addr := range defaultNSAddrs(keepIPv6) { + rc.md.ExtNameServers = append(rc.md.ExtNameServers, ExtDNSEntry{Addr: addr}) + } + rc.md.UsedDefaultNS = true + } + + // Validate the ndots option from host config or overrides, if present. + // TODO(robmry) - pre-existing behaviour, but ... + // Validating ndots from an override is good, but not-liking something in the + // host's resolv.conf isn't a reason to fail - just remove? (And it'll be + // replaced by the value in reqdOptions, if given.) + if ndots, exists := rc.Option("ndots"); exists { + if n, err := strconv.Atoi(ndots); err != nil || n < 0 { + return nil, errdefs.InvalidParameter( + fmt.Errorf("invalid number for ndots option: %v", ndots)) + } + } + // For each option required by the nameserver, add it if not already + // present (if the option already has a value don't change it). + for _, opt := range reqdOptions { + optName, _, _ := strings.Cut(opt, ":") + if _, exists := rc.Option(optName); !exists { + rc.AddOption(opt) + } + } + + rc.md.Transform = "internal resolver" + return append([]ExtDNSEntry(nil), rc.md.ExtNameServers...), nil +} + +// Generate returns content suitable for writing to a resolv.conf file. If comments +// is true, the file will include header information if supplied, and a trailing +// comment that describes how the file was constructed and lists external resolvers. +func (rc *ResolvConf) Generate(comments bool) ([]byte, error) { + s := struct { + Md *metadata + NameServers []netip.Addr + Search []string + Options []string + Other []string + Overrides []string + Comments bool + }{ + Md: &rc.md, + NameServers: rc.nameServers, + Search: rc.search, + Options: rc.options, + Other: rc.other, + Comments: comments, + } + if rc.md.NSOverride { + s.Overrides = append(s.Overrides, "nameservers") + } + if rc.md.SearchOverride { + s.Overrides = append(s.Overrides, "search") + } + if rc.md.OptionsOverride { + s.Overrides = append(s.Overrides, "options") + } + + const templateText = `{{if .Comments}}{{with .Md.Header}}{{.}} + +{{end}}{{end}}{{range .NameServers -}} +nameserver {{.}} +{{end}}{{with .Search -}} +search {{join . " "}} +{{end}}{{with .Options -}} +options {{join . " "}} +{{end}}{{with .Other -}} +{{join . "\n"}} +{{end}}{{if .Comments}} +# Based on host file: '{{.Md.SourcePath}}'{{with .Md.Transform}} ({{.}}){{end}} +{{if .Md.UsedDefaultNS -}} +# Used default nameservers. +{{end -}} +{{with .Md.ExtNameServers -}} +# ExtServers: {{.}} +{{end -}} +{{with .Md.InvalidNSs -}} +# Invalid nameservers: {{.}} +{{end -}} +# Overrides: {{.Overrides}} +{{with .Md.NDotsFrom -}} +# Option ndots from: {{.}} +{{end -}} +{{end -}} +` + + funcs := template.FuncMap{"join": strings.Join} + var buf bytes.Buffer + templ, err := template.New("summary").Funcs(funcs).Parse(templateText) + if err != nil { + return nil, errdefs.System(err) + } + if err := templ.Execute(&buf, s); err != nil { + return nil, errdefs.System(err) + } + return buf.Bytes(), nil +} + +// WriteFile generates content and writes it to path. If hashPath is non-zero, it +// also writes a file containing a hash of the content, to enable UserModified() +// to determine whether the file has been modified. +func (rc *ResolvConf) WriteFile(path, hashPath string, perm os.FileMode) error { + content, err := rc.Generate(true) + if err != nil { + return err + } + + // Write the resolv.conf file - it's bind-mounted into the container, so can't + // move a temp file into place, just have to truncate and write it. + if err := os.WriteFile(path, content, perm); err != nil { + return errdefs.System(err) + } + + // Write the hash file. + if hashPath != "" { + hashFile, err := ioutils.NewAtomicFileWriter(hashPath, perm) + if err != nil { + return errdefs.System(err) + } + defer hashFile.Close() + + digest := digest.FromBytes(content) + if _, err = hashFile.Write([]byte(digest)); err != nil { + return err + } + } + + return nil +} + +// UserModified can be used to determine whether the resolv.conf file has been +// modified since it was generated. It returns false with no error if the file +// matches the hash, true with no error if the file no longer matches the hash, +// and false with an error if the result cannot be determined. +func UserModified(rcPath, rcHashPath string) (bool, error) { + currRCHash, err := os.ReadFile(rcHashPath) + if err != nil { + // If the hash file doesn't exist, can only assume it hasn't been written + // yet (so, the user hasn't modified the file it hashes). + if errors.Is(err, fs.ErrNotExist) { + return false, nil + } + return false, errors.Wrapf(err, "failed to read hash file %s", rcHashPath) + } + expected, err := digest.Parse(string(currRCHash)) + if err != nil { + return false, errors.Wrapf(err, "failed to parse hash file %s", rcHashPath) + } + v := expected.Verifier() + currRC, err := os.Open(rcPath) + if err != nil { + return false, errors.Wrapf(err, "failed to open %s to check for modifications", rcPath) + } + defer currRC.Close() + if _, err := io.Copy(v, currRC); err != nil { + return false, errors.Wrapf(err, "failed to hash %s to check for modifications", rcPath) + } + return !v.Verified(), nil +} + +func (rc *ResolvConf) processLine(line string) { + fields := strings.Fields(line) + + // Strip comments. + // TODO(robmry) - ignore comment chars except in column 0. + // This preserves old behaviour, but it's wrong. For example, resolvers + // will honour the option in line "options # ndots:0" (and ignore the + // "#" as an unknown option). + for i, s := range fields { + if s[0] == '#' || s[0] == ';' { + fields = fields[:i] + break + } + } + if len(fields) == 0 { + return + } + + switch fields[0] { + case "nameserver": + if len(fields) < 2 { + return + } + if addr, err := netip.ParseAddr(fields[1]); err != nil { + rc.md.InvalidNSs = append(rc.md.InvalidNSs, fields[1]) + } else { + rc.nameServers = append(rc.nameServers, addr) + } + case "domain": + // 'domain' is an obsolete name for 'search'. + fallthrough + case "search": + if len(fields) < 2 { + return + } + // Only the last 'search' directive is used. + rc.search = fields[1:] + case "options": + if len(fields) < 2 { + return + } + // Replace options from earlier directives. + // TODO(robmry) - preserving incorrect behaviour, options should accumulate. + // rc.options = append(rc.options, fields[1:]...) + rc.options = fields[1:] + default: + // Copy anything that's not a recognised directive. + rc.other = append(rc.other, line) + } +} + +func defaultNSAddrs(ipv6 bool) []netip.Addr { + var addrs []netip.Addr + addrs = append(addrs, defaultIPv4NSs...) + if ipv6 { + addrs = append(addrs, defaultIPv6NSs...) + } + return addrs +} diff --git a/vendor/github.com/docker/docker/libnetwork/internal/resolvconf/resolvconf_path.go b/vendor/github.com/docker/docker/libnetwork/internal/resolvconf/resolvconf_path.go new file mode 100644 index 000000000000..65d0fe14098d --- /dev/null +++ b/vendor/github.com/docker/docker/libnetwork/internal/resolvconf/resolvconf_path.go @@ -0,0 +1,56 @@ +package resolvconf + +import ( + "context" + "net/netip" + "sync" + + "github.com/containerd/log" +) + +const ( + // defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path(). + defaultPath = "/etc/resolv.conf" + // alternatePath is a path different from defaultPath, that may be used to resolve DNS. See Path(). + alternatePath = "/run/systemd/resolve/resolv.conf" +) + +// For Path to detect systemd (only needed for legacy networking). +var ( + detectSystemdResolvConfOnce sync.Once + pathAfterSystemdDetection = defaultPath +) + +// Path returns the path to the resolv.conf file that libnetwork should use. +// +// When /etc/resolv.conf contains 127.0.0.53 as the only nameserver, then +// it is assumed systemd-resolved manages DNS. Because inside the container 127.0.0.53 +// is not a valid DNS server, Path() returns /run/systemd/resolve/resolv.conf +// which is the resolv.conf that systemd-resolved generates and manages. +// Otherwise Path() returns /etc/resolv.conf. +// +// Errors are silenced as they will inevitably resurface at future open/read calls. +// +// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf +// +// TODO(robmry) - alternatePath is only needed for legacy networking ... +// +// Host networking can use the host's resolv.conf as-is, and with an internal +// resolver it's also possible to use nameservers on the host's loopback +// interface. Once legacy networking is removed, this can always return +// defaultPath. +func Path() string { + detectSystemdResolvConfOnce.Do(func() { + rc, err := Load(defaultPath) + if err != nil { + // silencing error as it will resurface at next calls trying to read defaultPath + return + } + ns := rc.nameServers + if len(ns) == 1 && ns[0] == netip.MustParseAddr("127.0.0.53") { + pathAfterSystemdDetection = alternatePath + log.G(context.TODO()).Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath) + } + }) + return pathAfterSystemdDetection +} diff --git a/vendor/github.com/docker/docker/libnetwork/resolvconf/resolvconf.go b/vendor/github.com/docker/docker/libnetwork/resolvconf/resolvconf.go index da20e1c03182..c3473872b172 100644 --- a/vendor/github.com/docker/docker/libnetwork/resolvconf/resolvconf.go +++ b/vendor/github.com/docker/docker/libnetwork/resolvconf/resolvconf.go @@ -3,20 +3,12 @@ package resolvconf import ( "bytes" - "context" + "fmt" "os" - "regexp" "strings" - "sync" - "github.com/containerd/log" -) - -const ( - // defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path(). - defaultPath = "/etc/resolv.conf" - // alternatePath is a path different from defaultPath, that may be used to resolve DNS. See Path(). - alternatePath = "/run/systemd/resolve/resolv.conf" + "github.com/docker/docker/libnetwork/internal/resolvconf" + "github.com/opencontainers/go-digest" ) // constants for the IP address type @@ -26,72 +18,16 @@ const ( IPv6 ) -var ( - detectSystemdResolvConfOnce sync.Once - pathAfterSystemdDetection = defaultPath -) - -// Path returns the path to the resolv.conf file that libnetwork should use. -// -// When /etc/resolv.conf contains 127.0.0.53 as the only nameserver, then -// it is assumed systemd-resolved manages DNS. Because inside the container 127.0.0.53 -// is not a valid DNS server, Path() returns /run/systemd/resolve/resolv.conf -// which is the resolv.conf that systemd-resolved generates and manages. -// Otherwise Path() returns /etc/resolv.conf. -// -// Errors are silenced as they will inevitably resurface at future open/read calls. -// -// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf -func Path() string { - detectSystemdResolvConfOnce.Do(func() { - candidateResolvConf, err := os.ReadFile(defaultPath) - if err != nil { - // silencing error as it will resurface at next calls trying to read defaultPath - return - } - ns := GetNameservers(candidateResolvConf, IP) - if len(ns) == 1 && ns[0] == "127.0.0.53" { - pathAfterSystemdDetection = alternatePath - log.G(context.TODO()).Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath) - } - }) - return pathAfterSystemdDetection -} - -const ( - // ipLocalhost is a regex pattern for IPv4 or IPv6 loopback range. - ipLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)` - ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` - ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock - - // This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also - // will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants - // -- e.g. other link-local types -- either won't work in containers or are unnecessary. - // For readability and sufficiency for Docker purposes this seemed more reasonable than a - // 1000+ character regexp with exact and complete IPv6 validation - ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})(%\w+)?` -) - -var ( - // Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS - defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"} - defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"} - - localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipLocalhost + `\s*\n*`) - nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) - nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) - nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`) - nsIPv4Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `))\s*$`) - searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) - optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`) -) - // File contains the resolv.conf content and its hash type File struct { Content []byte Hash []byte } +func Path() string { + return resolvconf.Path() +} + // Get returns the contents of /etc/resolv.conf and its hash func Get() (*File, error) { return GetSpecific(Path()) @@ -103,7 +39,8 @@ func GetSpecific(path string) (*File, error) { if err != nil { return nil, err } - return &File{Content: resolv, Hash: hashData(resolv)}, nil + hash := digest.FromBytes(resolv) + return &File{Content: resolv, Hash: []byte(hash)}, nil } // FilterResolvDNS cleans up the config in resolvConf. It has two main jobs: @@ -113,54 +50,34 @@ func GetSpecific(path string) (*File, error) { // 2. Given the caller provides the enable/disable state of IPv6, the filter // code will remove all IPv6 nameservers if it is not enabled for containers func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) { - cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{}) - // if IPv6 is not enabled, also clean out any IPv6 address nameserver - if !ipv6Enabled { - cleanedResolvConf = nsIPv6Regexp.ReplaceAll(cleanedResolvConf, []byte{}) - } - // if the resulting resolvConf has no more nameservers defined, add appropriate - // default DNS servers for IPv4 and (optionally) IPv6 - if len(GetNameservers(cleanedResolvConf, IP)) == 0 { - log.G(context.TODO()).Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns) - dns := defaultIPv4Dns - if ipv6Enabled { - log.G(context.TODO()).Infof("IPv6 enabled; Adding default IPv6 external servers: %v", defaultIPv6Dns) - dns = append(dns, defaultIPv6Dns...) - } - cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...) + rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") + if err != nil { + return nil, err } - return &File{Content: cleanedResolvConf, Hash: hashData(cleanedResolvConf)}, nil -} - -// getLines parses input into lines and strips away comments. -func getLines(input []byte, commentMarker []byte) [][]byte { - lines := bytes.Split(input, []byte("\n")) - var output [][]byte - for _, currentLine := range lines { - commentIndex := bytes.Index(currentLine, commentMarker) - if commentIndex == -1 { - output = append(output, currentLine) - } else { - output = append(output, currentLine[:commentIndex]) - } + rc.TransformForLegacyNw(ipv6Enabled) + content, err := rc.Generate(false) + if err != nil { + return nil, err } - return output + hash := digest.FromBytes(content) + return &File{Content: content, Hash: []byte(hash)}, nil } // GetNameservers returns nameservers (if any) listed in /etc/resolv.conf func GetNameservers(resolvConf []byte, kind int) []string { + rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") + if err != nil { + return nil + } + nsAddrs := rc.NameServers() var nameservers []string - for _, line := range getLines(resolvConf, []byte("#")) { - var ns [][]byte + for _, addr := range nsAddrs { if kind == IP { - ns = nsRegexp.FindSubmatch(line) - } else if kind == IPv4 { - ns = nsIPv4Regexpmatch.FindSubmatch(line) - } else if kind == IPv6 { - ns = nsIPv6Regexpmatch.FindSubmatch(line) - } - if len(ns) > 0 { - nameservers = append(nameservers, string(ns[1])) + nameservers = append(nameservers, addr.String()) + } else if kind == IPv4 && addr.Is4() { + nameservers = append(nameservers, addr.String()) + } else if kind == IPv6 && addr.Is6() { + nameservers = append(nameservers, addr.String()) } } return nameservers @@ -170,16 +87,15 @@ func GetNameservers(resolvConf []byte, kind int) []string { // /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") // This function's output is intended for net.ParseCIDR func GetNameserversAsCIDR(resolvConf []byte) []string { - var nameservers []string - for _, nameserver := range GetNameservers(resolvConf, IP) { - var address string - // If IPv6, strip zone if present - if strings.Contains(nameserver, ":") { - address = strings.Split(nameserver, "%")[0] + "/128" - } else { - address = nameserver + "/32" - } - nameservers = append(nameservers, address) + rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") + if err != nil { + return nil + } + nsAddrs := rc.NameServers() + nameservers := make([]string, 0, len(nsAddrs)) + for _, addr := range nsAddrs { + str := fmt.Sprintf("%s/%d", addr.WithZone("").String(), addr.BitLen()) + nameservers = append(nameservers, str) } return nameservers } @@ -188,36 +104,30 @@ func GetNameserversAsCIDR(resolvConf []byte) []string { // If more than one search line is encountered, only the contents of the last // one is returned. func GetSearchDomains(resolvConf []byte) []string { - var domains []string - for _, line := range getLines(resolvConf, []byte("#")) { - match := searchRegexp.FindSubmatch(line) - if match == nil { - continue - } - domains = strings.Fields(string(match[1])) + rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") + if err != nil { + return nil } - return domains + return rc.Search() } // GetOptions returns options (if any) listed in /etc/resolv.conf // If more than one options line is encountered, only the contents of the last // one is returned. func GetOptions(resolvConf []byte) []string { - var options []string - for _, line := range getLines(resolvConf, []byte("#")) { - match := optionsRegexp.FindSubmatch(line) - if match == nil { - continue - } - options = strings.Fields(string(match[1])) + rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "") + if err != nil { + return nil } - return options + return rc.Options() } // Build generates and writes a configuration file to path containing a nameserver // entry for every element in nameservers, a "search" entry for every element in // dnsSearch, and an "options" entry for every element in dnsOptions. It returns // a File containing the generated content and its (sha256) hash. +// +// Note that the resolv.conf file is written, but the hash file is not. func Build(path string, nameservers, dnsSearch, dnsOptions []string) (*File, error) { content := bytes.NewBuffer(nil) if len(dnsSearch) > 0 { @@ -244,5 +154,6 @@ func Build(path string, nameservers, dnsSearch, dnsOptions []string) (*File, err return nil, err } - return &File{Content: content.Bytes(), Hash: hashData(content.Bytes())}, nil + hash := digest.FromBytes(content.Bytes()) + return &File{Content: content.Bytes(), Hash: []byte(hash)}, nil } diff --git a/vendor/github.com/docker/docker/libnetwork/resolvconf/utils.go b/vendor/github.com/docker/docker/libnetwork/resolvconf/utils.go deleted file mode 100644 index 8e005e2a1922..000000000000 --- a/vendor/github.com/docker/docker/libnetwork/resolvconf/utils.go +++ /dev/null @@ -1,14 +0,0 @@ -package resolvconf - -import ( - "crypto/sha256" - "encoding/hex" -) - -// hashData returns the sha256 sum of data. -func hashData(data []byte) []byte { - f := sha256.Sum256(data) - out := make([]byte, 2*sha256.Size) - hex.Encode(out, f[:]) - return append([]byte("sha256:"), out...) -} diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir.go b/vendor/github.com/docker/docker/pkg/homedir/homedir.go index 590683206c3b..c0ab3f5bf359 100644 --- a/vendor/github.com/docker/docker/pkg/homedir/homedir.go +++ b/vendor/github.com/docker/docker/pkg/homedir/homedir.go @@ -6,14 +6,6 @@ import ( "runtime" ) -// Key returns the env var name for the user's home dir based on -// the platform being run on. -// -// Deprecated: this function is no longer used, and will be removed in the next release. -func Key() string { - return envKeyName -} - // Get returns the home directory of the current user with the help of // environment variables depending on the target operating system. // Returned path should be used with "path/filepath" to form new paths. @@ -34,11 +26,3 @@ func Get() string { } return home } - -// GetShortcutString returns the string that is shortcut to user's home directory -// in the native shell of the platform running on. -// -// Deprecated: this function is no longer used, and will be removed in the next release. -func GetShortcutString() string { - return homeShortCut -} diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir_unix.go b/vendor/github.com/docker/docker/pkg/homedir/homedir_unix.go deleted file mode 100644 index feae4d736c4f..000000000000 --- a/vendor/github.com/docker/docker/pkg/homedir/homedir_unix.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build !windows - -package homedir // import "github.com/docker/docker/pkg/homedir" - -const ( - envKeyName = "HOME" - homeShortCut = "~" -) diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir_windows.go b/vendor/github.com/docker/docker/pkg/homedir/homedir_windows.go deleted file mode 100644 index 37f4ee67014d..000000000000 --- a/vendor/github.com/docker/docker/pkg/homedir/homedir_windows.go +++ /dev/null @@ -1,6 +0,0 @@ -package homedir // import "github.com/docker/docker/pkg/homedir" - -const ( - envKeyName = "USERPROFILE" - homeShortCut = "%USERPROFILE%" // be careful while using in format functions -) diff --git a/vendor/github.com/docker/docker/pkg/system/image_os_deprecated.go b/vendor/github.com/docker/docker/pkg/system/image_os_deprecated.go deleted file mode 100644 index afb57dae6a94..000000000000 --- a/vendor/github.com/docker/docker/pkg/system/image_os_deprecated.go +++ /dev/null @@ -1,19 +0,0 @@ -package system - -import ( - "errors" - "runtime" - "strings" -) - -// ErrNotSupportedOperatingSystem means the operating system is not supported. -// -// Deprecated: use [github.com/docker/docker/image.CheckOS] and check the error returned. -var ErrNotSupportedOperatingSystem = errors.New("operating system is not supported") - -// IsOSSupported determines if an operating system is supported by the host. -// -// Deprecated: use [github.com/docker/docker/image.CheckOS] and check the error returned. -func IsOSSupported(os string) bool { - return strings.EqualFold(runtime.GOOS, os) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 7a7a9ae295a4..dc3f3d3eb5a5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -446,14 +446,14 @@ github.com/dimchansky/utfbom # github.com/distribution/reference v0.5.0 ## explicit; go 1.20 github.com/distribution/reference -# github.com/docker/cli v25.0.3+incompatible +# github.com/docker/cli v26.0.0-rc1+incompatible ## explicit github.com/docker/cli/cli/config github.com/docker/cli/cli/config/configfile github.com/docker/cli/cli/config/credentials github.com/docker/cli/cli/config/types github.com/docker/cli/cli/connhelper/commandconn -# github.com/docker/docker v25.0.3+incompatible +# github.com/docker/docker v26.0.0-rc1.0.20240229165828-b8aa8579cad7+incompatible => github.com/robmry/moby v0.0.0-20240229231553-bcc0ad7b9776 ## explicit github.com/docker/docker/api github.com/docker/docker/api/types @@ -475,10 +475,10 @@ github.com/docker/docker/api/types/versions github.com/docker/docker/api/types/volume github.com/docker/docker/client github.com/docker/docker/errdefs -github.com/docker/docker/image/spec/specs-go/v1 github.com/docker/docker/internal/mounttree github.com/docker/docker/internal/multierror github.com/docker/docker/internal/unshare +github.com/docker/docker/libnetwork/internal/resolvconf github.com/docker/docker/libnetwork/resolvconf github.com/docker/docker/pkg/archive github.com/docker/docker/pkg/chrootarchive @@ -1099,3 +1099,4 @@ kernel.org/pub/linux/libs/security/libcap/cap # kernel.org/pub/linux/libs/security/libcap/psx v1.2.67 ## explicit; go 1.11 kernel.org/pub/linux/libs/security/libcap/psx +# github.com/docker/docker => github.com/robmry/moby v0.0.0-20240229231553-bcc0ad7b9776