update fsutil and docker
update fsutil to include this patch:masterd952e50eae
docker also had to be updated due to2d121ce88f
Signed-off-by: Alex Couture-Beil <alex@earthly.dev>
parent
90690ca0c9
commit
197f619956
|
@ -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"
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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, ".")
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 ""
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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") != ""
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !windows
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package request
|
package request
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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)))
|
||||||
|
}
|
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue