update fsutil and docker

update fsutil to include this patch: d952e50eae

docker also had to be updated due to 2d121ce88f

Signed-off-by: Alex Couture-Beil <alex@earthly.dev>
master
Alex Couture-Beil 2021-11-25 09:50:28 -08:00
parent 90690ca0c9
commit 197f619956
No known key found for this signature in database
GPG Key ID: 097FC39A210CE7F7
100 changed files with 1116 additions and 767 deletions

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package api // import "github.com/docker/docker/api" package api // import "github.com/docker/docker/api"

View File

@ -382,11 +382,13 @@ definitions:
type: "string" type: "string"
description: | description: |
- Empty string means not to restart - Empty string means not to restart
- `no` Do not automatically restart
- `always` Always restart - `always` Always restart
- `unless-stopped` Restart always except when the user has manually stopped the container - `unless-stopped` Restart always except when the user has manually stopped the container
- `on-failure` Restart only when the container exit code is non-zero - `on-failure` Restart only when the container exit code is non-zero
enum: enum:
- "" - ""
- "no"
- "always" - "always"
- "unless-stopped" - "unless-stopped"
- "on-failure" - "on-failure"
@ -744,6 +746,7 @@ definitions:
description: | description: |
Health stores information about the container's healthcheck results. Health stores information about the container's healthcheck results.
type: "object" type: "object"
x-nullable: true
properties: properties:
Status: Status:
description: | description: |
@ -769,13 +772,13 @@ definitions:
description: | description: |
Log contains the last few results (oldest first) Log contains the last few results (oldest first)
items: items:
x-nullable: true
$ref: "#/definitions/HealthcheckResult" $ref: "#/definitions/HealthcheckResult"
HealthcheckResult: HealthcheckResult:
description: | description: |
HealthcheckResult stores information about a single run of a healthcheck probe HealthcheckResult stores information about a single run of a healthcheck probe
type: "object" type: "object"
x-nullable: true
properties: properties:
Start: Start:
description: | description: |
@ -2188,6 +2191,25 @@ definitions:
type: "string" type: "string"
x-nullable: false x-nullable: false
PluginPrivilege:
description: |
Describes a permission the user has to accept upon installing
the plugin.
type: "object"
x-go-name: "PluginPrivilege"
properties:
Name:
type: "string"
example: "network"
Description:
type: "string"
Value:
type: "array"
items:
type: "string"
example:
- "host"
Plugin: Plugin:
description: "A plugin for the Engine API" description: "A plugin for the Engine API"
type: "object" type: "object"
@ -2970,19 +2992,7 @@ definitions:
PluginPrivilege: PluginPrivilege:
type: "array" type: "array"
items: items:
description: | $ref: "#/definitions/PluginPrivilege"
Describes a permission accepted by the user upon installing the
plugin.
type: "object"
properties:
Name:
type: "string"
Description:
type: "string"
Value:
type: "array"
items:
type: "string"
ContainerSpec: ContainerSpec:
type: "object" type: "object"
description: | description: |
@ -4022,73 +4032,71 @@ definitions:
Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found"
ContainerSummary: ContainerSummary:
type: "array" type: "object"
items: properties:
type: "object" Id:
properties: description: "The ID of this container"
Id: type: "string"
description: "The ID of this container" x-go-name: "ID"
Names:
description: "The names that this container has been given"
type: "array"
items:
type: "string" type: "string"
x-go-name: "ID" Image:
Names: description: "The name of the image used when creating this container"
description: "The names that this container has been given" type: "string"
type: "array" ImageID:
items: description: "The ID of the image that this container was created from"
type: "string"
Command:
description: "Command to run when starting the container"
type: "string"
Created:
description: "When the container was created"
type: "integer"
format: "int64"
Ports:
description: "The ports exposed by this container"
type: "array"
items:
$ref: "#/definitions/Port"
SizeRw:
description: "The size of files that have been created or changed by this container"
type: "integer"
format: "int64"
SizeRootFs:
description: "The total size of all the files in this container"
type: "integer"
format: "int64"
Labels:
description: "User-defined key/value metadata."
type: "object"
additionalProperties:
type: "string"
State:
description: "The state of this container (e.g. `Exited`)"
type: "string"
Status:
description: "Additional human-readable status of this container (e.g. `Exit 0`)"
type: "string"
HostConfig:
type: "object"
properties:
NetworkMode:
type: "string" type: "string"
Image: NetworkSettings:
description: "The name of the image used when creating this container" description: "A summary of the container's network settings"
type: "string" type: "object"
ImageID: properties:
description: "The ID of the image that this container was created from" Networks:
type: "string" type: "object"
Command: additionalProperties:
description: "Command to run when starting the container" $ref: "#/definitions/EndpointSettings"
type: "string" Mounts:
Created: type: "array"
description: "When the container was created" items:
type: "integer" $ref: "#/definitions/Mount"
format: "int64"
Ports:
description: "The ports exposed by this container"
type: "array"
items:
$ref: "#/definitions/Port"
SizeRw:
description: "The size of files that have been created or changed by this container"
type: "integer"
format: "int64"
SizeRootFs:
description: "The total size of all the files in this container"
type: "integer"
format: "int64"
Labels:
description: "User-defined key/value metadata."
type: "object"
additionalProperties:
type: "string"
State:
description: "The state of this container (e.g. `Exited`)"
type: "string"
Status:
description: "Additional human-readable status of this container (e.g. `Exit 0`)"
type: "string"
HostConfig:
type: "object"
properties:
NetworkMode:
type: "string"
NetworkSettings:
description: "A summary of the container's network settings"
type: "object"
properties:
Networks:
type: "object"
additionalProperties:
$ref: "#/definitions/EndpointSettings"
Mounts:
type: "array"
items:
$ref: "#/definitions/Mount"
Driver: Driver:
description: "Driver represents a driver (network, logging, secrets)." description: "Driver represents a driver (network, logging, secrets)."
@ -4210,6 +4218,7 @@ definitions:
ContainerState stores container's running state. It's part of ContainerJSONBase ContainerState stores container's running state. It's part of ContainerJSONBase
and will be returned by the "inspect" command. and will be returned by the "inspect" command.
type: "object" type: "object"
x-nullable: true
properties: properties:
Status: Status:
description: | description: |
@ -4267,7 +4276,6 @@ definitions:
type: "string" type: "string"
example: "2020-01-06T09:07:59.461876391Z" example: "2020-01-06T09:07:59.461876391Z"
Health: Health:
x-nullable: true
$ref: "#/definitions/Health" $ref: "#/definitions/Health"
SystemVersion: SystemVersion:
@ -4366,7 +4374,6 @@ definitions:
type: "string" type: "string"
example: "2020-06-22T15:49:27.000000000+00:00" example: "2020-06-22T15:49:27.000000000+00:00"
SystemInfo: SystemInfo:
type: "object" type: "object"
properties: properties:
@ -5199,6 +5206,158 @@ definitions:
additionalProperties: additionalProperties:
type: "string" type: "string"
EventActor:
description: |
Actor describes something that generates events, like a container, network,
or a volume.
type: "object"
properties:
ID:
description: "The ID of the object emitting the event"
type: "string"
example: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743"
Attributes:
description: |
Various key/value attributes of the object, depending on its type.
type: "object"
additionalProperties:
type: "string"
example:
com.example.some-label: "some-label-value"
image: "alpine:latest"
name: "my-container"
EventMessage:
description: |
EventMessage represents the information an event contains.
type: "object"
title: "SystemEventsResponse"
properties:
Type:
description: "The type of object emitting the event"
type: "string"
enum: ["builder", "config", "container", "daemon", "image", "network", "node", "plugin", "secret", "service", "volume"]
example: "container"
Action:
description: "The type of event"
type: "string"
example: "create"
Actor:
$ref: "#/definitions/EventActor"
scope:
description: |
Scope of the event. Engine events are `local` scope. Cluster (Swarm)
events are `swarm` scope.
type: "string"
enum: ["local", "swarm"]
time:
description: "Timestamp of event"
type: "integer"
format: "int64"
example: 1629574695
timeNano:
description: "Timestamp of event, with nanosecond accuracy"
type: "integer"
format: "int64"
example: 1629574695515050031
OCIDescriptor:
type: "object"
x-go-name: Descriptor
description: |
A descriptor struct containing digest, media type, and size, as defined in
the [OCI Content Descriptors Specification](https://github.com/opencontainers/image-spec/blob/v1.0.1/descriptor.md).
properties:
mediaType:
description: |
The media type of the object this schema refers to.
type: "string"
example: "application/vnd.docker.distribution.manifest.v2+json"
digest:
description: |
The digest of the targeted content.
type: "string"
example: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96"
size:
description: |
The size in bytes of the blob.
type: "integer"
format: "int64"
example: 3987495
# TODO Not yet including these fields for now, as they are nil / omitted in our response.
# urls:
# description: |
# List of URLs from which this object MAY be downloaded.
# type: "array"
# items:
# type: "string"
# format: "uri"
# annotations:
# description: |
# Arbitrary metadata relating to the targeted content.
# type: "object"
# additionalProperties:
# type: "string"
# platform:
# $ref: "#/definitions/OCIPlatform"
OCIPlatform:
type: "object"
x-go-name: Platform
description: |
Describes the platform which the image in the manifest runs on, as defined
in the [OCI Image Index Specification](https://github.com/opencontainers/image-spec/blob/v1.0.1/image-index.md).
properties:
architecture:
description: |
The CPU architecture, for example `amd64` or `ppc64`.
type: "string"
example: "arm"
os:
description: |
The operating system, for example `linux` or `windows`.
type: "string"
example: "windows"
os.version:
description: |
Optional field specifying the operating system version, for example on
Windows `10.0.19041.1165`.
type: "string"
example: "10.0.19041.1165"
os.features:
description: |
Optional field specifying an array of strings, each listing a required
OS feature (for example on Windows `win32k`).
type: "array"
items:
type: "string"
example:
- "win32k"
variant:
description: |
Optional field specifying a variant of the CPU, for example `v7` to
specify ARMv7 when architecture is `arm`.
type: "string"
example: "v7"
DistributionInspect:
type: "object"
x-go-name: DistributionInspect
title: "DistributionInspectResponse"
required: [Descriptor, Platforms]
description: |
Describes the result obtained from contacting the registry to retrieve
image metadata.
properties:
Descriptor:
$ref: "#/definitions/OCIDescriptor"
Platforms:
type: "array"
description: |
An array containing all platforms supported by the image.
items:
$ref: "#/definitions/OCIPlatform"
paths: paths:
/containers/json: /containers/json:
get: get:
@ -5261,7 +5420,9 @@ paths:
200: 200:
description: "no error" description: "no error"
schema: schema:
$ref: "#/definitions/ContainerSummary" type: "array"
items:
$ref: "#/definitions/ContainerSummary"
examples: examples:
application/json: application/json:
- Id: "8dfafdbc3a40" - Id: "8dfafdbc3a40"
@ -5627,7 +5788,6 @@ paths:
items: items:
type: "string" type: "string"
State: State:
x-nullable: true
$ref: "#/definitions/ContainerState" $ref: "#/definitions/ContainerState"
Image: Image:
description: "The container's image ID" description: "The container's image ID"
@ -7505,6 +7665,18 @@ paths:
Refer to the [authentication section](#section/Authentication) for Refer to the [authentication section](#section/Authentication) for
details. details.
type: "string" type: "string"
- name: "changes"
in: "query"
description: |
Apply `Dockerfile` instructions to the image that is created,
for example: `changes=ENV DEBUG=true`.
Note that `ENV DEBUG=true` should be URI component encoded.
Supported `Dockerfile` instructions:
`CMD`|`ENTRYPOINT`|`ENV`|`EXPOSE`|`ONBUILD`|`USER`|`VOLUME`|`WORKDIR`
type: "array"
items:
type: "string"
- name: "platform" - name: "platform"
in: "query" in: "query"
description: "Platform in the format os[/arch[/variant]]" description: "Platform in the format os[/arch[/variant]]"
@ -8179,44 +8351,7 @@ paths:
200: 200:
description: "no error" description: "no error"
schema: schema:
type: "object" $ref: "#/definitions/EventMessage"
title: "SystemEventsResponse"
properties:
Type:
description: "The type of object emitting the event"
type: "string"
Action:
description: "The type of event"
type: "string"
Actor:
type: "object"
properties:
ID:
description: "The ID of the object emitting the event"
type: "string"
Attributes:
description: "Various key/value attributes of the object, depending on its type"
type: "object"
additionalProperties:
type: "string"
time:
description: "Timestamp of event"
type: "integer"
timeNano:
description: "Timestamp of event, with nanosecond accuracy"
type: "integer"
format: "int64"
examples:
application/json:
Type: "container"
Action: "create"
Actor:
ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743"
Attributes:
com.example.some-label: "some-label-value"
image: "alpine"
name: "my-container"
time: 1461943101
400: 400:
description: "bad parameter" description: "bad parameter"
schema: schema:
@ -8531,6 +8666,7 @@ paths:
description: "Exec configuration" description: "Exec configuration"
schema: schema:
type: "object" type: "object"
title: "ExecConfig"
properties: properties:
AttachStdin: AttachStdin:
type: "boolean" type: "boolean"
@ -8621,6 +8757,7 @@ paths:
in: "body" in: "body"
schema: schema:
type: "object" type: "object"
title: "ExecStartConfig"
properties: properties:
Detach: Detach:
type: "boolean" type: "boolean"
@ -9155,6 +9292,7 @@ paths:
required: true required: true
schema: schema:
type: "object" type: "object"
title: "NetworkCreateRequest"
required: ["Name"] required: ["Name"]
properties: properties:
Name: Name:
@ -9265,6 +9403,7 @@ paths:
required: true required: true
schema: schema:
type: "object" type: "object"
title: "NetworkConnectRequest"
properties: properties:
Container: Container:
type: "string" type: "string"
@ -9311,6 +9450,7 @@ paths:
required: true required: true
schema: schema:
type: "object" type: "object"
title: "NetworkDisconnectRequest"
properties: properties:
Container: Container:
type: "string" type: "string"
@ -9395,20 +9535,7 @@ paths:
schema: schema:
type: "array" type: "array"
items: items:
description: | $ref: "#/definitions/PluginPrivilege"
Describes a permission the user has to accept upon installing
the plugin.
type: "object"
title: "PluginPrivilegeItem"
properties:
Name:
type: "string"
Description:
type: "string"
Value:
type: "array"
items:
type: "string"
example: example:
- Name: "network" - Name: "network"
Description: "" Description: ""
@ -9484,19 +9611,7 @@ paths:
schema: schema:
type: "array" type: "array"
items: items:
description: | $ref: "#/definitions/PluginPrivilege"
Describes a permission accepted by the user upon installing the
plugin.
type: "object"
properties:
Name:
type: "string"
Description:
type: "string"
Value:
type: "array"
items:
type: "string"
example: example:
- Name: "network" - Name: "network"
Description: "" Description: ""
@ -9668,19 +9783,7 @@ paths:
schema: schema:
type: "array" type: "array"
items: items:
description: | $ref: "#/definitions/PluginPrivilege"
Describes a permission accepted by the user upon installing the
plugin.
type: "object"
properties:
Name:
type: "string"
Description:
type: "string"
Value:
type: "array"
items:
type: "string"
example: example:
- Name: "network" - Name: "network"
Description: "" Description: ""
@ -9970,6 +10073,7 @@ paths:
required: true required: true
schema: schema:
type: "object" type: "object"
title: "SwarmInitRequest"
properties: properties:
ListenAddr: ListenAddr:
description: | description: |
@ -10068,6 +10172,7 @@ paths:
required: true required: true
schema: schema:
type: "object" type: "object"
title: "SwarmJoinRequest"
properties: properties:
ListenAddr: ListenAddr:
description: | description: |
@ -10228,6 +10333,7 @@ paths:
required: true required: true
schema: schema:
type: "object" type: "object"
title: "SwarmUnlockRequest"
properties: properties:
UnlockKey: UnlockKey:
description: "The swarm's unlock key." description: "The swarm's unlock key."
@ -11339,67 +11445,7 @@ paths:
200: 200:
description: "descriptor and platform information" description: "descriptor and platform information"
schema: schema:
type: "object" $ref: "#/definitions/DistributionInspect"
x-go-name: DistributionInspect
title: "DistributionInspectResponse"
required: [Descriptor, Platforms]
properties:
Descriptor:
type: "object"
description: |
A descriptor struct containing digest, media type, and size.
properties:
MediaType:
type: "string"
Size:
type: "integer"
format: "int64"
Digest:
type: "string"
URLs:
type: "array"
items:
type: "string"
Platforms:
type: "array"
description: |
An array containing all platforms supported by the image.
items:
type: "object"
properties:
Architecture:
type: "string"
OS:
type: "string"
OSVersion:
type: "string"
OSFeatures:
type: "array"
items:
type: "string"
Variant:
type: "string"
Features:
type: "array"
items:
type: "string"
examples:
application/json:
Descriptor:
MediaType: "application/vnd.docker.distribution.manifest.v2+json"
Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96"
Size: 3987495
URLs:
- ""
Platforms:
- Architecture: "amd64"
OS: "linux"
OSVersion: ""
OSFeatures:
- ""
Variant: ""
Features:
- ""
401: 401:
description: "Failed authentication or no image found" description: "Failed authentication or no image found"
schema: schema:

View File

@ -13,19 +13,26 @@ import (
// CgroupnsMode represents the cgroup namespace mode of the container // CgroupnsMode represents the cgroup namespace mode of the container
type CgroupnsMode string type CgroupnsMode string
// cgroup namespace modes for containers
const (
CgroupnsModeEmpty CgroupnsMode = ""
CgroupnsModePrivate CgroupnsMode = "private"
CgroupnsModeHost CgroupnsMode = "host"
)
// IsPrivate indicates whether the container uses its own private cgroup namespace // IsPrivate indicates whether the container uses its own private cgroup namespace
func (c CgroupnsMode) IsPrivate() bool { func (c CgroupnsMode) IsPrivate() bool {
return c == "private" return c == CgroupnsModePrivate
} }
// IsHost indicates whether the container shares the host's cgroup namespace // IsHost indicates whether the container shares the host's cgroup namespace
func (c CgroupnsMode) IsHost() bool { func (c CgroupnsMode) IsHost() bool {
return c == "host" return c == CgroupnsModeHost
} }
// IsEmpty indicates whether the container cgroup namespace mode is unset // IsEmpty indicates whether the container cgroup namespace mode is unset
func (c CgroupnsMode) IsEmpty() bool { func (c CgroupnsMode) IsEmpty() bool {
return c == "" return c == CgroupnsModeEmpty
} }
// Valid indicates whether the cgroup namespace mode is valid // Valid indicates whether the cgroup namespace mode is valid
@ -37,60 +44,69 @@ func (c CgroupnsMode) Valid() bool {
// values are platform specific // values are platform specific
type Isolation string type Isolation string
// Isolation modes for containers
const (
IsolationEmpty Isolation = "" // IsolationEmpty is unspecified (same behavior as default)
IsolationDefault Isolation = "default" // IsolationDefault is the default isolation mode on current daemon
IsolationProcess Isolation = "process" // IsolationProcess is process isolation mode
IsolationHyperV Isolation = "hyperv" // IsolationHyperV is HyperV isolation mode
)
// IsDefault indicates the default isolation technology of a container. On Linux this // IsDefault indicates the default isolation technology of a container. On Linux this
// is the native driver. On Windows, this is a Windows Server Container. // is the native driver. On Windows, this is a Windows Server Container.
func (i Isolation) IsDefault() bool { func (i Isolation) IsDefault() bool {
return strings.ToLower(string(i)) == "default" || string(i) == "" // TODO consider making isolation-mode strict (case-sensitive)
v := Isolation(strings.ToLower(string(i)))
return v == IsolationDefault || v == IsolationEmpty
} }
// IsHyperV indicates the use of a Hyper-V partition for isolation // IsHyperV indicates the use of a Hyper-V partition for isolation
func (i Isolation) IsHyperV() bool { func (i Isolation) IsHyperV() bool {
return strings.ToLower(string(i)) == "hyperv" // TODO consider making isolation-mode strict (case-sensitive)
return Isolation(strings.ToLower(string(i))) == IsolationHyperV
} }
// IsProcess indicates the use of process isolation // IsProcess indicates the use of process isolation
func (i Isolation) IsProcess() bool { func (i Isolation) IsProcess() bool {
return strings.ToLower(string(i)) == "process" // TODO consider making isolation-mode strict (case-sensitive)
return Isolation(strings.ToLower(string(i))) == IsolationProcess
} }
const (
// IsolationEmpty is unspecified (same behavior as default)
IsolationEmpty = Isolation("")
// IsolationDefault is the default isolation mode on current daemon
IsolationDefault = Isolation("default")
// IsolationProcess is process isolation mode
IsolationProcess = Isolation("process")
// IsolationHyperV is HyperV isolation mode
IsolationHyperV = Isolation("hyperv")
)
// IpcMode represents the container ipc stack. // IpcMode represents the container ipc stack.
type IpcMode string type IpcMode string
// IpcMode constants
const (
IPCModeNone IpcMode = "none"
IPCModeHost IpcMode = "host"
IPCModeContainer IpcMode = "container"
IPCModePrivate IpcMode = "private"
IPCModeShareable IpcMode = "shareable"
)
// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared. // IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared.
func (n IpcMode) IsPrivate() bool { func (n IpcMode) IsPrivate() bool {
return n == "private" return n == IPCModePrivate
} }
// IsHost indicates whether the container shares the host's ipc namespace. // IsHost indicates whether the container shares the host's ipc namespace.
func (n IpcMode) IsHost() bool { func (n IpcMode) IsHost() bool {
return n == "host" return n == IPCModeHost
} }
// IsShareable indicates whether the container's ipc namespace can be shared with another container. // IsShareable indicates whether the container's ipc namespace can be shared with another container.
func (n IpcMode) IsShareable() bool { func (n IpcMode) IsShareable() bool {
return n == "shareable" return n == IPCModeShareable
} }
// IsContainer indicates whether the container uses another container's ipc namespace. // IsContainer indicates whether the container uses another container's ipc namespace.
func (n IpcMode) IsContainer() bool { func (n IpcMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2) return strings.HasPrefix(string(n), string(IPCModeContainer)+":")
return len(parts) > 1 && parts[0] == "container"
} }
// IsNone indicates whether container IpcMode is set to "none". // IsNone indicates whether container IpcMode is set to "none".
func (n IpcMode) IsNone() bool { func (n IpcMode) IsNone() bool {
return n == "none" return n == IPCModeNone
} }
// IsEmpty indicates whether container IpcMode is empty // IsEmpty indicates whether container IpcMode is empty
@ -105,9 +121,8 @@ func (n IpcMode) Valid() bool {
// Container returns the name of the container ipc stack is going to be used. // Container returns the name of the container ipc stack is going to be used.
func (n IpcMode) Container() string { func (n IpcMode) Container() string {
parts := strings.SplitN(string(n), ":", 2) if n.IsContainer() {
if len(parts) > 1 && parts[0] == "container" { return strings.TrimPrefix(string(n), string(IPCModeContainer)+":")
return parts[1]
} }
return "" return ""
} }
@ -326,7 +341,7 @@ type LogMode string
// Available logging modes // Available logging modes
const ( const (
LogModeUnset = "" LogModeUnset LogMode = ""
LogModeBlocking LogMode = "blocking" LogModeBlocking LogMode = "blocking"
LogModeNonBlock LogMode = "non-blocking" LogModeNonBlock LogMode = "non-blocking"
) )

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package container // import "github.com/docker/docker/api/types/container" package container // import "github.com/docker/docker/api/types/container"

View File

@ -1,33 +1,26 @@
package events // import "github.com/docker/docker/api/types/events" package events // import "github.com/docker/docker/api/types/events"
// Type is used for event-types.
type Type = string
// List of known event types.
const ( const (
// BuilderEventType is the event type that the builder generates BuilderEventType Type = "builder" // BuilderEventType is the event type that the builder generates.
BuilderEventType = "builder" ConfigEventType Type = "config" // ConfigEventType is the event type that configs generate.
// ContainerEventType is the event type that containers generate ContainerEventType Type = "container" // ContainerEventType is the event type that containers generate.
ContainerEventType = "container" DaemonEventType Type = "daemon" // DaemonEventType is the event type that daemon generate.
// DaemonEventType is the event type that daemon generate ImageEventType Type = "image" // ImageEventType is the event type that images generate.
DaemonEventType = "daemon" NetworkEventType Type = "network" // NetworkEventType is the event type that networks generate.
// ImageEventType is the event type that images generate NodeEventType Type = "node" // NodeEventType is the event type that nodes generate.
ImageEventType = "image" PluginEventType Type = "plugin" // PluginEventType is the event type that plugins generate.
// NetworkEventType is the event type that networks generate SecretEventType Type = "secret" // SecretEventType is the event type that secrets generate.
NetworkEventType = "network" ServiceEventType Type = "service" // ServiceEventType is the event type that services generate.
// PluginEventType is the event type that plugins generate VolumeEventType Type = "volume" // VolumeEventType is the event type that volumes generate.
PluginEventType = "plugin"
// VolumeEventType is the event type that volumes generate
VolumeEventType = "volume"
// ServiceEventType is the event type that services generate
ServiceEventType = "service"
// NodeEventType is the event type that nodes generate
NodeEventType = "node"
// SecretEventType is the event type that secrets generate
SecretEventType = "secret"
// ConfigEventType is the event type that configs generate
ConfigEventType = "config"
) )
// Actor describes something that generates events, // Actor describes something that generates events,
// like a container, or a network, or a volume. // like a container, or a network, or a volume.
// It has a defined name and a set or attributes. // It has a defined name and a set of attributes.
// The container attributes are its labels, other actors // The container attributes are its labels, other actors
// can generate these attributes from other properties. // can generate these attributes from other properties.
type Actor struct { type Actor struct {
@ -39,11 +32,11 @@ type Actor struct {
type Message struct { type Message struct {
// Deprecated information from JSONMessage. // Deprecated information from JSONMessage.
// With data only in container events. // With data only in container events.
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"` // Deprecated: use Action instead.
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"` // Deprecated: use Actor.ID instead.
From string `json:"from,omitempty"` From string `json:"from,omitempty"` // Deprecated: use Actor.Attributes["image"] instead.
Type string Type Type
Action string Action string
Actor Actor Actor Actor
// Engine events are local scope. Cluster events are swarm scope. // Engine events are local scope. Cluster events are swarm scope.

View File

@ -8,6 +8,9 @@ import (
// compare compares two version strings // compare compares two version strings
// returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise. // returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise.
func compare(v1, v2 string) int { func compare(v1, v2 string) int {
if v1 == v2 {
return 0
}
var ( var (
currTab = strings.Split(v1, ".") currTab = strings.Split(v1, ".")
otherTab = strings.Split(v2, ".") otherTab = strings.Split(v2, ".")

View File

@ -281,21 +281,6 @@ func ParseHostURL(host string) (*url.URL, error) {
}, nil }, nil
} }
// CustomHTTPHeaders returns the custom http headers stored by the client.
func (cli *Client) CustomHTTPHeaders() map[string]string {
m := make(map[string]string)
for k, v := range cli.customHTTPHeaders {
m[k] = v
}
return m
}
// SetCustomHTTPHeaders that will be set on every HTTP request made by the client.
// Deprecated: use WithHTTPHeaders when creating the client.
func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) {
cli.customHTTPHeaders = headers
}
// Dialer returns a dialer for a raw stream connection, with HTTP/1.1 header, that can be used for proxying the daemon connection. // Dialer returns a dialer for a raw stream connection, with HTTP/1.1 header, that can be used for proxying the daemon connection.
// Used by `docker dial-stdio` (docker/cli#889). // Used by `docker dial-stdio` (docker/cli#889).
func (cli *Client) Dialer() func(context.Context) (net.Conn, error) { func (cli *Client) Dialer() func(context.Context) (net.Conn, error) {

View File

@ -1,3 +1,4 @@
//go:build linux || freebsd || openbsd || netbsd || darwin || solaris || illumos || dragonfly
// +build linux freebsd openbsd netbsd darwin solaris illumos dragonfly // +build linux freebsd openbsd netbsd darwin solaris illumos dragonfly
package client // import "github.com/docker/docker/client" package client // import "github.com/docker/docker/client"

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
) )
@ -23,7 +23,7 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id) return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id)
} }
body, err := ioutil.ReadAll(resp.body) body, err := io.ReadAll(resp.body)
if err != nil { if err != nil {
return swarm.Config{}, nil, err return swarm.Config{}, nil, err
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"net/url" "net/url"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
@ -41,7 +41,7 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri
return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID) return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID)
} }
body, err := ioutil.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)
if err != nil { if err != nil {
return types.ContainerJSON{}, nil, err return types.ContainerJSON{}, nil, err
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
) )
@ -20,7 +20,7 @@ func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (typ
return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID) return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID)
} }
body, err := ioutil.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)
if err != nil { if err != nil {
return types.ImageInspect{}, nil, err return types.ImageInspect{}, nil, err
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"net/url" "net/url"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
@ -39,7 +39,7 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string,
return networkResource, nil, wrapResponseError(err, resp, "network", networkID) return networkResource, nil, wrapResponseError(err, resp, "network", networkID)
} }
body, err := ioutil.ReadAll(resp.body) body, err := io.ReadAll(resp.body)
if err != nil { if err != nil {
return networkResource, nil, err return networkResource, nil, err
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
) )
@ -20,7 +20,7 @@ func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm
return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID) return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID)
} }
body, err := ioutil.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return swarm.Node{}, nil, err
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
) )
@ -20,7 +20,7 @@ func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*type
return nil, nil, wrapResponseError(err, resp, "plugin", name) return nil, nil, wrapResponseError(err, resp, "plugin", name)
} }
body, err := ioutil.ReadAll(resp.body) body, err := io.ReadAll(resp.body)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -6,7 +6,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -206,7 +205,7 @@ func (cli *Client) checkResponseErr(serverResp serverResponse) error {
R: serverResp.body, R: serverResp.body,
N: int64(bodyMax), N: int64(bodyMax),
} }
body, err = ioutil.ReadAll(bodyR) body, err = io.ReadAll(bodyR)
if err != nil { if err != nil {
return err return err
} }
@ -266,7 +265,7 @@ func encodeData(data interface{}) (*bytes.Buffer, error) {
func ensureReaderClosed(response serverResponse) { func ensureReaderClosed(response serverResponse) {
if response.body != nil { if response.body != nil {
// Drain up to 512 bytes and close the body to let the Transport reuse the connection // Drain up to 512 bytes and close the body to let the Transport reuse the connection
io.CopyN(ioutil.Discard, response.body, 512) io.CopyN(io.Discard, response.body, 512)
response.body.Close() response.body.Close()
} }
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
) )
@ -23,7 +23,7 @@ func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.S
return swarm.Secret{}, nil, wrapResponseError(err, resp, "secret", id) return swarm.Secret{}, nil, wrapResponseError(err, resp, "secret", id)
} }
body, err := ioutil.ReadAll(resp.body) body, err := io.ReadAll(resp.body)
if err != nil { if err != nil {
return swarm.Secret{}, nil, err return swarm.Secret{}, nil, err
} }

View File

@ -5,7 +5,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/url" "net/url"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
@ -25,7 +25,7 @@ func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string,
return swarm.Service{}, nil, wrapResponseError(err, serverResp, "service", serviceID) return swarm.Service{}, nil, wrapResponseError(err, serverResp, "service", serviceID)
} }
body, err := ioutil.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)
if err != nil { if err != nil {
return swarm.Service{}, nil, err return swarm.Service{}, nil, err
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
) )
@ -20,7 +20,7 @@ func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm
return swarm.Task{}, nil, wrapResponseError(err, serverResp, "task", taskID) return swarm.Task{}, nil, wrapResponseError(err, serverResp, "task", taskID)
} }
body, err := ioutil.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)
if err != nil { if err != nil {
return swarm.Task{}, nil, err return swarm.Task{}, nil, err
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
) )
@ -28,7 +28,7 @@ func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (t
return volume, nil, wrapResponseError(err, resp, "volume", volumeID) return volume, nil, wrapResponseError(err, resp, "volume", volumeID)
} }
body, err := ioutil.ReadAll(resp.body) body, err := io.ReadAll(resp.body)
if err != nil { if err != nil {
return volume, nil, err return volume, nil, err
} }

View File

@ -100,10 +100,10 @@ func FromStatusCode(err error, statusCode int) error {
err = System(err) err = System(err)
} }
default: default:
logrus.WithFields(logrus.Fields{ logrus.WithError(err).WithFields(logrus.Fields{
"module": "api", "module": "api",
"status_code": fmt.Sprintf("%d", statusCode), "status_code": statusCode,
}).Debugf("FIXME: Got an status-code for which error does not match any expected type!!!: %d", statusCode) }).Debug("FIXME: Got an status-code for which error does not match any expected type!!!")
switch { switch {
case statusCode >= 200 && statusCode < 400: case statusCode >= 200 && statusCode < 400:

View File

@ -1 +0,0 @@
Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf

View File

@ -1,26 +0,0 @@
package dns
import (
"regexp"
)
// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
// IPv4Localhost is a regex pattern for IPv4 localhost address range.
const IPv4Localhost = `(127\.([0-9]{1,3}\.){2}[0-9]{1,3})`
var localhostIPRegexp = regexp.MustCompile(IPLocalhost)
var localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost)
// IsLocalhost returns true if ip matches the localhost IP regular expression.
// Used for determining if nameserver settings are being passed which are
// localhost addresses
func IsLocalhost(ip string) bool {
return localhostIPRegexp.MatchString(ip)
}
// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression.
func IsIPv4Localhost(ip string) bool {
return localhostIPv4Regexp.MatchString(ip)
}

View File

@ -3,14 +3,11 @@ package resolvconf
import ( import (
"bytes" "bytes"
"io/ioutil" "os"
"regexp" "regexp"
"strings" "strings"
"sync" "sync"
"github.com/docker/docker/libnetwork/resolvconf/dns"
"github.com/docker/docker/libnetwork/types"
"github.com/docker/docker/pkg/ioutils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -21,6 +18,13 @@ const (
alternatePath = "/run/systemd/resolve/resolv.conf" alternatePath = "/run/systemd/resolve/resolv.conf"
) )
// constants for the IP address type
const (
IP = iota // IPv4 and IPv6
IPv4
IPv6
)
var ( var (
detectSystemdResolvConfOnce sync.Once detectSystemdResolvConfOnce sync.Once
pathAfterSystemdDetection = defaultPath pathAfterSystemdDetection = defaultPath
@ -39,12 +43,12 @@ var (
// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf // More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf
func Path() string { func Path() string {
detectSystemdResolvConfOnce.Do(func() { detectSystemdResolvConfOnce.Do(func() {
candidateResolvConf, err := ioutil.ReadFile(defaultPath) candidateResolvConf, err := os.ReadFile(defaultPath)
if err != nil { if err != nil {
// silencing error as it will resurface at next calls trying to read defaultPath // silencing error as it will resurface at next calls trying to read defaultPath
return return
} }
ns := GetNameservers(candidateResolvConf, types.IP) ns := GetNameservers(candidateResolvConf, IP)
if len(ns) == 1 && ns[0] == "127.0.0.53" { if len(ns) == 1 && ns[0] == "127.0.0.53" {
pathAfterSystemdDetection = alternatePath pathAfterSystemdDetection = alternatePath
logrus.Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath) logrus.Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath)
@ -53,20 +57,26 @@ func Path() string {
return pathAfterSystemdDetection return pathAfterSystemdDetection
} }
var ( const (
// Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS // ipLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"} ipLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"} ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`
ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock
ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock
// This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also // 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 // 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. // -- 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 // For readability and sufficiency for Docker purposes this seemed more reasonable than a
// 1000+ character regexp with exact and complete IPv6 validation // 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+)?` ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})(%\w+)?`
)
localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`) 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*`) nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`)
nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`)
nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`) nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`)
@ -94,11 +104,11 @@ func Get() (*File, error) {
// GetSpecific returns the contents of the user specified resolv.conf file and its hash // GetSpecific returns the contents of the user specified resolv.conf file and its hash
func GetSpecific(path string) (*File, error) { func GetSpecific(path string) (*File, error) {
resolv, err := ioutil.ReadFile(path) resolv, err := os.ReadFile(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
hash, err := ioutils.HashData(bytes.NewReader(resolv)) hash, err := hashData(bytes.NewReader(resolv))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -112,11 +122,11 @@ func GetIfChanged() (*File, error) {
lastModified.Lock() lastModified.Lock()
defer lastModified.Unlock() defer lastModified.Unlock()
resolv, err := ioutil.ReadFile(Path()) resolv, err := os.ReadFile(Path())
if err != nil { if err != nil {
return nil, err return nil, err
} }
newHash, err := ioutils.HashData(bytes.NewReader(resolv)) newHash, err := hashData(bytes.NewReader(resolv))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -153,7 +163,7 @@ func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
} }
// if the resulting resolvConf has no more nameservers defined, add appropriate // if the resulting resolvConf has no more nameservers defined, add appropriate
// default DNS servers for IPv4 and (optionally) IPv6 // default DNS servers for IPv4 and (optionally) IPv6
if len(GetNameservers(cleanedResolvConf, types.IP)) == 0 { if len(GetNameservers(cleanedResolvConf, IP)) == 0 {
logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns) logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns)
dns := defaultIPv4Dns dns := defaultIPv4Dns
if ipv6Enabled { if ipv6Enabled {
@ -162,7 +172,7 @@ func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
} }
cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...) cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...)
} }
hash, err := ioutils.HashData(bytes.NewReader(cleanedResolvConf)) hash, err := hashData(bytes.NewReader(cleanedResolvConf))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -189,11 +199,11 @@ func GetNameservers(resolvConf []byte, kind int) []string {
nameservers := []string{} nameservers := []string{}
for _, line := range getLines(resolvConf, []byte("#")) { for _, line := range getLines(resolvConf, []byte("#")) {
var ns [][]byte var ns [][]byte
if kind == types.IP { if kind == IP {
ns = nsRegexp.FindSubmatch(line) ns = nsRegexp.FindSubmatch(line)
} else if kind == types.IPv4 { } else if kind == IPv4 {
ns = nsIPv4Regexpmatch.FindSubmatch(line) ns = nsIPv4Regexpmatch.FindSubmatch(line)
} else if kind == types.IPv6 { } else if kind == IPv6 {
ns = nsIPv6Regexpmatch.FindSubmatch(line) ns = nsIPv6Regexpmatch.FindSubmatch(line)
} }
if len(ns) > 0 { if len(ns) > 0 {
@ -208,7 +218,7 @@ func GetNameservers(resolvConf []byte, kind int) []string {
// This function's output is intended for net.ParseCIDR // This function's output is intended for net.ParseCIDR
func GetNameserversAsCIDR(resolvConf []byte) []string { func GetNameserversAsCIDR(resolvConf []byte) []string {
nameservers := []string{} nameservers := []string{}
for _, nameserver := range GetNameservers(resolvConf, types.IP) { for _, nameserver := range GetNameservers(resolvConf, IP) {
var address string var address string
// If IPv6, strip zone if present // If IPv6, strip zone if present
if strings.Contains(nameserver, ":") { if strings.Contains(nameserver, ":") {
@ -276,10 +286,10 @@ func Build(path string, dns, dnsSearch, dnsOptions []string) (*File, error) {
} }
} }
hash, err := ioutils.HashData(bytes.NewReader(content.Bytes())) hash, err := hashData(bytes.NewReader(content.Bytes()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &File{Content: content.Bytes(), Hash: hash}, ioutil.WriteFile(path, content.Bytes(), 0644) return &File{Content: content.Bytes(), Hash: hash}, os.WriteFile(path, content.Bytes(), 0644)
} }

View File

@ -0,0 +1,16 @@
package resolvconf
import (
"crypto/sha256"
"encoding/hex"
"io"
)
// hashData returns the sha256 sum of src.
func hashData(src io.Reader) (string, error) {
h := sha256.New()
if _, err := io.Copy(h, src); err != nil {
return "", err
}
return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
}

View File

@ -5,13 +5,13 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"net" "net"
"strconv"
"strings" "strings"
"github.com/ishidawataru/sctp" "github.com/ishidawataru/sctp"
) )
// constants for the IP address type // constants for the IP address type
// Deprecated: use the consts defined in github.com/docker/docker/libnetwork/resolvconf
const ( const (
IP = iota // IPv4 and IPv6 IP = iota // IPv4 and IPv6
IPv4 IPv4
@ -68,19 +68,6 @@ func (t *TransportPort) String() string {
return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port) return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port)
} }
// FromString reads the TransportPort structure from string
func (t *TransportPort) FromString(s string) error {
ps := strings.Split(s, "/")
if len(ps) == 2 {
t.Proto = ParseProtocol(ps[0])
if p, err := strconv.ParseUint(ps[1], 10, 16); err == nil {
t.Port = uint16(p)
return nil
}
}
return BadRequestErrorf("invalid format for transport port: %s", s)
}
// PortBinding represents a port binding between the container and the host // PortBinding represents a port binding between the container and the host
type PortBinding struct { type PortBinding struct {
Proto Protocol Proto Protocol
@ -145,51 +132,6 @@ func (p *PortBinding) String() string {
return ret return ret
} }
// FromString reads the PortBinding structure from string s.
// String s is a triple of "protocol/containerIP:port/hostIP:port"
// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form.
// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported.
// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString
// returns an error.
func (p *PortBinding) FromString(s string) error {
ps := strings.Split(s, "/")
if len(ps) != 3 {
return BadRequestErrorf("invalid format for port binding: %s", s)
}
p.Proto = ParseProtocol(ps[0])
var err error
if p.IP, p.Port, err = parseIPPort(ps[1]); err != nil {
return BadRequestErrorf("failed to parse Container IP/Port in port binding: %s", err.Error())
}
if p.HostIP, p.HostPort, err = parseIPPort(ps[2]); err != nil {
return BadRequestErrorf("failed to parse Host IP/Port in port binding: %s", err.Error())
}
return nil
}
func parseIPPort(s string) (net.IP, uint16, error) {
hoststr, portstr, err := net.SplitHostPort(s)
if err != nil {
return nil, 0, err
}
ip := net.ParseIP(hoststr)
if ip == nil {
return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr)
}
port, err := strconv.ParseUint(portstr, 10, 16)
if err != nil {
return nil, 0, BadRequestErrorf("invalid port: %s", portstr)
}
return ip, uint16(port), nil
}
// Equal checks if this instance of PortBinding is equal to the passed one // Equal checks if this instance of PortBinding is equal to the passed one
func (p *PortBinding) Equal(o *PortBinding) bool { func (p *PortBinding) Equal(o *PortBinding) bool {
if p == o { if p == o {
@ -341,21 +283,6 @@ func GetMinimalIP(ip net.IP) net.IP {
return ip return ip
} }
// GetMinimalIPNet returns a copy of the passed IP Network with congruent ip and mask notation
func GetMinimalIPNet(nw *net.IPNet) *net.IPNet {
if nw == nil {
return nil
}
if len(nw.IP) == 16 && nw.IP.To4() != nil {
m := nw.Mask
if len(m) == 16 {
m = m[12:16]
}
return &net.IPNet{IP: nw.IP.To4(), Mask: m}
}
return nw
}
// IsIPNetValid returns true if the ipnet is a valid network/mask // IsIPNetValid returns true if the ipnet is a valid network/mask
// combination. Otherwise returns false. // combination. Otherwise returns false.
func IsIPNetValid(nw *net.IPNet) bool { func IsIPNetValid(nw *net.IPNet) bool {

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package opts // import "github.com/docker/docker/opts" package opts // import "github.com/docker/docker/opts"

View File

@ -7,9 +7,9 @@ import (
"compress/bzip2" "compress/bzip2"
"compress/gzip" "compress/gzip"
"context" "context"
"encoding/binary"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -23,6 +23,7 @@ import (
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/pools"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
"github.com/klauspost/compress/zstd"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
exec "golang.org/x/sys/execabs" exec "golang.org/x/sys/execabs"
) )
@ -84,6 +85,8 @@ const (
Gzip Gzip
// Xz is xz compression algorithm. // Xz is xz compression algorithm.
Xz Xz
// Zstd is zstd compression algorithm.
Zstd
) )
const ( const (
@ -122,14 +125,59 @@ func IsArchivePath(path string) bool {
return err == nil return err == nil
} }
const (
zstdMagicSkippableStart = 0x184D2A50
zstdMagicSkippableMask = 0xFFFFFFF0
)
var (
bzip2Magic = []byte{0x42, 0x5A, 0x68}
gzipMagic = []byte{0x1F, 0x8B, 0x08}
xzMagic = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
zstdMagic = []byte{0x28, 0xb5, 0x2f, 0xfd}
)
type matcher = func([]byte) bool
func magicNumberMatcher(m []byte) matcher {
return func(source []byte) bool {
return bytes.HasPrefix(source, m)
}
}
// zstdMatcher detects zstd compression algorithm.
// Zstandard compressed data is made of one or more frames.
// There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames.
// See https://tools.ietf.org/id/draft-kucherawy-dispatch-zstd-00.html#rfc.section.2 for more details.
func zstdMatcher() matcher {
return func(source []byte) bool {
if bytes.HasPrefix(source, zstdMagic) {
// Zstandard frame
return true
}
// skippable frame
if len(source) < 8 {
return false
}
// magic number from 0x184D2A50 to 0x184D2A5F.
if binary.LittleEndian.Uint32(source[:4])&zstdMagicSkippableMask == zstdMagicSkippableStart {
return true
}
return false
}
}
// DetectCompression detects the compression algorithm of the source. // DetectCompression detects the compression algorithm of the source.
func DetectCompression(source []byte) Compression { func DetectCompression(source []byte) Compression {
for compression, m := range map[Compression][]byte{ compressionMap := map[Compression]matcher{
Bzip2: {0x42, 0x5A, 0x68}, Bzip2: magicNumberMatcher(bzip2Magic),
Gzip: {0x1F, 0x8B, 0x08}, Gzip: magicNumberMatcher(gzipMagic),
Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, Xz: magicNumberMatcher(xzMagic),
} { Zstd: zstdMatcher(),
if bytes.HasPrefix(source, m) { }
for _, compression := range []Compression{Bzip2, Gzip, Xz, Zstd} {
fn := compressionMap[compression]
if fn(source) {
return compression return compression
} }
} }
@ -216,6 +264,13 @@ func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
} }
readBufWrapper := p.NewReadCloserWrapper(buf, xzReader) readBufWrapper := p.NewReadCloserWrapper(buf, xzReader)
return wrapReadCloser(readBufWrapper, cancel), nil return wrapReadCloser(readBufWrapper, cancel), nil
case Zstd:
zstdReader, err := zstd.NewReader(buf)
if err != nil {
return nil, err
}
readBufWrapper := p.NewReadCloserWrapper(buf, zstdReader)
return readBufWrapper, nil
default: default:
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension()) return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
} }
@ -342,6 +397,8 @@ func (compression *Compression) Extension() string {
return "tar.gz" return "tar.gz"
case Xz: case Xz:
return "tar.xz" return "tar.xz"
case Zstd:
return "tar.zst"
} }
return "" return ""
} }
@ -809,8 +866,8 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
rebaseName := options.RebaseNames[include] rebaseName := options.RebaseNames[include]
var ( var (
parentMatched []bool parentMatchInfo []fileutils.MatchInfo
parentDirs []string parentDirs []string
) )
walkRoot := getWalkRoot(srcPath, include) walkRoot := getWalkRoot(srcPath, include)
@ -845,13 +902,14 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
break break
} }
parentDirs = parentDirs[:len(parentDirs)-1] parentDirs = parentDirs[:len(parentDirs)-1]
parentMatched = parentMatched[:len(parentMatched)-1] parentMatchInfo = parentMatchInfo[:len(parentMatchInfo)-1]
} }
if len(parentMatched) != 0 { var matchInfo fileutils.MatchInfo
skip, err = pm.MatchesUsingParentResult(relFilePath, parentMatched[len(parentMatched)-1]) if len(parentMatchInfo) != 0 {
skip, matchInfo, err = pm.MatchesUsingParentResults(relFilePath, parentMatchInfo[len(parentMatchInfo)-1])
} else { } else {
skip, err = pm.MatchesOrParentMatches(relFilePath) skip, matchInfo, err = pm.MatchesUsingParentResults(relFilePath, fileutils.MatchInfo{})
} }
if err != nil { if err != nil {
logrus.Errorf("Error matching %s: %v", relFilePath, err) logrus.Errorf("Error matching %s: %v", relFilePath, err)
@ -860,7 +918,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
if f.IsDir() { if f.IsDir() {
parentDirs = append(parentDirs, relFilePath) parentDirs = append(parentDirs, relFilePath)
parentMatched = append(parentMatched, skip) parentMatchInfo = append(parentMatchInfo, matchInfo)
} }
} }
@ -1284,7 +1342,7 @@ func cmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) {
// of that file as an archive. The archive can only be read once - as soon as reading completes, // of that file as an archive. The archive can only be read once - as soon as reading completes,
// the file will be deleted. // the file will be deleted.
func NewTempArchive(src io.Reader, dir string) (*TempArchive, error) { func NewTempArchive(src io.Reader, dir string) (*TempArchive, error) {
f, err := ioutil.TempFile(dir, "") f, err := os.CreateTemp(dir, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package archive // import "github.com/docker/docker/pkg/archive" package archive // import "github.com/docker/docker/pkg/archive"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package archive // import "github.com/docker/docker/pkg/archive" package archive // import "github.com/docker/docker/pkg/archive"

View File

@ -5,7 +5,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -348,7 +347,7 @@ func ChangesDirs(newDir, oldDir string) ([]Change, error) {
oldRoot, newRoot *FileInfo oldRoot, newRoot *FileInfo
) )
if oldDir == "" { if oldDir == "" {
emptyDir, err := ioutil.TempDir("", "empty") emptyDir, err := os.MkdirTemp("", "empty")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package archive // import "github.com/docker/docker/pkg/archive" package archive // import "github.com/docker/docker/pkg/archive"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package archive // import "github.com/docker/docker/pkg/archive" package archive // import "github.com/docker/docker/pkg/archive"

View File

@ -4,7 +4,6 @@ import (
"archive/tar" "archive/tar"
"errors" "errors"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -261,7 +260,7 @@ func PrepareArchiveCopy(srcContent io.Reader, srcInfo, dstInfo CopyInfo) (dstDir
// The destination exists as a directory. No alteration // The destination exists as a directory. No alteration
// to srcContent is needed as its contents can be // to srcContent is needed as its contents can be
// simply extracted to the destination directory. // simply extracted to the destination directory.
return dstInfo.Path, ioutil.NopCloser(srcContent), nil return dstInfo.Path, io.NopCloser(srcContent), nil
case dstInfo.Exists && srcInfo.IsDir: case dstInfo.Exists && srcInfo.IsDir:
// The destination exists as some type of file and the source // The destination exists as some type of file and the source
// content is a directory. This is an error condition since // content is a directory. This is an error condition since

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package archive // import "github.com/docker/docker/pkg/archive" package archive // import "github.com/docker/docker/pkg/archive"

View File

@ -4,7 +4,6 @@ import (
"archive/tar" "archive/tar"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -100,7 +99,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
basename := filepath.Base(hdr.Name) basename := filepath.Base(hdr.Name)
aufsHardlinks[basename] = hdr aufsHardlinks[basename] = hdr
if aufsTempdir == "" { if aufsTempdir == "" {
if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil { if aufsTempdir, err = os.MkdirTemp("", "dockerplnk"); err != nil {
return 0, err return 0, err
} }
defer os.RemoveAll(aufsTempdir) defer os.RemoveAll(aufsTempdir)

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package archive // import "github.com/docker/docker/pkg/archive" package archive // import "github.com/docker/docker/pkg/archive"

View File

@ -3,7 +3,6 @@ package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"os" "os"
"os/user" "os/user"
@ -74,17 +73,21 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions
options.ExcludePatterns = []string{} options.ExcludePatterns = []string{}
} }
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) // If dest is inside a root then directory is created within chroot by extractor.
rootIDs := idMapping.RootPair() // This case is only currently used by cp.
if dest == root {
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
rootIDs := idMapping.RootPair()
dest = filepath.Clean(dest) dest = filepath.Clean(dest)
if _, err := os.Stat(dest); os.IsNotExist(err) { if _, err := os.Stat(dest); os.IsNotExist(err) {
if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil { if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil {
return err return err
}
} }
} }
r := ioutil.NopCloser(tarArchive) r := io.NopCloser(tarArchive)
if decompress { if decompress {
decompressedArchive, err := archive.DecompressStream(tarArchive) decompressedArchive, err := archive.DecompressStream(tarArchive)
if err != nil { if err != nil {

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
@ -8,7 +9,6 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -111,7 +111,7 @@ func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.T
// when `xz -d -c -q | docker-untar ...` failed on docker-untar side, // when `xz -d -c -q | docker-untar ...` failed on docker-untar side,
// we need to exhaust `xz`'s output, otherwise the `xz` side will be // we need to exhaust `xz`'s output, otherwise the `xz` side will be
// pending on write pipe forever // pending on write pipe forever
io.Copy(ioutil.Discard, decompressedArchive) io.Copy(io.Discard, decompressedArchive)
return fmt.Errorf("Error processing tar file(%v): %s", err, output) return fmt.Errorf("Error processing tar file(%v): %s", err, output)
} }

View File

@ -2,7 +2,6 @@ package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -44,7 +43,7 @@ func chroot(path string) (err error) {
} }
// setup oldRoot for pivot_root // setup oldRoot for pivot_root
pivotDir, err := ioutil.TempDir(path, ".pivot_root") pivotDir, err := os.MkdirTemp(path, ".pivot_root")
if err != nil { if err != nil {
return fmt.Errorf("Error setting up pivot dir: %v", err) return fmt.Errorf("Error setting up pivot dir: %v", err)
} }

View File

@ -1,3 +1,4 @@
//go:build !windows && !linux
// +build !windows,!linux // +build !windows,!linux
package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"

View File

@ -1,4 +1,5 @@
//+build !windows //go:build !windows
// +build !windows
package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
@ -8,7 +9,6 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -56,7 +56,7 @@ func applyLayer() {
options.InUserNS = true options.InUserNS = true
} }
if tmpDir, err = ioutil.TempDir("/", "temp-docker-extract"); err != nil { if tmpDir, err = os.MkdirTemp("/", "temp-docker-extract"); err != nil {
fatal(err) fatal(err)
} }

View File

@ -3,7 +3,6 @@ package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -30,7 +29,7 @@ func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions
layer = decompressed layer = decompressed
} }
tmpDir, err := ioutil.TempDir(os.Getenv("temp"), "temp-docker-extract") tmpDir, err := os.MkdirTemp(os.Getenv("temp"), "temp-docker-extract")
if err != nil { if err != nil {
return 0, fmt.Errorf("ApplyLayer failed to create temp-docker-extract under %s. %s", dest, err) return 0, fmt.Errorf("ApplyLayer failed to create temp-docker-extract under %s. %s", dest, err)
} }

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
@ -5,7 +6,6 @@ package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/reexec"
@ -25,5 +25,5 @@ func fatal(err error) {
// flush consumes all the bytes from the reader discarding // flush consumes all the bytes from the reader discarding
// any errors // any errors
func flush(r io.Reader) (bytes int64, err error) { func flush(r io.Reader) (bytes int64, err error) {
return io.Copy(ioutil.Discard, r) return io.Copy(io.Discard, r)
} }

View File

@ -9,8 +9,30 @@ import (
"regexp" "regexp"
"strings" "strings"
"text/scanner" "text/scanner"
"unicode/utf8"
) )
// escapeBytes is a bitmap used to check whether a character should be escaped when creating the regex.
var escapeBytes [8]byte
// shouldEscape reports whether a rune should be escaped as part of the regex.
//
// This only includes characters that require escaping in regex but are also NOT valid filepath pattern characters.
// Additionally, '\' is not excluded because there is specific logic to properly handle this, as it's a path separator
// on Windows.
//
// Adapted from regexp::QuoteMeta in go stdlib.
// See https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/regexp/regexp.go;l=703-715;drc=refs%2Ftags%2Fgo1.17.2
func shouldEscape(b rune) bool {
return b < utf8.RuneSelf && escapeBytes[b%8]&(1<<(b/8)) != 0
}
func init() {
for _, b := range []byte(`.+()|{}$`) {
escapeBytes[b%8] |= 1 << (b / 8)
}
}
// PatternMatcher allows checking paths against a list of patterns // PatternMatcher allows checking paths against a list of patterns
type PatternMatcher struct { type PatternMatcher struct {
patterns []*Pattern patterns []*Pattern
@ -62,9 +84,9 @@ func NewPatternMatcher(patterns []string) (*PatternMatcher, error) {
// //
// Matches is not safe to call concurrently. // Matches is not safe to call concurrently.
// //
// This implementation is buggy (it only checks a single parent dir against the // Deprecated: This implementation is buggy (it only checks a single parent dir
// pattern) and will be removed soon. Use either MatchesOrParentMatches or // against the pattern) and will be removed soon. Use either
// MatchesUsingParentResult instead. // MatchesOrParentMatches or MatchesUsingParentResults instead.
func (pm *PatternMatcher) Matches(file string) (bool, error) { func (pm *PatternMatcher) Matches(file string) (bool, error) {
matched := false matched := false
file = filepath.FromSlash(file) file = filepath.FromSlash(file)
@ -150,6 +172,11 @@ func (pm *PatternMatcher) MatchesOrParentMatches(file string) (bool, error) {
// The "file" argument should be a slash-delimited path. // The "file" argument should be a slash-delimited path.
// //
// MatchesUsingParentResult is not safe to call concurrently. // MatchesUsingParentResult is not safe to call concurrently.
//
// Deprecated: this function does behave correctly in some cases (see
// https://github.com/docker/buildx/issues/850).
//
// Use MatchesUsingParentResults instead.
func (pm *PatternMatcher) MatchesUsingParentResult(file string, parentMatched bool) (bool, error) { func (pm *PatternMatcher) MatchesUsingParentResult(file string, parentMatched bool) (bool, error) {
matched := parentMatched matched := parentMatched
file = filepath.FromSlash(file) file = filepath.FromSlash(file)
@ -174,6 +201,78 @@ func (pm *PatternMatcher) MatchesUsingParentResult(file string, parentMatched bo
return matched, nil return matched, nil
} }
// MatchInfo tracks information about parent dir matches while traversing a
// filesystem.
type MatchInfo struct {
parentMatched []bool
}
// MatchesUsingParentResults returns true if "file" matches any of the patterns
// and isn't excluded by any of the subsequent patterns. The functionality is
// the same as Matches, but as an optimization, the caller passes in
// intermediate results from matching the parent directory.
//
// The "file" argument should be a slash-delimited path.
//
// MatchesUsingParentResults is not safe to call concurrently.
func (pm *PatternMatcher) MatchesUsingParentResults(file string, parentMatchInfo MatchInfo) (bool, MatchInfo, error) {
parentMatched := parentMatchInfo.parentMatched
if len(parentMatched) != 0 && len(parentMatched) != len(pm.patterns) {
return false, MatchInfo{}, errors.New("wrong number of values in parentMatched")
}
file = filepath.FromSlash(file)
matched := false
matchInfo := MatchInfo{
parentMatched: make([]bool, len(pm.patterns)),
}
for i, pattern := range pm.patterns {
match := false
// If the parent matched this pattern, we don't need to recheck.
if len(parentMatched) != 0 {
match = parentMatched[i]
}
if !match {
// Skip evaluation if this is an inclusion and the filename
// already matched the pattern, or it's an exclusion and it has
// not matched the pattern yet.
if pattern.exclusion != matched {
continue
}
var err error
match, err = pattern.match(file)
if err != nil {
return false, matchInfo, err
}
// If the zero value of MatchInfo was passed in, we don't have
// any information about the parent dir's match results, and we
// apply the same logic as MatchesOrParentMatches.
if len(parentMatched) == 0 {
if parentPath := filepath.Dir(file); parentPath != "." {
parentPathDirs := strings.Split(parentPath, string(os.PathSeparator))
// Check to see if the pattern matches one of our parent dirs.
for i := range parentPathDirs {
match, _ = pattern.match(strings.Join(parentPathDirs[:i+1], string(os.PathSeparator)))
if match {
break
}
}
}
}
}
matchInfo.parentMatched[i] = match
if match {
matched = !pattern.exclusion
}
}
return matched, matchInfo, nil
}
// Exclusions returns true if any of the patterns define exclusions // Exclusions returns true if any of the patterns define exclusions
func (pm *PatternMatcher) Exclusions() bool { func (pm *PatternMatcher) Exclusions() bool {
return pm.exclusions return pm.exclusions
@ -256,7 +355,7 @@ func (p *Pattern) compile() error {
} else if ch == '?' { } else if ch == '?' {
// "?" is any char except "/" // "?" is any char except "/"
regStr += "[^" + escSL + "]" regStr += "[^" + escSL + "]"
} else if ch == '.' || ch == '$' { } else if shouldEscape(ch) {
// Escape some regexp special chars that have no meaning // Escape some regexp special chars that have no meaning
// in golang's filepath.Match // in golang's filepath.Match
regStr += `\` + string(ch) regStr += `\` + string(ch)

View File

@ -1,10 +1,10 @@
//go:build linux || freebsd
// +build linux freebsd // +build linux freebsd
package fileutils // import "github.com/docker/docker/pkg/fileutils" package fileutils // import "github.com/docker/docker/pkg/fileutils"
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -13,7 +13,7 @@ import (
// GetTotalUsedFds Returns the number of used File Descriptors by // GetTotalUsedFds Returns the number of used File Descriptors by
// reading it via /proc filesystem. // reading it via /proc filesystem.
func GetTotalUsedFds() int { func GetTotalUsedFds() int {
if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { if fds, err := os.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err) logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err)
} else { } else {
return len(fds) return len(fds)

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package homedir // import "github.com/docker/docker/pkg/homedir" package homedir // import "github.com/docker/docker/pkg/homedir"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package homedir // import "github.com/docker/docker/pkg/homedir" package homedir // import "github.com/docker/docker/pkg/homedir"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package idtools // import "github.com/docker/docker/pkg/idtools" package idtools // import "github.com/docker/docker/pkg/idtools"

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package idtools // import "github.com/docker/docker/pkg/idtools" package idtools // import "github.com/docker/docker/pkg/idtools"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package idtools // import "github.com/docker/docker/pkg/idtools" package idtools // import "github.com/docker/docker/pkg/idtools"

View File

@ -50,12 +50,12 @@ func NewBytesPipe() *BytesPipe {
// It can allocate new []byte slices in a process of writing. // It can allocate new []byte slices in a process of writing.
func (bp *BytesPipe) Write(p []byte) (int, error) { func (bp *BytesPipe) Write(p []byte) (int, error) {
bp.mu.Lock() bp.mu.Lock()
defer bp.mu.Unlock()
written := 0 written := 0
loop0: loop0:
for { for {
if bp.closeErr != nil { if bp.closeErr != nil {
bp.mu.Unlock()
return written, ErrClosed return written, ErrClosed
} }
@ -72,7 +72,6 @@ loop0:
// errBufferFull is an error we expect to get if the buffer is full // errBufferFull is an error we expect to get if the buffer is full
if err != nil && err != errBufferFull { if err != nil && err != errBufferFull {
bp.wait.Broadcast() bp.wait.Broadcast()
bp.mu.Unlock()
return written, err return written, err
} }
@ -100,7 +99,6 @@ loop0:
bp.buf = append(bp.buf, getBuffer(nextCap)) bp.buf = append(bp.buf, getBuffer(nextCap))
} }
bp.wait.Broadcast() bp.wait.Broadcast()
bp.mu.Unlock()
return written, nil return written, nil
} }
@ -126,17 +124,14 @@ func (bp *BytesPipe) Close() error {
// Data could be read only once. // Data could be read only once.
func (bp *BytesPipe) Read(p []byte) (n int, err error) { func (bp *BytesPipe) Read(p []byte) (n int, err error) {
bp.mu.Lock() bp.mu.Lock()
defer bp.mu.Unlock()
if bp.bufLen == 0 { if bp.bufLen == 0 {
if bp.closeErr != nil { if bp.closeErr != nil {
err := bp.closeErr return 0, bp.closeErr
bp.mu.Unlock()
return 0, err
} }
bp.wait.Wait() bp.wait.Wait()
if bp.bufLen == 0 && bp.closeErr != nil { if bp.bufLen == 0 && bp.closeErr != nil {
err := bp.closeErr return 0, bp.closeErr
bp.mu.Unlock()
return 0, err
} }
} }
@ -161,7 +156,6 @@ func (bp *BytesPipe) Read(p []byte) (n int, err error) {
} }
bp.wait.Broadcast() bp.wait.Broadcast()
bp.mu.Unlock()
return return
} }

View File

@ -2,7 +2,6 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils"
import ( import (
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
) )
@ -11,7 +10,7 @@ import (
// temporary file and closing it atomically changes the temporary file to // temporary file and closing it atomically changes the temporary file to
// destination path. Writing and closing concurrently is not allowed. // destination path. Writing and closing concurrently is not allowed.
func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) { func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) {
f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -94,7 +93,7 @@ type AtomicWriteSet struct {
// commit. If no temporary directory is given the system // commit. If no temporary directory is given the system
// default is used. // default is used.
func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) { func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) {
td, err := ioutil.TempDir(tmpDir, "write-set-") td, err := os.MkdirTemp(tmpDir, "write-set-")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,9 +2,12 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils"
import ( import (
"context" "context"
"crypto/sha256"
"encoding/hex"
"io" "io"
// make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered
// TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged.
_ "crypto/sha256"
_ "crypto/sha512"
) )
// ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser // ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser
@ -49,15 +52,6 @@ func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader {
} }
} }
// HashData returns the sha256 sum of src.
func HashData(src io.Reader) (string, error) {
h := sha256.New()
if _, err := io.Copy(h, src); err != nil {
return "", err
}
return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
}
// OnEOFReader wraps an io.ReadCloser and a function // OnEOFReader wraps an io.ReadCloser and a function
// the function will run at the end of file or close the file. // the function will run at the end of file or close the file.
type OnEOFReader struct { type OnEOFReader struct {

View File

@ -1,10 +1,11 @@
//go:build !windows
// +build !windows // +build !windows
package ioutils // import "github.com/docker/docker/pkg/ioutils" package ioutils // import "github.com/docker/docker/pkg/ioutils"
import "io/ioutil" import "os"
// TempDir on Unix systems is equivalent to ioutil.TempDir. // TempDir on Unix systems is equivalent to os.MkdirTemp.
func TempDir(dir, prefix string) (string, error) { func TempDir(dir, prefix string) (string, error) {
return ioutil.TempDir(dir, prefix) return os.MkdirTemp(dir, prefix)
} }

View File

@ -1,14 +1,14 @@
package ioutils // import "github.com/docker/docker/pkg/ioutils" package ioutils // import "github.com/docker/docker/pkg/ioutils"
import ( import (
"io/ioutil" "os"
"github.com/docker/docker/pkg/longpath" "github.com/docker/docker/pkg/longpath"
) )
// TempDir is the equivalent of ioutil.TempDir, except that the result is in Windows longpath format. // TempDir is the equivalent of os.MkdirTemp, except that the result is in Windows longpath format.
func TempDir(dir, prefix string) (string, error) { func TempDir(dir, prefix string) (string, error) {
tempDir, err := ioutil.TempDir(dir, prefix) tempDir, err := os.MkdirTemp(dir, prefix)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -1,3 +1,4 @@
//go:build freebsd || darwin
// +build freebsd darwin // +build freebsd darwin
package reexec // import "github.com/docker/docker/pkg/reexec" package reexec // import "github.com/docker/docker/pkg/reexec"

View File

@ -1,3 +1,4 @@
//go:build !linux && !windows && !freebsd && !darwin
// +build !linux,!windows,!freebsd,!darwin // +build !linux,!windows,!freebsd,!darwin
package reexec // import "github.com/docker/docker/pkg/reexec" package reexec // import "github.com/docker/docker/pkg/reexec"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,9 +1,9 @@
//go:build !windows
// +build !windows // +build !windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"
import ( import (
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
) )
@ -63,5 +63,5 @@ func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, erro
// to find the pathname of the file. It is the caller's responsibility // to find the pathname of the file. It is the caller's responsibility
// to remove the file when no longer needed. // to remove the file when no longer needed.
func TempFileSequential(dir, prefix string) (f *os.File, err error) { func TempFileSequential(dir, prefix string) (f *os.File, err error) {
return ioutil.TempFile(dir, prefix) return os.CreateTemp(dir, prefix)
} }

View File

@ -258,7 +258,7 @@ func nextSuffix() string {
return strconv.Itoa(int(1e9 + r%1e9))[1:] return strconv.Itoa(int(1e9 + r%1e9))[1:]
} }
// TempFileSequential is a copy of ioutil.TempFile, modified to use sequential // TempFileSequential is a copy of os.CreateTemp, modified to use sequential
// file access. Below is the original comment from golang: // file access. Below is the original comment from golang:
// TempFile creates a new temporary file in the directory dir // TempFile creates a new temporary file in the directory dir
// with a name beginning with prefix, opens the file for reading // with a name beginning with prefix, opens the file for reading

View File

@ -1,29 +1,18 @@
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"
import (
"os"
"github.com/sirupsen/logrus"
)
var ( var (
// containerdRuntimeSupported determines if ContainerD should be the runtime. // containerdRuntimeSupported determines if containerd should be the runtime.
// As of March 2019, this is an experimental feature.
containerdRuntimeSupported = false containerdRuntimeSupported = false
) )
// InitContainerdRuntime sets whether to use ContainerD for runtime // InitContainerdRuntime sets whether to use containerd for runtime on Windows.
// on Windows. This is an experimental feature still in development, and func InitContainerdRuntime(cdPath string) {
// also requires an environment variable to be set (so as not to turn the if len(cdPath) > 0 {
// feature on from simply experimental which would also mean LCOW.
func InitContainerdRuntime(experimental bool, cdPath string) {
if experimental && len(cdPath) > 0 && len(os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME")) > 0 {
logrus.Warnf("Using ContainerD runtime. This feature is experimental")
containerdRuntimeSupported = true containerdRuntimeSupported = true
} }
} }
// ContainerdRuntimeSupported returns true if the use of ContainerD runtime is supported. // ContainerdRuntimeSupported returns true if the use of containerd runtime is supported.
func ContainerdRuntimeSupported() bool { func ContainerdRuntimeSupported() bool {
return containerdRuntimeSupported return containerdRuntimeSupported
} }

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build !linux && !windows
// +build !linux,!windows // +build !linux,!windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"
@ -6,12 +7,6 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
// Mknod creates a filesystem node (file, device special file or named pipe) named path
// with attributes specified by mode and dev.
func Mknod(path string, mode uint32, dev int) error {
return unix.Mknod(path, mode, dev)
}
// Mkdev is used to build the value of linux devices (in /dev/) which specifies major // Mkdev is used to build the value of linux devices (in /dev/) which specifies major
// and minor number of the newly created device special file. // and minor number of the newly created device special file.
// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. // Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes.

View File

@ -0,0 +1,14 @@
//go:build freebsd
// +build freebsd
package system // import "github.com/docker/docker/pkg/system"
import (
"golang.org/x/sys/unix"
)
// Mknod creates a filesystem node (file, device special file or named pipe) named path
// with attributes specified by mode and dev.
func Mknod(path string, mode uint32, dev int) error {
return unix.Mknod(path, mode, uint64(dev))
}

View File

@ -0,0 +1,11 @@
package system // import "github.com/docker/docker/pkg/system"
import (
"golang.org/x/sys/unix"
)
// Mknod creates a filesystem node (file, device special file or named pipe) named path
// with attributes specified by mode and dev.
func Mknod(path string, mode uint32, dev int) error {
return unix.Mknod(path, mode, dev)
}

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,10 +1,11 @@
//go:build linux || freebsd || darwin
// +build linux freebsd darwin // +build linux freebsd darwin
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"
import ( import (
"fmt" "fmt"
"io/ioutil" "os"
"strings" "strings"
"syscall" "syscall"
@ -30,7 +31,7 @@ func KillProcess(pid int) {
// http://man7.org/linux/man-pages/man5/proc.5.html // http://man7.org/linux/man-pages/man5/proc.5.html
func IsProcessZombie(pid int) (bool, error) { func IsProcessZombie(pid int) (bool, error) {
statPath := fmt.Sprintf("/proc/%d/stat", pid) statPath := fmt.Sprintf("/proc/%d/stat", pid)
dataBytes, err := ioutil.ReadFile(statPath) dataBytes, err := os.ReadFile(statPath)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -1,3 +1,4 @@
//go:build !darwin && !windows
// +build !darwin,!windows // +build !darwin,!windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build freebsd || netbsd
// +build freebsd netbsd // +build freebsd netbsd
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build linux || freebsd
// +build linux freebsd // +build linux freebsd
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build !linux && !freebsd
// +build !linux,!freebsd // +build !linux,!freebsd
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package system // import "github.com/docker/docker/pkg/system" package system // import "github.com/docker/docker/pkg/system"

View File

@ -1,3 +1,4 @@
//go:build seccomp
// +build seccomp // +build seccomp
package seccomp // import "github.com/docker/docker/profiles/seccomp" package seccomp // import "github.com/docker/docker/profiles/seccomp"

View File

@ -1,3 +1,4 @@
//go:build linux && !seccomp
// +build linux,!seccomp // +build linux,!seccomp
package seccomp // import "github.com/docker/docker/profiles/seccomp" package seccomp // import "github.com/docker/docker/profiles/seccomp"

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
@ -18,7 +17,6 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/events"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/testutil/request" "github.com/docker/docker/testutil/request"
@ -43,6 +41,8 @@ const (
defaultDockerdBinary = "dockerd" defaultDockerdBinary = "dockerd"
defaultContainerdSocket = "/var/run/docker/containerd/containerd.sock" defaultContainerdSocket = "/var/run/docker/containerd/containerd.sock"
defaultDockerdRootlessBinary = "dockerd-rootless.sh" defaultDockerdRootlessBinary = "dockerd-rootless.sh"
defaultUnixSocket = "/var/run/docker.sock"
defaultTLSHost = "localhost:2376"
) )
var errDaemonNotStarted = errors.New("daemon not started") var errDaemonNotStarted = errors.New("daemon not started")
@ -262,7 +262,8 @@ func (d *Daemon) LogFileName() string {
// ReadLogFile returns the content of the daemon log file // ReadLogFile returns the content of the daemon log file
func (d *Daemon) ReadLogFile() ([]byte, error) { func (d *Daemon) ReadLogFile() ([]byte, error) {
return ioutil.ReadFile(d.logFile.Name()) _ = d.logFile.Sync()
return os.ReadFile(d.logFile.Name())
} }
// NewClientT creates new client based on daemon's socket path // NewClientT creates new client based on daemon's socket path
@ -290,6 +291,7 @@ func (d *Daemon) Cleanup(t testing.TB) {
t.Helper() t.Helper()
cleanupMount(t, d) cleanupMount(t, d)
cleanupRaftDir(t, d) cleanupRaftDir(t, d)
cleanupDaemonStorage(t, d)
cleanupNetworkNamespace(t, d) cleanupNetworkNamespace(t, d)
} }
@ -739,11 +741,11 @@ func (d *Daemon) getClientConfig() (*clientConfig, error) {
transport = &http.Transport{ transport = &http.Transport{
TLSClientConfig: tlsConfig, TLSClientConfig: tlsConfig,
} }
addr = fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort) addr = defaultTLSHost
scheme = "https" scheme = "https"
proto = "tcp" proto = "tcp"
} else if d.UseDefaultHost { } else if d.UseDefaultHost {
addr = opts.DefaultUnixSocket addr = defaultUnixSocket
proto = "unix" proto = "unix"
scheme = "http" scheme = "http"
transport = &http.Transport{} transport = &http.Transport{}
@ -831,3 +833,36 @@ func cleanupRaftDir(t testing.TB, d *Daemon) {
} }
} }
} }
// cleanupDaemonStorage removes the daemon's storage directory.
//
// Note that we don't delete the whole directory, as some files (e.g. daemon
// logs) are collected for inclusion in the "bundles" that are stored as Jenkins
// artifacts.
//
// We currently do not include container logs in the bundles, so this also
// removes the "containers" sub-directory.
func cleanupDaemonStorage(t testing.TB, d *Daemon) {
t.Helper()
dirs := []string{
"builder",
"buildkit",
"containers",
"image",
"network",
"plugins",
"tmp",
"trust",
"volumes",
// note: this assumes storage-driver name matches the subdirectory,
// which is currently true, but not guaranteed.
d.storageDriver,
}
for _, p := range dirs {
dir := filepath.Join(d.Root, p)
if err := os.RemoveAll(dir); err != nil {
t.Logf("[%s] error removing %v: %v", d.id, dir, err)
}
}
}

View File

@ -0,0 +1,18 @@
//go:build freebsd
// +build freebsd
package daemon // import "github.com/docker/docker/testutil/daemon"
import (
"testing"
"gotest.tools/v3/assert"
)
func cleanupNetworkNamespace(_ testing.TB, _ *Daemon) {}
// CgroupNamespace returns the cgroup namespace the daemon is running in
func (d *Daemon) CgroupNamespace(t testing.TB) string {
assert.Assert(t, false, "cgroup namespaces are not supported on FreeBSD")
return ""
}

View File

@ -0,0 +1,37 @@
package daemon // import "github.com/docker/docker/testutil/daemon"
import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/sys/unix"
"gotest.tools/v3/assert"
)
func cleanupNetworkNamespace(t testing.TB, d *Daemon) {
t.Helper()
// Cleanup network namespaces in the exec root of this
// daemon because this exec root is specific to this
// daemon instance and has no chance of getting
// cleaned up when a new daemon is instantiated with a
// new exec root.
netnsPath := filepath.Join(d.execRoot, "netns")
filepath.Walk(netnsPath, func(path string, info os.FileInfo, err error) error {
if err := unix.Unmount(path, unix.MNT_DETACH); err != nil && err != unix.EINVAL && err != unix.ENOENT {
t.Logf("[%s] unmount of %s failed: %v", d.id, path, err)
}
os.Remove(path)
return nil
})
}
// CgroupNamespace returns the cgroup namespace the daemon is running in
func (d *Daemon) CgroupNamespace(t testing.TB) string {
link, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/cgroup", d.Pid()))
assert.NilError(t, err)
return strings.TrimSpace(link)
}

View File

@ -1,19 +1,15 @@
//go:build !windows
// +build !windows // +build !windows
package daemon // import "github.com/docker/docker/testutil/daemon" package daemon // import "github.com/docker/docker/testutil/daemon"
import ( import (
"fmt"
"os"
"os/exec" "os/exec"
"path/filepath"
"strings"
"syscall" "syscall"
"testing" "testing"
"github.com/moby/sys/mount" "github.com/moby/sys/mount"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"gotest.tools/v3/assert"
) )
// cleanupMount unmounts the daemon root directory, or logs a message if // cleanupMount unmounts the daemon root directory, or logs a message if
@ -25,31 +21,6 @@ func cleanupMount(t testing.TB, d *Daemon) {
} }
} }
func cleanupNetworkNamespace(t testing.TB, d *Daemon) {
t.Helper()
// Cleanup network namespaces in the exec root of this
// daemon because this exec root is specific to this
// daemon instance and has no chance of getting
// cleaned up when a new daemon is instantiated with a
// new exec root.
netnsPath := filepath.Join(d.execRoot, "netns")
filepath.Walk(netnsPath, func(path string, info os.FileInfo, err error) error {
if err := unix.Unmount(path, unix.MNT_DETACH); err != nil && err != unix.EINVAL && err != unix.ENOENT {
t.Logf("[%s] unmount of %s failed: %v", d.id, path, err)
}
os.Remove(path)
return nil
})
}
// CgroupNamespace returns the cgroup namespace the daemon is running in
func (d *Daemon) CgroupNamespace(t testing.TB) string {
link, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/cgroup", d.Pid()))
assert.NilError(t, err)
return strings.TrimSpace(link)
}
// SignalDaemonDump sends a signal to the daemon to write a dump file // SignalDaemonDump sends a signal to the daemon to write a dump file
func SignalDaemonDump(pid int) { func SignalDaemonDump(pid int) {
unix.Kill(pid, unix.SIGQUIT) unix.Kill(pid, unix.SIGQUIT)

View File

@ -162,6 +162,11 @@ func (e *Execution) IsUserNamespace() bool {
return root != "" return root != ""
} }
// RuntimeIsWindowsContainerd returns whether containerd runtime is used on Windows
func (e *Execution) RuntimeIsWindowsContainerd() bool {
return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1"
}
// IsRootless returns whether the rootless mode is enabled // IsRootless returns whether the rootless mode is enabled
func (e *Execution) IsRootless() bool { func (e *Execution) IsRootless() bool {
return os.Getenv("DOCKER_ROOTLESS") != "" return os.Getenv("DOCKER_ROOTLESS") != ""

View File

@ -10,7 +10,7 @@ import (
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
) )
var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:bullseye"} var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:bullseye-slim"}
type protectedElements struct { type protectedElements struct {
containers map[string]struct{} containers map[string]struct{}

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package request package request

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"strings" "strings"
) )
@ -39,7 +38,7 @@ func Method(method string) func(*Options) {
// RawString sets the specified string as body for the request // RawString sets the specified string as body for the request
func RawString(content string) func(*Options) { func RawString(content string) func(*Options) {
return RawContent(ioutil.NopCloser(strings.NewReader(content))) return RawContent(io.NopCloser(strings.NewReader(content)))
} }
// RawContent sets the specified reader as body for the request // RawContent sets the specified reader as body for the request
@ -71,7 +70,7 @@ func JSONBody(data interface{}) func(*Options) {
if err := json.NewEncoder(jsonData).Encode(data); err != nil { if err := json.NewEncoder(jsonData).Encode(data); err != nil {
return err return err
} }
req.Body = ioutil.NopCloser(jsonData) req.Body = io.NopCloser(jsonData)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
return nil return nil
}) })

View File

@ -5,7 +5,6 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -15,7 +14,6 @@ import (
"time" "time"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/testutil/environment" "github.com/docker/docker/testutil/environment"
"github.com/docker/go-connections/sockets" "github.com/docker/go-connections/sockets"
@ -106,7 +104,7 @@ func Do(endpoint string, modifiers ...func(*Options)) (*http.Response, io.ReadCl
// ReadBody read the specified ReadCloser content and returns it // ReadBody read the specified ReadCloser content and returns it
func ReadBody(b io.ReadCloser) ([]byte, error) { func ReadBody(b io.ReadCloser) ([]byte, error) {
defer b.Close() defer b.Close()
return ioutil.ReadAll(b) return io.ReadAll(b)
} }
// newRequest creates a new http Request to the specified host and endpoint, with the specified request modifiers // newRequest creates a new http Request to the specified host and endpoint, with the specified request modifiers
@ -180,7 +178,7 @@ func getTLSConfig() (*tls.Config, error) {
// DaemonHost return the daemon host string for this test execution // DaemonHost return the daemon host string for this test execution
func DaemonHost() string { func DaemonHost() string {
daemonURLStr := "unix://" + opts.DefaultUnixSocket daemonURLStr := client.DefaultDockerHost
if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" { if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" {
daemonURLStr = daemonHostVar daemonURLStr = daemonHostVar
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
@ -13,6 +14,7 @@ import (
"github.com/containerd/continuity/fs" "github.com/containerd/continuity/fs"
"github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/fileutils"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/tonistiigi/fsutil"
) )
var bufferPool = &sync.Pool{ var bufferPool = &sync.Pool{
@ -87,7 +89,7 @@ func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) e
return err return err
} }
c, err := newCopier(ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns) c, err := newCopier(dstRoot, ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns, ci.ChangeFunc)
if err != nil { if err != nil {
return err return err
} }
@ -113,7 +115,7 @@ func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) e
if err != nil { if err != nil {
return err return err
} }
if err := c.copy(ctx, srcFollowed, "", dst, false, false, false); err != nil { if err := c.copy(ctx, srcFollowed, "", dst, false, fileutils.MatchInfo{}, fileutils.MatchInfo{}); err != nil {
return err return err
} }
} }
@ -170,6 +172,7 @@ type CopyInfo struct {
IncludePatterns []string IncludePatterns []string
// Exclude files/dir matching any of these patterns (even if they match an include pattern) // Exclude files/dir matching any of these patterns (even if they match an include pattern)
ExcludePatterns []string ExcludePatterns []string
ChangeFunc fsutil.ChangeFunc
} }
type Opt func(*CopyInfo) type Opt func(*CopyInfo)
@ -217,6 +220,12 @@ func WithExcludePattern(excludePattern string) Opt {
} }
} }
func WithChangeNotifier(fn fsutil.ChangeFunc) Opt {
return func(ci *CopyInfo) {
ci.ChangeFunc = fn
}
}
type copier struct { type copier struct {
chown Chowner chown Chowner
utime *time.Time utime *time.Time
@ -226,6 +235,8 @@ type copier struct {
includePatternMatcher *fileutils.PatternMatcher includePatternMatcher *fileutils.PatternMatcher
excludePatternMatcher *fileutils.PatternMatcher excludePatternMatcher *fileutils.PatternMatcher
parentDirs []parentDir parentDirs []parentDir
changefn fsutil.ChangeFunc
root string
} }
type parentDir struct { type parentDir struct {
@ -234,7 +245,7 @@ type parentDir struct {
copied bool copied bool
} }
func newCopier(chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, includePatterns, excludePatterns []string) (*copier, error) { func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, includePatterns, excludePatterns []string, changeFunc fsutil.ChangeFunc) (*copier, error) {
if xeh == nil { if xeh == nil {
xeh = func(dst, src, key string, err error) error { xeh = func(dst, src, key string, err error) error {
return err return err
@ -260,6 +271,7 @@ func newCopier(chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, i
} }
return &copier{ return &copier{
root: root,
inodes: map[uint64]string{}, inodes: map[uint64]string{},
chown: chown, chown: chown,
utime: tm, utime: tm,
@ -267,11 +279,12 @@ func newCopier(chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, i
mode: mode, mode: mode,
includePatternMatcher: includePatternMatcher, includePatternMatcher: includePatternMatcher,
excludePatternMatcher: excludePatternMatcher, excludePatternMatcher: excludePatternMatcher,
changefn: changeFunc,
}, nil }, nil
} }
// dest is always clean // dest is always clean
func (c *copier) copy(ctx context.Context, src, srcComponents, target string, overwriteTargetMetadata, parentMatchedInclude, parentMatchedExclude bool) error { func (c *copier) copy(ctx context.Context, src, srcComponents, target string, overwriteTargetMetadata bool, parentIncludeMatchInfo, parentExcludeMatchInfo fileutils.MatchInfo) error {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return ctx.Err()
@ -284,16 +297,20 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
} }
include := true include := true
matchesIncludePattern := false var (
matchesExcludePattern := false includeMatchInfo fileutils.MatchInfo
excludeMatchInfo fileutils.MatchInfo
)
if srcComponents != "" { if srcComponents != "" {
matchesIncludePattern, err = c.include(srcComponents, fi, parentMatchedInclude) matchesIncludePattern := false
matchesExcludePattern := false
matchesIncludePattern, includeMatchInfo, err = c.include(srcComponents, fi, parentIncludeMatchInfo)
if err != nil { if err != nil {
return err return err
} }
include = matchesIncludePattern include = matchesIncludePattern
matchesExcludePattern, err = c.exclude(srcComponents, fi, parentMatchedExclude) matchesExcludePattern, excludeMatchInfo, err = c.exclude(srcComponents, fi, parentExcludeMatchInfo)
if err != nil { if err != nil {
return err return err
} }
@ -319,17 +336,19 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
} }
copyFileInfo := true copyFileInfo := true
notify := true
switch { switch {
case fi.IsDir(): case fi.IsDir():
if created, err := c.copyDirectory( if created, err := c.copyDirectory(
ctx, src, srcComponents, target, fi, overwriteTargetMetadata, ctx, src, srcComponents, target, fi, overwriteTargetMetadata,
include, matchesIncludePattern, matchesExcludePattern, include, includeMatchInfo, excludeMatchInfo,
); err != nil { ); err != nil {
return err return err
} else if !overwriteTargetMetadata || c.includePatternMatcher != nil { } else if !overwriteTargetMetadata || c.includePatternMatcher != nil {
copyFileInfo = created copyFileInfo = created
} }
notify = false
case (fi.Mode() & os.ModeType) == 0: case (fi.Mode() & os.ModeType) == 0:
link, err := getLinkSource(target, fi, c.inodes) link, err := getLinkSource(target, fi, c.inodes)
if err != nil { if err != nil {
@ -368,31 +387,45 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
return errors.Wrap(err, "failed to copy xattrs") return errors.Wrap(err, "failed to copy xattrs")
} }
} }
if notify {
if err := c.notifyChange(target, fi); err != nil {
return err
}
}
return nil return nil
} }
func (c *copier) include(path string, fi os.FileInfo, parentMatchedInclude bool) (bool, error) { func (c *copier) notifyChange(target string, fi os.FileInfo) error {
if c.includePatternMatcher == nil { if c.changefn != nil {
return true, nil if err := c.changefn(fsutil.ChangeKindAdd, path.Clean(strings.TrimPrefix(target, c.root)), fi, nil); err != nil {
return errors.Wrap(err, "failed to notify file change")
}
} }
return nil
m, err := c.includePatternMatcher.MatchesUsingParentResult(path, parentMatchedInclude)
if err != nil {
return false, errors.Wrap(err, "failed to match includepatterns")
}
return m, nil
} }
func (c *copier) exclude(path string, fi os.FileInfo, parentMatchedExclude bool) (bool, error) { func (c *copier) include(path string, fi os.FileInfo, parentIncludeMatchInfo fileutils.MatchInfo) (bool, fileutils.MatchInfo, error) {
if c.excludePatternMatcher == nil { if c.includePatternMatcher == nil {
return false, nil return true, fileutils.MatchInfo{}, nil
} }
m, err := c.excludePatternMatcher.MatchesUsingParentResult(path, parentMatchedExclude) m, matchInfo, err := c.includePatternMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo)
if err != nil { if err != nil {
return false, errors.Wrap(err, "failed to match excludepatterns") return false, matchInfo, errors.Wrap(err, "failed to match includepatterns")
} }
return m, nil return m, matchInfo, nil
}
func (c *copier) exclude(path string, fi os.FileInfo, parentExcludeMatchInfo fileutils.MatchInfo) (bool, fileutils.MatchInfo, error) {
if c.excludePatternMatcher == nil {
return false, fileutils.MatchInfo{}, nil
}
m, matchInfo, err := c.excludePatternMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo)
if err != nil {
return false, matchInfo, errors.Wrap(err, "failed to match excludepatterns")
}
return m, matchInfo, nil
} }
// Delayed creation of parent directories when a file or dir matches an include // Delayed creation of parent directories when a file or dir matches an include
@ -438,8 +471,8 @@ func (c *copier) copyDirectory(
stat os.FileInfo, stat os.FileInfo,
overwriteTargetMetadata bool, overwriteTargetMetadata bool,
include bool, include bool,
matchesIncludePattern bool, includeMatchInfo fileutils.MatchInfo,
matchesExcludePattern bool, excludeMatchInfo fileutils.MatchInfo,
) (bool, error) { ) (bool, error) {
if !stat.IsDir() { if !stat.IsDir() {
return false, errors.Errorf("source is not directory") return false, errors.Errorf("source is not directory")
@ -462,6 +495,11 @@ func (c *copier) copyDirectory(
if err != nil { if err != nil {
return created, err return created, err
} }
if created || overwriteTargetMetadata {
if err := c.notifyChange(dst, stat); err != nil {
return created, err
}
}
parentDir.copied = true parentDir.copied = true
} }
@ -481,7 +519,7 @@ func (c *copier) copyDirectory(
ctx, ctx,
filepath.Join(src, fi.Name()), filepath.Join(srcComponents, fi.Name()), filepath.Join(src, fi.Name()), filepath.Join(srcComponents, fi.Name()),
filepath.Join(dst, fi.Name()), filepath.Join(dst, fi.Name()),
true, matchesIncludePattern, matchesExcludePattern, true, includeMatchInfo, excludeMatchInfo,
); err != nil { ); err != nil {
return false, err return false, err
} }

View File

@ -0,0 +1,32 @@
// +build freebsd
package fs
import (
"io"
"os"
"github.com/pkg/errors"
)
func copyFile(source, target string) error {
src, err := os.Open(source)
if err != nil {
return errors.Wrapf(err, "failed to open source %s", source)
}
defer src.Close()
tgt, err := os.Create(target)
if err != nil {
return errors.Wrapf(err, "failed to open target %s", target)
}
defer tgt.Close()
return copyFileContent(tgt, src)
}
func copyFileContent(dst, src *os.File) error {
buf := bufferPool.Get().(*[]byte)
_, err := io.CopyBuffer(dst, src, *buf)
bufferPool.Put(buf)
return err
}

View File

@ -97,7 +97,10 @@ func copyFileContent(dst, src *os.File) error {
buf := bufferPool.Get().(*[]byte) buf := bufferPool.Get().(*[]byte)
_, err = io.CopyBuffer(dst, src, *buf) _, err = io.CopyBuffer(dst, src, *buf)
bufferPool.Put(buf) bufferPool.Put(buf)
return errors.Wrap(err, "userspace copy failed") if err != nil {
return errors.Wrap(err, "userspace copy failed")
}
return nil
} }
first = false first = false

View File

@ -1,3 +1,4 @@
//go:build solaris || darwin || freebsd
// +build solaris darwin freebsd // +build solaris darwin freebsd
package fs package fs
@ -51,11 +52,3 @@ func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
} }
return nil return nil
} }
func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
}

View File

@ -0,0 +1,20 @@
//go:build darwin
// +build darwin
package fs
import (
"os"
"syscall"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
}

View File

@ -0,0 +1,20 @@
//go:build freebsd || solaris
// +build freebsd solaris
package fs
import (
"os"
"syscall"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
return unix.Mknod(dst, uint32(fi.Mode()), st.Rdev)
}

View File

@ -33,6 +33,19 @@ const (
ChangeKindDelete ChangeKindDelete
) )
func (k ChangeKind) String() string {
switch k {
case ChangeKindAdd:
return "add"
case ChangeKindModify:
return "modify"
case ChangeKindDelete:
return "delete"
default:
return "unknown"
}
}
// ChangeFunc is the type of function called for each change // ChangeFunc is the type of function called for each change
// computed during a directory changes calculation. // computed during a directory changes calculation.
type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error

View File

@ -0,0 +1,14 @@
// +build freebsd
package fsutil
import (
"github.com/tonistiigi/fsutil/types"
"golang.org/x/sys/unix"
)
func createSpecialFile(path string, mode uint32, stat *types.Stat) error {
dev := unix.Mkdev(uint32(stat.Devmajor), uint32(stat.Devminor))
return unix.Mknod(path, mode, dev)
}

View File

@ -45,7 +45,7 @@ func handleTarTypeBlockCharFifo(path string, stat *types.Stat) error {
mode |= syscall.S_IFBLK mode |= syscall.S_IFBLK
} }
if err := syscall.Mknod(path, mode, int(mkdev(stat.Devmajor, stat.Devminor))); err != nil { if err := createSpecialFile(path, mode, stat); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
return nil return nil

View File

@ -0,0 +1,13 @@
// +build !windows,!freebsd
package fsutil
import (
"syscall"
"github.com/tonistiigi/fsutil/types"
)
func createSpecialFile(path string, mode uint32, stat *types.Stat) error {
return syscall.Mknod(path, mode, int(mkdev(stat.Devmajor, stat.Devminor)))
}

View File

@ -63,5 +63,5 @@ target "shfmt" {
target "cross" { target "cross" {
inherits = ["build"] inherits = ["build"]
platforms = ["linux/amd64", "linux/386", "linux/arm64", "linux/arm", "linux/ppc64le", "linux/s390x"] platforms = ["linux/amd64", "linux/386", "linux/arm64", "linux/arm", "linux/ppc64le", "linux/s390x", "darwin/amd64", "darwin/arm64", "windows/amd64", "freebsd/amd64", "freebsd/arm64"]
} }

View File

@ -1,45 +0,0 @@
package prefix
import (
"path"
"path/filepath"
"strings"
)
// Match matches a path against a pattern. It returns m = true if the path
// matches the pattern, and partial = true if the pattern has more separators
// than the path and the common components match (for example, name = foo and
// pattern = foo/bar/*). slashSeparator determines whether the path and pattern
// are '/' delimited (true) or use the native path separator (false).
func Match(pattern, name string, slashSeparator bool) (m bool, partial bool) {
separator := filepath.Separator
if slashSeparator {
separator = '/'
}
count := strings.Count(name, string(separator))
if strings.Count(pattern, string(separator)) > count {
pattern = trimUntilIndex(pattern, string(separator), count)
partial = true
}
if slashSeparator {
m, _ = path.Match(pattern, name)
} else {
m, _ = filepath.Match(pattern, name)
}
return m, partial
}
func trimUntilIndex(str, sep string, count int) string {
s := str
i := 0
c := 0
for {
idx := strings.Index(s, sep)
s = s[idx+len(sep):]
i += idx + len(sep)
c++
if c > count {
return str[:i-len(sep)]
}
}
}

View File

@ -10,7 +10,6 @@ import (
"github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/fileutils"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/tonistiigi/fsutil/prefix"
"github.com/tonistiigi/fsutil/types" "github.com/tonistiigi/fsutil/types"
) )
@ -36,20 +35,15 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
return errors.WithStack(&os.PathError{Op: "walk", Path: root, Err: syscall.ENOTDIR}) return errors.WithStack(&os.PathError{Op: "walk", Path: root, Err: syscall.ENOTDIR})
} }
var pm *fileutils.PatternMatcher var (
if opt != nil && opt.ExcludePatterns != nil { includePatterns []string
pm, err = fileutils.NewPatternMatcher(opt.ExcludePatterns) includeMatcher *fileutils.PatternMatcher
if err != nil { excludeMatcher *fileutils.PatternMatcher
return errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns) )
}
}
var includePatterns []string
if opt != nil && opt.IncludePatterns != nil { if opt != nil && opt.IncludePatterns != nil {
includePatterns = make([]string, len(opt.IncludePatterns)) includePatterns = make([]string, len(opt.IncludePatterns))
for k := range opt.IncludePatterns { copy(includePatterns, opt.IncludePatterns)
includePatterns[k] = filepath.Clean(opt.IncludePatterns[k])
}
} }
if opt != nil && opt.FollowPaths != nil { if opt != nil && opt.FollowPaths != nil {
targets, err := FollowLinks(p, opt.FollowPaths) targets, err := FollowLinks(p, opt.FollowPaths)
@ -61,13 +55,32 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
includePatterns = dedupePaths(includePatterns) includePatterns = dedupePaths(includePatterns)
} }
} }
if len(includePatterns) != 0 {
includeMatcher, err = fileutils.NewPatternMatcher(includePatterns)
if err != nil {
return errors.Wrapf(err, "invalid includepatterns: %s", opt.IncludePatterns)
}
}
var ( if opt != nil && opt.ExcludePatterns != nil {
lastIncludedDir string excludeMatcher, err = fileutils.NewPatternMatcher(opt.ExcludePatterns)
if err != nil {
return errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns)
}
}
parentDirs []string // used only for exclude handling type visitedDir struct {
parentMatchedExclude []bool fi os.FileInfo
) path string
origpath string
pathWithSep string
includeMatchInfo fileutils.MatchInfo
excludeMatchInfo fileutils.MatchInfo
calledFn bool
}
// used only for include/exclude handling
var parentDirs []visitedDir
seenFiles := make(map[uint64]string) seenFiles := make(map[uint64]string)
return filepath.Walk(root, func(path string, fi os.FileInfo, err error) (retErr error) { return filepath.Walk(root, func(path string, fi os.FileInfo, err error) (retErr error) {
@ -90,87 +103,84 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
return nil return nil
} }
if opt != nil { var dir visitedDir
if includePatterns != nil {
skip := false
if lastIncludedDir != "" {
if strings.HasPrefix(path, lastIncludedDir+string(filepath.Separator)) {
skip = true
}
}
if !skip { if includeMatcher != nil || excludeMatcher != nil {
matched := false for len(parentDirs) != 0 {
partial := true lastParentDir := parentDirs[len(parentDirs)-1].pathWithSep
for _, pattern := range includePatterns { if strings.HasPrefix(path, lastParentDir) {
if ok, p := prefix.Match(pattern, path, false); ok { break
matched = true
if !p {
partial = false
break
}
}
}
if !matched {
if fi.IsDir() {
return filepath.SkipDir
}
return nil
}
if !partial && fi.IsDir() {
lastIncludedDir = path
}
} }
parentDirs = parentDirs[:len(parentDirs)-1]
} }
if pm != nil {
for len(parentMatchedExclude) != 0 {
lastParentDir := parentDirs[len(parentDirs)-1]
if strings.HasPrefix(path, lastParentDir) {
break
}
parentDirs = parentDirs[:len(parentDirs)-1]
parentMatchedExclude = parentMatchedExclude[:len(parentMatchedExclude)-1]
}
var m bool if fi.IsDir() {
if len(parentMatchedExclude) != 0 { dir = visitedDir{
m, err = pm.MatchesUsingParentResult(path, parentMatchedExclude[len(parentMatchedExclude)-1]) fi: fi,
} else { path: path,
m, err = pm.MatchesOrParentMatches(path) origpath: origpath,
} pathWithSep: path + string(filepath.Separator),
if err != nil {
return errors.Wrap(err, "failed to match excludepatterns")
}
var dirSlash string
if fi.IsDir() {
dirSlash = path + string(filepath.Separator)
parentDirs = append(parentDirs, dirSlash)
parentMatchedExclude = append(parentMatchedExclude, m)
}
if m {
if fi.IsDir() {
if !pm.Exclusions() {
return filepath.SkipDir
}
for _, pat := range pm.Patterns() {
if !pat.Exclusion() {
continue
}
patStr := pat.String() + string(filepath.Separator)
if strings.HasPrefix(patStr, dirSlash) {
goto passedFilter
}
}
return filepath.SkipDir
}
return nil
} }
} }
} }
passedFilter: skip := false
if includeMatcher != nil {
var parentIncludeMatchInfo fileutils.MatchInfo
if len(parentDirs) != 0 {
parentIncludeMatchInfo = parentDirs[len(parentDirs)-1].includeMatchInfo
}
m, matchInfo, err := includeMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo)
if err != nil {
return errors.Wrap(err, "failed to match includepatterns")
}
if fi.IsDir() {
dir.includeMatchInfo = matchInfo
}
if !m {
skip = true
}
}
if excludeMatcher != nil {
var parentExcludeMatchInfo fileutils.MatchInfo
if len(parentDirs) != 0 {
parentExcludeMatchInfo = parentDirs[len(parentDirs)-1].excludeMatchInfo
}
m, matchInfo, err := excludeMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo)
if err != nil {
return errors.Wrap(err, "failed to match excludepatterns")
}
if fi.IsDir() {
dir.excludeMatchInfo = matchInfo
}
if m {
if fi.IsDir() && !excludeMatcher.Exclusions() {
return filepath.SkipDir
}
skip = true
}
}
if includeMatcher != nil || excludeMatcher != nil {
defer func() {
if fi.IsDir() {
parentDirs = append(parentDirs, dir)
}
}()
}
if skip {
return nil
}
dir.calledFn = true
stat, err := mkstat(origpath, path, fi, seenFiles) stat, err := mkstat(origpath, path, fi, seenFiles)
if err != nil { if err != nil {
return err return err
@ -185,6 +195,31 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
return nil return nil
} }
} }
for i, parentDir := range parentDirs {
if parentDir.calledFn {
continue
}
parentStat, err := mkstat(parentDir.origpath, parentDir.path, parentDir.fi, seenFiles)
if err != nil {
return err
}
select {
case <-ctx.Done():
return ctx.Err()
default:
}
if opt != nil && opt.Map != nil {
if allowed := opt.Map(parentStat.Path, parentStat); !allowed {
continue
}
}
if err := fn(parentStat.Path, &StatInfo{parentStat}, nil); err != nil {
return err
}
parentDirs[i].calledFn = true
}
if err := fn(stat.Path, &StatInfo{stat}, nil); err != nil { if err := fn(stat.Path, &StatInfo{stat}, nil); err != nil {
return err return err
} }

8
vendor/modules.txt vendored
View File

@ -218,7 +218,7 @@ github.com/docker/cli/cli/connhelper/commandconn
github.com/docker/distribution/digestset github.com/docker/distribution/digestset
github.com/docker/distribution/reference github.com/docker/distribution/reference
github.com/docker/distribution/registry/api/errcode github.com/docker/distribution/registry/api/errcode
# github.com/docker/docker v20.10.7+incompatible => github.com/docker/docker v20.10.3-0.20210817025855-ba2adeebdb8d+incompatible # github.com/docker/docker v20.10.7+incompatible => github.com/docker/docker v20.10.3-0.20211124173851-93d560d5b3b0+incompatible
## explicit ## explicit
github.com/docker/docker/api github.com/docker/docker/api
github.com/docker/docker/api/types github.com/docker/docker/api/types
@ -240,7 +240,6 @@ github.com/docker/docker/client
github.com/docker/docker/errdefs github.com/docker/docker/errdefs
github.com/docker/docker/libnetwork/ipamutils github.com/docker/docker/libnetwork/ipamutils
github.com/docker/docker/libnetwork/resolvconf github.com/docker/docker/libnetwork/resolvconf
github.com/docker/docker/libnetwork/resolvconf/dns
github.com/docker/docker/libnetwork/types github.com/docker/docker/libnetwork/types
github.com/docker/docker/opts github.com/docker/docker/opts
github.com/docker/docker/pkg/archive github.com/docker/docker/pkg/archive
@ -459,11 +458,10 @@ github.com/sirupsen/logrus
## explicit; go 1.13 ## explicit; go 1.13
github.com/stretchr/testify/assert github.com/stretchr/testify/assert
github.com/stretchr/testify/require github.com/stretchr/testify/require
# github.com/tonistiigi/fsutil v0.0.0-20210818161904-4442383b5028 # github.com/tonistiigi/fsutil v0.0.0-20211125000220-d952e50eae30
## explicit; go 1.13 ## explicit; go 1.13
github.com/tonistiigi/fsutil github.com/tonistiigi/fsutil
github.com/tonistiigi/fsutil/copy github.com/tonistiigi/fsutil/copy
github.com/tonistiigi/fsutil/prefix
github.com/tonistiigi/fsutil/types github.com/tonistiigi/fsutil/types
# github.com/tonistiigi/go-actions-cache v0.0.0-20211202175116-9642704158ff # github.com/tonistiigi/go-actions-cache v0.0.0-20211202175116-9642704158ff
## explicit; go 1.16 ## explicit; go 1.16
@ -710,6 +708,6 @@ gotest.tools/v3/internal/difflib
gotest.tools/v3/internal/format gotest.tools/v3/internal/format
gotest.tools/v3/internal/source gotest.tools/v3/internal/source
gotest.tools/v3/poll gotest.tools/v3/poll
# github.com/docker/docker => github.com/docker/docker v20.10.3-0.20210817025855-ba2adeebdb8d+incompatible # github.com/docker/docker => github.com/docker/docker v20.10.3-0.20211124173851-93d560d5b3b0+incompatible
# go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20211026174723-2f82a1e0c997 # go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20211026174723-2f82a1e0c997
# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/otelhttp v0.0.0-20211026174723-2f82a1e0c997 # go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/otelhttp v0.0.0-20211026174723-2f82a1e0c997