Compare commits

...

8 Commits

Author SHA1 Message Date
Laura Brehm cdaf4f8151
Merge d6ce04640f into da9e984231 2024-10-18 10:43:46 +01:00
Sebastiaan van Stijn da9e984231
Merge pull request #5541 from thaJeztah/template_coverage
templates: add test for HeaderFunctions
2024-10-18 11:42:00 +02:00
Paweł Gronowski 38653277af
Merge pull request #5539 from thaJeztah/bump_swarmkit
vendor: github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e
2024-10-18 10:44:41 +02:00
Sebastiaan van Stijn 12dcc6e25c
templates: add test for HeaderFunctions
Before:

    go test -test.coverprofile -
    PASS
    coverage: 65.2% of statements
    ok  	github.com/docker/cli/templates	0.607s

After:

    go test -test.coverprofile -
    PASS
    coverage: 95.7% of statements
    ok  	github.com/docker/cli/templates	0.259s

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-18 10:07:33 +02:00
Sebastiaan van Stijn cbbb917323
vendor: github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e
- add Unwrap error to custom error types
- removes dependency on github.com/rexray/gocsi
- fix CSI plugin load issue

full diff: ea1a7cec35...e8ecf83ee0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-17 23:01:19 +02:00
Paweł Gronowski 3590f946a3
Merge pull request #5535 from dvdksn/fix-image-tag-spec
docs: update prose about image tag/name format
2024-10-17 12:46:46 +02:00
David Karlsson 2c6b80491b docs: update prose about image tag/name format
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-10-17 12:36:32 +02:00
Laura Brehm d6ce04640f
Support plaintext credentials as multi-call binary
The Docker CLI supports storing/managing credentials without a
credential-helper, in which case credentials are fetched from/saved to
the CLI config file (`~/.docker/config.json`). This is all managed
entirely by the CLI itself, without resort to a separate binary.

There are a few issues with this approach – for one, saving the
credentials together with all the configurations make it impossible to
share one without the other, so one can't for example bind mount the
config file into a container without also including all configured
credentials.

Another issue is that this has made it so that any other clients
accessing registry credentials (such as
https://github.com/google/go-containerregistry) all have to both:
- read/parse the CLI `config.json`, to check for credentials there,
  which also means they're dependent on this type and might break if the
  type changes/we need to be careful not to break other codebases parsing
  this file, and can't change the location where plaintext credentials
  are stored.
- support the credential helper protocol, so that they can access
  credentials when users do have configured credential helpers.

This means that if we want to do something like support oauth
credentials by having credential-helpers refresh oauth tokens before
returning them, we have to both implement that in each credential-helper
and in the CLI itself, and any client directly reading `config.json`
will also need to implement this logic.

This commit turns the Docker CLI binary into a multicall binary, acting
as a standalone credentials helper when invoked as
`docker-credential-file`, while still storing/fetching credentials from
the configuration file (`~/.docker/config.json`), and without any
further changes.

This represents a first step into aligning the "no credhelper"/plaintext
flow with the "credhelper" flow, meaning that instead of this being an
exception where credentials must be read directly from the config file,
credentials can now be accessed in the exact same way as with other
credential helpers – by invoking `docker-credential-[credhelper name]`,
such as `docker-credential-pass`, `docker-credential-osxkeychain` or
`docker-credential-wincred`.

This would also make it possible for any other clients accessing
credentials to untangle themselves from things like the location of the
credentials, parsing credentials from `config.json`, etc. and instead
simply support the credential-helper protocol, and call the
`docker-credential-file` binary as they do others.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-10-15 15:19:01 +01:00
11 changed files with 293 additions and 32 deletions

View File

@ -19,8 +19,10 @@ import (
cliflags "github.com/docker/cli/cli/flags" cliflags "github.com/docker/cli/cli/flags"
"github.com/docker/cli/cli/version" "github.com/docker/cli/cli/version"
platformsignals "github.com/docker/cli/cmd/docker/internal/signals" platformsignals "github.com/docker/cli/cmd/docker/internal/signals"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/reexec"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -29,6 +31,10 @@ import (
) )
func main() { func main() {
if reexec.Init() {
return
}
err := dockerMain(context.Background()) err := dockerMain(context.Background())
if err != nil && !errdefs.IsCancelled(err) { if err != nil && !errdefs.IsCancelled(err) {
_, _ = fmt.Fprintln(os.Stderr, err) _, _ = fmt.Fprintln(os.Stderr, err)

69
cmd/docker/file_helper.go Normal file
View File

@ -0,0 +1,69 @@
package main
import (
"os"
credhelpers "github.com/docker/docker-credential-helpers/credentials"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/credentials"
"github.com/docker/cli/cli/config/types"
)
//nolint:gosec // ignore G101: Potential hardcoded credentials
const fileCredsHelperBinary = "docker-credential-file"
func init() {
reexec.Register(fileCredsHelperBinary, serveFileCredHelper)
}
func serveFileCredHelper() {
configfile := config.LoadDefaultConfigFile(os.Stderr)
store := credentials.NewFileStore(configfile)
credhelpers.Serve(&FileHelper{
fileStore: store,
})
}
var _ credhelpers.Helper = &FileHelper{}
type FileHelper struct {
fileStore credentials.Store
}
func (f *FileHelper) Add(creds *credhelpers.Credentials) error {
return f.fileStore.Store(types.AuthConfig{
Username: creds.Username,
Password: creds.Secret,
ServerAddress: creds.ServerURL,
})
}
func (f *FileHelper) Delete(serverAddress string) error {
return f.fileStore.Erase(serverAddress)
}
func (f *FileHelper) Get(serverAddress string) (string, string, error) {
authConfig, err := f.fileStore.Get(serverAddress)
if err != nil {
return "", "", err
}
return authConfig.Username, authConfig.Password, nil
}
func (f *FileHelper) List() (map[string]string, error) {
creds := make(map[string]string)
authConfig, err := f.fileStore.GetAll()
if err != nil {
return nil, err
}
for k, v := range authConfig {
creds[k] = v.Username
}
return creds, nil
}

View File

@ -12,38 +12,50 @@ Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
## Description ## Description
A full image name has the following format and components: A Docker image reference consists of several components that describe where the
image is stored and its identity. These components are:
`[HOST[:PORT_NUMBER]/]PATH` ```text
[HOST[:PORT]/]NAMESPACE/REPOSITORY[:TAG]
```
- `HOST`: The optional registry hostname specifies where the image is located. `HOST`
The hostname must comply with standard DNS rules, but may not contain : Specifies the registry location where the image resides. If omitted, Docker
underscores. If you don't specify a hostname, the command uses Docker's public defaults to Docker Hub (`docker.io`).
registry at `registry-1.docker.io` by default. Note that `docker.io` is the
canonical reference for Docker's public registry.
- `PORT_NUMBER`: If a hostname is present, it may optionally be followed by a
registry port number in the format `:8080`.
- `PATH`: The path consists of slash-separated components. Each
component may contain lowercase letters, digits and separators. A separator is
defined as a period, one or two underscores, or one or more hyphens. A component
may not start or end with a separator. While the
[OCI Distribution Specification](https://github.com/opencontainers/distribution-spec)
supports more than two slash-separated components, most registries only support
two slash-separated components. For Docker's public registry, the path format is
as follows:
- `[NAMESPACE/]REPOSITORY`: The first, optional component is typically a
user's or an organization's namespace. The second, mandatory component is the
repository name. When the namespace is not present, Docker uses `library`
as the default namespace.
After the image name, the optional `TAG` is a custom, human-readable manifest `PORT`
identifier that's typically a specific version or variant of an image. The tag : An optional port number for the registry, if necessary (for example, `:5000`).
must be valid ASCII and can contain lowercase and uppercase letters, digits,
underscores, periods, and hyphens. It can't start with a period or hyphen and
must be no longer than 128 characters. If you don't specify a tag, the command uses `latest` by default.
You can group your images together using names and tags, and then `NAMESPACE/REPOSITORY`
[push](image_push.md) them to a registry. : The namespace (optional) usually represents a user or organization. The
repository is required and identifies the specific image. If the namespace is
omitted, Docker defaults to `library`, the namespace reserved for Docker
Official Images.
`TAG`
: An optional identifier used to specify a particular version or variant of the
image. If no tag is provided, Docker defaults to `latest`.
### Example image references
`example.com:5000/team/my-app:2.0`
- Host: `example.com`
- Port: `5000`
- Namespace: `team`
- Repository: `my-app`
- Tag: `2.0`
`alpine`
- Host: `docker.io` (default)
- Namespace: `library` (default)
- Repository: `alpine`
- Tag: `latest` (default)
For more information on the structure and rules of image naming, refer to the
[Distribution reference](https://pkg.go.dev/github.com/distribution/reference#pkg-overview)
as the canonical definition of the format.
## Examples ## Examples

View File

@ -89,3 +89,55 @@ func TestParseTruncateFunction(t *testing.T) {
}) })
} }
} }
func TestHeaderFunctions(t *testing.T) {
const source = "hello world"
tests := []struct {
doc string
template string
}{
{
doc: "json",
template: `{{ json .}}`,
},
{
doc: "split",
template: `{{ split . ","}}`,
},
{
doc: "join",
template: `{{ join . ","}}`,
},
{
doc: "title",
template: `{{ title .}}`,
},
{
doc: "lower",
template: `{{ lower .}}`,
},
{
doc: "upper",
template: `{{ upper .}}`,
},
{
doc: "truncate",
template: `{{ truncate . 2}}`,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
tmpl, err := New("").Funcs(HeaderFunctions).Parse(tc.template)
assert.NilError(t, err)
var b bytes.Buffer
assert.NilError(t, tmpl.Execute(&b, source))
// All header-functions are currently stubs, and don't modify the input.
expected := source
assert.Equal(t, expected, b.String())
})
}
}

View File

@ -25,7 +25,7 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/mattn/go-runewidth v0.0.15 github.com/mattn/go-runewidth v0.0.15
github.com/moby/patternmatcher v0.6.0 github.com/moby/patternmatcher v0.6.0
github.com/moby/swarmkit/v2 v2.0.0-20240611172349-ea1a7cec35cb github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e
github.com/moby/sys/capability v0.3.0 github.com/moby/sys/capability v0.3.0
github.com/moby/sys/sequential v0.6.0 github.com/moby/sys/sequential v0.6.0
github.com/moby/sys/signal v0.7.1 github.com/moby/sys/signal v0.7.1

View File

@ -180,8 +180,8 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/swarmkit/v2 v2.0.0-20240611172349-ea1a7cec35cb h1:1UTTg2EgO3nuyV03wREDzldqqePzQ4+0a5G1C1y1bIo= github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e h1:1yC8fRqStY6NirU/swI74fsrHvZVMbtxsHcvl8YpzDg=
github.com/moby/swarmkit/v2 v2.0.0-20240611172349-ea1a7cec35cb/go.mod h1:kNy225f/gWAnF8wPftteMc5nbAHhrH+HUfvyjmhFjeQ= github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e/go.mod h1:mTTGIAz/59OGZR5Qe+QByIe3Nxc+sSuJkrsStFhr6Lg=
github.com/moby/sys/capability v0.3.0 h1:kEP+y6te0gEXIaeQhIi0s7vKs/w0RPoH1qPa6jROcVg= github.com/moby/sys/capability v0.3.0 h1:kEP+y6te0gEXIaeQhIi0s7vKs/w0RPoH1qPa6jROcVg=
github.com/moby/sys/capability v0.3.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/capability v0.3.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=

View File

@ -0,0 +1,26 @@
package reexec
import (
"os/exec"
"syscall"
)
// Command returns an [*exec.Cmd] which has Path as current binary which,
// on Linux, is set to the in-memory version (/proc/self/exe) of the current
// binary, it is thus safe to delete or replace the on-disk binary (os.Args[0]).
//
// On Linux, the Pdeathsig of [*exec.Cmd.SysProcAttr] is set to SIGTERM.
// This signal will be sent to the process when the OS thread which created
// the process dies.
//
// It is the caller's responsibility to ensure that the creating thread is
// not terminated prematurely. See https://go.dev/issue/27505 for more details.
func Command(args ...string) *exec.Cmd {
return &exec.Cmd{
Path: Self(),
Args: args,
SysProcAttr: &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
},
}
}

View File

@ -0,0 +1,19 @@
//go:build freebsd || darwin || windows
package reexec
import (
"os/exec"
)
// Command returns *exec.Cmd with its Path set to the path of the current
// binary using the result of [Self]. For example if current binary is
// "my-binary" at "/usr/bin/" (or "my-binary.exe" at "C:\" on Windows),
// then cmd.Path is set to "/usr/bin/my-binary" and "C:\my-binary.exe"
// respectively.
func Command(args ...string) *exec.Cmd {
return &exec.Cmd{
Path: Self(),
Args: args,
}
}

View File

@ -0,0 +1,12 @@
//go:build !linux && !windows && !freebsd && !darwin
package reexec
import (
"os/exec"
)
// Command is unsupported on operating systems apart from Linux, Windows, and Darwin.
func Command(args ...string) *exec.Cmd {
return nil
}

64
vendor/github.com/docker/docker/pkg/reexec/reexec.go generated vendored Normal file
View File

@ -0,0 +1,64 @@
// Package reexec facilitates the busybox style reexec of a binary.
//
// Handlers can be registered with a name and the argv 0 of the exec of
// the binary will be used to find and execute custom init paths.
//
// It is used in dockerd to work around forking limitations when using Go.
package reexec
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
)
var registeredInitializers = make(map[string]func())
// Register adds an initialization func under the specified name. It panics
// if the given name is already registered.
func Register(name string, initializer func()) {
if _, exists := registeredInitializers[name]; exists {
panic(fmt.Sprintf("reexec func already registered under name %q", name))
}
registeredInitializers[name] = initializer
}
// Init is called as the first part of the exec process and returns true if an
// initialization function was called.
func Init() bool {
if initializer, ok := registeredInitializers[os.Args[0]]; ok {
initializer()
return true
}
return false
}
// Self returns the path to the current process's binary. On Linux, it
// returns "/proc/self/exe", which provides the in-memory version of the
// current binary, whereas on other platforms it attempts to looks up the
// absolute path for os.Args[0], or otherwise returns os.Args[0] as-is.
func Self() string {
if runtime.GOOS == "linux" {
return "/proc/self/exe"
}
return naiveSelf()
}
func naiveSelf() string {
name := os.Args[0]
if filepath.Base(name) == name {
if lp, err := exec.LookPath(name); err == nil {
return lp
}
}
// handle conversion of relative paths to absolute
if absName, err := filepath.Abs(name); err == nil {
return absName
}
// if we couldn't get absolute name, return original
// (NOTE: Go only errors on Abs() if os.Getwd fails)
return name
}

3
vendor/modules.txt vendored
View File

@ -91,6 +91,7 @@ github.com/docker/docker/pkg/longpath
github.com/docker/docker/pkg/pools github.com/docker/docker/pkg/pools
github.com/docker/docker/pkg/process github.com/docker/docker/pkg/process
github.com/docker/docker/pkg/progress github.com/docker/docker/pkg/progress
github.com/docker/docker/pkg/reexec
github.com/docker/docker/pkg/stdcopy github.com/docker/docker/pkg/stdcopy
github.com/docker/docker/pkg/streamformatter github.com/docker/docker/pkg/streamformatter
github.com/docker/docker/pkg/stringid github.com/docker/docker/pkg/stringid
@ -197,7 +198,7 @@ github.com/moby/docker-image-spec/specs-go/v1
## explicit; go 1.19 ## explicit; go 1.19
github.com/moby/patternmatcher github.com/moby/patternmatcher
github.com/moby/patternmatcher/ignorefile github.com/moby/patternmatcher/ignorefile
# github.com/moby/swarmkit/v2 v2.0.0-20240611172349-ea1a7cec35cb # github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e
## explicit; go 1.18 ## explicit; go 1.18
github.com/moby/swarmkit/v2/api github.com/moby/swarmkit/v2/api
github.com/moby/swarmkit/v2/api/deepcopy github.com/moby/swarmkit/v2/api/deepcopy