mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-18 03:18:27 +02:00
chore: update gcr and docker cred helpers (#2910)
This commit is contained in:
+3
-1
@@ -37,7 +37,6 @@ type ConfigFile struct {
|
||||
PruneFilters []string `json:"pruneFilters,omitempty"`
|
||||
Proxies map[string]ProxyConfig `json:"proxies,omitempty"`
|
||||
Experimental string `json:"experimental,omitempty"`
|
||||
StackOrchestrator string `json:"stackOrchestrator,omitempty"` // Deprecated: swarm is now the default orchestrator, and this option is ignored.
|
||||
CurrentContext string `json:"currentContext,omitempty"`
|
||||
CLIPluginsExtraDirs []string `json:"cliPluginsExtraDirs,omitempty"`
|
||||
Plugins map[string]map[string]string `json:"plugins,omitempty"`
|
||||
@@ -95,6 +94,9 @@ func (configFile *ConfigFile) ContainsAuth() bool {
|
||||
|
||||
// GetAuthConfigs returns the mapping of repo to auth configuration
|
||||
func (configFile *ConfigFile) GetAuthConfigs() map[string]types.AuthConfig {
|
||||
if configFile.AuthConfigs == nil {
|
||||
configFile.AuthConfigs = make(map[string]types.AuthConfig)
|
||||
}
|
||||
return configFile.AuthConfigs
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -52,7 +52,8 @@ func (c *fileStore) GetAll() (map[string]types.AuthConfig, error) {
|
||||
|
||||
// Store saves the given credentials in the file store.
|
||||
func (c *fileStore) Store(authConfig types.AuthConfig) error {
|
||||
c.file.GetAuthConfigs()[authConfig.ServerAddress] = authConfig
|
||||
authConfigs := c.file.GetAuthConfigs()
|
||||
authConfigs[authConfig.ServerAddress] = authConfig
|
||||
return c.file.Save()
|
||||
}
|
||||
|
||||
|
||||
+18
@@ -29,6 +29,7 @@ Adam Pointer <adam.pointer@skybettingandgaming.com>
|
||||
Adam Singer <financeCoding@gmail.com>
|
||||
Adam Walz <adam@adamwalz.net>
|
||||
Adam Williams <awilliams@mirantis.com>
|
||||
AdamKorcz <adam@adalogics.com>
|
||||
Addam Hardy <addam.hardy@gmail.com>
|
||||
Aditi Rajagopal <arajagopal@us.ibm.com>
|
||||
Aditya <aditya@netroy.in>
|
||||
@@ -81,6 +82,7 @@ Alex Goodman <wagoodman@gmail.com>
|
||||
Alex Nordlund <alexander.nordlund@nasdaq.com>
|
||||
Alex Olshansky <i@creagenics.com>
|
||||
Alex Samorukov <samm@os2.kiev.ua>
|
||||
Alex Stockinger <alex@atomicjar.com>
|
||||
Alex Warhawk <ax.warhawk@gmail.com>
|
||||
Alexander Artemenko <svetlyak.40wt@gmail.com>
|
||||
Alexander Boyd <alex@opengroove.org>
|
||||
@@ -198,6 +200,7 @@ Anusha Ragunathan <anusha.ragunathan@docker.com>
|
||||
Anyu Wang <wanganyu@outlook.com>
|
||||
apocas <petermdias@gmail.com>
|
||||
Arash Deshmeh <adeshmeh@ca.ibm.com>
|
||||
arcosx <arcosx@outlook.com>
|
||||
ArikaChen <eaglesora@gmail.com>
|
||||
Arko Dasgupta <arko@tetrate.io>
|
||||
Arnaud Lefebvre <a.lefebvre@outlook.fr>
|
||||
@@ -241,6 +244,7 @@ Benjamin Atkin <ben@benatkin.com>
|
||||
Benjamin Baker <Benjamin.baker@utexas.edu>
|
||||
Benjamin Boudreau <boudreau.benjamin@gmail.com>
|
||||
Benjamin Böhmke <benjamin@boehmke.net>
|
||||
Benjamin Wang <wachao@vmware.com>
|
||||
Benjamin Yolken <yolken@stripe.com>
|
||||
Benny Ng <benny.tpng@gmail.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
@@ -634,6 +638,7 @@ Eng Zer Jun <engzerjun@gmail.com>
|
||||
Enguerran <engcolson@gmail.com>
|
||||
Eohyung Lee <liquidnuker@gmail.com>
|
||||
epeterso <epeterson@breakpoint-labs.com>
|
||||
er0k <er0k@er0k.net>
|
||||
Eric Barch <barch@tomesoftware.com>
|
||||
Eric Curtin <ericcurtin17@gmail.com>
|
||||
Eric G. Noriega <enoriega@vizuri.com>
|
||||
@@ -754,6 +759,7 @@ Félix Baylac-Jacqué <baylac.felix@gmail.com>
|
||||
Félix Cantournet <felix.cantournet@cloudwatt.com>
|
||||
Gabe Rosenhouse <gabe@missionst.com>
|
||||
Gabor Nagy <mail@aigeruth.hu>
|
||||
Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
|
||||
Gabriel Goller <gabrielgoller123@gmail.com>
|
||||
Gabriel L. Somlo <gsomlo@gmail.com>
|
||||
Gabriel Linder <linder.gabriel@gmail.com>
|
||||
@@ -855,6 +861,7 @@ Hongbin Lu <hongbin034@gmail.com>
|
||||
Hongxu Jia <hongxu.jia@windriver.com>
|
||||
Honza Pokorny <me@honza.ca>
|
||||
Hsing-Hui Hsu <hsinghui@amazon.com>
|
||||
Hsing-Yu (David) Chen <davidhsingyuchen@gmail.com>
|
||||
hsinko <21551195@zju.edu.cn>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Hu Tao <hutao@cn.fujitsu.com>
|
||||
@@ -887,6 +894,7 @@ Igor Dolzhikov <bluesriverz@gmail.com>
|
||||
Igor Karpovich <i.karpovich@currencysolutions.com>
|
||||
Iliana Weller <iweller@amazon.com>
|
||||
Ilkka Laukkanen <ilkka@ilkka.io>
|
||||
Illia Antypenko <ilya@antipenko.pp.ua>
|
||||
Illo Abdulrahim <abdulrahim.illo@nokia.com>
|
||||
Ilya Dmitrichenko <errordeveloper@gmail.com>
|
||||
Ilya Gusev <mail@igusev.ru>
|
||||
@@ -938,6 +946,7 @@ Jamie Hannaford <jamie@limetree.org>
|
||||
Jamshid Afshar <jafshar@yahoo.com>
|
||||
Jan Breig <git@pygos.space>
|
||||
Jan Chren <dev.rindeal@gmail.com>
|
||||
Jan Garcia <github-public@n-garcia.com>
|
||||
Jan Götte <jaseg@jaseg.net>
|
||||
Jan Keromnes <janx@linux.com>
|
||||
Jan Koprowski <jan.koprowski@gmail.com>
|
||||
@@ -1206,6 +1215,7 @@ Kimbro Staken <kstaken@kstaken.com>
|
||||
Kir Kolyshkin <kolyshkin@gmail.com>
|
||||
Kiran Gangadharan <kiran.daredevil@gmail.com>
|
||||
Kirill SIbirev <l0kix2@gmail.com>
|
||||
Kirk Easterson <kirk.easterson@gmail.com>
|
||||
knappe <tyler.knappe@gmail.com>
|
||||
Kohei Tsuruta <coheyxyz@gmail.com>
|
||||
Koichi Shiraishi <k@zchee.io>
|
||||
@@ -1240,10 +1250,12 @@ Lars Kellogg-Stedman <lars@redhat.com>
|
||||
Lars R. Damerow <lars@pixar.com>
|
||||
Lars-Magnus Skog <ralphtheninja@riseup.net>
|
||||
Laszlo Meszaros <lacienator@gmail.com>
|
||||
Laura Brehm <laurabrehm@hey.com>
|
||||
Laura Frank <ljfrank@gmail.com>
|
||||
Laurent Bernaille <laurent.bernaille@datadoghq.com>
|
||||
Laurent Erignoux <lerignoux@gmail.com>
|
||||
Laurie Voss <github@seldo.com>
|
||||
Leandro Motta Barros <lmb@stackedboxes.org>
|
||||
Leandro Siqueira <leandro.siqueira@gmail.com>
|
||||
Lee Calcote <leecalcote@gmail.com>
|
||||
Lee Chao <932819864@qq.com>
|
||||
@@ -1563,6 +1575,7 @@ Nick Neisen <nwneisen@gmail.com>
|
||||
Nick Parker <nikaios@gmail.com>
|
||||
Nick Payne <nick@kurai.co.uk>
|
||||
Nick Russo <nicholasjamesrusso@gmail.com>
|
||||
Nick Santos <nick.santos@docker.com>
|
||||
Nick Stenning <nick.stenning@digital.cabinet-office.gov.uk>
|
||||
Nick Stinemates <nick@stinemates.org>
|
||||
Nick Wood <nwood@microsoft.com>
|
||||
@@ -1584,6 +1597,7 @@ NikolaMandic <mn080202@gmail.com>
|
||||
Nikolas Garofil <nikolas.garofil@uantwerpen.be>
|
||||
Nikolay Edigaryev <edigaryev@gmail.com>
|
||||
Nikolay Milovanov <nmil@itransformers.net>
|
||||
ningmingxiao <ning.mingxiao@zte.com.cn>
|
||||
Nirmal Mehta <nirmalkmehta@gmail.com>
|
||||
Nishant Totla <nishanttotla@gmail.com>
|
||||
NIWA Hideyuki <niwa.niwa@nifty.ne.jp>
|
||||
@@ -1615,6 +1629,7 @@ Omri Shiv <Omri.Shiv@teradata.com>
|
||||
Onur Filiz <onur.filiz@microsoft.com>
|
||||
Oriol Francès <oriolfa@gmail.com>
|
||||
Oscar Bonilla <6f6231@gmail.com>
|
||||
oscar.chen <2972789494@qq.com>
|
||||
Oskar Niburski <oskarniburski@gmail.com>
|
||||
Otto Kekäläinen <otto@seravo.fi>
|
||||
Ouyang Liduo <oyld0210@163.com>
|
||||
@@ -1822,6 +1837,7 @@ Rory Hunter <roryhunter2@gmail.com>
|
||||
Rory McCune <raesene@gmail.com>
|
||||
Ross Boucher <rboucher@gmail.com>
|
||||
Rovanion Luckey <rovanion.luckey@gmail.com>
|
||||
Roy Reznik <roy@wiz.io>
|
||||
Royce Remer <royceremer@gmail.com>
|
||||
Rozhnov Alexandr <nox73@ya.ru>
|
||||
Rudolph Gottesheim <r.gottesheim@loot.at>
|
||||
@@ -2271,6 +2287,7 @@ Xiaoyu Zhang <zhang.xiaoyu33@zte.com.cn>
|
||||
xichengliudui <1693291525@qq.com>
|
||||
xiekeyang <xiekeyang@huawei.com>
|
||||
Ximo Guanter Gonzálbez <joaquin.guantergonzalbez@telefonica.com>
|
||||
xin.li <xin.li@daocloud.io>
|
||||
Xinbo Weng <xihuanbo_0521@zju.edu.cn>
|
||||
Xinfeng Liu <xinfeng.liu@gmail.com>
|
||||
Xinzi Zhou <imdreamrunner@gmail.com>
|
||||
@@ -2282,6 +2299,7 @@ Yahya <ya7yaz@gmail.com>
|
||||
yalpul <yalpul@gmail.com>
|
||||
YAMADA Tsuyoshi <tyamada@minimum2scp.org>
|
||||
Yamasaki Masahide <masahide.y@gmail.com>
|
||||
Yamazaki Masashi <masi19bw@gmail.com>
|
||||
Yan Feng <yanfeng2@huawei.com>
|
||||
Yan Zhu <yanzhu@alauda.io>
|
||||
Yang Bai <hamo.by@gmail.com>
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// DefaultVersion of Current REST API
|
||||
DefaultVersion = "1.42"
|
||||
DefaultVersion = "1.43"
|
||||
|
||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||
// command to specify that no base image is to be used.
|
||||
|
||||
+71
-78
@@ -19,10 +19,10 @@ produces:
|
||||
consumes:
|
||||
- "application/json"
|
||||
- "text/plain"
|
||||
basePath: "/v1.42"
|
||||
basePath: "/v1.43"
|
||||
info:
|
||||
title: "Docker Engine API"
|
||||
version: "1.42"
|
||||
version: "1.43"
|
||||
x-logo:
|
||||
url: "https://docs.docker.com/assets/images/logo-docker-main.png"
|
||||
description: |
|
||||
@@ -55,8 +55,8 @@ info:
|
||||
the URL is not supported by the daemon, a HTTP `400 Bad Request` error message
|
||||
is returned.
|
||||
|
||||
If you omit the version-prefix, the current version of the API (v1.42) is used.
|
||||
For example, calling `/info` is the same as calling `/v1.42/info`. Using the
|
||||
If you omit the version-prefix, the current version of the API (v1.43) is used.
|
||||
For example, calling `/info` is the same as calling `/v1.43/info`. Using the
|
||||
API without a version-prefix is deprecated and will be removed in a future release.
|
||||
|
||||
Engine releases in the near future should support this version of the API,
|
||||
@@ -976,6 +976,13 @@ definitions:
|
||||
items:
|
||||
type: "integer"
|
||||
minimum: 0
|
||||
Annotations:
|
||||
type: "object"
|
||||
description: |
|
||||
Arbitrary non-identifying metadata attached to container and
|
||||
provided to the runtime when the container is started.
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
# Applicable to UNIX platforms
|
||||
CapAdd:
|
||||
@@ -1122,6 +1129,7 @@ definitions:
|
||||
remapping option is enabled.
|
||||
ShmSize:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
description: |
|
||||
Size of `/dev/shm` in bytes. If omitted, the system uses 64MB.
|
||||
minimum: 0
|
||||
@@ -1610,6 +1618,34 @@ definitions:
|
||||
"WorkDir": "/var/lib/docker/overlay2/ef749362d13333e65fc95c572eb525abbe0052e16e086cb64bc3b98ae9aa6d74/work"
|
||||
}
|
||||
|
||||
FilesystemChange:
|
||||
description: |
|
||||
Change in the container's filesystem.
|
||||
type: "object"
|
||||
required: [Path, Kind]
|
||||
properties:
|
||||
Path:
|
||||
description: |
|
||||
Path to file or directory that has changed.
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Kind:
|
||||
$ref: "#/definitions/ChangeType"
|
||||
|
||||
ChangeType:
|
||||
description: |
|
||||
Kind of change
|
||||
|
||||
Can be one of:
|
||||
|
||||
- `0`: Modified ("C")
|
||||
- `1`: Added ("A")
|
||||
- `2`: Deleted ("D")
|
||||
type: "integer"
|
||||
format: "uint8"
|
||||
enum: [0, 1, 2]
|
||||
x-nullable: false
|
||||
|
||||
ImageInspect:
|
||||
description: |
|
||||
Information about an image in the local image cache.
|
||||
@@ -1746,15 +1782,14 @@ definitions:
|
||||
Total size of the image including all layers it is composed of.
|
||||
|
||||
In versions of Docker before v1.10, this field was calculated from
|
||||
the image itself and all of its parent images. Docker v1.10 and up
|
||||
store images self-contained, and no longer use a parent-chain, making
|
||||
this field an equivalent of the Size field.
|
||||
the image itself and all of its parent images. Images are now stored
|
||||
self-contained, and no longer use a parent-chain, making this field
|
||||
an equivalent of the Size field.
|
||||
|
||||
This field is kept for backward compatibility, but may be removed in
|
||||
a future version of the API.
|
||||
> **Deprecated**: this field is kept for backward compatibility, but
|
||||
> will be removed in API v1.44.
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
x-nullable: false
|
||||
example: 1239828
|
||||
GraphDriver:
|
||||
$ref: "#/definitions/GraphDriverData"
|
||||
@@ -1802,7 +1837,6 @@ definitions:
|
||||
- Created
|
||||
- Size
|
||||
- SharedSize
|
||||
- VirtualSize
|
||||
- Labels
|
||||
- Containers
|
||||
properties:
|
||||
@@ -1888,19 +1922,17 @@ definitions:
|
||||
x-nullable: false
|
||||
example: 1239828
|
||||
VirtualSize:
|
||||
description: |
|
||||
description: |-
|
||||
Total size of the image including all layers it is composed of.
|
||||
|
||||
In versions of Docker before v1.10, this field was calculated from
|
||||
the image itself and all of its parent images. Docker v1.10 and up
|
||||
store images self-contained, and no longer use a parent-chain, making
|
||||
this field an equivalent of the Size field.
|
||||
the image itself and all of its parent images. Images are now stored
|
||||
self-contained, and no longer use a parent-chain, making this field
|
||||
an equivalent of the Size field.
|
||||
|
||||
This field is kept for backward compatibility, but may be removed in
|
||||
a future version of the API.
|
||||
Deprecated: this field is kept for backward compatibility, and will be removed in API v1.44.
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
x-nullable: false
|
||||
example: 172064416
|
||||
Labels:
|
||||
description: "User-defined key/value metadata."
|
||||
@@ -4652,7 +4684,8 @@ definitions:
|
||||
example: false
|
||||
OOMKilled:
|
||||
description: |
|
||||
Whether this container has been killed because it ran out of memory.
|
||||
Whether a process within this container has been killed because it ran
|
||||
out of memory since the container was last started.
|
||||
type: "boolean"
|
||||
example: false
|
||||
Dead:
|
||||
@@ -5035,7 +5068,7 @@ definitions:
|
||||
Go runtime (`GOOS`).
|
||||
|
||||
Currently returned values are "linux" and "windows". A full list of
|
||||
possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment).
|
||||
possible values can be found in the [Go documentation](https://go.dev/doc/install/source#environment).
|
||||
type: "string"
|
||||
example: "linux"
|
||||
Architecture:
|
||||
@@ -5043,7 +5076,7 @@ definitions:
|
||||
Hardware architecture of the host, as returned by the Go runtime
|
||||
(`GOARCH`).
|
||||
|
||||
A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment).
|
||||
A full list of possible values can be found in the [Go documentation](https://go.dev/doc/install/source#environment).
|
||||
type: "string"
|
||||
example: "x86_64"
|
||||
NCPU:
|
||||
@@ -5129,42 +5162,8 @@ definitions:
|
||||
ServerVersion:
|
||||
description: |
|
||||
Version string of the daemon.
|
||||
|
||||
> **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/)
|
||||
> returns the Swarm version instead of the daemon version, for example
|
||||
> `swarm/1.2.8`.
|
||||
type: "string"
|
||||
example: "17.06.0-ce"
|
||||
ClusterStore:
|
||||
description: |
|
||||
URL of the distributed storage backend.
|
||||
|
||||
|
||||
The storage backend is used for multihost networking (to store
|
||||
network and endpoint information) and by the node discovery mechanism.
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
> **Deprecated**: This field is only propagated when using standalone Swarm
|
||||
> mode, and overlay networking using an external k/v store. Overlay
|
||||
> networks with Swarm mode enabled use the built-in raft store, and
|
||||
> this field will be empty.
|
||||
type: "string"
|
||||
example: "consul://consul.corp.example.com:8600/some/path"
|
||||
ClusterAdvertise:
|
||||
description: |
|
||||
The network endpoint that the Engine advertises for the purpose of
|
||||
node discovery. ClusterAdvertise is a `host:port` combination on which
|
||||
the daemon is reachable by other hosts.
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
> **Deprecated**: This field is only propagated when using standalone Swarm
|
||||
> mode, and overlay networking using an external k/v store. Overlay
|
||||
> networks with Swarm mode enabled use the built-in raft store, and
|
||||
> this field will be empty.
|
||||
type: "string"
|
||||
example: "node5.corp.example.com:8000"
|
||||
example: "24.0.2"
|
||||
Runtimes:
|
||||
description: |
|
||||
List of [OCI compliant](https://github.com/opencontainers/runtime-spec)
|
||||
@@ -5242,7 +5241,8 @@ definitions:
|
||||
SecurityOptions:
|
||||
description: |
|
||||
List of security features that are enabled on the daemon, such as
|
||||
apparmor, seccomp, SELinux, user-namespaces (userns), and rootless.
|
||||
apparmor, seccomp, SELinux, user-namespaces (userns), rootless and
|
||||
no-new-privileges.
|
||||
|
||||
Additional configuration options for each security feature may
|
||||
be present, and are included as a comma-separated list of key/value
|
||||
@@ -6875,9 +6875,9 @@ paths:
|
||||
Returns which files in a container's filesystem have been added, deleted,
|
||||
or modified. The `Kind` of modification can be one of:
|
||||
|
||||
- `0`: Modified
|
||||
- `1`: Added
|
||||
- `2`: Deleted
|
||||
- `0`: Modified ("C")
|
||||
- `1`: Added ("A")
|
||||
- `2`: Deleted ("D")
|
||||
operationId: "ContainerChanges"
|
||||
produces: ["application/json"]
|
||||
responses:
|
||||
@@ -6886,22 +6886,7 @@ paths:
|
||||
schema:
|
||||
type: "array"
|
||||
items:
|
||||
type: "object"
|
||||
x-go-name: "ContainerChangeResponseItem"
|
||||
title: "ContainerChangeResponseItem"
|
||||
description: "change item in response to ContainerChanges operation"
|
||||
required: [Path, Kind]
|
||||
properties:
|
||||
Path:
|
||||
description: "Path to file that has changed"
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Kind:
|
||||
description: "Kind of change"
|
||||
type: "integer"
|
||||
format: "uint8"
|
||||
enum: [0, 1, 2]
|
||||
x-nullable: false
|
||||
$ref: "#/definitions/FilesystemChange"
|
||||
examples:
|
||||
application/json:
|
||||
- Path: "/dev"
|
||||
@@ -8228,7 +8213,7 @@ paths:
|
||||
|
||||
Available filters:
|
||||
|
||||
- `until=<duration>`: duration relative to daemon's time, during which build cache was not used, in Go's duration format (e.g., '24h')
|
||||
- `until=<timestamp>` remove cache older than `<timestamp>`. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon's local time.
|
||||
- `id=<id>`
|
||||
- `parent=<id>`
|
||||
- `type=<string>`
|
||||
@@ -9911,7 +9896,9 @@ paths:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
description: |
|
||||
Forbidden operation. This happens when trying to create a network named after a pre-defined network,
|
||||
or when trying to create an overlay network on a daemon which is not part of a Swarm cluster.
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
404:
|
||||
@@ -10374,6 +10361,12 @@ paths:
|
||||
default if omitted.
|
||||
required: true
|
||||
type: "string"
|
||||
- name: "force"
|
||||
in: "query"
|
||||
description: |
|
||||
Force disable a plugin even if still in use.
|
||||
required: false
|
||||
type: "boolean"
|
||||
tags: ["Plugin"]
|
||||
/plugins/{name}/upgrade:
|
||||
post:
|
||||
|
||||
+5
-20
@@ -1,22 +1,7 @@
|
||||
package types // import "github.com/docker/docker/api/types"
|
||||
import "github.com/docker/docker/api/types/registry"
|
||||
|
||||
// AuthConfig contains authorization information for connecting to a Registry
|
||||
type AuthConfig struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth,omitempty"`
|
||||
|
||||
// Email is an optional value associated with the username.
|
||||
// This field is deprecated and will be removed in a later
|
||||
// version of docker.
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
ServerAddress string `json:"serveraddress,omitempty"`
|
||||
|
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `json:"identitytoken,omitempty"`
|
||||
|
||||
// RegistryToken is a bearer token to be sent to a registry
|
||||
RegistryToken string `json:"registrytoken,omitempty"`
|
||||
}
|
||||
// AuthConfig contains authorization information for connecting to a Registry.
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/api/types/registry.AuthConfig
|
||||
type AuthConfig = registry.AuthConfig
|
||||
|
||||
+2
-2
@@ -5,6 +5,7 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
@@ -102,8 +103,7 @@ type ExecProcessConfig struct {
|
||||
// CreateImageConfig is the configuration for creating an image from a
|
||||
// container.
|
||||
type CreateImageConfig struct {
|
||||
Repo string
|
||||
Tag string
|
||||
Tag reference.NamedTagged
|
||||
Pause bool
|
||||
Author string
|
||||
Comment string
|
||||
|
||||
+4
-3
@@ -4,8 +4,9 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// PullOption defines different modes for accessing images
|
||||
@@ -39,7 +40,7 @@ type BuildConfig struct {
|
||||
// GetImageAndLayerOptions are the options supported by GetImageAndReleasableLayer
|
||||
type GetImageAndLayerOptions struct {
|
||||
PullOption PullOption
|
||||
AuthConfig map[string]types.AuthConfig
|
||||
AuthConfig map[string]registry.AuthConfig
|
||||
Output io.Writer
|
||||
Platform *specs.Platform
|
||||
Platform *ocispec.Platform
|
||||
}
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@@ -180,7 +181,7 @@ type ImageBuildOptions struct {
|
||||
// at all (nil). See the parsing of buildArgs in
|
||||
// api/server/router/build/build_routes.go for even more info.
|
||||
BuildArgs map[string]*string
|
||||
AuthConfigs map[string]AuthConfig
|
||||
AuthConfigs map[string]registry.AuthConfig
|
||||
Context io.Reader
|
||||
Labels map[string]string
|
||||
// squash the resulting image's layers to the parent
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ package types // import "github.com/docker/docker/api/types"
|
||||
import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// configs holds structs used for internal communication between the
|
||||
@@ -16,7 +16,7 @@ type ContainerCreateConfig struct {
|
||||
Config *container.Config
|
||||
HostConfig *container.HostConfig
|
||||
NetworkingConfig *network.NetworkingConfig
|
||||
Platform *specs.Platform
|
||||
Platform *ocispec.Platform
|
||||
AdjustCPUShares bool
|
||||
}
|
||||
|
||||
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
package container
|
||||
|
||||
// ContainerChangeResponseItem change item in response to ContainerChanges operation
|
||||
//
|
||||
// Deprecated: use [FilesystemChange].
|
||||
type ContainerChangeResponseItem = FilesystemChange
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package container
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// ChangeType Kind of change
|
||||
//
|
||||
// Can be one of:
|
||||
//
|
||||
// - `0`: Modified ("C")
|
||||
// - `1`: Added ("A")
|
||||
// - `2`: Deleted ("D")
|
||||
//
|
||||
// swagger:model ChangeType
|
||||
type ChangeType uint8
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package container
|
||||
|
||||
const (
|
||||
// ChangeModify represents the modify operation.
|
||||
ChangeModify ChangeType = 0
|
||||
// ChangeAdd represents the add operation.
|
||||
ChangeAdd ChangeType = 1
|
||||
// ChangeDelete represents the delete operation.
|
||||
ChangeDelete ChangeType = 2
|
||||
)
|
||||
|
||||
func (ct ChangeType) String() string {
|
||||
switch ct {
|
||||
case ChangeModify:
|
||||
return "C"
|
||||
case ChangeAdd:
|
||||
return "A"
|
||||
case ChangeDelete:
|
||||
return "D"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerChangeResponseItem change item in response to ContainerChanges operation
|
||||
// swagger:model ContainerChangeResponseItem
|
||||
type ContainerChangeResponseItem struct {
|
||||
|
||||
// Kind of change
|
||||
// Required: true
|
||||
Kind uint8 `json:"Kind"`
|
||||
|
||||
// Path to file that has changed
|
||||
// Required: true
|
||||
Path string `json:"Path"`
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ContainerCreateCreatedBody OK response to ContainerCreate operation
|
||||
//
|
||||
// Deprecated: use CreateResponse
|
||||
type ContainerCreateCreatedBody = CreateResponse
|
||||
|
||||
// ContainerWaitOKBody OK response to ContainerWait operation
|
||||
//
|
||||
// Deprecated: use WaitResponse
|
||||
type ContainerWaitOKBody = WaitResponse
|
||||
|
||||
// ContainerWaitOKBodyError container waiting error, if any
|
||||
//
|
||||
// Deprecated: use WaitExitError
|
||||
type ContainerWaitOKBodyError = WaitExitError
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package container
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// FilesystemChange Change in the container's filesystem.
|
||||
//
|
||||
// swagger:model FilesystemChange
|
||||
type FilesystemChange struct {
|
||||
|
||||
// kind
|
||||
// Required: true
|
||||
Kind ChangeType `json:"Kind"`
|
||||
|
||||
// Path to file or directory that has changed.
|
||||
//
|
||||
// Required: true
|
||||
Path string `json:"Path"`
|
||||
}
|
||||
Generated
Vendored
+60
-69
@@ -101,7 +101,8 @@ func (n IpcMode) IsShareable() bool {
|
||||
|
||||
// IsContainer indicates whether the container uses another container's ipc namespace.
|
||||
func (n IpcMode) IsContainer() bool {
|
||||
return strings.HasPrefix(string(n), string(IPCModeContainer)+":")
|
||||
_, ok := containerID(string(n))
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsNone indicates whether container IpcMode is set to "none".
|
||||
@@ -116,15 +117,14 @@ func (n IpcMode) IsEmpty() bool {
|
||||
|
||||
// Valid indicates whether the ipc mode is valid.
|
||||
func (n IpcMode) Valid() bool {
|
||||
// TODO(thaJeztah): align with PidMode, and consider container-mode without a container name/ID to be invalid.
|
||||
return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer()
|
||||
}
|
||||
|
||||
// Container returns the name of the container ipc stack is going to be used.
|
||||
func (n IpcMode) Container() string {
|
||||
if n.IsContainer() {
|
||||
return strings.TrimPrefix(string(n), string(IPCModeContainer)+":")
|
||||
}
|
||||
return ""
|
||||
func (n IpcMode) Container() (idOrName string) {
|
||||
idOrName, _ = containerID(string(n))
|
||||
return idOrName
|
||||
}
|
||||
|
||||
// NetworkMode represents the container network stack.
|
||||
@@ -147,17 +147,14 @@ func (n NetworkMode) IsPrivate() bool {
|
||||
|
||||
// IsContainer indicates whether container uses a container network stack.
|
||||
func (n NetworkMode) IsContainer() bool {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
_, ok := containerID(string(n))
|
||||
return ok
|
||||
}
|
||||
|
||||
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||
func (n NetworkMode) ConnectedContainer() string {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
func (n NetworkMode) ConnectedContainer() (idOrName string) {
|
||||
idOrName, _ = containerID(string(n))
|
||||
return idOrName
|
||||
}
|
||||
|
||||
// UserDefined indicates user-created network
|
||||
@@ -178,18 +175,12 @@ func (n UsernsMode) IsHost() bool {
|
||||
|
||||
// IsPrivate indicates whether the container uses the a private userns.
|
||||
func (n UsernsMode) IsPrivate() bool {
|
||||
return !(n.IsHost())
|
||||
return !n.IsHost()
|
||||
}
|
||||
|
||||
// Valid indicates whether the userns is valid.
|
||||
func (n UsernsMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return n == "" || n.IsHost()
|
||||
}
|
||||
|
||||
// CgroupSpec represents the cgroup to use for the container.
|
||||
@@ -197,22 +188,20 @@ type CgroupSpec string
|
||||
|
||||
// IsContainer indicates whether the container is using another container cgroup
|
||||
func (c CgroupSpec) IsContainer() bool {
|
||||
parts := strings.SplitN(string(c), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
_, ok := containerID(string(c))
|
||||
return ok
|
||||
}
|
||||
|
||||
// Valid indicates whether the cgroup spec is valid.
|
||||
func (c CgroupSpec) Valid() bool {
|
||||
return c.IsContainer() || c == ""
|
||||
// TODO(thaJeztah): align with PidMode, and consider container-mode without a container name/ID to be invalid.
|
||||
return c == "" || c.IsContainer()
|
||||
}
|
||||
|
||||
// Container returns the name of the container whose cgroup will be used.
|
||||
func (c CgroupSpec) Container() string {
|
||||
parts := strings.SplitN(string(c), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
// Container returns the ID or name of the container whose cgroup will be used.
|
||||
func (c CgroupSpec) Container() (idOrName string) {
|
||||
idOrName, _ = containerID(string(c))
|
||||
return idOrName
|
||||
}
|
||||
|
||||
// UTSMode represents the UTS namespace of the container.
|
||||
@@ -220,7 +209,7 @@ type UTSMode string
|
||||
|
||||
// IsPrivate indicates whether the container uses its private UTS namespace.
|
||||
func (n UTSMode) IsPrivate() bool {
|
||||
return !(n.IsHost())
|
||||
return !n.IsHost()
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container uses the host's UTS namespace.
|
||||
@@ -230,13 +219,7 @@ func (n UTSMode) IsHost() bool {
|
||||
|
||||
// Valid indicates whether the UTS namespace is valid.
|
||||
func (n UTSMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return n == "" || n.IsHost()
|
||||
}
|
||||
|
||||
// PidMode represents the pid namespace of the container.
|
||||
@@ -254,32 +237,19 @@ func (n PidMode) IsHost() bool {
|
||||
|
||||
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||
func (n PidMode) IsContainer() bool {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
_, ok := containerID(string(n))
|
||||
return ok
|
||||
}
|
||||
|
||||
// Valid indicates whether the pid namespace is valid.
|
||||
func (n PidMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
case "container":
|
||||
if len(parts) != 2 || parts[1] == "" {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return n == "" || n.IsHost() || validContainer(string(n))
|
||||
}
|
||||
|
||||
// Container returns the name of the container whose pid namespace is going to be used.
|
||||
func (n PidMode) Container() string {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
func (n PidMode) Container() (idOrName string) {
|
||||
idOrName, _ = containerID(string(n))
|
||||
return idOrName
|
||||
}
|
||||
|
||||
// DeviceRequest represents a request for devices from a device driver.
|
||||
@@ -408,16 +378,17 @@ type UpdateConfig struct {
|
||||
// Portable information *should* appear in Config.
|
||||
type HostConfig struct {
|
||||
// Applicable to all platforms
|
||||
Binds []string // List of volume bindings for this container
|
||||
ContainerIDFile string // File (path) where the containerId is written
|
||||
LogConfig LogConfig // Configuration of the logs for this container
|
||||
NetworkMode NetworkMode // Network mode to use for the container
|
||||
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||
AutoRemove bool // Automatically remove container when it exits
|
||||
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||
VolumesFrom []string // List of volumes to take from other container
|
||||
ConsoleSize [2]uint // Initial console size (height,width)
|
||||
Binds []string // List of volume bindings for this container
|
||||
ContainerIDFile string // File (path) where the containerId is written
|
||||
LogConfig LogConfig // Configuration of the logs for this container
|
||||
NetworkMode NetworkMode // Network mode to use for the container
|
||||
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||
AutoRemove bool // Automatically remove container when it exits
|
||||
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||
VolumesFrom []string // List of volumes to take from other container
|
||||
ConsoleSize [2]uint // Initial console size (height,width)
|
||||
Annotations map[string]string `json:",omitempty"` // Arbitrary non-identifying metadata attached to container and provided to the runtime
|
||||
|
||||
// Applicable to UNIX platforms
|
||||
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||
@@ -463,3 +434,23 @@ type HostConfig struct {
|
||||
// Run a custom init inside the container, if null, use the daemon's configured settings
|
||||
Init *bool `json:",omitempty"`
|
||||
}
|
||||
|
||||
// containerID splits "container:<ID|name>" values. It returns the container
|
||||
// ID or name, and whether an ID/name was found. It returns an empty string and
|
||||
// a "false" if the value does not have a "container:" prefix. Further validation
|
||||
// of the returned, including checking if the value is empty, should be handled
|
||||
// by the caller.
|
||||
func containerID(val string) (idOrName string, ok bool) {
|
||||
k, v, hasSep := strings.Cut(val, ":")
|
||||
if !hasSep || k != "container" {
|
||||
return "", false
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
// validContainer checks if the given value is a "container:" mode with
|
||||
// a non-empty name/ID.
|
||||
func validContainer(val string) bool {
|
||||
id, ok := containerID(val)
|
||||
return ok && id != ""
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package types // import "github.com/docker/docker/api/types"
|
||||
|
||||
import "github.com/docker/docker/api/types/volume"
|
||||
|
||||
// Volume volume
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/api/types/volume.Volume
|
||||
type Volume = volume.Volume
|
||||
|
||||
// VolumeUsageData Usage details about the volume. This information is used by the
|
||||
// `GET /system/df` endpoint, and omitted in other endpoints.
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/api/types/volume.UsageData
|
||||
type VolumeUsageData = volume.UsageData
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package filters
|
||||
|
||||
import "fmt"
|
||||
|
||||
// invalidFilter indicates that the provided filter or its value is invalid
|
||||
type invalidFilter struct {
|
||||
Filter string
|
||||
Value []string
|
||||
}
|
||||
|
||||
func (e invalidFilter) Error() string {
|
||||
msg := "invalid filter"
|
||||
if e.Filter != "" {
|
||||
msg += " '" + e.Filter
|
||||
if e.Value != nil {
|
||||
msg = fmt.Sprintf("%s=%s", msg, e.Value)
|
||||
}
|
||||
msg += "'"
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// InvalidParameter marks this error as ErrInvalidParameter
|
||||
func (e invalidFilter) InvalidParameter() {}
|
||||
|
||||
// unreachableCode is an error indicating that the code path was not expected to be reached.
|
||||
type unreachableCode struct {
|
||||
Filter string
|
||||
Value []string
|
||||
}
|
||||
|
||||
// System marks this error as ErrSystem
|
||||
func (e unreachableCode) System() {}
|
||||
|
||||
func (e unreachableCode) Error() string {
|
||||
return fmt.Sprintf("unreachable code reached for filter: %q with values: %s", e.Filter, e.Value)
|
||||
}
|
||||
+37
-14
@@ -10,7 +10,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Args stores a mapping of keys to a set of multiple values.
|
||||
@@ -99,7 +98,7 @@ func FromJSON(p string) (Args, error) {
|
||||
// Fallback to parsing arguments in the legacy slice format
|
||||
deprecated := map[string][]string{}
|
||||
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
|
||||
return args, invalidFilter{errors.Wrap(err, "invalid filter")}
|
||||
return args, &invalidFilter{}
|
||||
}
|
||||
|
||||
args.fields = deprecatedArgs(deprecated)
|
||||
@@ -163,13 +162,13 @@ func (args Args) MatchKVList(key string, sources map[string]string) bool {
|
||||
}
|
||||
|
||||
for value := range fieldValues {
|
||||
testKV := strings.SplitN(value, "=", 2)
|
||||
testK, testV, hasValue := strings.Cut(value, "=")
|
||||
|
||||
v, ok := sources[testKV[0]]
|
||||
v, ok := sources[testK]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if len(testKV) == 2 && testKV[1] != v {
|
||||
if hasValue && testV != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -196,6 +195,38 @@ func (args Args) Match(field, source string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetBoolOrDefault returns a boolean value of the key if the key is present
|
||||
// and is intepretable as a boolean value. Otherwise the default value is returned.
|
||||
// Error is not nil only if the filter values are not valid boolean or are conflicting.
|
||||
func (args Args) GetBoolOrDefault(key string, defaultValue bool) (bool, error) {
|
||||
fieldValues, ok := args.fields[key]
|
||||
|
||||
if !ok {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
if len(fieldValues) == 0 {
|
||||
return defaultValue, &invalidFilter{key, nil}
|
||||
}
|
||||
|
||||
isFalse := fieldValues["0"] || fieldValues["false"]
|
||||
isTrue := fieldValues["1"] || fieldValues["true"]
|
||||
|
||||
conflicting := isFalse && isTrue
|
||||
invalid := !isFalse && !isTrue
|
||||
|
||||
if conflicting || invalid {
|
||||
return defaultValue, &invalidFilter{key, args.Get(key)}
|
||||
} else if isFalse {
|
||||
return false, nil
|
||||
} else if isTrue {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// This code shouldn't be reached.
|
||||
return defaultValue, &unreachableCode{Filter: key, Value: args.Get(key)}
|
||||
}
|
||||
|
||||
// ExactMatch returns true if the source matches exactly one of the values.
|
||||
func (args Args) ExactMatch(key, source string) bool {
|
||||
fieldValues, ok := args.fields[key]
|
||||
@@ -246,20 +277,12 @@ func (args Args) Contains(field string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
type invalidFilter struct{ error }
|
||||
|
||||
func (e invalidFilter) Error() string {
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
func (invalidFilter) InvalidParameter() {}
|
||||
|
||||
// Validate compared the set of accepted keys against the keys in the mapping.
|
||||
// An error is returned if any mapping keys are not in the accepted set.
|
||||
func (args Args) Validate(accepted map[string]bool) error {
|
||||
for name := range args.fields {
|
||||
if !accepted[name] {
|
||||
return invalidFilter{errors.New("invalid filter '" + name + "'")}
|
||||
return &invalidFilter{name, nil}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package image
|
||||
|
||||
import ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
||||
// GetImageOpts holds parameters to inspect an image.
|
||||
type GetImageOpts struct {
|
||||
Platform *ocispec.Platform
|
||||
Details bool
|
||||
}
|
||||
+5
-8
@@ -85,13 +85,10 @@ type ImageSummary struct {
|
||||
// Total size of the image including all layers it is composed of.
|
||||
//
|
||||
// In versions of Docker before v1.10, this field was calculated from
|
||||
// the image itself and all of its parent images. Docker v1.10 and up
|
||||
// store images self-contained, and no longer use a parent-chain, making
|
||||
// this field an equivalent of the Size field.
|
||||
// the image itself and all of its parent images. Images are now stored
|
||||
// self-contained, and no longer use a parent-chain, making this field
|
||||
// an equivalent of the Size field.
|
||||
//
|
||||
// This field is kept for backward compatibility, but may be removed in
|
||||
// a future version of the API.
|
||||
//
|
||||
// Required: true
|
||||
VirtualSize int64 `json:"VirtualSize"`
|
||||
// Deprecated: this field is kept for backward compatibility, and will be removed in API v1.44.
|
||||
VirtualSize int64 `json:"VirtualSize,omitempty"`
|
||||
}
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package registry // import "github.com/docker/docker/api/types/registry"
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// AuthHeader is the name of the header used to send encoded registry
|
||||
// authorization credentials for registry operations (push/pull).
|
||||
const AuthHeader = "X-Registry-Auth"
|
||||
|
||||
// AuthConfig contains authorization information for connecting to a Registry.
|
||||
type AuthConfig struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth,omitempty"`
|
||||
|
||||
// Email is an optional value associated with the username.
|
||||
// This field is deprecated and will be removed in a later
|
||||
// version of docker.
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
ServerAddress string `json:"serveraddress,omitempty"`
|
||||
|
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `json:"identitytoken,omitempty"`
|
||||
|
||||
// RegistryToken is a bearer token to be sent to a registry
|
||||
RegistryToken string `json:"registrytoken,omitempty"`
|
||||
}
|
||||
|
||||
// EncodeAuthConfig serializes the auth configuration as a base64url encoded
|
||||
// RFC4648, section 5) JSON string for sending through the X-Registry-Auth header.
|
||||
//
|
||||
// For details on base64url encoding, see:
|
||||
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
|
||||
func EncodeAuthConfig(authConfig AuthConfig) (string, error) {
|
||||
buf, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return "", errInvalidParameter{err}
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
// DecodeAuthConfig decodes base64url encoded (RFC4648, section 5) JSON
|
||||
// authentication information as sent through the X-Registry-Auth header.
|
||||
//
|
||||
// This function always returns an AuthConfig, even if an error occurs. It is up
|
||||
// to the caller to decide if authentication is required, and if the error can
|
||||
// be ignored.
|
||||
//
|
||||
// For details on base64url encoding, see:
|
||||
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
|
||||
func DecodeAuthConfig(authEncoded string) (*AuthConfig, error) {
|
||||
if authEncoded == "" {
|
||||
return &AuthConfig{}, nil
|
||||
}
|
||||
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
return decodeAuthConfigFromReader(authJSON)
|
||||
}
|
||||
|
||||
// DecodeAuthConfigBody decodes authentication information as sent as JSON in the
|
||||
// body of a request. This function is to provide backward compatibility with old
|
||||
// clients and API versions. Current clients and API versions expect authentication
|
||||
// to be provided through the X-Registry-Auth header.
|
||||
//
|
||||
// Like DecodeAuthConfig, this function always returns an AuthConfig, even if an
|
||||
// error occurs. It is up to the caller to decide if authentication is required,
|
||||
// and if the error can be ignored.
|
||||
func DecodeAuthConfigBody(rdr io.ReadCloser) (*AuthConfig, error) {
|
||||
return decodeAuthConfigFromReader(rdr)
|
||||
}
|
||||
|
||||
func decodeAuthConfigFromReader(rdr io.Reader) (*AuthConfig, error) {
|
||||
authConfig := &AuthConfig{}
|
||||
if err := json.NewDecoder(rdr).Decode(authConfig); err != nil {
|
||||
// always return an (empty) AuthConfig to increase compatibility with
|
||||
// the existing API.
|
||||
return &AuthConfig{}, invalid(err)
|
||||
}
|
||||
return authConfig, nil
|
||||
}
|
||||
|
||||
func invalid(err error) error {
|
||||
return errInvalidParameter{errors.Wrap(err, "invalid X-Registry-Auth header")}
|
||||
}
|
||||
|
||||
type errInvalidParameter struct{ error }
|
||||
|
||||
func (errInvalidParameter) InvalidParameter() {}
|
||||
|
||||
func (e errInvalidParameter) Cause() error { return e.error }
|
||||
|
||||
func (e errInvalidParameter) Unwrap() error { return e.error }
|
||||
+3
-3
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ServiceConfig stores daemon registry services configuration.
|
||||
@@ -113,8 +113,8 @@ type SearchResults struct {
|
||||
type DistributionInspect struct {
|
||||
// Descriptor contains information about the manifest, including
|
||||
// the content addressable digest
|
||||
Descriptor v1.Descriptor
|
||||
Descriptor ocispec.Descriptor
|
||||
// Platforms contains the list of platforms supported by the image,
|
||||
// obtained by parsing the manifest
|
||||
Platforms []v1.Platform
|
||||
Platforms []ocispec.Platform
|
||||
}
|
||||
|
||||
+20
-20
@@ -95,37 +95,37 @@ func GetTimestamp(value string, reference time.Time) (string, error) {
|
||||
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
|
||||
}
|
||||
|
||||
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
|
||||
// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
|
||||
// if the incoming nanosecond portion is longer or shorter than 9 digits it is
|
||||
// converted to nanoseconds. The expectation is that the seconds and
|
||||
// seconds will be used to create a time variable. For example:
|
||||
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has
|
||||
// the format ("%d.%09d", time.Unix(), int64(time.Nanosecond())).
|
||||
// If the incoming nanosecond portion is longer than 9 digits it is truncated.
|
||||
// The expectation is that the seconds and nanoseconds will be used to create a
|
||||
// time variable. For example:
|
||||
//
|
||||
// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
|
||||
// if err == nil since := time.Unix(seconds, nanoseconds)
|
||||
// seconds, nanoseconds, _ := ParseTimestamp("1136073600.000000001",0)
|
||||
// since := time.Unix(seconds, nanoseconds)
|
||||
//
|
||||
// returns seconds as def(aultSeconds) if value == ""
|
||||
func ParseTimestamps(value string, def int64) (int64, int64, error) {
|
||||
// returns seconds as defaultSeconds if value == ""
|
||||
func ParseTimestamps(value string, defaultSeconds int64) (seconds int64, nanoseconds int64, err error) {
|
||||
if value == "" {
|
||||
return def, 0, nil
|
||||
return defaultSeconds, 0, nil
|
||||
}
|
||||
return parseTimestamp(value)
|
||||
}
|
||||
|
||||
func parseTimestamp(value string) (int64, int64, error) {
|
||||
sa := strings.SplitN(value, ".", 2)
|
||||
s, err := strconv.ParseInt(sa[0], 10, 64)
|
||||
func parseTimestamp(value string) (sec int64, nsec int64, err error) {
|
||||
s, n, ok := strings.Cut(value, ".")
|
||||
sec, err = strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return s, 0, err
|
||||
return sec, 0, err
|
||||
}
|
||||
if len(sa) != 2 {
|
||||
return s, 0, nil
|
||||
if !ok {
|
||||
return sec, 0, nil
|
||||
}
|
||||
n, err := strconv.ParseInt(sa[1], 10, 64)
|
||||
nsec, err = strconv.ParseInt(n, 10, 64)
|
||||
if err != nil {
|
||||
return s, n, err
|
||||
return sec, nsec, err
|
||||
}
|
||||
// should already be in nanoseconds but just in case convert n to nanoseconds
|
||||
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
|
||||
return s, n, nil
|
||||
nsec = int64(float64(nsec) * math.Pow(float64(10), float64(9-len(n))))
|
||||
return sec, nsec, nil
|
||||
}
|
||||
|
||||
+18
-16
@@ -123,9 +123,8 @@ type ImageInspect struct {
|
||||
// store images self-contained, and no longer use a parent-chain, making
|
||||
// this field an equivalent of the Size field.
|
||||
//
|
||||
// This field is kept for backward compatibility, but may be removed in
|
||||
// a future version of the API.
|
||||
VirtualSize int64 // TODO(thaJeztah): deprecate this field
|
||||
// Deprecated: Unused in API 1.43 and up, but kept for backward compatibility with older API versions.
|
||||
VirtualSize int64 `json:"VirtualSize,omitempty"`
|
||||
|
||||
// GraphDriver holds information about the storage driver used to store the
|
||||
// container's and image's filesystem.
|
||||
@@ -297,8 +296,6 @@ type Info struct {
|
||||
Labels []string
|
||||
ExperimentalBuild bool
|
||||
ServerVersion string
|
||||
ClusterStore string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated
|
||||
ClusterAdvertise string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated
|
||||
Runtimes map[string]Runtime
|
||||
DefaultRuntime string
|
||||
Swarm swarm.Info
|
||||
@@ -350,20 +347,19 @@ func DecodeSecurityOptions(opts []string) ([]SecurityOpt, error) {
|
||||
continue
|
||||
}
|
||||
secopt := SecurityOpt{}
|
||||
split := strings.Split(opt, ",")
|
||||
for _, s := range split {
|
||||
kv := strings.SplitN(s, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
for _, s := range strings.Split(opt, ",") {
|
||||
k, v, ok := strings.Cut(s, "=")
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid security option %q", s)
|
||||
}
|
||||
if kv[0] == "" || kv[1] == "" {
|
||||
if k == "" || v == "" {
|
||||
return nil, errors.New("invalid empty security option")
|
||||
}
|
||||
if kv[0] == "name" {
|
||||
secopt.Name = kv[1]
|
||||
if k == "name" {
|
||||
secopt.Name = v
|
||||
continue
|
||||
}
|
||||
secopt.Options = append(secopt.Options, KeyValue{Key: kv[0], Value: kv[1]})
|
||||
secopt.Options = append(secopt.Options, KeyValue{Key: k, Value: v})
|
||||
}
|
||||
so = append(so, secopt)
|
||||
}
|
||||
@@ -656,12 +652,18 @@ type Checkpoint struct {
|
||||
|
||||
// Runtime describes an OCI runtime
|
||||
type Runtime struct {
|
||||
Path string `json:"path"`
|
||||
// "Legacy" runtime configuration for runc-compatible runtimes.
|
||||
|
||||
Path string `json:"path,omitempty"`
|
||||
Args []string `json:"runtimeArgs,omitempty"`
|
||||
|
||||
// Shimv2 runtime configuration. Mutually exclusive with the legacy config above.
|
||||
|
||||
Type string `json:"runtimeType,omitempty"`
|
||||
Options map[string]interface{} `json:"options,omitempty"`
|
||||
|
||||
// This is exposed here only for internal use
|
||||
// It is not currently supported to specify custom shim configs
|
||||
Shim *ShimConfig `json:"-"`
|
||||
ShimConfig *ShimConfig `json:"-"`
|
||||
}
|
||||
|
||||
// ShimConfig is used by runtime to configure containerd shims
|
||||
|
||||
+4
-4
@@ -16,11 +16,11 @@ func compare(v1, v2 string) int {
|
||||
otherTab = strings.Split(v2, ".")
|
||||
)
|
||||
|
||||
max := len(currTab)
|
||||
if len(otherTab) > max {
|
||||
max = len(otherTab)
|
||||
maxVer := len(currTab)
|
||||
if len(otherTab) > maxVer {
|
||||
maxVer = len(otherTab)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
for i := 0; i < maxVer; i++ {
|
||||
var currInt, otherInt int
|
||||
|
||||
if len(currTab) > i {
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package volume // import "github.com/docker/docker/api/types/volume"
|
||||
|
||||
// VolumeCreateBody Volume configuration
|
||||
//
|
||||
// Deprecated: use CreateOptions
|
||||
type VolumeCreateBody = CreateOptions
|
||||
|
||||
// VolumeListOKBody Volume list response
|
||||
//
|
||||
// Deprecated: use ListResponse
|
||||
type VolumeListOKBody = ListResponse
|
||||
+9
-8
@@ -14,7 +14,7 @@ import (
|
||||
containerpkg "github.com/docker/docker/container"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
// instructions in the builder.
|
||||
type Source interface {
|
||||
// Root returns root path for accessing source
|
||||
Root() containerfs.ContainerFS
|
||||
Root() string
|
||||
// Close allows to signal that the filesystem tree won't be used anymore.
|
||||
// For Context implementations using a temporary directory, it is recommended to
|
||||
// delete the temporary directory in Close().
|
||||
@@ -42,11 +42,11 @@ type Backend interface {
|
||||
|
||||
// CommitBuildStep creates a new Docker image from the config generated by
|
||||
// a build step.
|
||||
CommitBuildStep(backend.CommitConfig) (image.ID, error)
|
||||
CommitBuildStep(context.Context, backend.CommitConfig) (image.ID, error)
|
||||
// ContainerCreateWorkdir creates the workdir
|
||||
ContainerCreateWorkdir(containerID string) error
|
||||
|
||||
CreateImage(config []byte, parent string) (Image, error)
|
||||
CreateImage(ctx context.Context, config []byte, parent string, contentStoreDigest digest.Digest) (Image, error)
|
||||
|
||||
ImageCacheBuilder
|
||||
}
|
||||
@@ -61,13 +61,13 @@ type ExecBackend interface {
|
||||
// ContainerAttachRaw attaches to container.
|
||||
ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error
|
||||
// ContainerCreateIgnoreImagesArgsEscaped creates a new Docker container and returns potential warnings
|
||||
ContainerCreateIgnoreImagesArgsEscaped(config types.ContainerCreateConfig) (container.CreateResponse, error)
|
||||
ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error)
|
||||
// ContainerRm removes a container specified by `id`.
|
||||
ContainerRm(name string, config *types.ContainerRmConfig) error
|
||||
// ContainerKill stops the container execution abruptly.
|
||||
ContainerKill(containerID string, sig string) error
|
||||
// ContainerStart starts a new container
|
||||
ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
||||
ContainerStart(ctx context.Context, containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
||||
// ContainerWait stops processing until the given container is stopped.
|
||||
ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
|
||||
}
|
||||
@@ -81,7 +81,7 @@ type Result struct {
|
||||
// ImageCacheBuilder represents a generator for stateful image cache.
|
||||
type ImageCacheBuilder interface {
|
||||
// MakeImageCache creates a stateful image cache.
|
||||
MakeImageCache(cacheFrom []string) ImageCache
|
||||
MakeImageCache(ctx context.Context, cacheFrom []string) (ImageCache, error)
|
||||
}
|
||||
|
||||
// ImageCache abstracts an image cache.
|
||||
@@ -105,11 +105,12 @@ type ROLayer interface {
|
||||
Release() error
|
||||
NewRWLayer() (RWLayer, error)
|
||||
DiffID() layer.DiffID
|
||||
ContentStoreDigest() digest.Digest
|
||||
}
|
||||
|
||||
// RWLayer is active layer that can be read/modified
|
||||
type RWLayer interface {
|
||||
Release() error
|
||||
Root() containerfs.ContainerFS
|
||||
Root() string
|
||||
Commit() (ROLayer, error)
|
||||
}
|
||||
|
||||
+21
-18
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/shell"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/syncmap"
|
||||
@@ -95,7 +95,7 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.build(source, dockerfile)
|
||||
return b.build(ctx, source, dockerfile)
|
||||
}
|
||||
|
||||
// builderOptions are the dependencies required by the builder
|
||||
@@ -117,8 +117,7 @@ type Builder struct {
|
||||
Aux *streamformatter.AuxFormatter
|
||||
Output io.Writer
|
||||
|
||||
docker builder.Backend
|
||||
clientCtx context.Context
|
||||
docker builder.Backend
|
||||
|
||||
idMapping idtools.IdentityMapping
|
||||
disableCommit bool
|
||||
@@ -126,18 +125,22 @@ type Builder struct {
|
||||
pathCache pathCache
|
||||
containerManager *containerManager
|
||||
imageProber ImageProber
|
||||
platform *specs.Platform
|
||||
platform *ocispec.Platform
|
||||
}
|
||||
|
||||
// newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options.
|
||||
func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, error) {
|
||||
func newBuilder(ctx context.Context, options builderOptions) (*Builder, error) {
|
||||
config := options.Options
|
||||
if config == nil {
|
||||
config = new(types.ImageBuildOptions)
|
||||
}
|
||||
|
||||
imageProber, err := newImageProber(ctx, options.Backend, config.CacheFrom, config.NoCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := &Builder{
|
||||
clientCtx: clientCtx,
|
||||
options: config,
|
||||
Stdout: options.ProgressWriter.StdoutFormatter,
|
||||
Stderr: options.ProgressWriter.StderrFormatter,
|
||||
@@ -145,9 +148,9 @@ func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, er
|
||||
Output: options.ProgressWriter.Output,
|
||||
docker: options.Backend,
|
||||
idMapping: options.IDMapping,
|
||||
imageSources: newImageSources(clientCtx, options),
|
||||
imageSources: newImageSources(options),
|
||||
pathCache: options.PathCache,
|
||||
imageProber: newImageProber(options.Backend, config.CacheFrom, config.NoCache),
|
||||
imageProber: imageProber,
|
||||
containerManager: newContainerManager(options.Backend),
|
||||
}
|
||||
|
||||
@@ -181,7 +184,7 @@ func buildLabelOptions(labels map[string]string, stages []instructions.Stage) {
|
||||
|
||||
// Build runs the Dockerfile builder by parsing the Dockerfile and executing
|
||||
// the instructions from the file.
|
||||
func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*builder.Result, error) {
|
||||
func (b *Builder) build(ctx context.Context, source builder.Source, dockerfile *parser.Result) (*builder.Result, error) {
|
||||
defer b.imageSources.Unmount()
|
||||
|
||||
stages, metaArgs, err := instructions.Parse(dockerfile.AST)
|
||||
@@ -205,7 +208,7 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil
|
||||
buildLabelOptions(b.options.Labels, stages)
|
||||
|
||||
dockerfile.PrintWarnings(b.Stderr)
|
||||
dispatchState, err := b.dispatchDockerfileWithCancellation(stages, metaArgs, dockerfile.EscapeToken, source)
|
||||
dispatchState, err := b.dispatchDockerfileWithCancellation(ctx, stages, metaArgs, dockerfile.EscapeToken, source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -244,7 +247,7 @@ func printCommand(out io.Writer, currentCommandIndex int, totalCommands int, cmd
|
||||
return currentCommandIndex + 1
|
||||
}
|
||||
|
||||
func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.Stage, metaArgs []instructions.ArgCommand, escapeToken rune, source builder.Source) (*dispatchState, error) {
|
||||
func (b *Builder) dispatchDockerfileWithCancellation(ctx context.Context, parseResult []instructions.Stage, metaArgs []instructions.ArgCommand, escapeToken rune, source builder.Source) (*dispatchState, error) {
|
||||
dispatchRequest := dispatchRequest{}
|
||||
buildArgs := NewBuildArgs(b.options.BuildArgs)
|
||||
totalCommands := len(metaArgs) + len(parseResult)
|
||||
@@ -272,14 +275,14 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.
|
||||
dispatchRequest = newDispatchRequest(b, escapeToken, source, buildArgs, stagesResults)
|
||||
|
||||
currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, stage.SourceCode)
|
||||
if err := initializeStage(dispatchRequest, &stage); err != nil {
|
||||
if err := initializeStage(ctx, dispatchRequest, &stage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dispatchRequest.state.updateRunConfig()
|
||||
fmt.Fprintf(b.Stdout, " ---> %s\n", stringid.TruncateID(dispatchRequest.state.imageID))
|
||||
for _, cmd := range stage.Commands {
|
||||
select {
|
||||
case <-b.clientCtx.Done():
|
||||
case <-ctx.Done():
|
||||
logrus.Debug("Builder: build cancelled!")
|
||||
fmt.Fprint(b.Stdout, "Build cancelled\n")
|
||||
buildsFailed.WithValues(metricsBuildCanceled).Inc()
|
||||
@@ -290,7 +293,7 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.
|
||||
|
||||
currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, cmd)
|
||||
|
||||
if err := dispatch(dispatchRequest, cmd); err != nil {
|
||||
if err := dispatch(ctx, dispatchRequest, cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dispatchRequest.state.updateRunConfig()
|
||||
@@ -317,7 +320,7 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.
|
||||
// coming from the query parameter of the same name.
|
||||
//
|
||||
// TODO: Remove?
|
||||
func BuildFromConfig(config *container.Config, changes []string, os string) (*container.Config, error) {
|
||||
func BuildFromConfig(ctx context.Context, config *container.Config, changes []string, os string) (*container.Config, error) {
|
||||
if len(changes) == 0 {
|
||||
return config, nil
|
||||
}
|
||||
@@ -327,7 +330,7 @@ func BuildFromConfig(config *container.Config, changes []string, os string) (*co
|
||||
return nil, errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
b, err := newBuilder(context.Background(), builderOptions{
|
||||
b, err := newBuilder(ctx, builderOptions{
|
||||
Options: &types.ImageBuildOptions{NoCache: true},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -360,7 +363,7 @@ func BuildFromConfig(config *container.Config, changes []string, os string) (*co
|
||||
dispatchRequest.state.imageID = config.Image
|
||||
dispatchRequest.state.operatingSystem = os
|
||||
for _, cmd := range commands {
|
||||
err := dispatch(dispatchRequest, cmd)
|
||||
err := dispatch(ctx, dispatchRequest, cmd)
|
||||
if err != nil {
|
||||
return nil, errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
+3
-3
@@ -28,8 +28,8 @@ func newContainerManager(docker builder.ExecBackend) *containerManager {
|
||||
}
|
||||
|
||||
// Create a container
|
||||
func (c *containerManager) Create(runConfig *container.Config, hostConfig *container.HostConfig) (container.CreateResponse, error) {
|
||||
container, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(types.ContainerCreateConfig{
|
||||
func (c *containerManager) Create(ctx context.Context, runConfig *container.Config, hostConfig *container.HostConfig) (container.CreateResponse, error) {
|
||||
container, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(ctx, types.ContainerCreateConfig{
|
||||
Config: runConfig,
|
||||
HostConfig: hostConfig,
|
||||
})
|
||||
@@ -69,7 +69,7 @@ func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr i
|
||||
}
|
||||
}()
|
||||
|
||||
if err := c.backend.ContainerStart(cID, nil, "", ""); err != nil {
|
||||
if err := c.backend.ContainerStart(ctx, cID, nil, "", ""); err != nil {
|
||||
close(finished)
|
||||
logCancellationError(cancelErrCh, "error from ContainerStart: "+err.Error())
|
||||
return err
|
||||
|
||||
+33
-58
@@ -1,7 +1,6 @@
|
||||
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
@@ -20,12 +19,12 @@ import (
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/longpath"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -39,14 +38,14 @@ type pathCache interface {
|
||||
// copyInfo is a data object which stores the metadata about each source file in
|
||||
// a copyInstruction
|
||||
type copyInfo struct {
|
||||
root containerfs.ContainerFS
|
||||
root string
|
||||
path string
|
||||
hash string
|
||||
noDecompress bool
|
||||
}
|
||||
|
||||
func (c copyInfo) fullPath() (string, error) {
|
||||
return c.root.ResolveScopedPath(c.path, true)
|
||||
return containerfs.ResolveScopedPath(c.root, c.path)
|
||||
}
|
||||
|
||||
func newCopyInfoFromSource(source builder.Source, path string, hash string) copyInfo {
|
||||
@@ -75,7 +74,7 @@ type copier struct {
|
||||
source builder.Source
|
||||
pathCache pathCache
|
||||
download sourceDownloader
|
||||
platform *specs.Platform
|
||||
platform *ocispec.Platform
|
||||
// for cleanup. TODO: having copier.cleanup() is error prone and hard to
|
||||
// follow. Code calling performCopy should manage the lifecycle of its params.
|
||||
// Copier should take override source as input, not imageMount.
|
||||
@@ -87,7 +86,7 @@ func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, i
|
||||
platform := req.builder.platform
|
||||
if platform == nil {
|
||||
// May be nil if not explicitly set in API/dockerfile
|
||||
platform = &specs.Platform{}
|
||||
platform = &ocispec.Platform{}
|
||||
}
|
||||
if platform.OS == "" {
|
||||
// Default to the dispatch requests operating system if not explicit in API/dockerfile
|
||||
@@ -159,7 +158,7 @@ func (o *copier) getCopyInfoForSourcePath(orig, dest string) ([]copyInfo, error)
|
||||
}
|
||||
path = unnamedFilename
|
||||
}
|
||||
o.tmpPaths = append(o.tmpPaths, remote.Root().Path())
|
||||
o.tmpPaths = append(o.tmpPaths, remote.Root())
|
||||
|
||||
hash, err := remote.Hash(path)
|
||||
ci := newCopyInfoFromSource(remote, path, hash)
|
||||
@@ -202,7 +201,7 @@ func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo,
|
||||
|
||||
o.source, err = remotecontext.NewLazySource(rwLayer.Root())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create context for copy from %s", rwLayer.Root().Path())
|
||||
return nil, errors.Wrapf(err, "failed to create context for copy from %s", rwLayer.Root())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +258,7 @@ func (o *copier) storeInPathCache(im *imageMount, path string, hash string) {
|
||||
func (o *copier) copyWithWildcards(origPath string) ([]copyInfo, error) {
|
||||
root := o.source.Root()
|
||||
var copyInfos []copyInfo
|
||||
if err := root.Walk(root.Path(), func(path string, info os.FileInfo, err error) error {
|
||||
if err := filepath.WalkDir(root, func(path string, _ os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -271,7 +270,7 @@ func (o *copier) copyWithWildcards(origPath string) ([]copyInfo, error) {
|
||||
if rel == "." {
|
||||
return nil
|
||||
}
|
||||
if match, _ := root.Match(origPath, rel); !match {
|
||||
if match, _ := filepath.Match(origPath, rel); !match {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -317,7 +316,7 @@ func walkSource(source builder.Source, origPath string) ([]string, error) {
|
||||
}
|
||||
// Must be a dir
|
||||
var subfiles []string
|
||||
err = source.Root().Walk(fp, func(path string, info os.FileInfo, err error) error {
|
||||
err = filepath.WalkDir(fp, func(path string, _ os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -391,7 +390,7 @@ func downloadSource(output io.Writer, stdout io.Writer, srcURL string) (remote b
|
||||
filename := getFilenameForDownload(u.Path, resp)
|
||||
|
||||
// Prepare file in a tmp dir
|
||||
tmpDir, err := ioutils.TempDir("", "docker-remote")
|
||||
tmpDir, err := longpath.MkdirTemp("", "docker-remote")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -442,19 +441,14 @@ func downloadSource(output io.Writer, stdout io.Writer, srcURL string) (remote b
|
||||
return
|
||||
}
|
||||
|
||||
lc, err := remotecontext.NewLazySource(containerfs.NewLocalContainerFS(tmpDir))
|
||||
lc, err := remotecontext.NewLazySource(tmpDir)
|
||||
return lc, filename, err
|
||||
}
|
||||
|
||||
type copyFileOptions struct {
|
||||
decompress bool
|
||||
identity *idtools.Identity
|
||||
archiver Archiver
|
||||
}
|
||||
|
||||
type copyEndpoint struct {
|
||||
driver containerfs.Driver
|
||||
path string
|
||||
archiver *archive.Archiver
|
||||
}
|
||||
|
||||
func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions) error {
|
||||
@@ -470,96 +464,77 @@ func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions)
|
||||
|
||||
archiver := options.archiver
|
||||
|
||||
srcEndpoint := ©Endpoint{driver: source.root, path: srcPath}
|
||||
destEndpoint := ©Endpoint{driver: dest.root, path: destPath}
|
||||
|
||||
src, err := source.root.Stat(srcPath)
|
||||
src, err := os.Stat(srcPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "source path not found")
|
||||
}
|
||||
if src.IsDir() {
|
||||
return copyDirectory(archiver, srcEndpoint, destEndpoint, options.identity)
|
||||
return copyDirectory(archiver, srcPath, destPath, options.identity)
|
||||
}
|
||||
if options.decompress && isArchivePath(source.root, srcPath) && !source.noDecompress {
|
||||
if options.decompress && archive.IsArchivePath(srcPath) && !source.noDecompress {
|
||||
return archiver.UntarPath(srcPath, destPath)
|
||||
}
|
||||
|
||||
destExistsAsDir, err := isExistingDirectory(destEndpoint)
|
||||
destExistsAsDir, err := isExistingDirectory(destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// dest.path must be used because destPath has already been cleaned of any
|
||||
// trailing slash
|
||||
if endsInSlash(dest.root, dest.path) || destExistsAsDir {
|
||||
if endsInSlash(dest.path) || destExistsAsDir {
|
||||
// source.path must be used to get the correct filename when the source
|
||||
// is a symlink
|
||||
destPath = dest.root.Join(destPath, source.root.Base(source.path))
|
||||
destEndpoint = ©Endpoint{driver: dest.root, path: destPath}
|
||||
destPath = filepath.Join(destPath, filepath.Base(source.path))
|
||||
}
|
||||
return copyFile(archiver, srcEndpoint, destEndpoint, options.identity)
|
||||
return copyFile(archiver, srcPath, destPath, options.identity)
|
||||
}
|
||||
|
||||
func isArchivePath(driver containerfs.ContainerFS, path string) bool {
|
||||
file, err := driver.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
rdr, err := archive.DecompressStream(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
r := tar.NewReader(rdr)
|
||||
_, err = r.Next()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func copyDirectory(archiver Archiver, source, dest *copyEndpoint, identity *idtools.Identity) error {
|
||||
func copyDirectory(archiver *archive.Archiver, source, dest string, identity *idtools.Identity) error {
|
||||
destExists, err := isExistingDirectory(dest)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to query destination path")
|
||||
}
|
||||
|
||||
if err := archiver.CopyWithTar(source.path, dest.path); err != nil {
|
||||
if err := archiver.CopyWithTar(source, dest); err != nil {
|
||||
return errors.Wrapf(err, "failed to copy directory")
|
||||
}
|
||||
if identity != nil {
|
||||
return fixPermissions(source.path, dest.path, *identity, !destExists)
|
||||
return fixPermissions(source, dest, *identity, !destExists)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(archiver Archiver, source, dest *copyEndpoint, identity *idtools.Identity) error {
|
||||
func copyFile(archiver *archive.Archiver, source, dest string, identity *idtools.Identity) error {
|
||||
if identity == nil {
|
||||
// Use system.MkdirAll here, which is a custom version of os.MkdirAll
|
||||
// modified for use on Windows to handle volume GUID paths. These paths
|
||||
// are of the form \\?\Volume{<GUID>}\<path>. An example would be:
|
||||
// \\?\Volume{dae8d3ac-b9a1-11e9-88eb-e8554b2ba1db}\bin\busybox.exe
|
||||
if err := system.MkdirAll(filepath.Dir(dest.path), 0755); err != nil {
|
||||
if err := system.MkdirAll(filepath.Dir(dest), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, *identity); err != nil {
|
||||
if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest), 0755, *identity); err != nil {
|
||||
return errors.Wrapf(err, "failed to create new directory")
|
||||
}
|
||||
}
|
||||
|
||||
if err := archiver.CopyFileWithTar(source.path, dest.path); err != nil {
|
||||
if err := archiver.CopyFileWithTar(source, dest); err != nil {
|
||||
return errors.Wrapf(err, "failed to copy file")
|
||||
}
|
||||
if identity != nil {
|
||||
return fixPermissions(source.path, dest.path, *identity, false)
|
||||
return fixPermissions(source, dest, *identity, false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func endsInSlash(driver containerfs.Driver, path string) bool {
|
||||
return strings.HasSuffix(path, string(driver.Separator()))
|
||||
func endsInSlash(path string) bool {
|
||||
return strings.HasSuffix(path, string(filepath.Separator))
|
||||
}
|
||||
|
||||
// isExistingDirectory returns true if the path exists and is a directory
|
||||
func isExistingDirectory(point *copyEndpoint) (bool, error) {
|
||||
destStat, err := point.driver.Stat(point.path)
|
||||
func isExistingDirectory(path string) (bool, error) {
|
||||
destStat, err := os.Stat(path)
|
||||
switch {
|
||||
case errors.Is(err, os.ErrNotExist):
|
||||
return false, nil
|
||||
|
||||
+2
-4
@@ -9,7 +9,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
)
|
||||
|
||||
@@ -19,8 +18,7 @@ func fixPermissions(source, destination string, identity idtools.Identity, overr
|
||||
err error
|
||||
)
|
||||
if !overrideSkip {
|
||||
destEndpoint := ©Endpoint{driver: containerfs.NewLocalDriver(), path: destination}
|
||||
skipChownRoot, err = isExistingDirectory(destEndpoint)
|
||||
skipChownRoot, err = isExistingDirectory(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -28,7 +26,7 @@ func fixPermissions(source, destination string, identity idtools.Identity, overr
|
||||
|
||||
// We Walk on the source rather than on the destination because we don't
|
||||
// want to change permissions on things we haven't created or modified.
|
||||
return filepath.Walk(source, func(fullpath string, _ os.FileInfo, _ error) error {
|
||||
return filepath.WalkDir(source, func(fullpath string, _ os.DirEntry, _ error) error {
|
||||
// Do not alter the walk root iff. it existed before, as it doesn't fall under
|
||||
// the domain of "things we should chown".
|
||||
if skipChownRoot && source == fullpath {
|
||||
|
||||
+2
-2
@@ -15,8 +15,8 @@ import (
|
||||
)
|
||||
|
||||
var pathDenyList = map[string]bool{
|
||||
"c:\\": true,
|
||||
"c:\\windows": true,
|
||||
`c:\`: true,
|
||||
`c:\windows`: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
+61
-57
@@ -9,6 +9,7 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
@@ -27,7 +28,7 @@ import (
|
||||
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/shell"
|
||||
"github.com/moby/sys/signal"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -35,7 +36,7 @@ import (
|
||||
//
|
||||
// Sets the environment variable foo to bar, also makes interpolation
|
||||
// in the dockerfile available from the next statement on via ${foo}.
|
||||
func dispatchEnv(d dispatchRequest, c *instructions.EnvCommand) error {
|
||||
func dispatchEnv(ctx context.Context, d dispatchRequest, c *instructions.EnvCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
commitMessage := bytes.NewBufferString("ENV")
|
||||
for _, e := range c.Env {
|
||||
@@ -45,8 +46,7 @@ func dispatchEnv(d dispatchRequest, c *instructions.EnvCommand) error {
|
||||
commitMessage.WriteString(" " + newVar)
|
||||
gotOne := false
|
||||
for i, envVar := range runConfig.Env {
|
||||
envParts := strings.SplitN(envVar, "=", 2)
|
||||
compareFrom := envParts[0]
|
||||
compareFrom, _, _ := strings.Cut(envVar, "=")
|
||||
if shell.EqualEnvKeys(compareFrom, name) {
|
||||
runConfig.Env[i] = newVar
|
||||
gotOne = true
|
||||
@@ -57,21 +57,21 @@ func dispatchEnv(d dispatchRequest, c *instructions.EnvCommand) error {
|
||||
runConfig.Env = append(runConfig.Env, newVar)
|
||||
}
|
||||
}
|
||||
return d.builder.commit(d.state, commitMessage.String())
|
||||
return d.builder.commit(ctx, d.state, commitMessage.String())
|
||||
}
|
||||
|
||||
// MAINTAINER some text <maybe@an.email.address>
|
||||
//
|
||||
// Sets the maintainer metadata.
|
||||
func dispatchMaintainer(d dispatchRequest, c *instructions.MaintainerCommand) error {
|
||||
func dispatchMaintainer(ctx context.Context, d dispatchRequest, c *instructions.MaintainerCommand) error {
|
||||
d.state.maintainer = c.Maintainer
|
||||
return d.builder.commit(d.state, "MAINTAINER "+c.Maintainer)
|
||||
return d.builder.commit(ctx, d.state, "MAINTAINER "+c.Maintainer)
|
||||
}
|
||||
|
||||
// LABEL some json data describing the image
|
||||
//
|
||||
// Sets the Label variable foo to bar,
|
||||
func dispatchLabel(d dispatchRequest, c *instructions.LabelCommand) error {
|
||||
func dispatchLabel(ctx context.Context, d dispatchRequest, c *instructions.LabelCommand) error {
|
||||
if d.state.runConfig.Labels == nil {
|
||||
d.state.runConfig.Labels = make(map[string]string)
|
||||
}
|
||||
@@ -80,14 +80,14 @@ func dispatchLabel(d dispatchRequest, c *instructions.LabelCommand) error {
|
||||
d.state.runConfig.Labels[v.Key] = v.Value
|
||||
commitStr += " " + v.String()
|
||||
}
|
||||
return d.builder.commit(d.state, commitStr)
|
||||
return d.builder.commit(ctx, d.state, commitStr)
|
||||
}
|
||||
|
||||
// ADD foo /path
|
||||
//
|
||||
// Add the file 'foo' to '/path'. Tarball and Remote URL (http, https) handling
|
||||
// exist here. If you do not wish to have this automatic handling, use COPY.
|
||||
func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error {
|
||||
func dispatchAdd(ctx context.Context, d dispatchRequest, c *instructions.AddCommand) error {
|
||||
if c.Chmod != "" {
|
||||
return errors.New("the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
|
||||
}
|
||||
@@ -102,20 +102,20 @@ func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error {
|
||||
copyInstruction.chownStr = c.Chown
|
||||
copyInstruction.allowLocalDecompression = true
|
||||
|
||||
return d.builder.performCopy(d, copyInstruction)
|
||||
return d.builder.performCopy(ctx, d, copyInstruction)
|
||||
}
|
||||
|
||||
// COPY foo /path
|
||||
//
|
||||
// Same as 'ADD' but without the tar and remote url handling.
|
||||
func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error {
|
||||
func dispatchCopy(ctx context.Context, d dispatchRequest, c *instructions.CopyCommand) error {
|
||||
if c.Chmod != "" {
|
||||
return errors.New("the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
|
||||
}
|
||||
var im *imageMount
|
||||
var err error
|
||||
if c.From != "" {
|
||||
im, err = d.getImageMount(c.From)
|
||||
im, err = d.getImageMount(ctx, c.From)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "invalid from flag value %s", c.From)
|
||||
}
|
||||
@@ -130,10 +130,10 @@ func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error {
|
||||
if c.From != "" && copyInstruction.chownStr == "" {
|
||||
copyInstruction.preserveOwnership = true
|
||||
}
|
||||
return d.builder.performCopy(d, copyInstruction)
|
||||
return d.builder.performCopy(ctx, d, copyInstruction)
|
||||
}
|
||||
|
||||
func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error) {
|
||||
func (d *dispatchRequest) getImageMount(ctx context.Context, imageRefOrID string) (*imageMount, error) {
|
||||
if imageRefOrID == "" {
|
||||
// TODO: this could return the source in the default case as well?
|
||||
return nil, nil
|
||||
@@ -148,14 +148,17 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
|
||||
imageRefOrID = stage.Image
|
||||
localOnly = true
|
||||
}
|
||||
return d.builder.imageSources.Get(imageRefOrID, localOnly, d.builder.platform)
|
||||
return d.builder.imageSources.Get(ctx, imageRefOrID, localOnly, d.builder.platform)
|
||||
}
|
||||
|
||||
// FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name]
|
||||
func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
|
||||
d.builder.imageProber.Reset()
|
||||
func initializeStage(ctx context.Context, d dispatchRequest, cmd *instructions.Stage) error {
|
||||
err := d.builder.imageProber.Reset(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var platform *specs.Platform
|
||||
var platform *ocispec.Platform
|
||||
if v := cmd.Platform; v != "" {
|
||||
v, err := d.getExpandedString(d.shlex, v)
|
||||
if err != nil {
|
||||
@@ -169,7 +172,7 @@ func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
|
||||
platform = &p
|
||||
}
|
||||
|
||||
image, err := d.getFromImage(d.shlex, cmd.BaseName, platform)
|
||||
image, err := d.getFromImage(ctx, d.shlex, cmd.BaseName, platform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -180,12 +183,12 @@ func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
|
||||
if len(state.runConfig.OnBuild) > 0 {
|
||||
triggers := state.runConfig.OnBuild
|
||||
state.runConfig.OnBuild = nil
|
||||
return dispatchTriggeredOnBuild(d, triggers)
|
||||
return dispatchTriggeredOnBuild(ctx, d, triggers)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
|
||||
func dispatchTriggeredOnBuild(ctx context.Context, d dispatchRequest, triggers []string) error {
|
||||
fmt.Fprintf(d.builder.Stdout, "# Executing %d build trigger", len(triggers))
|
||||
if len(triggers) > 1 {
|
||||
fmt.Fprint(d.builder.Stdout, "s")
|
||||
@@ -208,7 +211,7 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = dispatch(d, cmd)
|
||||
err = dispatch(ctx, d, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -229,7 +232,7 @@ func (d *dispatchRequest) getExpandedString(shlex *shell.Lex, str string) (strin
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform) (builder.Image, error) {
|
||||
func (d *dispatchRequest) getImageOrStage(ctx context.Context, name string, platform *ocispec.Platform) (builder.Image, error) {
|
||||
var localOnly bool
|
||||
if im, ok := d.stages.getByName(name); ok {
|
||||
name = im.Image
|
||||
@@ -256,13 +259,14 @@ func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform)
|
||||
}
|
||||
return builder.Image(imageImage), nil
|
||||
}
|
||||
imageMount, err := d.builder.imageSources.Get(name, localOnly, platform)
|
||||
imageMount, err := d.builder.imageSources.Get(ctx, name, localOnly, platform)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return imageMount.Image(), nil
|
||||
}
|
||||
func (d *dispatchRequest) getFromImage(shlex *shell.Lex, basename string, platform *specs.Platform) (builder.Image, error) {
|
||||
|
||||
func (d *dispatchRequest) getFromImage(ctx context.Context, shlex *shell.Lex, basename string, platform *ocispec.Platform) (builder.Image, error) {
|
||||
name, err := d.getExpandedString(shlex, basename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -273,18 +277,18 @@ func (d *dispatchRequest) getFromImage(shlex *shell.Lex, basename string, platfo
|
||||
return nil, errors.Errorf("base name (%s) should not be blank", basename)
|
||||
}
|
||||
|
||||
return d.getImageOrStage(name, platform)
|
||||
return d.getImageOrStage(ctx, name, platform)
|
||||
}
|
||||
|
||||
func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
|
||||
func dispatchOnbuild(ctx context.Context, d dispatchRequest, c *instructions.OnbuildCommand) error {
|
||||
d.state.runConfig.OnBuild = append(d.state.runConfig.OnBuild, c.Expression)
|
||||
return d.builder.commit(d.state, "ONBUILD "+c.Expression)
|
||||
return d.builder.commit(ctx, d.state, "ONBUILD "+c.Expression)
|
||||
}
|
||||
|
||||
// WORKDIR /tmp
|
||||
//
|
||||
// Set the working directory for future RUN/CMD/etc statements.
|
||||
func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
||||
func dispatchWorkdir(ctx context.Context, d dispatchRequest, c *instructions.WorkdirCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
var err error
|
||||
runConfig.WorkingDir, err = normalizeWorkdir(d.state.operatingSystem, runConfig.WorkingDir, c.Path)
|
||||
@@ -305,7 +309,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
||||
comment := "WORKDIR " + runConfig.WorkingDir
|
||||
runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.state.operatingSystem))
|
||||
|
||||
containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd)
|
||||
containerID, err := d.builder.probeAndCreate(ctx, d.state, runConfigWithCommentCmd)
|
||||
if err != nil || containerID == "" {
|
||||
return err
|
||||
}
|
||||
@@ -314,7 +318,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.builder.commitContainer(d.state, containerID, runConfigWithCommentCmd)
|
||||
return d.builder.commitContainer(ctx, d.state, containerID, runConfigWithCommentCmd)
|
||||
}
|
||||
|
||||
// RUN some command yo
|
||||
@@ -326,7 +330,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
||||
// RUN echo hi # sh -c echo hi (Linux and LCOW)
|
||||
// RUN echo hi # cmd /S /C echo hi (Windows)
|
||||
// RUN [ "echo", "hi" ] # echo hi
|
||||
func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
|
||||
func dispatchRun(ctx context.Context, d dispatchRequest, c *instructions.RunCommand) error {
|
||||
if !system.IsOSSupported(d.state.operatingSystem) {
|
||||
return system.ErrNotSupportedOperatingSystem
|
||||
}
|
||||
@@ -360,12 +364,12 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
|
||||
withEntrypointOverride(saveCmd, strslice.StrSlice{""}),
|
||||
withoutHealthcheck())
|
||||
|
||||
cID, err := d.builder.create(runConfig)
|
||||
cID, err := d.builder.create(ctx, runConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.builder.containerManager.Run(d.builder.clientCtx, cID, d.builder.Stdout, d.builder.Stderr); err != nil {
|
||||
if err := d.builder.containerManager.Run(ctx, cID, d.builder.Stdout, d.builder.Stderr); err != nil {
|
||||
if err, ok := err.(*statusCodeError); ok {
|
||||
// TODO: change error type, because jsonmessage.JSONError assumes HTTP
|
||||
msg := fmt.Sprintf(
|
||||
@@ -388,7 +392,7 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
|
||||
runConfigForCacheProbe.ArgsEscaped = stateRunConfig.ArgsEscaped
|
||||
}
|
||||
|
||||
return d.builder.commitContainer(d.state, cID, runConfigForCacheProbe)
|
||||
return d.builder.commitContainer(ctx, d.state, cID, runConfigForCacheProbe)
|
||||
}
|
||||
|
||||
// Derive the command to use for probeCache() and to commit in this container.
|
||||
@@ -403,9 +407,9 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
|
||||
// These args are transparent so resulting image should be the same regardless
|
||||
// of the value.
|
||||
func prependEnvOnCmd(buildArgs *BuildArgs, buildArgVars []string, cmd strslice.StrSlice) strslice.StrSlice {
|
||||
var tmpBuildEnv []string
|
||||
tmpBuildEnv := make([]string, 0, len(buildArgVars))
|
||||
for _, env := range buildArgVars {
|
||||
key := strings.SplitN(env, "=", 2)[0]
|
||||
key, _, _ := strings.Cut(env, "=")
|
||||
if buildArgs.IsReferencedOrNotBuiltin(key) {
|
||||
tmpBuildEnv = append(tmpBuildEnv, env)
|
||||
}
|
||||
@@ -413,14 +417,14 @@ func prependEnvOnCmd(buildArgs *BuildArgs, buildArgVars []string, cmd strslice.S
|
||||
|
||||
sort.Strings(tmpBuildEnv)
|
||||
tmpEnv := append([]string{fmt.Sprintf("|%d", len(tmpBuildEnv))}, tmpBuildEnv...)
|
||||
return strslice.StrSlice(append(tmpEnv, cmd...))
|
||||
return append(tmpEnv, cmd...)
|
||||
}
|
||||
|
||||
// CMD foo
|
||||
//
|
||||
// Set the default command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
|
||||
func dispatchCmd(ctx context.Context, d dispatchRequest, c *instructions.CmdCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
cmd, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem, c.Name(), c.String())
|
||||
|
||||
@@ -436,7 +440,7 @@ func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
|
||||
runConfig.Cmd = cmd
|
||||
runConfig.ArgsEscaped = argsEscaped
|
||||
|
||||
if err := d.builder.commit(d.state, fmt.Sprintf("CMD %q", cmd)); err != nil {
|
||||
if err := d.builder.commit(ctx, d.state, fmt.Sprintf("CMD %q", cmd)); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(c.ShellDependantCmdLine.CmdLine) != 0 {
|
||||
@@ -450,7 +454,7 @@ func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
|
||||
//
|
||||
// Set the default healthcheck command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand) error {
|
||||
func dispatchHealthcheck(ctx context.Context, d dispatchRequest, c *instructions.HealthCheckCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
if runConfig.Healthcheck != nil {
|
||||
oldCmd := runConfig.Healthcheck.Test
|
||||
@@ -459,7 +463,7 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand)
|
||||
}
|
||||
}
|
||||
runConfig.Healthcheck = c.Health
|
||||
return d.builder.commit(d.state, fmt.Sprintf("HEALTHCHECK %q", runConfig.Healthcheck))
|
||||
return d.builder.commit(ctx, d.state, fmt.Sprintf("HEALTHCHECK %q", runConfig.Healthcheck))
|
||||
}
|
||||
|
||||
// ENTRYPOINT /usr/sbin/nginx
|
||||
@@ -469,7 +473,7 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand)
|
||||
//
|
||||
// Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint
|
||||
// is initialized at newBuilder time instead of through argument parsing.
|
||||
func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error {
|
||||
func dispatchEntrypoint(ctx context.Context, d dispatchRequest, c *instructions.EntrypointCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
cmd, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem, c.Name(), c.String())
|
||||
|
||||
@@ -491,14 +495,14 @@ func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) er
|
||||
runConfig.Cmd = nil
|
||||
}
|
||||
|
||||
return d.builder.commit(d.state, fmt.Sprintf("ENTRYPOINT %q", runConfig.Entrypoint))
|
||||
return d.builder.commit(ctx, d.state, fmt.Sprintf("ENTRYPOINT %q", runConfig.Entrypoint))
|
||||
}
|
||||
|
||||
// EXPOSE 6667/tcp 7000/tcp
|
||||
//
|
||||
// Expose ports for links and port mappings. This all ends up in
|
||||
// req.runConfig.ExposedPorts for runconfig.
|
||||
func dispatchExpose(d dispatchRequest, c *instructions.ExposeCommand, envs []string) error {
|
||||
func dispatchExpose(ctx context.Context, d dispatchRequest, c *instructions.ExposeCommand, envs []string) error {
|
||||
// custom multi word expansion
|
||||
// expose $FOO with FOO="80 443" is expanded as EXPOSE [80,443]. This is the only command supporting word to words expansion
|
||||
// so the word processing has been de-generalized
|
||||
@@ -524,22 +528,22 @@ func dispatchExpose(d dispatchRequest, c *instructions.ExposeCommand, envs []str
|
||||
d.state.runConfig.ExposedPorts[p] = struct{}{}
|
||||
}
|
||||
|
||||
return d.builder.commit(d.state, "EXPOSE "+strings.Join(c.Ports, " "))
|
||||
return d.builder.commit(ctx, d.state, "EXPOSE "+strings.Join(c.Ports, " "))
|
||||
}
|
||||
|
||||
// USER foo
|
||||
//
|
||||
// Set the user to 'foo' for future commands and when running the
|
||||
// ENTRYPOINT/CMD at container run time.
|
||||
func dispatchUser(d dispatchRequest, c *instructions.UserCommand) error {
|
||||
func dispatchUser(ctx context.Context, d dispatchRequest, c *instructions.UserCommand) error {
|
||||
d.state.runConfig.User = c.User
|
||||
return d.builder.commit(d.state, fmt.Sprintf("USER %v", c.User))
|
||||
return d.builder.commit(ctx, d.state, fmt.Sprintf("USER %v", c.User))
|
||||
}
|
||||
|
||||
// VOLUME /foo
|
||||
//
|
||||
// Expose the volume /foo for use. Will also accept the JSON array form.
|
||||
func dispatchVolume(d dispatchRequest, c *instructions.VolumeCommand) error {
|
||||
func dispatchVolume(ctx context.Context, d dispatchRequest, c *instructions.VolumeCommand) error {
|
||||
if d.state.runConfig.Volumes == nil {
|
||||
d.state.runConfig.Volumes = map[string]struct{}{}
|
||||
}
|
||||
@@ -549,19 +553,19 @@ func dispatchVolume(d dispatchRequest, c *instructions.VolumeCommand) error {
|
||||
}
|
||||
d.state.runConfig.Volumes[v] = struct{}{}
|
||||
}
|
||||
return d.builder.commit(d.state, fmt.Sprintf("VOLUME %v", c.Volumes))
|
||||
return d.builder.commit(ctx, d.state, fmt.Sprintf("VOLUME %v", c.Volumes))
|
||||
}
|
||||
|
||||
// STOPSIGNAL signal
|
||||
//
|
||||
// Set the signal that will be used to kill the container.
|
||||
func dispatchStopSignal(d dispatchRequest, c *instructions.StopSignalCommand) error {
|
||||
func dispatchStopSignal(ctx context.Context, d dispatchRequest, c *instructions.StopSignalCommand) error {
|
||||
_, err := signal.ParseSignal(c.Signal)
|
||||
if err != nil {
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
d.state.runConfig.StopSignal = c.Signal
|
||||
return d.builder.commit(d.state, fmt.Sprintf("STOPSIGNAL %v", c.Signal))
|
||||
return d.builder.commit(ctx, d.state, fmt.Sprintf("STOPSIGNAL %v", c.Signal))
|
||||
}
|
||||
|
||||
// ARG name[=value]
|
||||
@@ -569,7 +573,7 @@ func dispatchStopSignal(d dispatchRequest, c *instructions.StopSignalCommand) er
|
||||
// Adds the variable foo to the trusted list of variables that can be passed
|
||||
// to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
|
||||
// Dockerfile author may optionally set a default value of this variable.
|
||||
func dispatchArg(d dispatchRequest, c *instructions.ArgCommand) error {
|
||||
func dispatchArg(ctx context.Context, d dispatchRequest, c *instructions.ArgCommand) error {
|
||||
var commitStr strings.Builder
|
||||
commitStr.WriteString("ARG ")
|
||||
for i, arg := range c.Args {
|
||||
@@ -584,13 +588,13 @@ func dispatchArg(d dispatchRequest, c *instructions.ArgCommand) error {
|
||||
d.state.buildArgs.AddArg(arg.Key, arg.Value)
|
||||
}
|
||||
|
||||
return d.builder.commit(d.state, commitStr.String())
|
||||
return d.builder.commit(ctx, d.state, commitStr.String())
|
||||
}
|
||||
|
||||
// SHELL powershell -command
|
||||
//
|
||||
// Set the non-default shell to use.
|
||||
func dispatchShell(d dispatchRequest, c *instructions.ShellCommand) error {
|
||||
func dispatchShell(ctx context.Context, d dispatchRequest, c *instructions.ShellCommand) error {
|
||||
d.state.runConfig.Shell = c.Shell
|
||||
return d.builder.commit(d.state, fmt.Sprintf("SHELL %v", d.state.runConfig.Shell))
|
||||
return d.builder.commit(ctx, d.state, fmt.Sprintf("SHELL %v", d.state.runConfig.Shell))
|
||||
}
|
||||
|
||||
+22
-19
@@ -20,6 +20,7 @@
|
||||
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/oci"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/runconfig/opts"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
@@ -34,7 +36,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
|
||||
func dispatch(ctx context.Context, d dispatchRequest, cmd instructions.Command) (err error) {
|
||||
if c, ok := cmd.(instructions.PlatformSpecific); ok {
|
||||
err := c.CheckPlatform(d.state.operatingSystem)
|
||||
if err != nil {
|
||||
@@ -65,39 +67,39 @@ func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
|
||||
}()
|
||||
switch c := cmd.(type) {
|
||||
case *instructions.EnvCommand:
|
||||
return dispatchEnv(d, c)
|
||||
return dispatchEnv(ctx, d, c)
|
||||
case *instructions.MaintainerCommand:
|
||||
return dispatchMaintainer(d, c)
|
||||
return dispatchMaintainer(ctx, d, c)
|
||||
case *instructions.LabelCommand:
|
||||
return dispatchLabel(d, c)
|
||||
return dispatchLabel(ctx, d, c)
|
||||
case *instructions.AddCommand:
|
||||
return dispatchAdd(d, c)
|
||||
return dispatchAdd(ctx, d, c)
|
||||
case *instructions.CopyCommand:
|
||||
return dispatchCopy(d, c)
|
||||
return dispatchCopy(ctx, d, c)
|
||||
case *instructions.OnbuildCommand:
|
||||
return dispatchOnbuild(d, c)
|
||||
return dispatchOnbuild(ctx, d, c)
|
||||
case *instructions.WorkdirCommand:
|
||||
return dispatchWorkdir(d, c)
|
||||
return dispatchWorkdir(ctx, d, c)
|
||||
case *instructions.RunCommand:
|
||||
return dispatchRun(d, c)
|
||||
return dispatchRun(ctx, d, c)
|
||||
case *instructions.CmdCommand:
|
||||
return dispatchCmd(d, c)
|
||||
return dispatchCmd(ctx, d, c)
|
||||
case *instructions.HealthCheckCommand:
|
||||
return dispatchHealthcheck(d, c)
|
||||
return dispatchHealthcheck(ctx, d, c)
|
||||
case *instructions.EntrypointCommand:
|
||||
return dispatchEntrypoint(d, c)
|
||||
return dispatchEntrypoint(ctx, d, c)
|
||||
case *instructions.ExposeCommand:
|
||||
return dispatchExpose(d, c, envs)
|
||||
return dispatchExpose(ctx, d, c, envs)
|
||||
case *instructions.UserCommand:
|
||||
return dispatchUser(d, c)
|
||||
return dispatchUser(ctx, d, c)
|
||||
case *instructions.VolumeCommand:
|
||||
return dispatchVolume(d, c)
|
||||
return dispatchVolume(ctx, d, c)
|
||||
case *instructions.StopSignalCommand:
|
||||
return dispatchStopSignal(d, c)
|
||||
return dispatchStopSignal(ctx, d, c)
|
||||
case *instructions.ArgCommand:
|
||||
return dispatchArg(d, c)
|
||||
return dispatchArg(ctx, d, c)
|
||||
case *instructions.ShellCommand:
|
||||
return dispatchShell(d, c)
|
||||
return dispatchShell(ctx, d, c)
|
||||
}
|
||||
return errors.Errorf("unsupported command type: %v", reflect.TypeOf(cmd))
|
||||
}
|
||||
@@ -235,7 +237,8 @@ func (s *dispatchState) beginStage(stageName string, image builder.Image) error
|
||||
// Add the default PATH to runConfig.ENV if one exists for the operating system and there
|
||||
// is no PATH set. Note that Windows containers on Windows won't have one as it's set by HCS
|
||||
func (s *dispatchState) setDefaultPath() {
|
||||
defaultPath := system.DefaultPathEnv(s.operatingSystem)
|
||||
// TODO(thaJeztah): use github.com/moby/buildkit/util/system.DefaultPathEnv() once https://github.com/moby/buildkit/pull/3158 is resolved.
|
||||
defaultPath := oci.DefaultPathEnv(s.operatingSystem)
|
||||
if defaultPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
+7
-7
@@ -8,12 +8,12 @@ import (
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/builder"
|
||||
dockerimage "github.com/docker/docker/image"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type getAndMountFunc func(string, bool, *specs.Platform) (builder.Image, builder.ROLayer, error)
|
||||
type getAndMountFunc func(context.Context, string, bool, *ocispec.Platform) (builder.Image, builder.ROLayer, error)
|
||||
|
||||
// imageSources mounts images and provides a cache for mounted images. It tracks
|
||||
// all images so they can be unmounted at the end of the build.
|
||||
@@ -23,8 +23,8 @@ type imageSources struct {
|
||||
getImage getAndMountFunc
|
||||
}
|
||||
|
||||
func newImageSources(ctx context.Context, options builderOptions) *imageSources {
|
||||
getAndMount := func(idOrRef string, localOnly bool, platform *specs.Platform) (builder.Image, builder.ROLayer, error) {
|
||||
func newImageSources(options builderOptions) *imageSources {
|
||||
getAndMount := func(ctx context.Context, idOrRef string, localOnly bool, platform *ocispec.Platform) (builder.Image, builder.ROLayer, error) {
|
||||
pullOption := backend.PullOptionNoPull
|
||||
if !localOnly {
|
||||
if options.Options.PullParent {
|
||||
@@ -47,12 +47,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
|
||||
}
|
||||
}
|
||||
|
||||
func (m *imageSources) Get(idOrRef string, localOnly bool, platform *specs.Platform) (*imageMount, error) {
|
||||
func (m *imageSources) Get(ctx context.Context, idOrRef string, localOnly bool, platform *ocispec.Platform) (*imageMount, error) {
|
||||
if im, ok := m.byImageID[idOrRef]; ok {
|
||||
return im, nil
|
||||
}
|
||||
|
||||
image, layer, err := m.getImage(idOrRef, localOnly, platform)
|
||||
image, layer, err := m.getImage(ctx, idOrRef, localOnly, platform)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func (m *imageSources) Unmount() (retErr error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (m *imageSources) Add(im *imageMount, platform *specs.Platform) {
|
||||
func (m *imageSources) Add(im *imageMount, platform *ocispec.Platform) {
|
||||
switch im.image {
|
||||
case nil:
|
||||
// Set the platform for scratch images
|
||||
|
||||
+26
-10
@@ -1,6 +1,8 @@
|
||||
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -9,30 +11,42 @@ import (
|
||||
// ImageProber exposes an Image cache to the Builder. It supports resetting a
|
||||
// cache.
|
||||
type ImageProber interface {
|
||||
Reset()
|
||||
Reset(ctx context.Context) error
|
||||
Probe(parentID string, runConfig *container.Config) (string, error)
|
||||
}
|
||||
|
||||
type resetFunc func(context.Context) (builder.ImageCache, error)
|
||||
|
||||
type imageProber struct {
|
||||
cache builder.ImageCache
|
||||
reset func() builder.ImageCache
|
||||
reset resetFunc
|
||||
cacheBusted bool
|
||||
}
|
||||
|
||||
func newImageProber(cacheBuilder builder.ImageCacheBuilder, cacheFrom []string, noCache bool) ImageProber {
|
||||
func newImageProber(ctx context.Context, cacheBuilder builder.ImageCacheBuilder, cacheFrom []string, noCache bool) (ImageProber, error) {
|
||||
if noCache {
|
||||
return &nopProber{}
|
||||
return &nopProber{}, nil
|
||||
}
|
||||
|
||||
reset := func() builder.ImageCache {
|
||||
return cacheBuilder.MakeImageCache(cacheFrom)
|
||||
reset := func(ctx context.Context) (builder.ImageCache, error) {
|
||||
return cacheBuilder.MakeImageCache(ctx, cacheFrom)
|
||||
}
|
||||
return &imageProber{cache: reset(), reset: reset}
|
||||
|
||||
cache, err := reset(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &imageProber{cache: cache, reset: reset}, nil
|
||||
}
|
||||
|
||||
func (c *imageProber) Reset() {
|
||||
c.cache = c.reset()
|
||||
func (c *imageProber) Reset(ctx context.Context) error {
|
||||
newCache, err := c.reset(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.cache = newCache
|
||||
c.cacheBusted = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// Probe checks if cache match can be found for current build instruction.
|
||||
@@ -56,7 +70,9 @@ func (c *imageProber) Probe(parentID string, runConfig *container.Config) (strin
|
||||
|
||||
type nopProber struct{}
|
||||
|
||||
func (c *nopProber) Reset() {}
|
||||
func (c *nopProber) Reset(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *nopProber) Probe(_ string, _ *container.Config) (string, error) {
|
||||
return "", nil
|
||||
|
||||
+29
-65
@@ -4,10 +4,10 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||
// non-contiguous functionality. Please read the comments.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
@@ -17,62 +17,18 @@ import (
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-connections/nat"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Archiver defines an interface for copying files from one destination to
|
||||
// another using Tar/Untar.
|
||||
type Archiver interface {
|
||||
TarUntar(src, dst string) error
|
||||
UntarPath(src, dst string) error
|
||||
CopyWithTar(src, dst string) error
|
||||
CopyFileWithTar(src, dst string) error
|
||||
IdentityMapping() idtools.IdentityMapping
|
||||
func (b *Builder) getArchiver() *archive.Archiver {
|
||||
return chrootarchive.NewArchiver(b.idMapping)
|
||||
}
|
||||
|
||||
// The builder will use the following interfaces if the container fs implements
|
||||
// these for optimized copies to and from the container.
|
||||
type extractor interface {
|
||||
ExtractArchive(src io.Reader, dst string, opts *archive.TarOptions) error
|
||||
}
|
||||
|
||||
type archiver interface {
|
||||
ArchivePath(src string, opts *archive.TarOptions) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// helper functions to get tar/untar func
|
||||
func untarFunc(i interface{}) containerfs.UntarFunc {
|
||||
if ea, ok := i.(extractor); ok {
|
||||
return ea.ExtractArchive
|
||||
}
|
||||
return chrootarchive.Untar
|
||||
}
|
||||
|
||||
func tarFunc(i interface{}) containerfs.TarFunc {
|
||||
if ap, ok := i.(archiver); ok {
|
||||
return ap.ArchivePath
|
||||
}
|
||||
return archive.TarWithOptions
|
||||
}
|
||||
|
||||
func (b *Builder) getArchiver(src, dst containerfs.Driver) Archiver {
|
||||
t, u := tarFunc(src), untarFunc(dst)
|
||||
return &containerfs.Archiver{
|
||||
SrcDriver: src,
|
||||
DstDriver: dst,
|
||||
Tar: t,
|
||||
Untar: u,
|
||||
IDMapping: b.idMapping,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
|
||||
func (b *Builder) commit(ctx context.Context, dispatchState *dispatchState, comment string) error {
|
||||
if b.disableCommit {
|
||||
return nil
|
||||
}
|
||||
@@ -81,15 +37,15 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
|
||||
}
|
||||
|
||||
runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, dispatchState.operatingSystem))
|
||||
id, err := b.probeAndCreate(dispatchState, runConfigWithCommentCmd)
|
||||
id, err := b.probeAndCreate(ctx, dispatchState, runConfigWithCommentCmd)
|
||||
if err != nil || id == "" {
|
||||
return err
|
||||
}
|
||||
|
||||
return b.commitContainer(dispatchState, id, runConfigWithCommentCmd)
|
||||
return b.commitContainer(ctx, dispatchState, id, runConfigWithCommentCmd)
|
||||
}
|
||||
|
||||
func (b *Builder) commitContainer(dispatchState *dispatchState, id string, containerConfig *container.Config) error {
|
||||
func (b *Builder) commitContainer(ctx context.Context, dispatchState *dispatchState, id string, containerConfig *container.Config) error {
|
||||
if b.disableCommit {
|
||||
return nil
|
||||
}
|
||||
@@ -102,12 +58,12 @@ func (b *Builder) commitContainer(dispatchState *dispatchState, id string, conta
|
||||
ContainerID: id,
|
||||
}
|
||||
|
||||
imageID, err := b.docker.CommitBuildStep(commitCfg)
|
||||
imageID, err := b.docker.CommitBuildStep(ctx, commitCfg)
|
||||
dispatchState.imageID = string(imageID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, parent builder.Image, runConfig *container.Config) error {
|
||||
func (b *Builder) exportImage(ctx context.Context, state *dispatchState, layer builder.RWLayer, parent builder.Image, runConfig *container.Config) error {
|
||||
newLayer, err := layer.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -118,7 +74,7 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren
|
||||
return errors.Errorf("unexpected image type")
|
||||
}
|
||||
|
||||
platform := &specs.Platform{
|
||||
platform := &ocispec.Platform{
|
||||
OS: parentImage.OS,
|
||||
Architecture: parentImage.Architecture,
|
||||
Variant: parentImage.Variant,
|
||||
@@ -142,7 +98,15 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren
|
||||
return errors.Wrap(err, "failed to encode image config")
|
||||
}
|
||||
|
||||
exportedImage, err := b.docker.CreateImage(config, state.imageID)
|
||||
// when writing the new image's manifest, we now need to pass in the new layer's digest.
|
||||
// before the containerd store work this was unnecessary since we get the layer id
|
||||
// from the image's RootFS ChainID -- see:
|
||||
// https://github.com/moby/moby/blob/8cf66ed7322fa885ef99c4c044fa23e1727301dc/image/store.go#L162
|
||||
// however, with the containerd store we can't do this. An alternative implementation here
|
||||
// without changing the signature would be to get the layer digest by walking the content store
|
||||
// and filtering the objects to find the layer with the DiffID we want, but that has performance
|
||||
// implications that should be called out/investigated
|
||||
exportedImage, err := b.docker.CreateImage(ctx, config, state.imageID, newLayer.ContentStoreDigest())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to export image")
|
||||
}
|
||||
@@ -152,7 +116,7 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
|
||||
func (b *Builder) performCopy(ctx context.Context, req dispatchRequest, inst copyInstruction) error {
|
||||
state := req.state
|
||||
srcHash := getSourceHashFromInfos(inst.infos)
|
||||
|
||||
@@ -171,7 +135,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
|
||||
return err
|
||||
}
|
||||
|
||||
imageMount, err := b.imageSources.Get(state.imageID, true, req.builder.platform)
|
||||
imageMount, err := b.imageSources.Get(ctx, state.imageID, true, req.builder.platform)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
|
||||
}
|
||||
@@ -192,7 +156,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
|
||||
// translated (if necessary because of user namespaces), and replace
|
||||
// the root pair with the chown pair for copy operations
|
||||
if inst.chownStr != "" {
|
||||
identity, err = parseChownFlag(b, state, inst.chownStr, destInfo.root.Path(), b.idMapping)
|
||||
identity, err = parseChownFlag(ctx, b, state, inst.chownStr, destInfo.root, b.idMapping)
|
||||
if err != nil {
|
||||
if b.options.Platform != "windows" {
|
||||
return errors.Wrapf(err, "unable to convert uid/gid chown string to host mapping")
|
||||
@@ -205,7 +169,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
|
||||
for _, info := range inst.infos {
|
||||
opts := copyFileOptions{
|
||||
decompress: inst.allowLocalDecompression,
|
||||
archiver: b.getArchiver(info.root, destInfo.root),
|
||||
archiver: b.getArchiver(),
|
||||
}
|
||||
if !inst.preserveOwnership {
|
||||
opts.identity = &identity
|
||||
@@ -214,7 +178,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
|
||||
return errors.Wrapf(err, "failed to copy files")
|
||||
}
|
||||
}
|
||||
return b.exportImage(state, rwLayer, imageMount.Image(), runConfigWithCommentCmd)
|
||||
return b.exportImage(ctx, state, rwLayer, imageMount.Image(), runConfigWithCommentCmd)
|
||||
}
|
||||
|
||||
func createDestInfo(workingDir string, inst copyInstruction, rwLayer builder.RWLayer, platform string) (copyInfo, error) {
|
||||
@@ -376,18 +340,18 @@ func (b *Builder) probeCache(dispatchState *dispatchState, runConfig *container.
|
||||
|
||||
var defaultLogConfig = container.LogConfig{Type: "none"}
|
||||
|
||||
func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *container.Config) (string, error) {
|
||||
func (b *Builder) probeAndCreate(ctx context.Context, dispatchState *dispatchState, runConfig *container.Config) (string, error) {
|
||||
if hit, err := b.probeCache(dispatchState, runConfig); err != nil || hit {
|
||||
return "", err
|
||||
}
|
||||
return b.create(runConfig)
|
||||
return b.create(ctx, runConfig)
|
||||
}
|
||||
|
||||
func (b *Builder) create(runConfig *container.Config) (string, error) {
|
||||
func (b *Builder) create(ctx context.Context, runConfig *container.Config) (string, error) {
|
||||
logrus.Debugf("[BUILDER] Command to be executed: %v", runConfig.Cmd)
|
||||
|
||||
hostConfig := hostConfigFromOptions(b.options)
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig)
|
||||
container, err := b.containerManager.Create(ctx, runConfig, hostConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
|
||||
func parseChownFlag(ctx context.Context, builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
|
||||
var userStr, grpStr string
|
||||
parts := strings.Split(chown, ":")
|
||||
if len(parts) > 2 {
|
||||
|
||||
+8
-7
@@ -2,6 +2,7 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -14,15 +15,15 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
|
||||
func parseChownFlag(ctx context.Context, builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
|
||||
if builder.options.Platform == "windows" {
|
||||
return getAccountIdentity(builder, chown, ctrRootPath, state)
|
||||
return getAccountIdentity(ctx, builder, chown, ctrRootPath, state)
|
||||
}
|
||||
|
||||
return identityMapping.RootPair(), nil
|
||||
}
|
||||
|
||||
func getAccountIdentity(builder *Builder, accountName string, ctrRootPath string, state *dispatchState) (idtools.Identity, error) {
|
||||
func getAccountIdentity(ctx context.Context, builder *Builder, accountName string, ctrRootPath string, state *dispatchState) (idtools.Identity, error) {
|
||||
// If this is potentially a string SID then attempt to convert it to verify
|
||||
// this, otherwise continue looking for the account.
|
||||
if strings.HasPrefix(accountName, "S-") || strings.HasPrefix(accountName, "s-") {
|
||||
@@ -51,10 +52,10 @@ func getAccountIdentity(builder *Builder, accountName string, ctrRootPath string
|
||||
|
||||
// All other lookups failed, so therefore determine if the account in
|
||||
// question exists in the container and if so, obtain its SID.
|
||||
return lookupNTAccount(builder, accountName, state)
|
||||
return lookupNTAccount(ctx, builder, accountName, state)
|
||||
}
|
||||
|
||||
func lookupNTAccount(builder *Builder, accountName string, state *dispatchState) (idtools.Identity, error) {
|
||||
func lookupNTAccount(ctx context.Context, builder *Builder, accountName string, state *dispatchState) (idtools.Identity, error) {
|
||||
|
||||
source, _ := filepath.Split(os.Args[0])
|
||||
|
||||
@@ -81,7 +82,7 @@ func lookupNTAccount(builder *Builder, accountName string, state *dispatchState)
|
||||
},
|
||||
}
|
||||
|
||||
container, err := builder.containerManager.Create(runConfig, hostConfig)
|
||||
container, err := builder.containerManager.Create(ctx, runConfig, hostConfig)
|
||||
if err != nil {
|
||||
return idtools.Identity{}, err
|
||||
}
|
||||
@@ -89,7 +90,7 @@ func lookupNTAccount(builder *Builder, accountName string, state *dispatchState)
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
|
||||
if err := builder.containerManager.Run(builder.clientCtx, container.ID, stdout, stderr); err != nil {
|
||||
if err := builder.containerManager.Run(ctx, container.ID, stdout, stderr); err != nil {
|
||||
if err, ok := err.(*statusCodeError); ok {
|
||||
return idtools.Identity{}, &jsonmessage.JSONError{
|
||||
Message: stderr.String(),
|
||||
|
||||
+11
-11
@@ -9,18 +9,18 @@ import (
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/longpath"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type archiveContext struct {
|
||||
root containerfs.ContainerFS
|
||||
root string
|
||||
sums tarsum.FileInfoSums
|
||||
}
|
||||
|
||||
func (c *archiveContext) Close() error {
|
||||
return c.root.RemoveAll(c.root.Path())
|
||||
return os.RemoveAll(c.root)
|
||||
}
|
||||
|
||||
func convertPathError(err error, cleanpath string) error {
|
||||
@@ -47,13 +47,13 @@ type modifiableContext interface {
|
||||
//
|
||||
// Closing tarStream has to be done by the caller.
|
||||
func FromArchive(tarStream io.Reader) (builder.Source, error) {
|
||||
root, err := ioutils.TempDir("", "docker-builder")
|
||||
root, err := longpath.MkdirTemp("", "docker-builder")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Assume local file system. Since it's coming from a tar file.
|
||||
tsc := &archiveContext{root: containerfs.NewLocalContainerFS(root)}
|
||||
tsc := &archiveContext{root: root}
|
||||
|
||||
// Make sure we clean-up upon error. In the happy case the caller
|
||||
// is expected to manage the clean-up
|
||||
@@ -82,7 +82,7 @@ func FromArchive(tarStream io.Reader) (builder.Source, error) {
|
||||
return tsc, nil
|
||||
}
|
||||
|
||||
func (c *archiveContext) Root() containerfs.ContainerFS {
|
||||
func (c *archiveContext) Root() string {
|
||||
return c.root
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ func (c *archiveContext) Remove(path string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.root.RemoveAll(fullpath)
|
||||
return os.RemoveAll(fullpath)
|
||||
}
|
||||
|
||||
func (c *archiveContext) Hash(path string) (string, error) {
|
||||
@@ -100,7 +100,7 @@ func (c *archiveContext) Hash(path string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rel, err := c.root.Rel(c.root.Path(), fullpath)
|
||||
rel, err := filepath.Rel(c.root, fullpath)
|
||||
if err != nil {
|
||||
return "", convertPathError(err, cleanpath)
|
||||
}
|
||||
@@ -115,9 +115,9 @@ func (c *archiveContext) Hash(path string) (string, error) {
|
||||
return path, nil // backwards compat TODO: see if really needed
|
||||
}
|
||||
|
||||
func normalize(path string, root containerfs.ContainerFS) (cleanPath, fullPath string, err error) {
|
||||
cleanPath = root.Clean(string(root.Separator()) + path)[1:]
|
||||
fullPath, err = root.ResolveScopedPath(path, true)
|
||||
func normalize(path string, root string) (cleanPath, fullPath string, err error) {
|
||||
cleanPath = filepath.Clean(string(filepath.Separator) + path)[1:]
|
||||
fullPath, err = containerfs.ResolveScopedPath(root, path)
|
||||
if err != nil {
|
||||
return "", "", errors.Wrapf(err, "forbidden path outside the build context: %s (%s)", path, cleanPath)
|
||||
}
|
||||
|
||||
+7
-6
@@ -13,9 +13,10 @@ import (
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/remotecontext/urlutil"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
||||
"github.com/moby/patternmatcher"
|
||||
"github.com/moby/patternmatcher/ignorefile"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -122,10 +123,10 @@ func removeDockerfile(c modifiableContext, filesToRemove ...string) error {
|
||||
case err != nil:
|
||||
return err
|
||||
}
|
||||
excludes, err := dockerignore.ReadAll(f)
|
||||
excludes, err := ignorefile.ReadAll(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
return errors.Wrap(err, "error reading .dockerignore")
|
||||
}
|
||||
f.Close()
|
||||
filesToRemove = append([]string{".dockerignore"}, filesToRemove...)
|
||||
@@ -161,7 +162,7 @@ func openAt(remote builder.Source, path string) (driver.File, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return remote.Root().Open(fullPath)
|
||||
return os.Open(fullPath)
|
||||
}
|
||||
|
||||
// StatAt is a helper for calling Stat on a path from a source
|
||||
@@ -170,12 +171,12 @@ func StatAt(remote builder.Source, path string) (os.FileInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return remote.Root().Stat(fullPath)
|
||||
return os.Stat(fullPath)
|
||||
}
|
||||
|
||||
// FullPath is a helper for getting a full path for a path from a source
|
||||
func FullPath(remote builder.Source, path string) (string, error) {
|
||||
fullPath, err := remote.Root().ResolveScopedPath(path, true)
|
||||
fullPath, err := containerfs.ResolveScopedPath(remote.Root(), path)
|
||||
if err != nil {
|
||||
if runtime.GOOS == "windows" {
|
||||
return "", fmt.Errorf("failed to resolve scoped path %s (%s): %s. Possible cause is a forbidden path outside the build context", path, fullPath, err)
|
||||
|
||||
+7
-16
@@ -4,12 +4,12 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/sys/symlink"
|
||||
"github.com/pkg/errors"
|
||||
exec "golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
type gitRepo struct {
|
||||
@@ -97,15 +97,10 @@ func parseRemoteURL(remoteURL string) (gitRepo, error) {
|
||||
remoteURL = "https://" + remoteURL
|
||||
}
|
||||
|
||||
var fragment string
|
||||
if strings.HasPrefix(remoteURL, "git@") {
|
||||
// git@.. is not an URL, so cannot be parsed as URL
|
||||
parts := strings.SplitN(remoteURL, "#", 2)
|
||||
|
||||
repo.remote = parts[0]
|
||||
if len(parts) == 2 {
|
||||
fragment = parts[1]
|
||||
}
|
||||
var fragment string
|
||||
repo.remote, fragment, _ = strings.Cut(remoteURL, "#")
|
||||
repo.ref, repo.subdir = getRefAndSubdir(fragment)
|
||||
} else {
|
||||
u, err := url.Parse(remoteURL)
|
||||
@@ -126,15 +121,11 @@ func parseRemoteURL(remoteURL string) (gitRepo, error) {
|
||||
}
|
||||
|
||||
func getRefAndSubdir(fragment string) (ref string, subdir string) {
|
||||
refAndDir := strings.SplitN(fragment, ":", 2)
|
||||
ref = "master"
|
||||
if len(refAndDir[0]) != 0 {
|
||||
ref = refAndDir[0]
|
||||
ref, subdir, _ = strings.Cut(fragment, ":")
|
||||
if ref == "" {
|
||||
ref = "master"
|
||||
}
|
||||
if len(refAndDir) > 1 && len(refAndDir[1]) != 0 {
|
||||
subdir = refAndDir[1]
|
||||
}
|
||||
return
|
||||
return ref, subdir
|
||||
}
|
||||
|
||||
func fetchArgs(remoteURL string, ref string) []string {
|
||||
|
||||
+12
-11
@@ -3,10 +3,11 @@ package remotecontext // import "github.com/docker/docker/builder/remotecontext"
|
||||
import (
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/pools"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -14,7 +15,7 @@ import (
|
||||
// NewLazySource creates a new LazyContext. LazyContext defines a hashed build
|
||||
// context based on a root directory. Individual files are hashed first time
|
||||
// they are asked. It is not safe to call methods of LazyContext concurrently.
|
||||
func NewLazySource(root containerfs.ContainerFS) (builder.Source, error) {
|
||||
func NewLazySource(root string) (builder.Source, error) {
|
||||
return &lazySource{
|
||||
root: root,
|
||||
sums: make(map[string]string),
|
||||
@@ -22,11 +23,11 @@ func NewLazySource(root containerfs.ContainerFS) (builder.Source, error) {
|
||||
}
|
||||
|
||||
type lazySource struct {
|
||||
root containerfs.ContainerFS
|
||||
root string
|
||||
sums map[string]string
|
||||
}
|
||||
|
||||
func (c *lazySource) Root() containerfs.ContainerFS {
|
||||
func (c *lazySource) Root() string {
|
||||
return c.root
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ func (c *lazySource) Hash(path string) (string, error) {
|
||||
return "", errors.WithStack(convertPathError(err, cleanPath))
|
||||
}
|
||||
|
||||
fi, err := c.root.Lstat(fullPath)
|
||||
fi, err := os.Lstat(fullPath)
|
||||
if err != nil {
|
||||
// Backwards compatibility: a missing file returns a path as hash.
|
||||
// This is reached in the case of a broken symlink.
|
||||
@@ -64,13 +65,13 @@ func (c *lazySource) Hash(path string) (string, error) {
|
||||
}
|
||||
|
||||
func (c *lazySource) prepareHash(relPath string, fi os.FileInfo) (string, error) {
|
||||
p := c.root.Join(c.root.Path(), relPath)
|
||||
p := filepath.Join(c.root, relPath)
|
||||
h, err := NewFileHash(p, relPath, fi)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to create hash for %s", relPath)
|
||||
}
|
||||
if fi.Mode().IsRegular() && fi.Size() > 0 {
|
||||
f, err := c.root.Open(p)
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to open %s", relPath)
|
||||
}
|
||||
@@ -86,10 +87,10 @@ func (c *lazySource) prepareHash(relPath string, fi os.FileInfo) (string, error)
|
||||
|
||||
// Rel makes a path relative to base path. Same as `filepath.Rel` but can also
|
||||
// handle UUID paths in windows.
|
||||
func Rel(basepath containerfs.ContainerFS, targpath string) (string, error) {
|
||||
func Rel(basepath string, targpath string) (string, error) {
|
||||
// filepath.Rel can't handle UUID paths in windows
|
||||
if basepath.OS() == "windows" {
|
||||
pfx := basepath.Path() + `\`
|
||||
if runtime.GOOS == "windows" {
|
||||
pfx := basepath + `\`
|
||||
if strings.HasPrefix(targpath, pfx) {
|
||||
p := strings.TrimPrefix(targpath, pfx)
|
||||
if p == "" {
|
||||
@@ -98,5 +99,5 @@ func Rel(basepath containerfs.ContainerFS, targpath string) (string, error) {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return basepath.Rel(basepath.Path(), targpath)
|
||||
return filepath.Rel(basepath, targpath)
|
||||
}
|
||||
|
||||
+11
-7
@@ -2,9 +2,9 @@ package remotecontext // import "github.com/docker/docker/builder/remotecontext"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
iradix "github.com/hashicorp/go-immutable-radix"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
@@ -15,19 +15,23 @@ type hashed interface {
|
||||
Digest() digest.Digest
|
||||
}
|
||||
|
||||
// CachableSource is a source that contains cache records for its contents
|
||||
// CachableSource is a source that contains cache records for its contents.
|
||||
//
|
||||
// Deprecated: this type was used for the experimental "stream" support for the classic builder, which is no longer supported.
|
||||
type CachableSource struct {
|
||||
mu sync.Mutex
|
||||
root containerfs.ContainerFS
|
||||
root string
|
||||
tree *iradix.Tree
|
||||
txn *iradix.Txn
|
||||
}
|
||||
|
||||
// NewCachableSource creates new CachableSource
|
||||
// NewCachableSource creates new CachableSource.
|
||||
//
|
||||
// Deprecated: this type was used for the experimental "stream" support for the classic builder, which is no longer supported.
|
||||
func NewCachableSource(root string) *CachableSource {
|
||||
ts := &CachableSource{
|
||||
tree: iradix.New(),
|
||||
root: containerfs.NewLocalContainerFS(root),
|
||||
root: root,
|
||||
}
|
||||
return ts
|
||||
}
|
||||
@@ -66,7 +70,7 @@ func (cs *CachableSource) Scan() error {
|
||||
return err
|
||||
}
|
||||
txn := iradix.New().Txn()
|
||||
err = cs.root.Walk(cs.root.Path(), func(path string, info os.FileInfo, err error) error {
|
||||
err = filepath.WalkDir(cs.root, func(path string, _ os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to walk %s", path)
|
||||
}
|
||||
@@ -144,7 +148,7 @@ func (cs *CachableSource) Hash(path string) (string, error) {
|
||||
}
|
||||
|
||||
// Root returns a root directory for the source
|
||||
func (cs *CachableSource) Root() containerfs.ContainerFS {
|
||||
func (cs *CachableSource) Root() string {
|
||||
return cs.root
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -3,8 +3,8 @@ package client // import "github.com/docker/docker/client"
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
@@ -23,12 +23,12 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePru
|
||||
if opts.All {
|
||||
query.Set("all", "1")
|
||||
}
|
||||
query.Set("keep-storage", fmt.Sprintf("%d", opts.KeepStorage))
|
||||
filters, err := filters.ToJSON(opts.Filters)
|
||||
query.Set("keep-storage", strconv.Itoa(int(opts.KeepStorage)))
|
||||
f, err := filters.ToJSON(opts.Filters)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "prune could not marshal filters option")
|
||||
}
|
||||
query.Set("filters", filters)
|
||||
query.Set("filters", f)
|
||||
|
||||
serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
@@ -38,7 +38,7 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePru
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
|
||||
return nil, fmt.Errorf("Error retrieving disk usage: %v", err)
|
||||
return nil, errors.Wrap(err, "error retrieving disk usage")
|
||||
}
|
||||
|
||||
return &report, nil
|
||||
|
||||
+43
-10
@@ -56,6 +56,36 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// DummyHost is a hostname used for local communication.
|
||||
//
|
||||
// It acts as a valid formatted hostname for local connections (such as "unix://"
|
||||
// or "npipe://") which do not require a hostname. It should never be resolved,
|
||||
// but uses the special-purpose ".localhost" TLD (as defined in [RFC 2606, Section 2]
|
||||
// and [RFC 6761, Section 6.3]).
|
||||
//
|
||||
// [RFC 7230, Section 5.4] defines that an empty header must be used for such
|
||||
// cases:
|
||||
//
|
||||
// If the authority component is missing or undefined for the target URI,
|
||||
// then a client MUST send a Host header field with an empty field-value.
|
||||
//
|
||||
// However, [Go stdlib] enforces the semantics of HTTP(S) over TCP, does not
|
||||
// allow an empty header to be used, and requires req.URL.Scheme to be either
|
||||
// "http" or "https".
|
||||
//
|
||||
// For further details, refer to:
|
||||
//
|
||||
// - https://github.com/docker/engine-api/issues/189
|
||||
// - https://github.com/golang/go/issues/13624
|
||||
// - https://github.com/golang/go/issues/61076
|
||||
// - https://github.com/moby/moby/issues/45935
|
||||
//
|
||||
// [RFC 2606, Section 2]: https://www.rfc-editor.org/rfc/rfc2606.html#section-2
|
||||
// [RFC 6761, Section 6.3]: https://www.rfc-editor.org/rfc/rfc6761#section-6.3
|
||||
// [RFC 7230, Section 5.4]: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4
|
||||
// [Go stdlib]: https://github.com/golang/go/blob/6244b1946bc2101b01955468f1be502dbadd6807/src/net/http/transport.go#L558-L569
|
||||
const DummyHost = "api.moby.localhost"
|
||||
|
||||
// ErrRedirect is the error returned by checkRedirect when the request is non-GET.
|
||||
var ErrRedirect = errors.New("unexpected redirect in response")
|
||||
|
||||
@@ -126,7 +156,12 @@ func CheckRedirect(req *http.Request, via []*http.Request) error {
|
||||
// client.WithAPIVersionNegotiation(),
|
||||
// )
|
||||
func NewClientWithOpts(ops ...Opt) (*Client, error) {
|
||||
client, err := defaultHTTPClient(DefaultDockerHost)
|
||||
hostURL, err := ParseHostURL(DefaultDockerHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := defaultHTTPClient(hostURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -134,8 +169,8 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
|
||||
host: DefaultDockerHost,
|
||||
version: api.DefaultVersion,
|
||||
client: client,
|
||||
proto: defaultProto,
|
||||
addr: defaultAddr,
|
||||
proto: hostURL.Scheme,
|
||||
addr: hostURL.Host,
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
@@ -161,13 +196,12 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func defaultHTTPClient(host string) (*http.Client, error) {
|
||||
hostURL, err := ParseHostURL(host)
|
||||
func defaultHTTPClient(hostURL *url.URL) (*http.Client, error) {
|
||||
transport := &http.Transport{}
|
||||
err := sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport := &http.Transport{}
|
||||
_ = sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: CheckRedirect,
|
||||
@@ -283,13 +317,12 @@ func (cli *Client) HTTPClient() *http.Client {
|
||||
// ParseHostURL parses a url string, validates the string is a host url, and
|
||||
// returns the parsed URL
|
||||
func ParseHostURL(host string) (*url.URL, error) {
|
||||
protoAddrParts := strings.SplitN(host, "://", 2)
|
||||
if len(protoAddrParts) == 1 {
|
||||
proto, addr, ok := strings.Cut(host, "://")
|
||||
if !ok || addr == "" {
|
||||
return nil, errors.Errorf("unable to parse docker host `%s`", host)
|
||||
}
|
||||
|
||||
var basePath string
|
||||
proto, addr := protoAddrParts[0], protoAddrParts[1]
|
||||
if proto == "tcp" {
|
||||
parsed, err := url.Parse("tcp://" + addr)
|
||||
if err != nil {
|
||||
|
||||
+2
-5
@@ -1,11 +1,8 @@
|
||||
//go:build linux || freebsd || openbsd || netbsd || darwin || solaris || illumos || dragonfly
|
||||
// +build linux freebsd openbsd netbsd darwin solaris illumos dragonfly
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package client // import "github.com/docker/docker/client"
|
||||
|
||||
// DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
|
||||
// (EnvOverrideHost) environment variable is unset or empty.
|
||||
const DefaultDockerHost = "unix:///var/run/docker.sock"
|
||||
|
||||
const defaultProto = "unix"
|
||||
const defaultAddr = "/var/run/docker.sock"
|
||||
|
||||
-3
@@ -3,6 +3,3 @@ package client // import "github.com/docker/docker/client"
|
||||
// DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
|
||||
// (EnvOverrideHost) environment variable is unset or empty.
|
||||
const DefaultDockerHost = "npipe:////./pipe/docker_engine"
|
||||
|
||||
const defaultProto = "npipe"
|
||||
const defaultAddr = "//./pipe/docker_engine"
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type configWrapper struct {
|
||||
@@ -20,7 +20,7 @@ type configWrapper struct {
|
||||
|
||||
// ContainerCreate creates a new container based on the given configuration.
|
||||
// It can be associated with a name, but it's not mandatory.
|
||||
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.CreateResponse, error) {
|
||||
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
|
||||
var response container.CreateResponse
|
||||
|
||||
if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil {
|
||||
@@ -75,7 +75,7 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
|
||||
// Similar to containerd's platforms.Format(), but does allow components to be
|
||||
// omitted (e.g. pass "architecture" only, without "os":
|
||||
// https://github.com/containerd/containerd/blob/v1.5.2/platforms/platforms.go#L243-L263
|
||||
func formatPlatform(platform *specs.Platform) string {
|
||||
func formatPlatform(platform *ocispec.Platform) string {
|
||||
if platform == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
// ContainerDiff shows differences in a container filesystem since it was started.
|
||||
func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.ContainerChangeResponseItem, error) {
|
||||
var changes []container.ContainerChangeResponseItem
|
||||
func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
|
||||
var changes []container.FilesystemChange
|
||||
|
||||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
|
||||
+4
-4
@@ -5,13 +5,13 @@ import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// DistributionInspect returns the image digest with the full manifest.
|
||||
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) {
|
||||
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registry.DistributionInspect, error) {
|
||||
// Contact the registry to retrieve digest and platform information
|
||||
var distributionInspect registrytypes.DistributionInspect
|
||||
var distributionInspect registry.DistributionInspect
|
||||
if image == "" {
|
||||
return distributionInspect, objectNotFoundError{object: "distribution", id: image}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegist
|
||||
|
||||
if encodedRegistryAuth != "" {
|
||||
headers = map[string][]string{
|
||||
"X-Registry-Auth": {encodedRegistryAuth},
|
||||
registry.AuthHeader: {encodedRegistryAuth},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-25
@@ -58,31 +58,6 @@ func (e objectNotFoundError) Error() string {
|
||||
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
|
||||
}
|
||||
|
||||
// IsErrUnauthorized returns true if the error is caused
|
||||
// when a remote registry authentication fails
|
||||
//
|
||||
// Deprecated: use errdefs.IsUnauthorized
|
||||
func IsErrUnauthorized(err error) bool {
|
||||
return errdefs.IsUnauthorized(err)
|
||||
}
|
||||
|
||||
type pluginPermissionDenied struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (e pluginPermissionDenied) Error() string {
|
||||
return "Permission denied while installing plugin " + e.name
|
||||
}
|
||||
|
||||
// IsErrNotImplemented returns true if the error is a NotImplemented error.
|
||||
// This is returned by the API when a requested feature has not been
|
||||
// implemented.
|
||||
//
|
||||
// Deprecated: use errdefs.IsNotImplemented
|
||||
func IsErrNotImplemented(err error) bool {
|
||||
return errdefs.IsNotImplemented(err)
|
||||
}
|
||||
|
||||
// NewVersionError returns an error if the APIVersion required
|
||||
// if less than the current supported version
|
||||
func (cli *Client) NewVersionError(APIrequired, feature string) error {
|
||||
|
||||
+4
-9
@@ -23,14 +23,10 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
|
||||
if err != nil {
|
||||
return types.HijackedResponse{}, err
|
||||
}
|
||||
|
||||
apiPath := cli.getAPIPath(ctx, path, query)
|
||||
req, err := http.NewRequest(http.MethodPost, apiPath, bodyEncoded)
|
||||
req, err := cli.buildRequest(http.MethodPost, cli.getAPIPath(ctx, path, query), bodyEncoded, headers)
|
||||
if err != nil {
|
||||
return types.HijackedResponse{}, err
|
||||
}
|
||||
req = cli.addHeaders(req, headers)
|
||||
|
||||
conn, mediaType, err := cli.setupHijackConn(ctx, req, "tcp")
|
||||
if err != nil {
|
||||
return types.HijackedResponse{}, err
|
||||
@@ -64,7 +60,6 @@ func fallbackDial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, string, error) {
|
||||
req.Host = cli.addr
|
||||
req.Header.Set("Connection", "Upgrade")
|
||||
req.Header.Set("Upgrade", proto)
|
||||
|
||||
@@ -80,8 +75,8 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
|
||||
// state. Setting TCP KeepAlive on the socket connection will prohibit
|
||||
// ECONNTIMEOUT unless the socket connection truly is broken
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.SetKeepAlive(true)
|
||||
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||
_ = tcpConn.SetKeepAlive(true)
|
||||
_ = tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||
}
|
||||
|
||||
clientconn := httputil.NewClientConn(conn, nil)
|
||||
@@ -96,7 +91,7 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
|
||||
return nil, "", err
|
||||
}
|
||||
if resp.StatusCode != http.StatusSwitchingProtocols {
|
||||
resp.Body.Close()
|
||||
_ = resp.Body.Close()
|
||||
return nil, "", fmt.Errorf("unable to upgrade to %s, received %d", proto, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// ImageCreate creates a new image based on the parent options.
|
||||
@@ -32,6 +33,6 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
|
||||
}
|
||||
|
||||
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.post(ctx, "/images/create", query, nil, headers)
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/errdefs"
|
||||
)
|
||||
|
||||
@@ -49,6 +50,6 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options types.Im
|
||||
}
|
||||
|
||||
func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.post(ctx, "/images/"+imageID+"/push", query, nil, headers)
|
||||
}
|
||||
|
||||
+1
-1
@@ -48,6 +48,6 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options types.I
|
||||
}
|
||||
|
||||
func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.get(ctx, "/images/search", query, headers)
|
||||
}
|
||||
|
||||
+5
-5
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// CommonAPIClient is the common methods between stable and experimental versions of APIClient.
|
||||
@@ -47,8 +47,8 @@ type CommonAPIClient interface {
|
||||
type ContainerAPIClient interface {
|
||||
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
|
||||
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error)
|
||||
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.CreateResponse, error)
|
||||
ContainerDiff(ctx context.Context, container string) ([]container.ContainerChangeResponseItem, error)
|
||||
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error)
|
||||
ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
|
||||
ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error)
|
||||
ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error)
|
||||
ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error)
|
||||
@@ -166,7 +166,7 @@ type SwarmAPIClient interface {
|
||||
type SystemAPIClient interface {
|
||||
Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error)
|
||||
Info(ctx context.Context) (types.Info, error)
|
||||
RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error)
|
||||
RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error)
|
||||
DiskUsage(ctx context.Context, options types.DiskUsageOptions) (types.DiskUsage, error)
|
||||
Ping(ctx context.Context) (types.Ping, error)
|
||||
}
|
||||
@@ -176,7 +176,7 @@ type VolumeAPIClient interface {
|
||||
VolumeCreate(ctx context.Context, options volume.CreateOptions) (volume.Volume, error)
|
||||
VolumeInspect(ctx context.Context, volumeID string) (volume.Volume, error)
|
||||
VolumeInspectWithRaw(ctx context.Context, volumeID string) (volume.Volume, []byte, error)
|
||||
VolumeList(ctx context.Context, filter filters.Args) (volume.ListResponse, error)
|
||||
VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error)
|
||||
VolumeRemove(ctx context.Context, volumeID string, force bool) error
|
||||
VolumesPrune(ctx context.Context, pruneFilter filters.Args) (types.VolumesPruneReport, error)
|
||||
VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options volume.UpdateOptions) error
|
||||
|
||||
+1
-2
@@ -5,13 +5,12 @@ import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// RegistryLogin authenticates the docker server with a given docker registry.
|
||||
// It returns unauthorizedError when the authentication fails.
|
||||
func (cli *Client) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) {
|
||||
func (cli *Client) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) {
|
||||
resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
|
||||
|
||||
+3
-3
@@ -64,10 +64,10 @@ func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
|
||||
ping.BuilderVersion = types.BuilderVersion(bv)
|
||||
}
|
||||
if si := resp.header.Get("Swarm"); si != "" {
|
||||
parts := strings.SplitN(si, "/", 2)
|
||||
state, role, _ := strings.Cut(si, "/")
|
||||
ping.SwarmStatus = &swarm.Status{
|
||||
NodeState: swarm.LocalNodeState(parts[0]),
|
||||
ControlAvailable: len(parts) == 2 && parts[1] == "manager",
|
||||
NodeState: swarm.LocalNodeState(state),
|
||||
ControlAvailable: role == "manager",
|
||||
}
|
||||
}
|
||||
err := cli.checkResponseErr(resp)
|
||||
|
||||
+4
-3
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -67,12 +68,12 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
|
||||
}
|
||||
|
||||
func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.get(ctx, "/plugins/privileges", query, headers)
|
||||
}
|
||||
|
||||
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges types.PluginPrivileges, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.post(ctx, "/plugins/pull", query, privileges, headers)
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values,
|
||||
return nil, err
|
||||
}
|
||||
if !accept {
|
||||
return nil, pluginPermissionDenied{options.RemoteRef}
|
||||
return nil, errors.Errorf("permission denied while installing plugin %s", options.RemoteRef)
|
||||
}
|
||||
}
|
||||
return privileges, nil
|
||||
|
||||
+3
-1
@@ -3,11 +3,13 @@ package client // import "github.com/docker/docker/client"
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// PluginPush pushes a plugin to a registry
|
||||
func (cli *Client) PluginPush(ctx context.Context, name string, registryAuth string) (io.ReadCloser, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, nil, headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -34,6 +35,6 @@ func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types
|
||||
}
|
||||
|
||||
func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges types.PluginPrivileges, name, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.post(ctx, "/plugins/"+name+"/upgrade", query, privileges, headers)
|
||||
}
|
||||
|
||||
+4
-6
@@ -96,16 +96,14 @@ func (cli *Client) buildRequest(method, path string, body io.Reader, headers hea
|
||||
return nil, err
|
||||
}
|
||||
req = cli.addHeaders(req, headers)
|
||||
req.URL.Scheme = cli.scheme
|
||||
req.URL.Host = cli.addr
|
||||
|
||||
if cli.proto == "unix" || cli.proto == "npipe" {
|
||||
// For local communications, it doesn't matter what the host is. We just
|
||||
// need a valid and meaningful host name. (See #189)
|
||||
req.Host = "docker"
|
||||
// Override host header for non-tcp connections.
|
||||
req.Host = DummyHost
|
||||
}
|
||||
|
||||
req.URL.Host = cli.addr
|
||||
req.URL.Scheme = cli.scheme
|
||||
|
||||
if expectedPayload && req.Header.Get("Content-Type") == "" {
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
@@ -21,7 +22,7 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
|
||||
}
|
||||
|
||||
if options.EncodedRegistryAuth != "" {
|
||||
headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
|
||||
headers[registry.AuthHeader] = []string{options.EncodedRegistryAuth}
|
||||
}
|
||||
|
||||
// Make sure containerSpec is not nil when no runtime is set or the runtime is set to container
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
@@ -23,7 +24,7 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
||||
}
|
||||
|
||||
if options.EncodedRegistryAuth != "" {
|
||||
headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
|
||||
headers[registry.AuthHeader] = []string{options.EncodedRegistryAuth}
|
||||
}
|
||||
|
||||
if options.RegistryAuthFrom != "" {
|
||||
|
||||
+3
-3
@@ -10,13 +10,13 @@ import (
|
||||
)
|
||||
|
||||
// VolumeList returns the volumes configured in the docker host.
|
||||
func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (volume.ListResponse, error) {
|
||||
func (cli *Client) VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) {
|
||||
var volumes volume.ListResponse
|
||||
query := url.Values{}
|
||||
|
||||
if filter.Len() > 0 {
|
||||
if options.Filters.Len() > 0 {
|
||||
//nolint:staticcheck // ignore SA1019 for old code
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, filter)
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||
if err != nil {
|
||||
return volumes, err
|
||||
}
|
||||
|
||||
Generated
Vendored
+13
-17
@@ -2,10 +2,10 @@ package container // import "github.com/docker/docker/container"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -14,24 +14,21 @@ import (
|
||||
// the absolute path to the resource relative to the container's rootfs, and
|
||||
// an error if the path points to outside the container's rootfs.
|
||||
func (container *Container) ResolvePath(path string) (resolvedPath, absPath string, err error) {
|
||||
if container.BaseFS == nil {
|
||||
return "", "", errors.New("ResolvePath: BaseFS of container " + container.ID + " is unexpectedly nil")
|
||||
if container.BaseFS == "" {
|
||||
return "", "", errors.New("ResolvePath: BaseFS of container " + container.ID + " is unexpectedly empty")
|
||||
}
|
||||
// Check if a drive letter supplied, it must be the system drive. No-op except on Windows
|
||||
path, err = system.CheckSystemDriveAndRemoveDriveLetter(path, container.BaseFS)
|
||||
path, err = archive.CheckSystemDriveAndRemoveDriveLetter(path)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Consider the given path as an absolute path in the container.
|
||||
absPath = archive.PreserveTrailingDotOrSeparator(
|
||||
container.BaseFS.Join(string(container.BaseFS.Separator()), path),
|
||||
path,
|
||||
container.BaseFS.Separator())
|
||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join(string(filepath.Separator), path), path)
|
||||
|
||||
// Split the absPath into its Directory and Base components. We will
|
||||
// resolve the dir in the scope of the container then append the base.
|
||||
dirPath, basePath := container.BaseFS.Split(absPath)
|
||||
dirPath, basePath := filepath.Split(absPath)
|
||||
|
||||
resolvedDirPath, err := container.GetResourcePath(dirPath)
|
||||
if err != nil {
|
||||
@@ -40,7 +37,7 @@ func (container *Container) ResolvePath(path string) (resolvedPath, absPath stri
|
||||
|
||||
// resolvedDirPath will have been cleaned (no trailing path separators) so
|
||||
// we can manually join it with the base path element.
|
||||
resolvedPath = resolvedDirPath + string(container.BaseFS.Separator()) + basePath
|
||||
resolvedPath = resolvedDirPath + string(filepath.Separator) + basePath
|
||||
return resolvedPath, absPath, nil
|
||||
}
|
||||
|
||||
@@ -49,12 +46,11 @@ func (container *Container) ResolvePath(path string) (resolvedPath, absPath stri
|
||||
// resolved to a path on the host corresponding to the given absolute path
|
||||
// inside the container.
|
||||
func (container *Container) StatPath(resolvedPath, absPath string) (stat *types.ContainerPathStat, err error) {
|
||||
if container.BaseFS == nil {
|
||||
return nil, errors.New("StatPath: BaseFS of container " + container.ID + " is unexpectedly nil")
|
||||
if container.BaseFS == "" {
|
||||
return nil, errors.New("StatPath: BaseFS of container " + container.ID + " is unexpectedly empty")
|
||||
}
|
||||
driver := container.BaseFS
|
||||
|
||||
lstat, err := driver.Lstat(resolvedPath)
|
||||
lstat, err := os.Lstat(resolvedPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -67,17 +63,17 @@ func (container *Container) StatPath(resolvedPath, absPath string) (stat *types.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
linkTarget, err = driver.Rel(driver.Path(), hostPath)
|
||||
linkTarget, err = filepath.Rel(container.BaseFS, hostPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make it an absolute path.
|
||||
linkTarget = driver.Join(string(driver.Separator()), linkTarget)
|
||||
linkTarget = filepath.Join(string(filepath.Separator), linkTarget)
|
||||
}
|
||||
|
||||
return &types.ContainerPathStat{
|
||||
Name: driver.Base(absPath),
|
||||
Name: filepath.Base(absPath),
|
||||
Size: lstat.Size(),
|
||||
Mode: lstat.Mode(),
|
||||
Mtime: lstat.ModTime(),
|
||||
+75
-36
@@ -19,7 +19,6 @@ import (
|
||||
mounttypes "github.com/docker/docker/api/types/mount"
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/container/stream"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
||||
"github.com/docker/docker/daemon/logger/local"
|
||||
@@ -28,10 +27,11 @@ import (
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
|
||||
"github.com/docker/docker/oci"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/restartmanager"
|
||||
"github.com/docker/docker/volume"
|
||||
volumemounts "github.com/docker/docker/volume/mounts"
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
agentexec "github.com/moby/swarmkit/v2/agent/exec"
|
||||
"github.com/moby/sys/signal"
|
||||
"github.com/moby/sys/symlink"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -53,9 +54,6 @@ type ExitStatus struct {
|
||||
// The exit code with which the container exited.
|
||||
ExitCode int
|
||||
|
||||
// Whether the container encountered an OOM.
|
||||
OOMKilled bool
|
||||
|
||||
// Time at which the container died
|
||||
ExitedAt time.Time
|
||||
}
|
||||
@@ -64,10 +62,10 @@ type ExitStatus struct {
|
||||
type Container struct {
|
||||
StreamConfig *stream.Config
|
||||
// embed for Container to support states directly.
|
||||
*State `json:"State"` // Needed for Engine API version <= 1.11
|
||||
Root string `json:"-"` // Path to the "home" of the container, including metadata.
|
||||
BaseFS containerfs.ContainerFS `json:"-"` // interface containing graphdriver mount
|
||||
RWLayer layer.RWLayer `json:"-"`
|
||||
*State `json:"State"` // Needed for Engine API version <= 1.11
|
||||
Root string `json:"-"` // Path to the "home" of the container, including metadata.
|
||||
BaseFS string `json:"-"` // Path to the graphdriver mountpoint
|
||||
RWLayer layer.RWLayer `json:"-"`
|
||||
ID string
|
||||
Created time.Time
|
||||
Managed bool
|
||||
@@ -75,38 +73,35 @@ type Container struct {
|
||||
Args []string
|
||||
Config *containertypes.Config
|
||||
ImageID image.ID `json:"Image"`
|
||||
ImageManifest *ocispec.Descriptor
|
||||
NetworkSettings *network.Settings
|
||||
LogPath string
|
||||
Name string
|
||||
Driver string
|
||||
OS string
|
||||
// MountLabel contains the options for the 'mount' command
|
||||
MountLabel string
|
||||
ProcessLabel string
|
||||
|
||||
RestartCount int
|
||||
HasBeenStartedBefore bool
|
||||
HasBeenManuallyStopped bool // used for unless-stopped restart policy
|
||||
HasBeenManuallyRestarted bool `json:"-"` // used to distinguish restart caused by restart policy from the manual one
|
||||
MountPoints map[string]*volumemounts.MountPoint
|
||||
HostConfig *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
|
||||
ExecCommands *exec.Store `json:"-"`
|
||||
ExecCommands *ExecStore `json:"-"`
|
||||
DependencyStore agentexec.DependencyGetter `json:"-"`
|
||||
SecretReferences []*swarmtypes.SecretReference
|
||||
ConfigReferences []*swarmtypes.ConfigReference
|
||||
// logDriver for closing
|
||||
LogDriver logger.Logger `json:"-"`
|
||||
LogCopier *logger.Copier `json:"-"`
|
||||
restartManager restartmanager.RestartManager
|
||||
restartManager *restartmanager.RestartManager
|
||||
attachContext *attachContext
|
||||
|
||||
// Fields here are specific to Unix platforms
|
||||
AppArmorProfile string
|
||||
HostnamePath string
|
||||
HostsPath string
|
||||
ShmPath string
|
||||
ResolvConfPath string
|
||||
SeccompProfile string
|
||||
NoNewPrivileges bool
|
||||
SecurityOptions
|
||||
HostnamePath string
|
||||
HostsPath string
|
||||
ShmPath string
|
||||
ResolvConfPath string
|
||||
|
||||
// Fields here are specific to Windows
|
||||
NetworkSharedContainerID string `json:"-"`
|
||||
@@ -114,6 +109,15 @@ type Container struct {
|
||||
LocalLogCacheMeta localLogCacheMeta `json:",omitempty"`
|
||||
}
|
||||
|
||||
type SecurityOptions struct {
|
||||
// MountLabel contains the options for the "mount" command.
|
||||
MountLabel string
|
||||
ProcessLabel string
|
||||
AppArmorProfile string
|
||||
SeccompProfile string
|
||||
NoNewPrivileges bool
|
||||
}
|
||||
|
||||
type localLogCacheMeta struct {
|
||||
HaveNotifyEnabled bool
|
||||
}
|
||||
@@ -124,7 +128,7 @@ func NewBaseContainer(id, root string) *Container {
|
||||
return &Container{
|
||||
ID: id,
|
||||
State: NewState(),
|
||||
ExecCommands: exec.NewStore(),
|
||||
ExecCommands: NewExecStore(),
|
||||
Root: root,
|
||||
MountPoints: make(map[string]*volumemounts.MountPoint),
|
||||
StreamConfig: stream.NewConfig(),
|
||||
@@ -196,7 +200,7 @@ func (container *Container) toDisk() (*Container, error) {
|
||||
|
||||
// CheckpointTo makes the Container's current state visible to queries, and persists state.
|
||||
// Callers must hold a Container lock.
|
||||
func (container *Container) CheckpointTo(store ViewDB) error {
|
||||
func (container *Container) CheckpointTo(store *ViewDB) error {
|
||||
deepCopy, err := container.toDisk()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -302,18 +306,18 @@ func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity)
|
||||
// symlinking to a different path) between using this method and using the
|
||||
// path. See symlink.FollowSymlinkInScope for more details.
|
||||
func (container *Container) GetResourcePath(path string) (string, error) {
|
||||
if container.BaseFS == nil {
|
||||
return "", errors.New("GetResourcePath: BaseFS of container " + container.ID + " is unexpectedly nil")
|
||||
if container.BaseFS == "" {
|
||||
return "", errors.New("GetResourcePath: BaseFS of container " + container.ID + " is unexpectedly empty")
|
||||
}
|
||||
// IMPORTANT - These are paths on the OS where the daemon is running, hence
|
||||
// any filepath operations must be done in an OS agnostic way.
|
||||
r, e := container.BaseFS.ResolveScopedPath(path, false)
|
||||
r, e := containerfs.ResolveScopedPath(container.BaseFS, containerfs.CleanScopedPath(path))
|
||||
|
||||
// Log this here on the daemon side as there's otherwise no indication apart
|
||||
// from the error being propagated all the way back to the client. This makes
|
||||
// debugging significantly easier and clearly indicates the error comes from the daemon.
|
||||
if e != nil {
|
||||
logrus.Errorf("Failed to ResolveScopedPath BaseFS %s path %s %s\n", container.BaseFS.Path(), path, e)
|
||||
logrus.Errorf("Failed to ResolveScopedPath BaseFS %s path %s %s\n", container.BaseFS, path, e)
|
||||
}
|
||||
return r, e
|
||||
}
|
||||
@@ -560,13 +564,7 @@ func (container *Container) InitDNSHostConfig() {
|
||||
|
||||
// UpdateMonitor updates monitor configure for running container
|
||||
func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPolicy) {
|
||||
type policySetter interface {
|
||||
SetPolicy(containertypes.RestartPolicy)
|
||||
}
|
||||
|
||||
if rm, ok := container.RestartManager().(policySetter); ok {
|
||||
rm.SetPolicy(restartPolicy)
|
||||
}
|
||||
container.RestartManager().SetPolicy(restartPolicy)
|
||||
}
|
||||
|
||||
// FullHostname returns hostname and optional domain appended to it.
|
||||
@@ -579,7 +577,7 @@ func (container *Container) FullHostname() string {
|
||||
}
|
||||
|
||||
// RestartManager returns the current restartmanager instance connected to container.
|
||||
func (container *Container) RestartManager() restartmanager.RestartManager {
|
||||
func (container *Container) RestartManager() *restartmanager.RestartManager {
|
||||
if container.restartManager == nil {
|
||||
container.restartManager = restartmanager.New(container.HostConfig.RestartPolicy, container.RestartCount)
|
||||
}
|
||||
@@ -740,7 +738,7 @@ func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string
|
||||
|
||||
env := make([]string, 0, envSize)
|
||||
if runtime.GOOS != "windows" {
|
||||
env = append(env, "PATH="+system.DefaultPathEnv(ctrOS))
|
||||
env = append(env, "PATH="+oci.DefaultPathEnv(ctrOS))
|
||||
env = append(env, "HOSTNAME="+container.Config.Hostname)
|
||||
if tty {
|
||||
env = append(env, "TERM=xterm")
|
||||
@@ -755,6 +753,47 @@ func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string
|
||||
return env
|
||||
}
|
||||
|
||||
// RestoreTask restores the containerd container and task handles and reattaches
|
||||
// the IO for the running task. Container state is not synced with containerd's
|
||||
// state.
|
||||
//
|
||||
// An errdefs.NotFound error is returned if the container does not exist in
|
||||
// containerd. However, a nil error is returned if the task does not exist in
|
||||
// containerd.
|
||||
func (container *Container) RestoreTask(ctx context.Context, client libcontainerdtypes.Client) error {
|
||||
container.Lock()
|
||||
defer container.Unlock()
|
||||
var err error
|
||||
container.ctr, err = client.LoadContainer(ctx, container.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.task, err = container.ctr.AttachTask(ctx, container.InitializeStdio)
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRunningTask asserts that the container is running and returns the Task for
|
||||
// the container. An errdefs.Conflict error is returned if the container is not
|
||||
// in the Running state.
|
||||
//
|
||||
// A system error is returned if container is in a bad state: Running is true
|
||||
// but has a nil Task.
|
||||
//
|
||||
// The container lock must be held when calling this method.
|
||||
func (container *Container) GetRunningTask() (libcontainerdtypes.Task, error) {
|
||||
if !container.Running {
|
||||
return nil, errdefs.Conflict(fmt.Errorf("container %s is not running", container.ID))
|
||||
}
|
||||
tsk, ok := container.Task()
|
||||
if !ok {
|
||||
return nil, errdefs.System(errors.WithStack(fmt.Errorf("container %s is in Running state but has no containerd Task set", container.ID)))
|
||||
}
|
||||
return tsk, nil
|
||||
}
|
||||
|
||||
type rio struct {
|
||||
cio.IO
|
||||
|
||||
|
||||
Generated
Vendored
+37
-54
@@ -1,20 +1,20 @@
|
||||
package exec // import "github.com/docker/docker/daemon/exec"
|
||||
package container // import "github.com/docker/docker/container"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd/cio"
|
||||
"github.com/docker/docker/container/stream"
|
||||
"github.com/docker/docker/libcontainerd/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Config holds the configurations for execs. The Daemon keeps
|
||||
// ExecConfig holds the configurations for execs. The Daemon keeps
|
||||
// track of both running and finished execs so that they can be
|
||||
// examined both during and after completion.
|
||||
type Config struct {
|
||||
type ExecConfig struct {
|
||||
sync.Mutex
|
||||
Started chan struct{}
|
||||
StreamConfig *stream.Config
|
||||
@@ -25,7 +25,7 @@ type Config struct {
|
||||
OpenStderr bool
|
||||
OpenStdout bool
|
||||
CanRemove bool
|
||||
ContainerID string
|
||||
Container *Container
|
||||
DetachKeys []byte
|
||||
Entrypoint string
|
||||
Args []string
|
||||
@@ -34,39 +34,22 @@ type Config struct {
|
||||
User string
|
||||
WorkingDir string
|
||||
Env []string
|
||||
Pid int
|
||||
Process types.Process
|
||||
ConsoleSize *[2]uint
|
||||
}
|
||||
|
||||
// NewConfig initializes the a new exec configuration
|
||||
func NewConfig() *Config {
|
||||
return &Config{
|
||||
// NewExecConfig initializes the a new exec configuration
|
||||
func NewExecConfig(c *Container) *ExecConfig {
|
||||
return &ExecConfig{
|
||||
ID: stringid.GenerateRandomID(),
|
||||
Container: c,
|
||||
StreamConfig: stream.NewConfig(),
|
||||
Started: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
type rio struct {
|
||||
cio.IO
|
||||
|
||||
sc *stream.Config
|
||||
}
|
||||
|
||||
func (i *rio) Close() error {
|
||||
i.IO.Close()
|
||||
|
||||
return i.sc.CloseStreams()
|
||||
}
|
||||
|
||||
func (i *rio) Wait() {
|
||||
i.sc.Wait(context.Background())
|
||||
|
||||
i.IO.Wait()
|
||||
}
|
||||
|
||||
// InitializeStdio is called by libcontainerd to connect the stdio.
|
||||
func (c *Config) InitializeStdio(iop *cio.DirectIO) (cio.IO, error) {
|
||||
func (c *ExecConfig) InitializeStdio(iop *cio.DirectIO) (cio.IO, error) {
|
||||
c.StreamConfig.CopyToPipe(iop)
|
||||
|
||||
if c.StreamConfig.Stdin() == nil && !c.Tty && runtime.GOOS == "windows" {
|
||||
@@ -81,68 +64,68 @@ func (c *Config) InitializeStdio(iop *cio.DirectIO) (cio.IO, error) {
|
||||
}
|
||||
|
||||
// CloseStreams closes the stdio streams for the exec
|
||||
func (c *Config) CloseStreams() error {
|
||||
func (c *ExecConfig) CloseStreams() error {
|
||||
return c.StreamConfig.CloseStreams()
|
||||
}
|
||||
|
||||
// SetExitCode sets the exec config's exit code
|
||||
func (c *Config) SetExitCode(code int) {
|
||||
func (c *ExecConfig) SetExitCode(code int) {
|
||||
c.ExitCode = &code
|
||||
}
|
||||
|
||||
// Store keeps track of the exec configurations.
|
||||
type Store struct {
|
||||
byID map[string]*Config
|
||||
sync.RWMutex
|
||||
// ExecStore keeps track of the exec configurations.
|
||||
type ExecStore struct {
|
||||
byID map[string]*ExecConfig
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewStore initializes a new exec store.
|
||||
func NewStore() *Store {
|
||||
return &Store{
|
||||
byID: make(map[string]*Config),
|
||||
// NewExecStore initializes a new exec store.
|
||||
func NewExecStore() *ExecStore {
|
||||
return &ExecStore{
|
||||
byID: make(map[string]*ExecConfig),
|
||||
}
|
||||
}
|
||||
|
||||
// Commands returns the exec configurations in the store.
|
||||
func (e *Store) Commands() map[string]*Config {
|
||||
e.RLock()
|
||||
byID := make(map[string]*Config, len(e.byID))
|
||||
func (e *ExecStore) Commands() map[string]*ExecConfig {
|
||||
e.mu.RLock()
|
||||
byID := make(map[string]*ExecConfig, len(e.byID))
|
||||
for id, config := range e.byID {
|
||||
byID[id] = config
|
||||
}
|
||||
e.RUnlock()
|
||||
e.mu.RUnlock()
|
||||
return byID
|
||||
}
|
||||
|
||||
// Add adds a new exec configuration to the store.
|
||||
func (e *Store) Add(id string, Config *Config) {
|
||||
e.Lock()
|
||||
func (e *ExecStore) Add(id string, Config *ExecConfig) {
|
||||
e.mu.Lock()
|
||||
e.byID[id] = Config
|
||||
e.Unlock()
|
||||
e.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns an exec configuration by its id.
|
||||
func (e *Store) Get(id string) *Config {
|
||||
e.RLock()
|
||||
func (e *ExecStore) Get(id string) *ExecConfig {
|
||||
e.mu.RLock()
|
||||
res := e.byID[id]
|
||||
e.RUnlock()
|
||||
e.mu.RUnlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete removes an exec configuration from the store.
|
||||
func (e *Store) Delete(id string, pid int) {
|
||||
e.Lock()
|
||||
func (e *ExecStore) Delete(id string) {
|
||||
e.mu.Lock()
|
||||
delete(e.byID, id)
|
||||
e.Unlock()
|
||||
e.mu.Unlock()
|
||||
}
|
||||
|
||||
// List returns the list of exec ids in the store.
|
||||
func (e *Store) List() []string {
|
||||
func (e *ExecStore) List() []string {
|
||||
var IDs []string
|
||||
e.RLock()
|
||||
e.mu.RLock()
|
||||
for id := range e.byID {
|
||||
IDs = append(IDs, id)
|
||||
}
|
||||
e.RUnlock()
|
||||
e.mu.RUnlock()
|
||||
return IDs
|
||||
}
|
||||
+36
-4
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@@ -36,6 +37,14 @@ type State struct {
|
||||
|
||||
stopWaiters []chan<- StateStatus
|
||||
removeOnlyWaiters []chan<- StateStatus
|
||||
|
||||
// The libcontainerd reference fields are unexported to force consumers
|
||||
// to access them through the getter methods with multi-valued returns
|
||||
// so that they can't forget to nil-check: the code won't compile unless
|
||||
// the nil-check result is explicitly consumed or discarded.
|
||||
|
||||
ctr libcontainerdtypes.Container
|
||||
task libcontainerdtypes.Task
|
||||
}
|
||||
|
||||
// StateStatus is used to return container wait results.
|
||||
@@ -260,7 +269,7 @@ func (s *State) SetExitCode(ec int) {
|
||||
}
|
||||
|
||||
// SetRunning sets the state of the container to "running".
|
||||
func (s *State) SetRunning(pid int, initial bool) {
|
||||
func (s *State) SetRunning(ctr libcontainerdtypes.Container, tsk libcontainerdtypes.Task, initial bool) {
|
||||
s.ErrorMsg = ""
|
||||
s.Paused = false
|
||||
s.Running = true
|
||||
@@ -269,7 +278,14 @@ func (s *State) SetRunning(pid int, initial bool) {
|
||||
s.Paused = false
|
||||
}
|
||||
s.ExitCodeValue = 0
|
||||
s.Pid = pid
|
||||
s.ctr = ctr
|
||||
s.task = tsk
|
||||
if tsk != nil {
|
||||
s.Pid = int(tsk.Pid())
|
||||
} else {
|
||||
s.Pid = 0
|
||||
}
|
||||
s.OOMKilled = false
|
||||
if initial {
|
||||
s.StartedAt = time.Now().UTC()
|
||||
}
|
||||
@@ -287,7 +303,6 @@ func (s *State) SetStopped(exitStatus *ExitStatus) {
|
||||
s.FinishedAt = exitStatus.ExitedAt
|
||||
}
|
||||
s.ExitCodeValue = exitStatus.ExitCode
|
||||
s.OOMKilled = exitStatus.OOMKilled
|
||||
|
||||
s.notifyAndClear(&s.stopWaiters)
|
||||
}
|
||||
@@ -303,7 +318,6 @@ func (s *State) SetRestarting(exitStatus *ExitStatus) {
|
||||
s.Pid = 0
|
||||
s.FinishedAt = time.Now().UTC()
|
||||
s.ExitCodeValue = exitStatus.ExitCode
|
||||
s.OOMKilled = exitStatus.OOMKilled
|
||||
|
||||
s.notifyAndClear(&s.stopWaiters)
|
||||
}
|
||||
@@ -405,3 +419,21 @@ func (s *State) notifyAndClear(waiters *[]chan<- StateStatus) {
|
||||
}
|
||||
*waiters = nil
|
||||
}
|
||||
|
||||
// C8dContainer returns a reference to the libcontainerd Container object for
|
||||
// the container and whether the reference is valid.
|
||||
//
|
||||
// The container lock must be held when calling this method.
|
||||
func (s *State) C8dContainer() (_ libcontainerdtypes.Container, ok bool) {
|
||||
return s.ctr, s.ctr != nil
|
||||
}
|
||||
|
||||
// Task returns a reference to the libcontainerd Task object for the container
|
||||
// and whether the reference is valid.
|
||||
//
|
||||
// The container lock must be held when calling this method.
|
||||
//
|
||||
// See also: (*Container).GetRunningTask().
|
||||
func (s *State) Task() (_ libcontainerdtypes.Task, ok bool) {
|
||||
return s.task, s.task != nil
|
||||
}
|
||||
|
||||
+82
-68
@@ -1,6 +1,7 @@
|
||||
package container // import "github.com/docker/docker/container"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/go-connections/nat"
|
||||
memdb "github.com/hashicorp/go-memdb"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -17,6 +19,7 @@ const (
|
||||
memdbContainersTable = "containers"
|
||||
memdbNamesTable = "names"
|
||||
memdbIDIndex = "id"
|
||||
memdbIDIndexPrefix = "id_prefix"
|
||||
memdbContainerIDIndex = "containerid"
|
||||
)
|
||||
|
||||
@@ -58,25 +61,6 @@ type nameAssociation struct {
|
||||
containerID string
|
||||
}
|
||||
|
||||
// ViewDB provides an in-memory transactional (ACID) container Store
|
||||
type ViewDB interface {
|
||||
Snapshot() View
|
||||
Save(*Container) error
|
||||
Delete(*Container) error
|
||||
|
||||
ReserveName(name, containerID string) error
|
||||
ReleaseName(name string) error
|
||||
}
|
||||
|
||||
// View can be used by readers to avoid locking
|
||||
type View interface {
|
||||
All() ([]Snapshot, error)
|
||||
Get(id string) (*Snapshot, error)
|
||||
|
||||
GetID(name string) (string, error)
|
||||
GetAllNames() map[string][]string
|
||||
}
|
||||
|
||||
var schema = &memdb.DBSchema{
|
||||
Tables: map[string]*memdb.TableSchema{
|
||||
memdbContainersTable: {
|
||||
@@ -107,43 +91,63 @@ var schema = &memdb.DBSchema{
|
||||
},
|
||||
}
|
||||
|
||||
type memDB struct {
|
||||
// ViewDB provides an in-memory transactional (ACID) container store.
|
||||
type ViewDB struct {
|
||||
store *memdb.MemDB
|
||||
}
|
||||
|
||||
// NoSuchContainerError indicates that the container wasn't found in the
|
||||
// database.
|
||||
type NoSuchContainerError struct {
|
||||
id string
|
||||
}
|
||||
|
||||
// Error satisfies the error interface.
|
||||
func (e NoSuchContainerError) Error() string {
|
||||
return "no such container " + e.id
|
||||
}
|
||||
|
||||
// NewViewDB provides the default implementation, with the default schema
|
||||
func NewViewDB() (ViewDB, error) {
|
||||
func NewViewDB() (*ViewDB, error) {
|
||||
store, err := memdb.NewMemDB(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errdefs.System(err)
|
||||
}
|
||||
return &memDB{store: store}, nil
|
||||
return &ViewDB{store: store}, nil
|
||||
}
|
||||
|
||||
// Snapshot provides a consistent read-only View of the database
|
||||
func (db *memDB) Snapshot() View {
|
||||
return &memdbView{
|
||||
// GetByPrefix returns a container with the given ID prefix. It returns an
|
||||
// error if an empty prefix was given or if multiple containers match the prefix.
|
||||
func (db *ViewDB) GetByPrefix(s string) (string, error) {
|
||||
if s == "" {
|
||||
return "", errdefs.InvalidParameter(errors.New("prefix can't be empty"))
|
||||
}
|
||||
iter, err := db.store.Txn(false).Get(memdbContainersTable, memdbIDIndexPrefix, s)
|
||||
if err != nil {
|
||||
return "", errdefs.System(err)
|
||||
}
|
||||
|
||||
var id string
|
||||
for {
|
||||
item := iter.Next()
|
||||
if item == nil {
|
||||
break
|
||||
}
|
||||
if id != "" {
|
||||
return "", errdefs.InvalidParameter(errors.New("multiple IDs found with provided prefix: " + s))
|
||||
}
|
||||
id = item.(*Container).ID
|
||||
}
|
||||
|
||||
if id != "" {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
return "", errdefs.NotFound(errors.New("No such container: " + s))
|
||||
}
|
||||
|
||||
// Snapshot provides a consistent read-only view of the database.
|
||||
func (db *ViewDB) Snapshot() *View {
|
||||
return &View{
|
||||
txn: db.store.Txn(false),
|
||||
}
|
||||
}
|
||||
|
||||
func (db *memDB) withTxn(cb func(*memdb.Txn) error) error {
|
||||
func (db *ViewDB) withTxn(cb func(*memdb.Txn) error) error {
|
||||
txn := db.store.Txn(true)
|
||||
err := cb(txn)
|
||||
if err != nil {
|
||||
txn.Abort()
|
||||
return err
|
||||
return errdefs.System(err)
|
||||
}
|
||||
txn.Commit()
|
||||
return nil
|
||||
@@ -151,16 +155,16 @@ func (db *memDB) withTxn(cb func(*memdb.Txn) error) error {
|
||||
|
||||
// Save atomically updates the in-memory store state for a Container.
|
||||
// Only read only (deep) copies of containers may be passed in.
|
||||
func (db *memDB) Save(c *Container) error {
|
||||
func (db *ViewDB) Save(c *Container) error {
|
||||
return db.withTxn(func(txn *memdb.Txn) error {
|
||||
return txn.Insert(memdbContainersTable, c)
|
||||
})
|
||||
}
|
||||
|
||||
// Delete removes an item by ID
|
||||
func (db *memDB) Delete(c *Container) error {
|
||||
func (db *ViewDB) Delete(c *Container) error {
|
||||
return db.withTxn(func(txn *memdb.Txn) error {
|
||||
view := &memdbView{txn: txn}
|
||||
view := &View{txn: txn}
|
||||
names := view.getNames(c.ID)
|
||||
|
||||
for _, name := range names {
|
||||
@@ -178,11 +182,11 @@ func (db *memDB) Delete(c *Container) error {
|
||||
// ReserveName is idempotent
|
||||
// Attempting to reserve a container ID to a name that already exists results in an `ErrNameReserved`
|
||||
// A name reservation is globally unique
|
||||
func (db *memDB) ReserveName(name, containerID string) error {
|
||||
func (db *ViewDB) ReserveName(name, containerID string) error {
|
||||
return db.withTxn(func(txn *memdb.Txn) error {
|
||||
s, err := txn.First(memdbNamesTable, memdbIDIndex, name)
|
||||
if err != nil {
|
||||
return err
|
||||
return errdefs.System(err)
|
||||
}
|
||||
if s != nil {
|
||||
if s.(nameAssociation).containerID != containerID {
|
||||
@@ -196,22 +200,23 @@ func (db *memDB) ReserveName(name, containerID string) error {
|
||||
|
||||
// ReleaseName releases the reserved name
|
||||
// Once released, a name can be reserved again
|
||||
func (db *memDB) ReleaseName(name string) error {
|
||||
func (db *ViewDB) ReleaseName(name string) error {
|
||||
return db.withTxn(func(txn *memdb.Txn) error {
|
||||
return txn.Delete(memdbNamesTable, nameAssociation{name: name})
|
||||
})
|
||||
}
|
||||
|
||||
type memdbView struct {
|
||||
// View provides a consistent read-only view of the database.
|
||||
type View struct {
|
||||
txn *memdb.Txn
|
||||
}
|
||||
|
||||
// All returns a all items in this snapshot. Returned objects must never be modified.
|
||||
func (v *memdbView) All() ([]Snapshot, error) {
|
||||
func (v *View) All() ([]Snapshot, error) {
|
||||
var all []Snapshot
|
||||
iter, err := v.txn.Get(memdbContainersTable, memdbIDIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errdefs.System(err)
|
||||
}
|
||||
for {
|
||||
item := iter.Next()
|
||||
@@ -225,19 +230,19 @@ func (v *memdbView) All() ([]Snapshot, error) {
|
||||
}
|
||||
|
||||
// Get returns an item by id. Returned objects must never be modified.
|
||||
func (v *memdbView) Get(id string) (*Snapshot, error) {
|
||||
func (v *View) Get(id string) (*Snapshot, error) {
|
||||
s, err := v.txn.First(memdbContainersTable, memdbIDIndex, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errdefs.System(err)
|
||||
}
|
||||
if s == nil {
|
||||
return nil, NoSuchContainerError{id: id}
|
||||
return nil, errdefs.NotFound(errors.New("No such container: " + id))
|
||||
}
|
||||
return v.transform(s.(*Container)), nil
|
||||
}
|
||||
|
||||
// getNames lists all the reserved names for the given container ID.
|
||||
func (v *memdbView) getNames(containerID string) []string {
|
||||
func (v *View) getNames(containerID string) []string {
|
||||
iter, err := v.txn.Get(memdbNamesTable, memdbContainerIDIndex, containerID)
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -256,10 +261,10 @@ func (v *memdbView) getNames(containerID string) []string {
|
||||
}
|
||||
|
||||
// GetID returns the container ID that the passed in name is reserved to.
|
||||
func (v *memdbView) GetID(name string) (string, error) {
|
||||
func (v *View) GetID(name string) (string, error) {
|
||||
s, err := v.txn.First(memdbNamesTable, memdbIDIndex, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", errdefs.System(err)
|
||||
}
|
||||
if s == nil {
|
||||
return "", ErrNameNotReserved
|
||||
@@ -268,7 +273,7 @@ func (v *memdbView) GetID(name string) (string, error) {
|
||||
}
|
||||
|
||||
// GetAllNames returns all registered names.
|
||||
func (v *memdbView) GetAllNames() map[string][]string {
|
||||
func (v *View) GetAllNames() map[string][]string {
|
||||
iter, err := v.txn.Get(memdbNamesTable, memdbContainerIDIndex)
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -289,7 +294,7 @@ func (v *memdbView) GetAllNames() map[string][]string {
|
||||
|
||||
// transform maps a (deep) copied Container object to what queries need.
|
||||
// A lock on the Container is not held because these are immutable deep copies.
|
||||
func (v *memdbView) transform(container *Container) *Snapshot {
|
||||
func (v *View) transform(container *Container) *Snapshot {
|
||||
health := types.NoHealthcheck
|
||||
if container.Health != nil {
|
||||
health = container.Health.Status()
|
||||
@@ -382,7 +387,7 @@ func (v *memdbView) transform(container *Container) *Snapshot {
|
||||
for port, bindings := range container.NetworkSettings.Ports {
|
||||
p, err := nat.ParsePort(port.Port())
|
||||
if err != nil {
|
||||
logrus.Warnf("invalid port map %+v", err)
|
||||
logrus.WithError(err).Warn("invalid port map")
|
||||
continue
|
||||
}
|
||||
if len(bindings) == 0 {
|
||||
@@ -395,7 +400,7 @@ func (v *memdbView) transform(container *Container) *Snapshot {
|
||||
for _, binding := range bindings {
|
||||
h, err := nat.ParsePort(binding.HostPort)
|
||||
if err != nil {
|
||||
logrus.Warnf("invalid host port map %+v", err)
|
||||
logrus.WithError(err).Warn("invalid host port map")
|
||||
continue
|
||||
}
|
||||
snapshot.Ports = append(snapshot.Ports, types.Port{
|
||||
@@ -416,6 +421,9 @@ func (v *memdbView) transform(container *Container) *Snapshot {
|
||||
// memdb.StringFieldIndex can not be used since ID is a field from an embedded struct.
|
||||
type containerByIDIndexer struct{}
|
||||
|
||||
// terminator is the null character, used as a terminator.
|
||||
const terminator = "\x00"
|
||||
|
||||
// FromObject implements the memdb.SingleIndexer interface for Container objects
|
||||
func (e *containerByIDIndexer) FromObject(obj interface{}) (bool, []byte, error) {
|
||||
c, ok := obj.(*Container)
|
||||
@@ -423,8 +431,7 @@ func (e *containerByIDIndexer) FromObject(obj interface{}) (bool, []byte, error)
|
||||
return false, nil, fmt.Errorf("%T is not a Container", obj)
|
||||
}
|
||||
// Add the null character as a terminator
|
||||
v := c.ID + "\x00"
|
||||
return true, []byte(v), nil
|
||||
return true, []byte(c.ID + terminator), nil
|
||||
}
|
||||
|
||||
// FromArgs implements the memdb.Indexer interface
|
||||
@@ -437,8 +444,17 @@ func (e *containerByIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
|
||||
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
|
||||
}
|
||||
// Add the null character as a terminator
|
||||
arg += "\x00"
|
||||
return []byte(arg), nil
|
||||
return []byte(arg + terminator), nil
|
||||
}
|
||||
|
||||
func (e *containerByIDIndexer) PrefixFromArgs(args ...interface{}) ([]byte, error) {
|
||||
val, err := e.FromArgs(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Strip the null terminator, the rest is a prefix
|
||||
return bytes.TrimSuffix(val, []byte(terminator)), nil
|
||||
}
|
||||
|
||||
// namesByNameIndexer is used to index container name associations by name.
|
||||
@@ -451,7 +467,7 @@ func (e *namesByNameIndexer) FromObject(obj interface{}) (bool, []byte, error) {
|
||||
}
|
||||
|
||||
// Add the null character as a terminator
|
||||
return true, []byte(n.name + "\x00"), nil
|
||||
return true, []byte(n.name + terminator), nil
|
||||
}
|
||||
|
||||
func (e *namesByNameIndexer) FromArgs(args ...interface{}) ([]byte, error) {
|
||||
@@ -463,8 +479,7 @@ func (e *namesByNameIndexer) FromArgs(args ...interface{}) ([]byte, error) {
|
||||
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
|
||||
}
|
||||
// Add the null character as a terminator
|
||||
arg += "\x00"
|
||||
return []byte(arg), nil
|
||||
return []byte(arg + terminator), nil
|
||||
}
|
||||
|
||||
// namesByContainerIDIndexer is used to index container names by container ID.
|
||||
@@ -477,7 +492,7 @@ func (e *namesByContainerIDIndexer) FromObject(obj interface{}) (bool, []byte, e
|
||||
}
|
||||
|
||||
// Add the null character as a terminator
|
||||
return true, []byte(n.containerID + "\x00"), nil
|
||||
return true, []byte(n.containerID + terminator), nil
|
||||
}
|
||||
|
||||
func (e *namesByContainerIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
|
||||
@@ -489,6 +504,5 @@ func (e *namesByContainerIDIndexer) FromArgs(args ...interface{}) ([]byte, error
|
||||
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
|
||||
}
|
||||
// Add the null character as a terminator
|
||||
arg += "\x00"
|
||||
return []byte(arg), nil
|
||||
return []byte(arg + terminator), nil
|
||||
}
|
||||
|
||||
+15
-3
@@ -1,13 +1,13 @@
|
||||
package graphdriver // import "github.com/docker/docker/daemon/graphdriver"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/pkg/errors"
|
||||
@@ -60,7 +60,7 @@ type ProtoDriver interface {
|
||||
// Get returns the mountpoint for the layered filesystem referred
|
||||
// to by this id. You can optionally specify a mountLabel or "".
|
||||
// Returns the absolute path to the mounted layered filesystem.
|
||||
Get(id, mountLabel string) (fs containerfs.ContainerFS, err error)
|
||||
Get(id, mountLabel string) (fs string, err error)
|
||||
// Put releases the system resources for the specified id,
|
||||
// e.g, unmounting layered filesystem.
|
||||
Put(id string) error
|
||||
@@ -193,6 +193,9 @@ type Options struct {
|
||||
func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) {
|
||||
if name != "" {
|
||||
logrus.Infof("[graphdriver] trying configured driver: %s", name)
|
||||
if err := checkRemoved(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isDeprecated(name) {
|
||||
logrus.Warnf("[graphdriver] WARNING: the %s storage-driver is deprecated and will be removed in a future release; visit https://docs.docker.com/go/storage-driver/ for more information", name)
|
||||
}
|
||||
@@ -315,8 +318,17 @@ func isEmptyDir(name string) bool {
|
||||
func isDeprecated(name string) bool {
|
||||
switch name {
|
||||
// NOTE: when deprecating a driver, update daemon.fillDriverInfo() accordingly
|
||||
case "aufs", "devicemapper", "overlay":
|
||||
case "devicemapper":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkRemoved checks if a storage-driver has been deprecated (and removed)
|
||||
func checkRemoved(name string) error {
|
||||
switch name {
|
||||
case "aufs", "overlay":
|
||||
return NotSupportedError(fmt.Sprintf("[graphdriver] ERROR: the %s storage-driver has been deprecated and removed; visit https://docs.docker.com/go/storage-driver/ for more information", name))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ const (
|
||||
|
||||
var (
|
||||
// List of drivers that should be used in an order
|
||||
priority = "overlay2,fuse-overlayfs,btrfs,zfs,aufs,overlay,devicemapper,vfs"
|
||||
priority = "overlay2,fuse-overlayfs,btrfs,zfs,devicemapper,vfs"
|
||||
|
||||
// FsNames maps filesystem id to name of the filesystem.
|
||||
FsNames = map[FsMagic]string{
|
||||
|
||||
+6
-11
@@ -21,7 +21,6 @@ var (
|
||||
// capability of the Diffing methods on the local file system,
|
||||
// which it may or may not support on its own. See the comment
|
||||
// on the exported NewNaiveDiffDriver function below.
|
||||
// Notably, the AUFS driver doesn't need to be wrapped like this.
|
||||
type NaiveDiffDriver struct {
|
||||
ProtoDriver
|
||||
idMap idtools.IdentityMapping
|
||||
@@ -50,7 +49,7 @@ func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layerFs := layerRootFs.Path()
|
||||
layerFs := layerRootFs
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
@@ -70,14 +69,12 @@ func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err err
|
||||
}), nil
|
||||
}
|
||||
|
||||
parentRootFs, err := driver.Get(parent, "")
|
||||
parentFs, err := driver.Get(parent, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer driver.Put(parent)
|
||||
|
||||
parentFs := parentRootFs.Path()
|
||||
|
||||
changes, err := archive.ChangesDirs(layerFs, parentFs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -106,22 +103,20 @@ func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err err
|
||||
func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) {
|
||||
driver := gdw.ProtoDriver
|
||||
|
||||
layerRootFs, err := driver.Get(id, "")
|
||||
layerFs, err := driver.Get(id, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer driver.Put(id)
|
||||
|
||||
layerFs := layerRootFs.Path()
|
||||
parentFs := ""
|
||||
|
||||
if parent != "" {
|
||||
parentRootFs, err := driver.Get(parent, "")
|
||||
parentFs, err = driver.Get(parent, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer driver.Put(parent)
|
||||
parentFs = parentRootFs.Path()
|
||||
}
|
||||
|
||||
return archive.ChangesDirs(layerFs, parentFs)
|
||||
@@ -140,7 +135,7 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff io.Reader) (size i
|
||||
}
|
||||
defer driver.Put(id)
|
||||
|
||||
layerFs := layerRootFs.Path()
|
||||
layerFs := layerRootFs
|
||||
options := &archive.TarOptions{IDMap: gdw.idMap}
|
||||
start := time.Now().UTC()
|
||||
logrus.WithField("id", id).Debug("Start untar layer")
|
||||
@@ -169,5 +164,5 @@ func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error)
|
||||
}
|
||||
defer driver.Put(id)
|
||||
|
||||
return archive.ChangesSize(layerFs.Path(), changes), nil
|
||||
return archive.ChangesSize(layerFs, changes), nil
|
||||
}
|
||||
|
||||
+3
-4
@@ -6,7 +6,6 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
@@ -128,20 +127,20 @@ func (d *graphDriverProxy) Remove(id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *graphDriverProxy) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
|
||||
func (d *graphDriverProxy) Get(id, mountLabel string) (string, error) {
|
||||
args := &graphDriverRequest{
|
||||
ID: id,
|
||||
MountLabel: mountLabel,
|
||||
}
|
||||
var ret graphDriverResponse
|
||||
if err := d.client.Call("GraphDriver.Get", args, &ret); err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
var err error
|
||||
if ret.Err != "" {
|
||||
err = errors.New(ret.Err)
|
||||
}
|
||||
return containerfs.NewLocalContainerFS(d.p.ScopedPath(ret.Dir)), err
|
||||
return d.p.ScopedPath(ret.Dir), err
|
||||
}
|
||||
|
||||
func (d *graphDriverProxy) Put(id string) error {
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ type ReadConfig struct {
|
||||
|
||||
// LogReader is the interface for reading log messages for loggers that support reading.
|
||||
type LogReader interface {
|
||||
// Read logs from underlying logging backend
|
||||
// ReadLogs reads logs from underlying logging backend.
|
||||
ReadLogs(ReadConfig) *LogWatcher
|
||||
}
|
||||
|
||||
|
||||
+10
-35
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/pkg/errors"
|
||||
@@ -12,10 +11,10 @@ import (
|
||||
)
|
||||
|
||||
type follow struct {
|
||||
LogFile *LogFile
|
||||
Watcher *logger.LogWatcher
|
||||
Decoder Decoder
|
||||
Since, Until time.Time
|
||||
LogFile *LogFile
|
||||
Watcher *logger.LogWatcher
|
||||
Decoder Decoder
|
||||
Forwarder *forwarder
|
||||
|
||||
log *logrus.Entry
|
||||
c chan logPos
|
||||
@@ -49,7 +48,7 @@ func (fl *follow) Do(f *os.File, read logPos) {
|
||||
fl.Watcher.Err <- err
|
||||
return
|
||||
}
|
||||
if fl.decode(f) {
|
||||
if !fl.forward(f) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -91,7 +90,7 @@ func (fl *follow) Do(f *os.File, read logPos) {
|
||||
read.size = 0
|
||||
}
|
||||
|
||||
if fl.decode(io.NewSectionReader(f, read.size, wrote.size-read.size)) {
|
||||
if !fl.forward(io.NewSectionReader(f, read.size, wrote.size-read.size)) {
|
||||
return
|
||||
}
|
||||
read = wrote
|
||||
@@ -132,34 +131,10 @@ func (fl *follow) nextPos(current logPos) (next logPos, ok bool) {
|
||||
return next, true
|
||||
}
|
||||
|
||||
// decode decodes log messages from r and sends messages with timestamps between
|
||||
// Since and Until to the log watcher.
|
||||
// forward decodes log messages from r and forwards them to the log watcher.
|
||||
//
|
||||
// The return value, done, signals whether following should end due to a
|
||||
// condition encountered during decode.
|
||||
func (fl *follow) decode(r io.Reader) (done bool) {
|
||||
// The return value, cont, signals whether following should continue.
|
||||
func (fl *follow) forward(r io.Reader) (cont bool) {
|
||||
fl.Decoder.Reset(r)
|
||||
for {
|
||||
msg, err := fl.Decoder.Decode()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return false
|
||||
}
|
||||
fl.Watcher.Err <- err
|
||||
return true
|
||||
}
|
||||
|
||||
if !fl.Since.IsZero() && msg.Timestamp.Before(fl.Since) {
|
||||
continue
|
||||
}
|
||||
if !fl.Until.IsZero() && msg.Timestamp.After(fl.Until) {
|
||||
return true
|
||||
}
|
||||
// send the message, unless the consumer is gone
|
||||
select {
|
||||
case fl.Watcher.Msg <- msg:
|
||||
case <-fl.Watcher.WatchConsumerGone():
|
||||
return true
|
||||
}
|
||||
}
|
||||
return fl.Forwarder.Do(fl.Watcher, fl.Decoder)
|
||||
}
|
||||
|
||||
+41
-20
@@ -411,6 +411,7 @@ func (w *LogFile) readLogsLocked(currentPos logPos, config logger.ReadConfig, wa
|
||||
defer dec.Close()
|
||||
|
||||
currentChunk := io.NewSectionReader(currentFile, 0, currentPos.size)
|
||||
fwd := newForwarder(config)
|
||||
|
||||
if config.Tail != 0 {
|
||||
// TODO(@cpuguy83): Instead of opening every file, only get the files which
|
||||
@@ -449,7 +450,7 @@ func (w *LogFile) readLogsLocked(currentPos logPos, config logger.ReadConfig, wa
|
||||
readers = append(readers, currentChunk)
|
||||
}
|
||||
|
||||
ok := tailFiles(readers, watcher, dec, w.getTailReader, config)
|
||||
ok := tailFiles(readers, watcher, dec, w.getTailReader, config.Tail, fwd)
|
||||
closeFiles()
|
||||
if !ok {
|
||||
return
|
||||
@@ -463,11 +464,10 @@ func (w *LogFile) readLogsLocked(currentPos logPos, config logger.ReadConfig, wa
|
||||
}
|
||||
|
||||
(&follow{
|
||||
LogFile: w,
|
||||
Watcher: watcher,
|
||||
Decoder: dec,
|
||||
Since: config.Since,
|
||||
Until: config.Until,
|
||||
LogFile: w,
|
||||
Watcher: watcher,
|
||||
Decoder: dec,
|
||||
Forwarder: fwd,
|
||||
}).Do(currentFile, currentPos)
|
||||
}
|
||||
|
||||
@@ -573,7 +573,7 @@ func decompress(dst io.WriteSeeker, src io.ReadSeeker) error {
|
||||
return rc.Close()
|
||||
}
|
||||
|
||||
func tailFiles(files []SizeReaderAt, watcher *logger.LogWatcher, dec Decoder, getTailReader GetTailReaderFunc, config logger.ReadConfig) (cont bool) {
|
||||
func tailFiles(files []SizeReaderAt, watcher *logger.LogWatcher, dec Decoder, getTailReader GetTailReaderFunc, nLines int, fwd *forwarder) (cont bool) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -583,20 +583,18 @@ func tailFiles(files []SizeReaderAt, watcher *logger.LogWatcher, dec Decoder, ge
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-watcher.WatchConsumerGone():
|
||||
cont = false
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
readers := make([]io.Reader, 0, len(files))
|
||||
|
||||
if config.Tail > 0 {
|
||||
nLines := config.Tail
|
||||
if nLines > 0 {
|
||||
for i := len(files) - 1; i >= 0 && nLines > 0; i-- {
|
||||
tail, n, err := getTailReader(ctx, files[i], nLines)
|
||||
if err != nil {
|
||||
watcher.Err <- errors.Wrap(err, "error finding file position to start log tailing")
|
||||
return
|
||||
return false
|
||||
}
|
||||
nLines -= n
|
||||
readers = append([]io.Reader{tail}, readers...)
|
||||
@@ -609,24 +607,47 @@ func tailFiles(files []SizeReaderAt, watcher *logger.LogWatcher, dec Decoder, ge
|
||||
|
||||
rdr := io.MultiReader(readers...)
|
||||
dec.Reset(rdr)
|
||||
return fwd.Do(watcher, dec)
|
||||
}
|
||||
|
||||
type forwarder struct {
|
||||
since, until time.Time
|
||||
}
|
||||
|
||||
func newForwarder(config logger.ReadConfig) *forwarder {
|
||||
return &forwarder{since: config.Since, until: config.Until}
|
||||
}
|
||||
|
||||
// Do reads log messages from dec and sends the messages matching the filter
|
||||
// conditions to watcher. Do returns cont=true iff it has read all messages from
|
||||
// dec without encountering a message with a timestamp which is after the
|
||||
// configured until time.
|
||||
func (fwd *forwarder) Do(watcher *logger.LogWatcher, dec Decoder) (cont bool) {
|
||||
for {
|
||||
msg, err := dec.Decode()
|
||||
if err != nil {
|
||||
if !errors.Is(err, io.EOF) {
|
||||
watcher.Err <- err
|
||||
if errors.Is(err, io.EOF) {
|
||||
return true
|
||||
}
|
||||
return
|
||||
watcher.Err <- err
|
||||
return false
|
||||
}
|
||||
if !config.Since.IsZero() && msg.Timestamp.Before(config.Since) {
|
||||
continue
|
||||
if !fwd.since.IsZero() {
|
||||
if msg.Timestamp.Before(fwd.since) {
|
||||
continue
|
||||
}
|
||||
// We've found our first message with a timestamp >= since. As message
|
||||
// timestamps might not be monotonic, we need to skip the since check for all
|
||||
// subsequent messages so we do not filter out later messages which happen to
|
||||
// have timestamps before since.
|
||||
fwd.since = time.Time{}
|
||||
}
|
||||
if !config.Until.IsZero() && msg.Timestamp.After(config.Until) {
|
||||
return
|
||||
if !fwd.until.IsZero() && msg.Timestamp.After(fwd.until) {
|
||||
return false
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-watcher.WatchConsumerGone():
|
||||
return false
|
||||
case watcher.Msg <- msg:
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -59,8 +59,8 @@ func (info *Info) ExtraAttributes(keyMod func(string) string) (map[string]string
|
||||
|
||||
envMapping := make(map[string]string)
|
||||
for _, e := range info.ContainerEnv {
|
||||
if kv := strings.SplitN(e, "=", 2); len(kv) == 2 {
|
||||
envMapping[kv[0]] = kv[1]
|
||||
if k, v, ok := strings.Cut(e, "="); ok {
|
||||
envMapping[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ func RegisterPluginGetter(plugingetter getter.PluginGetter) {
|
||||
pluginGetter = plugingetter
|
||||
}
|
||||
|
||||
// GetDriver returns a logging driver by its name.
|
||||
// getPlugin returns a logging driver by its name.
|
||||
// If the driver is empty, it looks for the local driver.
|
||||
func getPlugin(name string, mode int) (Creator, error) {
|
||||
p, err := pluginGetter.Get(name, extName, mode)
|
||||
|
||||
+53
-33
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/docker/pkg/useragent"
|
||||
@@ -16,40 +17,69 @@ type UAStringKey struct{}
|
||||
// In accordance with RFC 7231 (5.5.3) is of the form:
|
||||
//
|
||||
// [docker client's UA] UpstreamClient([upstream client's UA])
|
||||
func DockerUserAgent(ctx context.Context) string {
|
||||
httpVersion := make([]useragent.VersionInfo, 0, 6)
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "docker", Version: Version})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "go", Version: runtime.Version()})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "git-commit", Version: GitCommit})
|
||||
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "kernel", Version: kernelVersion.String()})
|
||||
func DockerUserAgent(ctx context.Context, extraVersions ...useragent.VersionInfo) string {
|
||||
ua := useragent.AppendVersions(getDaemonUserAgent(), extraVersions...)
|
||||
if upstreamUA := getUpstreamUserAgent(ctx); upstreamUA != "" {
|
||||
ua += " " + upstreamUA
|
||||
}
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "os", Version: runtime.GOOS})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "arch", Version: runtime.GOARCH})
|
||||
|
||||
dockerUA := useragent.AppendVersions("", httpVersion...)
|
||||
upstreamUA := getUserAgentFromContext(ctx)
|
||||
if len(upstreamUA) > 0 {
|
||||
ret := insertUpstreamUserAgent(upstreamUA, dockerUA)
|
||||
return ret
|
||||
}
|
||||
return dockerUA
|
||||
return ua
|
||||
}
|
||||
|
||||
// getUserAgentFromContext returns the previously saved user-agent context stored in ctx, if one exists
|
||||
func getUserAgentFromContext(ctx context.Context) string {
|
||||
var (
|
||||
daemonUAOnce sync.Once
|
||||
daemonUA string
|
||||
)
|
||||
|
||||
// getDaemonUserAgent returns the user-agent to use for requests made by
|
||||
// the daemon.
|
||||
//
|
||||
// It includes;
|
||||
//
|
||||
// - the docker version
|
||||
// - go version
|
||||
// - git-commit
|
||||
// - kernel version
|
||||
// - os
|
||||
// - architecture
|
||||
func getDaemonUserAgent() string {
|
||||
daemonUAOnce.Do(func() {
|
||||
httpVersion := make([]useragent.VersionInfo, 0, 6)
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "docker", Version: Version})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "go", Version: runtime.Version()})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "git-commit", Version: GitCommit})
|
||||
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "kernel", Version: kernelVersion.String()})
|
||||
}
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "os", Version: runtime.GOOS})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "arch", Version: runtime.GOARCH})
|
||||
daemonUA = useragent.AppendVersions("", httpVersion...)
|
||||
})
|
||||
return daemonUA
|
||||
}
|
||||
|
||||
// getUpstreamUserAgent returns the previously saved user-agent context stored
|
||||
// in ctx, if one exists, and formats it as:
|
||||
//
|
||||
// UpstreamClient(<upstream user agent string>)
|
||||
//
|
||||
// It returns an empty string if no user-agent is present in the context.
|
||||
func getUpstreamUserAgent(ctx context.Context) string {
|
||||
var upstreamUA string
|
||||
if ctx != nil {
|
||||
var ki interface{} = ctx.Value(UAStringKey{})
|
||||
if ki != nil {
|
||||
if ki := ctx.Value(UAStringKey{}); ki != nil {
|
||||
upstreamUA = ctx.Value(UAStringKey{}).(string)
|
||||
}
|
||||
}
|
||||
return upstreamUA
|
||||
if upstreamUA == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("UpstreamClient(%s)", escapeStr(upstreamUA))
|
||||
}
|
||||
|
||||
const charsToEscape = `();\`
|
||||
|
||||
// escapeStr returns s with every rune in charsToEscape escaped by a backslash
|
||||
func escapeStr(s string, charsToEscape string) string {
|
||||
func escapeStr(s string) string {
|
||||
var ret string
|
||||
for _, currRune := range s {
|
||||
appended := false
|
||||
@@ -66,13 +96,3 @@ func escapeStr(s string, charsToEscape string) string {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// insertUpstreamUserAgent adds the upstream client useragent to create a user-agent
|
||||
// string of the form:
|
||||
//
|
||||
// $dockerUA UpstreamClient($upstreamUA)
|
||||
func insertUpstreamUserAgent(upstreamUA string, dockerUA string) string {
|
||||
charsToEscape := `();\`
|
||||
upstreamUAEscaped := escapeStr(upstreamUA, charsToEscape)
|
||||
return fmt.Sprintf("%s UpstreamClient(%s)", dockerUA, upstreamUAEscaped)
|
||||
}
|
||||
|
||||
+31
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/layer"
|
||||
@@ -28,6 +29,8 @@ func (id ID) Digest() digest.Digest {
|
||||
}
|
||||
|
||||
// IDFromDigest creates an ID from a digest
|
||||
//
|
||||
// Deprecated: cast to an ID using ID(digest).
|
||||
func IDFromDigest(digest digest.Digest) ID {
|
||||
return ID(digest)
|
||||
}
|
||||
@@ -112,6 +115,18 @@ type Image struct {
|
||||
// computedID is the ID computed from the hash of the image config.
|
||||
// Not to be confused with the legacy V1 ID in V1Image.
|
||||
computedID ID
|
||||
|
||||
// Details holds additional details about image
|
||||
Details *Details `json:"-"`
|
||||
}
|
||||
|
||||
// Details provides additional image data
|
||||
type Details struct {
|
||||
References []reference.Named
|
||||
Size int64
|
||||
Metadata map[string]string
|
||||
Driver string
|
||||
LastUpdated time.Time
|
||||
}
|
||||
|
||||
// RawJSON returns the immutable JSON associated with the image.
|
||||
@@ -188,6 +203,13 @@ type ChildConfig struct {
|
||||
Config *container.Config
|
||||
}
|
||||
|
||||
// NewImage creates a new image with the given ID
|
||||
func NewImage(id ID) *Image {
|
||||
return &Image{
|
||||
computedID: id,
|
||||
}
|
||||
}
|
||||
|
||||
// NewChildImage creates a new Image as a child of this image.
|
||||
func NewChildImage(img *Image, child ChildConfig, os string) *Image {
|
||||
isEmptyLayer := layer.IsEmpty(child.DiffID)
|
||||
@@ -226,6 +248,15 @@ func NewChildImage(img *Image, child ChildConfig, os string) *Image {
|
||||
}
|
||||
}
|
||||
|
||||
// Clone clones an image and changes ID.
|
||||
func Clone(base *Image, id ID) *Image {
|
||||
img := *base
|
||||
img.RootFS = img.RootFS.Clone()
|
||||
img.V1Image.ID = id.String()
|
||||
img.computedID = id
|
||||
return &img
|
||||
}
|
||||
|
||||
// History stores build commands that were used to create an image
|
||||
type History struct {
|
||||
// Created is the timestamp at which the image was created
|
||||
|
||||
+28
-24
@@ -67,22 +67,29 @@ func NewImageStore(fs StoreBackend, lss LayerGetReleaser) (Store, error) {
|
||||
}
|
||||
|
||||
func (is *store) restore() error {
|
||||
// As the code below is run when restoring all images (which can be "many"),
|
||||
// constructing the "logrus.WithFields" is deliberately not "DRY", as the
|
||||
// logger is only used for error-cases, and we don't want to do allocations
|
||||
// if we don't need it. The "f" type alias is here is just for convenience,
|
||||
// and to make the code _slightly_ more DRY. See the discussion on GitHub;
|
||||
// https://github.com/moby/moby/pull/44426#discussion_r1059519071
|
||||
type f = logrus.Fields
|
||||
err := is.fs.Walk(func(dgst digest.Digest) error {
|
||||
img, err := is.Get(IDFromDigest(dgst))
|
||||
img, err := is.Get(ID(dgst))
|
||||
if err != nil {
|
||||
logrus.Errorf("invalid image %v, %v", dgst, err)
|
||||
logrus.WithFields(f{"digest": dgst, "err": err}).Error("invalid image")
|
||||
return nil
|
||||
}
|
||||
var l layer.Layer
|
||||
if chainID := img.RootFS.ChainID(); chainID != "" {
|
||||
if !system.IsOSSupported(img.OperatingSystem()) {
|
||||
logrus.Errorf("not restoring image with unsupported operating system %v, %v, %s", dgst, chainID, img.OperatingSystem())
|
||||
logrus.WithFields(f{"chainID": chainID, "os": img.OperatingSystem()}).Error("not restoring image with unsupported operating system")
|
||||
return nil
|
||||
}
|
||||
l, err = is.lss.Get(chainID)
|
||||
if err != nil {
|
||||
if err == layer.ErrLayerDoesNotExist {
|
||||
logrus.Errorf("layer does not exist, not restoring image %v, %v, %s", dgst, chainID, img.OperatingSystem())
|
||||
if errors.Is(err, layer.ErrLayerDoesNotExist) {
|
||||
logrus.WithFields(f{"chainID": chainID, "os": img.OperatingSystem(), "err": err}).Error("not restoring image")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@@ -92,13 +99,11 @@ func (is *store) restore() error {
|
||||
return err
|
||||
}
|
||||
|
||||
imageMeta := &imageMeta{
|
||||
is.images[ID(dgst)] = &imageMeta{
|
||||
layer: l,
|
||||
children: make(map[ID]struct{}),
|
||||
}
|
||||
|
||||
is.images[IDFromDigest(dgst)] = imageMeta
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@@ -141,15 +146,15 @@ func (is *store) Create(config []byte) (ID, error) {
|
||||
return "", errdefs.InvalidParameter(errors.New("too many non-empty layers in History section"))
|
||||
}
|
||||
|
||||
dgst, err := is.fs.Set(config)
|
||||
imageDigest, err := is.fs.Set(config)
|
||||
if err != nil {
|
||||
return "", errdefs.InvalidParameter(err)
|
||||
}
|
||||
imageID := IDFromDigest(dgst)
|
||||
|
||||
is.Lock()
|
||||
defer is.Unlock()
|
||||
|
||||
imageID := ID(imageDigest)
|
||||
if _, exists := is.images[imageID]; exists {
|
||||
return imageID, nil
|
||||
}
|
||||
@@ -167,13 +172,12 @@ func (is *store) Create(config []byte) (ID, error) {
|
||||
}
|
||||
}
|
||||
|
||||
imageMeta := &imageMeta{
|
||||
is.images[imageID] = &imageMeta{
|
||||
layer: l,
|
||||
children: make(map[ID]struct{}),
|
||||
}
|
||||
|
||||
is.images[imageID] = imageMeta
|
||||
if err := is.digestSet.Add(imageID.Digest()); err != nil {
|
||||
if err = is.digestSet.Add(imageDigest); err != nil {
|
||||
delete(is.images, imageID)
|
||||
return "", errdefs.InvalidParameter(err)
|
||||
}
|
||||
@@ -197,7 +201,7 @@ func (is *store) Search(term string) (ID, error) {
|
||||
}
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
return IDFromDigest(dgst), nil
|
||||
return ID(dgst), nil
|
||||
}
|
||||
|
||||
func (is *store) Get(id ID) (*Image, error) {
|
||||
@@ -226,16 +230,16 @@ func (is *store) Delete(id ID) ([]layer.Metadata, error) {
|
||||
is.Lock()
|
||||
defer is.Unlock()
|
||||
|
||||
imageMeta := is.images[id]
|
||||
if imageMeta == nil {
|
||||
imgMeta := is.images[id]
|
||||
if imgMeta == nil {
|
||||
return nil, errdefs.NotFound(fmt.Errorf("unrecognized image ID %s", id.String()))
|
||||
}
|
||||
_, err := is.Get(id)
|
||||
if err != nil {
|
||||
return nil, errdefs.NotFound(fmt.Errorf("unrecognized image %s, %v", id.String(), err))
|
||||
}
|
||||
for id := range imageMeta.children {
|
||||
is.fs.DeleteMetadata(id.Digest(), "parent")
|
||||
for cID := range imgMeta.children {
|
||||
is.fs.DeleteMetadata(cID.Digest(), "parent")
|
||||
}
|
||||
if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
|
||||
delete(is.images[parent].children, id)
|
||||
@@ -247,24 +251,24 @@ func (is *store) Delete(id ID) ([]layer.Metadata, error) {
|
||||
delete(is.images, id)
|
||||
is.fs.Delete(id.Digest())
|
||||
|
||||
if imageMeta.layer != nil {
|
||||
return is.lss.Release(imageMeta.layer)
|
||||
if imgMeta.layer != nil {
|
||||
return is.lss.Release(imgMeta.layer)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (is *store) SetParent(id, parent ID) error {
|
||||
func (is *store) SetParent(id, parentID ID) error {
|
||||
is.Lock()
|
||||
defer is.Unlock()
|
||||
parentMeta := is.images[parent]
|
||||
parentMeta := is.images[parentID]
|
||||
if parentMeta == nil {
|
||||
return errdefs.NotFound(fmt.Errorf("unknown parent image ID %s", parent.String()))
|
||||
return errdefs.NotFound(fmt.Errorf("unknown parent image ID %s", parentID.String()))
|
||||
}
|
||||
if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
|
||||
delete(is.images[parent].children, id)
|
||||
}
|
||||
parentMeta.children[id] = struct{}{}
|
||||
return is.fs.SetMetadata(id.Digest(), "parent", []byte(parent))
|
||||
return is.fs.SetMetadata(id.Digest(), "parent", []byte(parentID))
|
||||
}
|
||||
|
||||
func (is *store) GetParent(id ID) (ID, error) {
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package mounttree // import "github.com/docker/docker/internal/mounttree"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/moby/sys/mount"
|
||||
"github.com/moby/sys/mountinfo"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// SwitchRoot changes path to be the root of the mount tree and changes the
|
||||
// current working directory to the new root.
|
||||
//
|
||||
// This function bind-mounts onto path; it is the caller's responsibility to set
|
||||
// the desired propagation mode of path's parent mount beforehand to prevent
|
||||
// unwanted propagation into different mount namespaces.
|
||||
func SwitchRoot(path string) error {
|
||||
if mounted, _ := mountinfo.Mounted(path); !mounted {
|
||||
if err := mount.Mount(path, path, "bind", "rbind,rw"); err != nil {
|
||||
return realChroot(path)
|
||||
}
|
||||
}
|
||||
|
||||
// setup oldRoot for pivot_root
|
||||
pivotDir, err := os.MkdirTemp(path, ".pivot_root")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error setting up pivot dir: %v", err)
|
||||
}
|
||||
|
||||
var mounted bool
|
||||
defer func() {
|
||||
if mounted {
|
||||
// make sure pivotDir is not mounted before we try to remove it
|
||||
if errCleanup := unix.Unmount(pivotDir, unix.MNT_DETACH); errCleanup != nil {
|
||||
if err == nil {
|
||||
err = errCleanup
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
errCleanup := os.Remove(pivotDir)
|
||||
// pivotDir doesn't exist if pivot_root failed and chroot+chdir was successful
|
||||
// because we already cleaned it up on failed pivot_root
|
||||
if errCleanup != nil && !os.IsNotExist(errCleanup) {
|
||||
errCleanup = fmt.Errorf("Error cleaning up after pivot: %v", errCleanup)
|
||||
if err == nil {
|
||||
err = errCleanup
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := unix.PivotRoot(path, pivotDir); err != nil {
|
||||
// If pivot fails, fall back to the normal chroot after cleaning up temp dir
|
||||
if err := os.Remove(pivotDir); err != nil {
|
||||
return fmt.Errorf("Error cleaning up after failed pivot: %v", err)
|
||||
}
|
||||
return realChroot(path)
|
||||
}
|
||||
mounted = true
|
||||
|
||||
// This is the new path for where the old root (prior to the pivot) has been moved to
|
||||
// This dir contains the rootfs of the caller, which we need to remove so it is not visible during extraction
|
||||
pivotDir = filepath.Join("/", filepath.Base(pivotDir))
|
||||
|
||||
if err := unix.Chdir("/"); err != nil {
|
||||
return fmt.Errorf("Error changing to new root: %v", err)
|
||||
}
|
||||
|
||||
// Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host
|
||||
if err := unix.Mount("", pivotDir, "", unix.MS_PRIVATE|unix.MS_REC, ""); err != nil {
|
||||
return fmt.Errorf("Error making old root private after pivot: %v", err)
|
||||
}
|
||||
|
||||
// Now unmount the old root so it's no longer visible from the new root
|
||||
if err := unix.Unmount(pivotDir, unix.MNT_DETACH); err != nil {
|
||||
return fmt.Errorf("Error while unmounting old root after pivot: %v", err)
|
||||
}
|
||||
mounted = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func realChroot(path string) error {
|
||||
if err := unix.Chroot(path); err != nil {
|
||||
return fmt.Errorf("Error after fallback to chroot: %v", err)
|
||||
}
|
||||
if err := unix.Chdir("/"); err != nil {
|
||||
return fmt.Errorf("Error changing to new root after chroot: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
//go:build go1.10
|
||||
// +build go1.10
|
||||
|
||||
package unshare // import "github.com/docker/docker/internal/unshare"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// The startup thread of a process is special in a few different ways.
|
||||
// Most pertinent to the discussion at hand, any per-thread kernel state
|
||||
// reflected in the /proc/[pid]/ directory for a process is taken from
|
||||
// the state of the startup thread. Same goes for /proc/self/; it shows
|
||||
// the state of the current process' startup thread, no matter which
|
||||
// thread the files are being opened from. For most programs this is a
|
||||
// distinction without a difference as the kernel state, such as the
|
||||
// mount namespace and current working directory, is shared among (and
|
||||
// kept synchronized across) all threads of a process. But things start
|
||||
// to break down once threads start unsharing and modifying parts of
|
||||
// their kernel state.
|
||||
//
|
||||
// The Go runtime schedules goroutines to execute on the startup thread,
|
||||
// same as any other. How this could be problematic is best illustrated
|
||||
// with a concrete example. Consider what happens if a call to
|
||||
// Go(unix.CLONE_NEWNS, ...) spawned a goroutine which gets scheduled
|
||||
// onto the startup thread. The thread's mount namespace will be
|
||||
// unshared and modified. The contents of the /proc/[pid]/mountinfo file
|
||||
// will then describe the mount tree of the unshared namespace, not the
|
||||
// namespace of any other thread. It will remain this way until the
|
||||
// process exits. (The startup thread is special in another way: exiting
|
||||
// it puts the process into a "non-waitable zombie" state. To avoid this
|
||||
// fate, the Go runtime parks the thread instead of exiting if a
|
||||
// goroutine returns while locked to the startup thread. More
|
||||
// information can be found in the Go runtime sources:
|
||||
// `go doc -u -src runtime.mexit`.) The github.com/moby/sys/mountinfo
|
||||
// package reads from /proc/self/mountinfo, so will read the mount tree
|
||||
// for the wrong namespace if the startup thread has had its mount
|
||||
// namespace unshared! The /proc/thread-self/ directory, introduced in
|
||||
// Linux 3.17, is one potential solution to this problem, but every
|
||||
// package which opens files in /proc/self/ would need to be updated,
|
||||
// and fallbacks to /proc/self/task/[tid]/ would be required to support
|
||||
// older kernels. Overlooking any reference to /proc/self/ would
|
||||
// manifest as stochastically-reproducible bugs, so this is far from an
|
||||
// ideal solution.
|
||||
//
|
||||
// Reading from /proc/self/ would not be a problem if we could prevent
|
||||
// the per-thread state of the startup thread from being modified
|
||||
// nondeterministically in the first place. We can accomplish this
|
||||
// simply by locking the main() function to the startup thread! Doing so
|
||||
// excludes any other goroutine from being scheduled on the thread.
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
// reversibleSetnsFlags maps the unshare(2) flags whose effects can be fully
|
||||
// reversed using setns(2). The values are the basenames of the corresponding
|
||||
// /proc/self/task/[tid]/ns/ magic symlinks to use to save and restore the
|
||||
// state.
|
||||
var reversibleSetnsFlags = map[int]string{
|
||||
unix.CLONE_NEWCGROUP: "cgroup",
|
||||
unix.CLONE_NEWNET: "net",
|
||||
unix.CLONE_NEWUTS: "uts",
|
||||
unix.CLONE_NEWPID: "pid",
|
||||
unix.CLONE_NEWTIME: "time",
|
||||
|
||||
// The following CLONE_NEW* flags are not included because they imply
|
||||
// another, irreversible flag when used with unshare(2).
|
||||
// - unix.CLONE_NEWIPC: implies CLONE_SYSVMEM
|
||||
// - unix.CLONE_NEWNS: implies CLONE_FS
|
||||
// - unix.CLONE_NEWUSER: implies CLONE_FS since Linux 3.9
|
||||
}
|
||||
|
||||
// Go calls the given functions in a new goroutine, locked to an OS thread,
|
||||
// which has had the parts of its execution state disassociated from the rest of
|
||||
// the current process using [unshare(2)]. It blocks until the new goroutine has
|
||||
// started and setupfn has returned. fn is only called if setupfn returns nil. A
|
||||
// nil setupfn or fn is equivalent to passing a no-op function.
|
||||
//
|
||||
// The disassociated execution state and any changes made to it are only visible
|
||||
// to the goroutine which the functions are called in. Any other goroutines,
|
||||
// including ones started from the function, will see the same execution state
|
||||
// as the rest of the process.
|
||||
//
|
||||
// The acceptable flags are documented in the [unshare(2)] Linux man-page.
|
||||
// The corresponding CLONE_* constants are defined in package [unix].
|
||||
//
|
||||
// # Warning
|
||||
//
|
||||
// This function may terminate the thread which the new goroutine executed on
|
||||
// after fn returns, which could cause subprocesses started with the
|
||||
// [syscall.SysProcAttr] Pdeathsig field set to be signaled before process
|
||||
// termination. Any subprocess started before this function is called may be
|
||||
// affected, in addition to any subprocesses started inside setupfn or fn.
|
||||
// There are more details at https://go.dev/issue/27505.
|
||||
//
|
||||
// [unshare(2)]: https://man7.org/linux/man-pages/man2/unshare.2.html
|
||||
func Go(flags int, setupfn func() error, fn func()) error {
|
||||
started := make(chan error)
|
||||
|
||||
maskedFlags := flags
|
||||
for f := range reversibleSetnsFlags {
|
||||
maskedFlags &^= f
|
||||
}
|
||||
isReversible := maskedFlags == 0
|
||||
|
||||
go func() {
|
||||
// Prepare to manipulate per-thread kernel state.
|
||||
runtime.LockOSThread()
|
||||
|
||||
// Not all changes to the execution state can be reverted.
|
||||
// If an irreversible change to the execution state is made, our
|
||||
// only recourse is to have the tampered thread terminated by
|
||||
// returning from this function while the goroutine remains
|
||||
// wired to the thread. The Go runtime will terminate the thread
|
||||
// and replace it with a fresh one as needed.
|
||||
|
||||
if isReversible {
|
||||
defer func() {
|
||||
if isReversible {
|
||||
// All execution state has been restored without error.
|
||||
// The thread is once again fungible.
|
||||
runtime.UnlockOSThread()
|
||||
}
|
||||
}()
|
||||
tid := unix.Gettid()
|
||||
for f, ns := range reversibleSetnsFlags {
|
||||
if flags&f != f {
|
||||
continue
|
||||
}
|
||||
// The /proc/thread-self directory was added in Linux 3.17.
|
||||
// We are not using it to maximize compatibility.
|
||||
pth := fmt.Sprintf("/proc/self/task/%d/ns/%s", tid, ns)
|
||||
fd, err := unix.Open(pth, unix.O_RDONLY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
started <- &os.PathError{Op: "open", Path: pth, Err: err}
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if isReversible {
|
||||
if err := unix.Setns(fd, 0); err != nil {
|
||||
isReversible = false
|
||||
}
|
||||
}
|
||||
_ = unix.Close(fd)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Threads are implemented under Linux as processes which share
|
||||
// a virtual memory space. Therefore in a multithreaded process
|
||||
// unshare(2) disassociates parts of the calling thread's
|
||||
// context from the thread it was clone(2)'d from.
|
||||
if err := unix.Unshare(flags); err != nil {
|
||||
started <- os.NewSyscallError("unshare", err)
|
||||
return
|
||||
}
|
||||
|
||||
if setupfn != nil {
|
||||
if err := setupfn(); err != nil {
|
||||
started <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
close(started)
|
||||
|
||||
if fn != nil {
|
||||
fn()
|
||||
}
|
||||
}()
|
||||
|
||||
return <-started
|
||||
}
|
||||
+15
-17
@@ -3,7 +3,6 @@ package layer // import "github.com/docker/docker/layer"
|
||||
import (
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -40,7 +39,7 @@ type fileMetadataTransaction struct {
|
||||
// which is backed by files on disk using the provided root
|
||||
// as the root of metadata files.
|
||||
func newFSMetadataStore(root string) (*fileMetadataStore, error) {
|
||||
if err := os.MkdirAll(root, 0700); err != nil {
|
||||
if err := os.MkdirAll(root, 0o700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fileMetadataStore{
|
||||
@@ -67,7 +66,7 @@ func (fms *fileMetadataStore) getMountFilename(mount, filename string) string {
|
||||
|
||||
func (fms *fileMetadataStore) StartTransaction() (*fileMetadataTransaction, error) {
|
||||
tmpDir := filepath.Join(fms.root, "tmp")
|
||||
if err := os.MkdirAll(tmpDir, 0755); err != nil {
|
||||
if err := os.MkdirAll(tmpDir, 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ws, err := ioutils.NewAtomicWriteSet(tmpDir)
|
||||
@@ -82,20 +81,19 @@ func (fms *fileMetadataStore) StartTransaction() (*fileMetadataTransaction, erro
|
||||
}
|
||||
|
||||
func (fm *fileMetadataTransaction) SetSize(size int64) error {
|
||||
content := fmt.Sprintf("%d", size)
|
||||
return fm.ws.WriteFile("size", []byte(content), 0644)
|
||||
return fm.ws.WriteFile("size", []byte(strconv.FormatInt(size, 10)), 0o644)
|
||||
}
|
||||
|
||||
func (fm *fileMetadataTransaction) SetParent(parent ChainID) error {
|
||||
return fm.ws.WriteFile("parent", []byte(digest.Digest(parent).String()), 0644)
|
||||
return fm.ws.WriteFile("parent", []byte(digest.Digest(parent).String()), 0o644)
|
||||
}
|
||||
|
||||
func (fm *fileMetadataTransaction) SetDiffID(diff DiffID) error {
|
||||
return fm.ws.WriteFile("diff", []byte(digest.Digest(diff).String()), 0644)
|
||||
return fm.ws.WriteFile("diff", []byte(digest.Digest(diff).String()), 0o644)
|
||||
}
|
||||
|
||||
func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error {
|
||||
return fm.ws.WriteFile("cache-id", []byte(cacheID), 0644)
|
||||
return fm.ws.WriteFile("cache-id", []byte(cacheID), 0o644)
|
||||
}
|
||||
|
||||
func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) error {
|
||||
@@ -103,11 +101,11 @@ func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) er
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fm.ws.WriteFile("descriptor.json", jsonRef, 0644)
|
||||
return fm.ws.WriteFile("descriptor.json", jsonRef, 0o644)
|
||||
}
|
||||
|
||||
func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) {
|
||||
f, err := fm.ws.FileWriter("tar-split.json.gz", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
f, err := fm.ws.FileWriter("tar-split.json.gz", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -126,7 +124,7 @@ func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteC
|
||||
|
||||
func (fm *fileMetadataTransaction) Commit(layer ChainID) error {
|
||||
finalDir := fm.store.getLayerDirectory(layer)
|
||||
if err := os.MkdirAll(filepath.Dir(finalDir), 0755); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(finalDir), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -236,24 +234,24 @@ func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, erro
|
||||
}
|
||||
|
||||
func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error {
|
||||
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
|
||||
if err := os.MkdirAll(fms.getMountDirectory(mount), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644)
|
||||
return os.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0o644)
|
||||
}
|
||||
|
||||
func (fms *fileMetadataStore) SetInitID(mount string, init string) error {
|
||||
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
|
||||
if err := os.MkdirAll(fms.getMountDirectory(mount), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644)
|
||||
return os.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0o644)
|
||||
}
|
||||
|
||||
func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error {
|
||||
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
|
||||
if err := os.MkdirAll(fms.getMountDirectory(mount), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644)
|
||||
return os.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0o644)
|
||||
}
|
||||
|
||||
func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
|
||||
|
||||
+2
-3
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -114,7 +113,7 @@ type RWLayer interface {
|
||||
|
||||
// Mount mounts the RWLayer and returns the filesystem path
|
||||
// to the writable layer.
|
||||
Mount(mountLabel string) (containerfs.ContainerFS, error)
|
||||
Mount(mountLabel string) (string, error)
|
||||
|
||||
// Unmount unmounts the RWLayer. This should be called
|
||||
// for every mount. If there are multiple mount calls
|
||||
@@ -158,7 +157,7 @@ type Metadata struct {
|
||||
// writable mount. Changes made here will
|
||||
// not be included in the Tar stream of the
|
||||
// RWLayer.
|
||||
type MountInit func(root containerfs.ContainerFS) error
|
||||
type MountInit func(root string) error
|
||||
|
||||
// CreateRWLayerOpts contains optional arguments to be passed to CreateRWLayer
|
||||
type CreateRWLayerOpts struct {
|
||||
|
||||
+23
-20
@@ -62,6 +62,9 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
|
||||
ExperimentalEnabled: options.ExperimentalEnabled,
|
||||
})
|
||||
if err != nil {
|
||||
if options.GraphDriver != "" {
|
||||
return nil, fmt.Errorf("error initializing graphdriver: %v: %s", err, options.GraphDriver)
|
||||
}
|
||||
return nil, fmt.Errorf("error initializing graphdriver: %v", err)
|
||||
}
|
||||
logrus.Debugf("Initialized graph driver %s", driver)
|
||||
@@ -265,10 +268,10 @@ func (ls *layerStore) Register(ts io.Reader, parent ChainID) (Layer, error) {
|
||||
}
|
||||
|
||||
func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descriptor distribution.Descriptor) (Layer, error) {
|
||||
// err is used to hold the error which will always trigger
|
||||
// cErr is used to hold the error which will always trigger
|
||||
// cleanup of creates sources but may not be an error returned
|
||||
// to the caller (already exists).
|
||||
var err error
|
||||
var cErr error
|
||||
var pid string
|
||||
var p *roLayer
|
||||
|
||||
@@ -282,15 +285,15 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descr
|
||||
pid = p.cacheID
|
||||
// Release parent chain if error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if cErr != nil {
|
||||
ls.layerL.Lock()
|
||||
ls.releaseLayer(p)
|
||||
ls.layerL.Unlock()
|
||||
}
|
||||
}()
|
||||
if p.depth() >= maxLayerDepth {
|
||||
err = ErrMaxDepthExceeded
|
||||
return nil, err
|
||||
cErr = ErrMaxDepthExceeded
|
||||
return nil, cErr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,18 +307,18 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descr
|
||||
descriptor: descriptor,
|
||||
}
|
||||
|
||||
if err = ls.driver.Create(layer.cacheID, pid, nil); err != nil {
|
||||
return nil, err
|
||||
if cErr = ls.driver.Create(layer.cacheID, pid, nil); cErr != nil {
|
||||
return nil, cErr
|
||||
}
|
||||
|
||||
tx, err := ls.store.StartTransaction()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
tx, cErr := ls.store.StartTransaction()
|
||||
if cErr != nil {
|
||||
return nil, cErr
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
logrus.Debugf("Cleaning up layer %s: %v", layer.cacheID, err)
|
||||
if cErr != nil {
|
||||
logrus.Debugf("Cleaning up layer %s: %v", layer.cacheID, cErr)
|
||||
if err := ls.driver.Remove(layer.cacheID); err != nil {
|
||||
logrus.Errorf("Error cleaning up cache layer %s: %v", layer.cacheID, err)
|
||||
}
|
||||
@@ -325,8 +328,8 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descr
|
||||
}
|
||||
}()
|
||||
|
||||
if err = ls.applyTar(tx, ts, pid, layer); err != nil {
|
||||
return nil, err
|
||||
if cErr = ls.applyTar(tx, ts, pid, layer); cErr != nil {
|
||||
return nil, cErr
|
||||
}
|
||||
|
||||
if layer.parent == nil {
|
||||
@@ -335,8 +338,8 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descr
|
||||
layer.chainID = createChainIDFromParent(layer.parent.chainID, layer.diffID)
|
||||
}
|
||||
|
||||
if err = storeLayer(tx, layer); err != nil {
|
||||
return nil, err
|
||||
if cErr = storeLayer(tx, layer); cErr != nil {
|
||||
return nil, cErr
|
||||
}
|
||||
|
||||
ls.layerL.Lock()
|
||||
@@ -344,12 +347,12 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descr
|
||||
|
||||
if existingLayer := ls.get(layer.chainID); existingLayer != nil {
|
||||
// Set error for cleanup, but do not return the error
|
||||
err = errors.New("layer already exists")
|
||||
cErr = errors.New("layer already exists")
|
||||
return existingLayer.getReference(), nil
|
||||
}
|
||||
|
||||
if err = tx.Commit(layer.chainID); err != nil {
|
||||
return nil, err
|
||||
if cErr = tx.Commit(layer.chainID); cErr != nil {
|
||||
return nil, cErr
|
||||
}
|
||||
|
||||
ls.layerMap[layer.chainID] = layer
|
||||
@@ -786,5 +789,5 @@ func (n *naiveDiffPathDriver) DiffGetter(id string) (graphdriver.FileGetCloser,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fileGetPutter{storage.NewPathFileGetter(p.Path()), n.Driver, id}, nil
|
||||
return &fileGetPutter{storage.NewPathFileGetter(p), n.Driver, id}, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ func GetLayerPath(s Store, layer ChainID) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path.Path(), nil
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (ls *layerStore) mountID(name string) string {
|
||||
|
||||
+1
-2
@@ -5,7 +5,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
)
|
||||
|
||||
type mountedLayer struct {
|
||||
@@ -100,7 +99,7 @@ type referencedRWLayer struct {
|
||||
*mountedLayer
|
||||
}
|
||||
|
||||
func (rl *referencedRWLayer) Mount(mountLabel string) (containerfs.ContainerFS, error) {
|
||||
func (rl *referencedRWLayer) Mount(mountLabel string) (string, error) {
|
||||
return rl.layerStore.driver.Get(rl.mountedLayer.mountID, mountLabel)
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -175,6 +175,7 @@ func (vrc *verifiedReadCloser) Read(p []byte) (n int, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (vrc *verifiedReadCloser) Close() error {
|
||||
return vrc.rc.Close()
|
||||
}
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package types // import "github.com/docker/docker/libcontainerd/types"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/cio"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// EventType represents a possible event from libcontainerd
|
||||
type EventType string
|
||||
|
||||
// Event constants used when reporting events
|
||||
const (
|
||||
EventUnknown EventType = "unknown"
|
||||
EventExit EventType = "exit"
|
||||
EventOOM EventType = "oom"
|
||||
EventCreate EventType = "create"
|
||||
EventStart EventType = "start"
|
||||
EventExecAdded EventType = "exec-added"
|
||||
EventExecStarted EventType = "exec-started"
|
||||
EventPaused EventType = "paused"
|
||||
EventResumed EventType = "resumed"
|
||||
)
|
||||
|
||||
// EventInfo contains the event info
|
||||
type EventInfo struct {
|
||||
ContainerID string
|
||||
ProcessID string
|
||||
Pid uint32
|
||||
ExitCode uint32
|
||||
ExitedAt time.Time
|
||||
Error error
|
||||
}
|
||||
|
||||
// Backend defines callbacks that the client of the library needs to implement.
|
||||
type Backend interface {
|
||||
ProcessEvent(containerID string, event EventType, ei EventInfo) error
|
||||
}
|
||||
|
||||
// Process of a container
|
||||
type Process interface {
|
||||
// Pid is the system specific process id
|
||||
Pid() uint32
|
||||
// Kill sends the provided signal to the process
|
||||
Kill(ctx context.Context, signal syscall.Signal) error
|
||||
// Resize changes the width and height of the process's terminal
|
||||
Resize(ctx context.Context, width, height uint32) error
|
||||
// Delete removes the process and any resources allocated returning the exit status
|
||||
Delete(context.Context) (*containerd.ExitStatus, error)
|
||||
}
|
||||
|
||||
// Client provides access to containerd features.
|
||||
type Client interface {
|
||||
Version(ctx context.Context) (containerd.Version, error)
|
||||
// LoadContainer loads the metadata for a container from containerd.
|
||||
LoadContainer(ctx context.Context, containerID string) (Container, error)
|
||||
// NewContainer creates a new containerd container.
|
||||
NewContainer(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) (Container, error)
|
||||
}
|
||||
|
||||
// Container provides access to a containerd container.
|
||||
type Container interface {
|
||||
Start(ctx context.Context, checkpointDir string, withStdin bool, attachStdio StdioCallback) (Task, error)
|
||||
Task(ctx context.Context) (Task, error)
|
||||
// AttachTask returns the current task for the container and reattaches
|
||||
// to the IO for the running task. If no task exists for the container
|
||||
// a NotFound error is returned.
|
||||
//
|
||||
// Clients must make sure that only one reader is attached to the task.
|
||||
AttachTask(ctx context.Context, attachStdio StdioCallback) (Task, error)
|
||||
// Delete removes the container and associated resources
|
||||
Delete(context.Context) error
|
||||
}
|
||||
|
||||
// Task provides access to a running containerd container.
|
||||
type Task interface {
|
||||
Process
|
||||
// Pause suspends the execution of the task
|
||||
Pause(context.Context) error
|
||||
// Resume the execution of the task
|
||||
Resume(context.Context) error
|
||||
Stats(ctx context.Context) (*Stats, error)
|
||||
// Pids returns a list of system specific process ids inside the task
|
||||
Pids(context.Context) ([]containerd.ProcessInfo, error)
|
||||
Summary(ctx context.Context) ([]Summary, error)
|
||||
// ForceDelete forcefully kills the task's processes and deletes the task
|
||||
ForceDelete(context.Context) error
|
||||
// Status returns the executing status of the task
|
||||
Status(ctx context.Context) (containerd.Status, error)
|
||||
// Exec creates and starts a new process inside the task
|
||||
Exec(ctx context.Context, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (Process, error)
|
||||
UpdateResources(ctx context.Context, resources *Resources) error
|
||||
CreateCheckpoint(ctx context.Context, checkpointDir string, exit bool) error
|
||||
}
|
||||
|
||||
// StdioCallback is called to connect a container or process stdio.
|
||||
type StdioCallback func(io *cio.DirectIO) (cio.IO, error)
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package types // import "github.com/docker/docker/libcontainerd/types"
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// Summary is not used on linux
|
||||
type Summary struct{}
|
||||
|
||||
// Stats holds metrics properties as returned by containerd
|
||||
type Stats struct {
|
||||
Read time.Time
|
||||
// Metrics is expected to be either one of:
|
||||
// * github.com/containerd/cgroups/v3/cgroup1/stats.Metrics
|
||||
// * github.com/containerd/cgroups/v3/cgroup2/stats.Metrics
|
||||
Metrics interface{}
|
||||
}
|
||||
|
||||
// InterfaceToStats returns a stats object from the platform-specific interface.
|
||||
func InterfaceToStats(read time.Time, v interface{}) *Stats {
|
||||
return &Stats{
|
||||
Metrics: v,
|
||||
Read: read,
|
||||
}
|
||||
}
|
||||
|
||||
// Resources defines updatable container resource values. TODO: it must match containerd upcoming API
|
||||
type Resources = specs.LinuxResources
|
||||
|
||||
// Checkpoints contains the details of a checkpoint
|
||||
type Checkpoints struct{}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package types // import "github.com/docker/docker/libcontainerd/types"
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
|
||||
)
|
||||
|
||||
type Summary options.ProcessDetails
|
||||
|
||||
// Stats contains statistics from HCS
|
||||
type Stats struct {
|
||||
Read time.Time
|
||||
HCSStats *hcsshim.Statistics
|
||||
}
|
||||
|
||||
// InterfaceToStats returns a stats object from the platform-specific interface.
|
||||
func InterfaceToStats(read time.Time, v interface{}) *Stats {
|
||||
return &Stats{
|
||||
HCSStats: v.(*hcsshim.Statistics),
|
||||
Read: read,
|
||||
}
|
||||
}
|
||||
|
||||
// Resources defines updatable container resource values.
|
||||
type Resources struct{}
|
||||
|
||||
// Checkpoint holds the details of a checkpoint (not supported in windows)
|
||||
type Checkpoint struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// Checkpoints contains the details of a checkpoint
|
||||
type Checkpoints struct {
|
||||
Checkpoints []*Checkpoint
|
||||
}
|
||||
+20
-4
@@ -1,16 +1,31 @@
|
||||
package oci // import "github.com/docker/docker/oci"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/oci/caps"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
func iPtr(i int64) *int64 { return &i }
|
||||
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
|
||||
func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
|
||||
func iPtr(i int64) *int64 { return &i }
|
||||
|
||||
const defaultUnixPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
// DefaultPathEnv is unix style list of directories to search for
|
||||
// executables. Each directory is separated from the next by a colon
|
||||
// ':' character .
|
||||
// For Windows containers, an empty string is returned as the default
|
||||
// path will be set by the container, and Docker has no context of what the
|
||||
// default path should be.
|
||||
//
|
||||
// TODO(thaJeztah) align Windows default with BuildKit; see https://github.com/moby/buildkit/pull/1747
|
||||
// TODO(thaJeztah) use defaults from containerd (but align it with BuildKit; see https://github.com/moby/buildkit/pull/1747)
|
||||
func DefaultPathEnv(os string) string {
|
||||
if os == "windows" {
|
||||
return ""
|
||||
}
|
||||
return defaultUnixPathEnv
|
||||
}
|
||||
|
||||
// DefaultSpec returns the default spec used by docker for the current Platform
|
||||
func DefaultSpec() specs.Spec {
|
||||
@@ -98,6 +113,7 @@ func DefaultLinuxSpec() specs.Spec {
|
||||
"/proc/sched_debug",
|
||||
"/proc/scsi",
|
||||
"/sys/firmware",
|
||||
"/sys/devices/virtual/powercap",
|
||||
},
|
||||
ReadonlyPaths: []string{
|
||||
"/proc/bus",
|
||||
|
||||
+11
-25
@@ -6,31 +6,17 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/devices"
|
||||
coci "github.com/containerd/containerd/oci"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Device transforms a libcontainer devices.Device to a specs.LinuxDevice object.
|
||||
func Device(d *devices.Device) specs.LinuxDevice {
|
||||
return specs.LinuxDevice{
|
||||
Type: string(d.Type),
|
||||
Path: d.Path,
|
||||
Major: d.Major,
|
||||
Minor: d.Minor,
|
||||
FileMode: fmPtr(int64(d.FileMode &^ unix.S_IFMT)), // strip file type, as OCI spec only expects file-mode to be included
|
||||
UID: u32Ptr(int64(d.Uid)),
|
||||
GID: u32Ptr(int64(d.Gid)),
|
||||
}
|
||||
}
|
||||
|
||||
func deviceCgroup(d *devices.Device) specs.LinuxDeviceCgroup {
|
||||
func deviceCgroup(d *specs.LinuxDevice, permissions string) specs.LinuxDeviceCgroup {
|
||||
return specs.LinuxDeviceCgroup{
|
||||
Allow: true,
|
||||
Type: string(d.Type),
|
||||
Type: d.Type,
|
||||
Major: &d.Major,
|
||||
Minor: &d.Minor,
|
||||
Access: string(d.Permissions),
|
||||
Access: permissions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,22 +31,22 @@ func DevicesFromPath(pathOnHost, pathInContainer, cgroupPermissions string) (dev
|
||||
}
|
||||
}
|
||||
|
||||
device, err := devices.DeviceFromPath(resolvedPathOnHost, cgroupPermissions)
|
||||
device, err := coci.DeviceFromPath(resolvedPathOnHost)
|
||||
// if there was no error, return the device
|
||||
if err == nil {
|
||||
device.Path = pathInContainer
|
||||
return append(devs, Device(device)), append(devPermissions, deviceCgroup(device)), nil
|
||||
return append(devs, *device), append(devPermissions, deviceCgroup(device, cgroupPermissions)), nil
|
||||
}
|
||||
|
||||
// if the device is not a device node
|
||||
// try to see if it's a directory holding many devices
|
||||
if err == devices.ErrNotADevice {
|
||||
if err == coci.ErrNotADevice {
|
||||
// check if it is a directory
|
||||
if src, e := os.Stat(resolvedPathOnHost); e == nil && src.IsDir() {
|
||||
// mount the internal devices recursively
|
||||
// TODO check if additional errors should be handled or logged
|
||||
_ = filepath.Walk(resolvedPathOnHost, func(dpath string, f os.FileInfo, _ error) error {
|
||||
childDevice, e := devices.DeviceFromPath(dpath, cgroupPermissions)
|
||||
_ = filepath.WalkDir(resolvedPathOnHost, func(dpath string, f os.DirEntry, _ error) error {
|
||||
childDevice, e := coci.DeviceFromPath(dpath)
|
||||
if e != nil {
|
||||
// ignore the device
|
||||
return nil
|
||||
@@ -68,8 +54,8 @@ func DevicesFromPath(pathOnHost, pathInContainer, cgroupPermissions string) (dev
|
||||
|
||||
// add the device to userSpecified devices
|
||||
childDevice.Path = strings.Replace(dpath, resolvedPathOnHost, pathInContainer, 1)
|
||||
devs = append(devs, Device(childDevice))
|
||||
devPermissions = append(devPermissions, deviceCgroup(childDevice))
|
||||
devs = append(devs, *childDevice)
|
||||
devPermissions = append(devPermissions, deviceCgroup(childDevice, cgroupPermissions))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
+3
@@ -4,6 +4,9 @@ import specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
|
||||
// RemoveNamespace removes the `nsType` namespace from OCI spec `s`
|
||||
func RemoveNamespace(s *specs.Spec, nsType specs.LinuxNamespaceType) {
|
||||
if s.Linux == nil {
|
||||
return
|
||||
}
|
||||
for i, n := range s.Linux.Namespaces {
|
||||
if n.Type == nsType {
|
||||
s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)
|
||||
|
||||
+7
-13
@@ -20,19 +20,13 @@ var deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\
|
||||
// SetCapabilities sets the provided capabilities on the spec
|
||||
// All capabilities are added if privileged is true.
|
||||
func SetCapabilities(s *specs.Spec, caplist []string) error {
|
||||
// setUser has already been executed here
|
||||
if s.Process.User.UID == 0 {
|
||||
s.Process.Capabilities = &specs.LinuxCapabilities{
|
||||
Effective: caplist,
|
||||
Bounding: caplist,
|
||||
Permitted: caplist,
|
||||
}
|
||||
} else {
|
||||
// Do not set Effective and Permitted capabilities for non-root users,
|
||||
// to match what execve does.
|
||||
s.Process.Capabilities = &specs.LinuxCapabilities{
|
||||
Bounding: caplist,
|
||||
}
|
||||
if s.Process == nil {
|
||||
s.Process = &specs.Process{}
|
||||
}
|
||||
s.Process.Capabilities = &specs.LinuxCapabilities{
|
||||
Effective: caplist,
|
||||
Bounding: caplist,
|
||||
Permitted: caplist,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user