2016-09-08 13:11:39 -04:00
|
|
|
package container
|
|
|
|
|
|
|
|
import (
|
2018-05-03 21:02:44 -04:00
|
|
|
"context"
|
2016-09-08 13:11:39 -04:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
2018-12-13 06:59:35 -05:00
|
|
|
"regexp"
|
2016-09-08 13:11:39 -04:00
|
|
|
|
2024-05-26 05:50:13 -04:00
|
|
|
"github.com/containerd/platforms"
|
2023-08-30 18:36:58 -04:00
|
|
|
"github.com/distribution/reference"
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli"
|
|
|
|
"github.com/docker/cli/cli/command"
|
2022-03-30 09:27:25 -04:00
|
|
|
"github.com/docker/cli/cli/command/completion"
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli/command/image"
|
2023-04-11 17:52:40 -04:00
|
|
|
"github.com/docker/cli/cli/streams"
|
2018-12-13 16:19:46 -05:00
|
|
|
"github.com/docker/cli/opts"
|
2016-12-24 19:05:37 -05:00
|
|
|
"github.com/docker/docker/api/types/container"
|
2024-01-24 08:32:07 -05:00
|
|
|
imagetypes "github.com/docker/docker/api/types/image"
|
2020-05-27 14:32:22 -04:00
|
|
|
"github.com/docker/docker/api/types/versions"
|
2023-05-10 08:43:18 -04:00
|
|
|
"github.com/docker/docker/errdefs"
|
2016-09-08 13:11:39 -04:00
|
|
|
"github.com/docker/docker/pkg/jsonmessage"
|
2020-05-27 14:32:22 -04:00
|
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
2017-03-09 13:23:45 -05:00
|
|
|
"github.com/pkg/errors"
|
2016-09-08 13:11:39 -04:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/pflag"
|
|
|
|
)
|
|
|
|
|
2018-11-05 08:12:22 -05:00
|
|
|
// Pull constants
|
|
|
|
const (
|
|
|
|
PullImageAlways = "always"
|
2018-11-20 06:54:27 -05:00
|
|
|
PullImageMissing = "missing" // Default (matches previous behavior)
|
2018-11-05 08:12:22 -05:00
|
|
|
PullImageNever = "never"
|
|
|
|
)
|
|
|
|
|
2016-09-08 13:11:39 -04:00
|
|
|
type createOptions struct {
|
2018-03-08 08:35:17 -05:00
|
|
|
name string
|
|
|
|
platform string
|
|
|
|
untrusted bool
|
2021-12-03 11:51:01 -05:00
|
|
|
pull string // always, missing, never
|
|
|
|
quiet bool
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewCreateCommand creates a new cobra.Command for `docker create`
|
2017-05-03 17:58:52 -04:00
|
|
|
func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
|
2022-06-23 13:35:40 -04:00
|
|
|
var options createOptions
|
2016-12-23 14:09:12 -05:00
|
|
|
var copts *containerOptions
|
2016-09-08 13:11:39 -04:00
|
|
|
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "create [OPTIONS] IMAGE [COMMAND] [ARG...]",
|
|
|
|
Short: "Create a new container",
|
|
|
|
Args: cli.RequiresMinArgs(1),
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
copts.Image = args[0]
|
|
|
|
if len(args) > 1 {
|
|
|
|
copts.Args = args[1:]
|
|
|
|
}
|
2023-09-09 18:27:44 -04:00
|
|
|
return runCreate(cmd.Context(), dockerCli, cmd.Flags(), &options, copts)
|
2016-09-08 13:11:39 -04:00
|
|
|
},
|
cli: use custom annotation for aliases
Cobra allows for aliases to be defined for a command, but only allows these
to be defined at the same level (for example, `docker image ls` as alias for
`docker image list`). Our CLI has some commands that are available both as a
top-level shorthand as well as `docker <object> <verb>` subcommands. For example,
`docker ps` is a shorthand for `docker container ps` / `docker container ls`.
This patch introduces a custom "aliases" annotation that can be used to print
all available aliases for a command. While this requires these aliases to be
defined manually, in practice the list of aliases rarely changes, so maintenance
should be minimal.
As a convention, we could consider the first command in this list to be the
canonical command, so that we can use this information to add redirects in
our documentation in future.
Before this patch:
docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Options:
-a, --all Show all images (default hides intermediate images)
...
With this patch:
docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Aliases:
docker image ls, docker image list, docker images
Options:
-a, --all Show all images (default hides intermediate images)
...
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-06-28 04:52:25 -04:00
|
|
|
Annotations: map[string]string{
|
|
|
|
"aliases": "docker container create, docker create",
|
|
|
|
},
|
2022-03-30 09:27:25 -04:00
|
|
|
ValidArgsFunction: completion.ImageNames(dockerCli),
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
flags := cmd.Flags()
|
|
|
|
flags.SetInterspersed(false)
|
|
|
|
|
2022-06-23 13:35:40 -04:00
|
|
|
flags.StringVar(&options.name, "name", "", "Assign a name to the container")
|
2023-01-03 05:12:24 -05:00
|
|
|
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before creating ("`+PullImageAlways+`", "|`+PullImageMissing+`", "`+PullImageNever+`")`)
|
2022-06-23 13:35:40 -04:00
|
|
|
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the pull output")
|
2016-09-08 13:11:39 -04:00
|
|
|
|
|
|
|
// Add an explicit help that doesn't have a `-h` to prevent the conflict
|
|
|
|
// with hostname
|
|
|
|
flags.Bool("help", false, "Print usage")
|
|
|
|
|
2022-06-23 13:35:40 -04:00
|
|
|
command.AddPlatformFlag(flags, &options.platform)
|
|
|
|
command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
|
2016-12-23 14:09:12 -05:00
|
|
|
copts = addFlags(flags)
|
2024-07-05 08:18:38 -04:00
|
|
|
|
2024-07-05 09:54:09 -04:00
|
|
|
_ = cmd.RegisterFlagCompletionFunc("cap-add", completeLinuxCapabilityNames)
|
|
|
|
_ = cmd.RegisterFlagCompletionFunc("cap-drop", completeLinuxCapabilityNames)
|
2024-07-05 08:18:38 -04:00
|
|
|
_ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames)
|
|
|
|
_ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames)
|
|
|
|
_ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli))
|
2024-10-08 07:13:32 -04:00
|
|
|
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
|
2024-07-05 08:18:38 -04:00
|
|
|
_ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever))
|
2024-07-05 18:48:51 -04:00
|
|
|
_ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies)
|
2024-07-05 19:46:47 -04:00
|
|
|
_ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals)
|
2024-07-05 18:58:35 -04:00
|
|
|
_ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCli, true))
|
2016-09-08 13:11:39 -04:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-09 18:27:44 -04:00
|
|
|
func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, options *createOptions, copts *containerOptions) error {
|
2022-06-27 11:16:44 -04:00
|
|
|
if err := validatePullOpt(options.pull); err != nil {
|
cli/command/container: remove reportError, and put StatusError to use
The `reportError` utility was present because cli.StatusError would print
the error decorated with `Status: <error-message>, Code: <exit-code>`.
That was not desirable in many cases as it would mess-up the output. To
prevent this, the CLI had code to check for an empty `Status` (error message)
in which case the error would be "ignored" (and only used for the exit-status),
and the `reportError` utility would be used to manually print a custom error
message before returning the error.
Now that bca209006153d3e025cb3d31c3cd55eb2aec0c4f fixed the output format
of `cli.StatusError`, and 3dd6fc365d853e21f0e11f9e6ab62c4f8ae438e7 and
350a0b68a9584ec9ae712b6eca906c1018ba6dac no longer discard these error,
we can get rid of this utility, and just set the error-message for
the status-error.
This patch:
- Introduces a `withHelp` which takes care of decorating errors with
a "Run --help" hint for the user.
- Introduces a `toStatusError` utility that detects certain errors in
the container to assign a corresponding exit-code (these error-codes
can be used to distinguish "client" errors from "container" errors).
- Removes the `reportError` utility, and removes code that manually
printed errors before returning.
Behavior is mostly unmodified, with the exception of some slight reformatting
of the errors:
- `withHelp` adds a `docker:` prefix to the error, to indicate the error
is produced by the `docker` command. This prefix was already present
in most cases.
- The "--help" hint is slightly updated ("Run 'docker run --help' for
more information" instead of "See 'docker run --help'"), to make it
more clear that it's a "call to action".
- An empty is added before the "--help" hint to separate it better from
the error-message.
Before this patch:
$ docker run --pull=invalid-option alpine
docker: invalid pull option: 'invalid-option': must be one of "always", "missing" or "never".
See 'docker run --help'.
$ echo $?
125
$ docker run --rm alpine nosuchcommand
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "nosuchcommand": executable file not found in $PATH: unknown.
$ echo $?
127
With this patch:
$ docker run --pull=invalid-option alpine
docker: invalid pull option: 'invalid-option': must be one of "always", "missing" or "never"
Run 'docker run --help' for more information
$ echo $?
125
$ docker run --rm alpine nosuchcommand
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "nosuchcommand": executable file not found in $PATH: unknown.
Run 'docker run --help' for more information
$ echo $?
127
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-07-04 07:18:03 -04:00
|
|
|
return cli.StatusError{
|
|
|
|
Status: withHelp(err, "create").Error(),
|
|
|
|
StatusCode: 125,
|
|
|
|
}
|
2022-06-27 11:16:44 -04:00
|
|
|
}
|
2017-10-15 15:39:56 -04:00
|
|
|
proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), opts.ConvertKVStringsToMapWithNil(copts.env.GetAll()))
|
2018-12-13 16:19:46 -05:00
|
|
|
newEnv := []string{}
|
|
|
|
for k, v := range proxyConfig {
|
|
|
|
if v == nil {
|
|
|
|
newEnv = append(newEnv, k)
|
|
|
|
} else {
|
2023-06-12 12:19:50 -04:00
|
|
|
newEnv = append(newEnv, k+"="+*v)
|
2018-12-13 16:19:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
copts.env = *opts.NewListOptsRef(&newEnv, nil)
|
2023-01-18 11:39:35 -05:00
|
|
|
containerCfg, err := parse(flags, copts, dockerCli.ServerInfo().OSType)
|
2016-09-08 13:11:39 -04:00
|
|
|
if err != nil {
|
cli/command/container: remove reportError, and put StatusError to use
The `reportError` utility was present because cli.StatusError would print
the error decorated with `Status: <error-message>, Code: <exit-code>`.
That was not desirable in many cases as it would mess-up the output. To
prevent this, the CLI had code to check for an empty `Status` (error message)
in which case the error would be "ignored" (and only used for the exit-status),
and the `reportError` utility would be used to manually print a custom error
message before returning the error.
Now that bca209006153d3e025cb3d31c3cd55eb2aec0c4f fixed the output format
of `cli.StatusError`, and 3dd6fc365d853e21f0e11f9e6ab62c4f8ae438e7 and
350a0b68a9584ec9ae712b6eca906c1018ba6dac no longer discard these error,
we can get rid of this utility, and just set the error-message for
the status-error.
This patch:
- Introduces a `withHelp` which takes care of decorating errors with
a "Run --help" hint for the user.
- Introduces a `toStatusError` utility that detects certain errors in
the container to assign a corresponding exit-code (these error-codes
can be used to distinguish "client" errors from "container" errors).
- Removes the `reportError` utility, and removes code that manually
printed errors before returning.
Behavior is mostly unmodified, with the exception of some slight reformatting
of the errors:
- `withHelp` adds a `docker:` prefix to the error, to indicate the error
is produced by the `docker` command. This prefix was already present
in most cases.
- The "--help" hint is slightly updated ("Run 'docker run --help' for
more information" instead of "See 'docker run --help'"), to make it
more clear that it's a "call to action".
- An empty is added before the "--help" hint to separate it better from
the error-message.
Before this patch:
$ docker run --pull=invalid-option alpine
docker: invalid pull option: 'invalid-option': must be one of "always", "missing" or "never".
See 'docker run --help'.
$ echo $?
125
$ docker run --rm alpine nosuchcommand
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "nosuchcommand": executable file not found in $PATH: unknown.
$ echo $?
127
With this patch:
$ docker run --pull=invalid-option alpine
docker: invalid pull option: 'invalid-option': must be one of "always", "missing" or "never"
Run 'docker run --help' for more information
$ echo $?
125
$ docker run --rm alpine nosuchcommand
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "nosuchcommand": executable file not found in $PATH: unknown.
Run 'docker run --help' for more information
$ echo $?
127
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-07-04 07:18:03 -04:00
|
|
|
return cli.StatusError{
|
|
|
|
Status: withHelp(err, "create").Error(),
|
|
|
|
StatusCode: 125,
|
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2023-01-18 11:39:35 -05:00
|
|
|
if err = validateAPIVersion(containerCfg, dockerCli.Client().ClientVersion()); err != nil {
|
cli/command/container: remove reportError, and put StatusError to use
The `reportError` utility was present because cli.StatusError would print
the error decorated with `Status: <error-message>, Code: <exit-code>`.
That was not desirable in many cases as it would mess-up the output. To
prevent this, the CLI had code to check for an empty `Status` (error message)
in which case the error would be "ignored" (and only used for the exit-status),
and the `reportError` utility would be used to manually print a custom error
message before returning the error.
Now that bca209006153d3e025cb3d31c3cd55eb2aec0c4f fixed the output format
of `cli.StatusError`, and 3dd6fc365d853e21f0e11f9e6ab62c4f8ae438e7 and
350a0b68a9584ec9ae712b6eca906c1018ba6dac no longer discard these error,
we can get rid of this utility, and just set the error-message for
the status-error.
This patch:
- Introduces a `withHelp` which takes care of decorating errors with
a "Run --help" hint for the user.
- Introduces a `toStatusError` utility that detects certain errors in
the container to assign a corresponding exit-code (these error-codes
can be used to distinguish "client" errors from "container" errors).
- Removes the `reportError` utility, and removes code that manually
printed errors before returning.
Behavior is mostly unmodified, with the exception of some slight reformatting
of the errors:
- `withHelp` adds a `docker:` prefix to the error, to indicate the error
is produced by the `docker` command. This prefix was already present
in most cases.
- The "--help" hint is slightly updated ("Run 'docker run --help' for
more information" instead of "See 'docker run --help'"), to make it
more clear that it's a "call to action".
- An empty is added before the "--help" hint to separate it better from
the error-message.
Before this patch:
$ docker run --pull=invalid-option alpine
docker: invalid pull option: 'invalid-option': must be one of "always", "missing" or "never".
See 'docker run --help'.
$ echo $?
125
$ docker run --rm alpine nosuchcommand
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "nosuchcommand": executable file not found in $PATH: unknown.
$ echo $?
127
With this patch:
$ docker run --pull=invalid-option alpine
docker: invalid pull option: 'invalid-option': must be one of "always", "missing" or "never"
Run 'docker run --help' for more information
$ echo $?
125
$ docker run --rm alpine nosuchcommand
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "nosuchcommand": executable file not found in $PATH: unknown.
Run 'docker run --help' for more information
$ echo $?
127
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-07-04 07:18:03 -04:00
|
|
|
return cli.StatusError{
|
|
|
|
Status: withHelp(err, "create").Error(),
|
|
|
|
StatusCode: 125,
|
|
|
|
}
|
2018-10-10 06:18:29 -04:00
|
|
|
}
|
2023-09-09 18:27:44 -04:00
|
|
|
id, err := createContainer(ctx, dockerCli, containerCfg, options)
|
2016-09-08 13:11:39 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-06-08 10:46:15 -04:00
|
|
|
_, _ = fmt.Fprintln(dockerCli.Out(), id)
|
2016-09-08 13:11:39 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-04-11 17:52:40 -04:00
|
|
|
// FIXME(thaJeztah): this is the only code-path that uses APIClient.ImageCreate. Rewrite this to use the regular "pull" code (or vice-versa).
|
2023-11-20 11:38:50 -05:00
|
|
|
func pullImage(ctx context.Context, dockerCli command.Cli, img string, options *createOptions) error {
|
|
|
|
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), img)
|
2016-09-08 13:11:39 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-01-24 08:32:07 -05:00
|
|
|
responseBody, err := dockerCli.Client().ImageCreate(ctx, img, imagetypes.CreateOptions{
|
2016-09-08 13:11:39 -04:00
|
|
|
RegistryAuth: encodedAuth,
|
2023-11-20 11:38:50 -05:00
|
|
|
Platform: options.platform,
|
2023-04-11 14:23:11 -04:00
|
|
|
})
|
2016-09-08 13:11:39 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer responseBody.Close()
|
|
|
|
|
2023-04-11 17:52:40 -04:00
|
|
|
out := dockerCli.Err()
|
2023-11-20 11:38:50 -05:00
|
|
|
if options.quiet {
|
2024-06-11 10:49:25 -04:00
|
|
|
out = streams.NewOut(io.Discard)
|
2023-04-11 17:52:40 -04:00
|
|
|
}
|
2024-06-11 10:49:25 -04:00
|
|
|
return jsonmessage.DisplayJSONMessagesToStream(responseBody, out, nil)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type cidFile struct {
|
|
|
|
path string
|
|
|
|
file *os.File
|
|
|
|
written bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *cidFile) Close() error {
|
2017-05-09 18:35:25 -04:00
|
|
|
if cid.file == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
cid.file.Close()
|
|
|
|
|
2016-12-24 19:05:37 -05:00
|
|
|
if cid.written {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := os.Remove(cid.path); err != nil {
|
linting: fix incorrectly formatted errors (revive)
cli/compose/interpolation/interpolation.go:102:4: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
^
cli/command/stack/loader/loader.go:30:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
^
cli/command/formatter/formatter.go:76:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/formatter/formatter.go:97:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/image/build.go:257:25: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("error checking context: '%s'.", err)
^
cli/command/volume/create.go:35:27: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
^
cli/command/container/create.go:160:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-27 15:13:03 -04:00
|
|
|
return errors.Wrapf(err, "failed to remove the CID file '%s'", cid.path)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *cidFile) Write(id string) error {
|
2017-05-09 18:35:25 -04:00
|
|
|
if cid.file == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
if _, err := cid.file.Write([]byte(id)); err != nil {
|
linting: fix incorrectly formatted errors (revive)
cli/compose/interpolation/interpolation.go:102:4: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
^
cli/command/stack/loader/loader.go:30:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
^
cli/command/formatter/formatter.go:76:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/formatter/formatter.go:97:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/image/build.go:257:25: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("error checking context: '%s'.", err)
^
cli/command/volume/create.go:35:27: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
^
cli/command/container/create.go:160:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-27 15:13:03 -04:00
|
|
|
return errors.Wrap(err, "failed to write the container ID to the file")
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
cid.written = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func newCIDFile(path string) (*cidFile, error) {
|
2017-05-09 18:35:25 -04:00
|
|
|
if path == "" {
|
|
|
|
return &cidFile{}, nil
|
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
if _, err := os.Stat(path); err == nil {
|
linting: fix incorrectly formatted errors (revive)
cli/compose/interpolation/interpolation.go:102:4: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
^
cli/command/stack/loader/loader.go:30:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
^
cli/command/formatter/formatter.go:76:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/formatter/formatter.go:97:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/image/build.go:257:25: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("error checking context: '%s'.", err)
^
cli/command/volume/create.go:35:27: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
^
cli/command/container/create.go:160:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-27 15:13:03 -04:00
|
|
|
return nil, errors.Errorf("container ID file found, make sure the other container isn't running or delete %s", path)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Create(path)
|
|
|
|
if err != nil {
|
linting: fix incorrectly formatted errors (revive)
cli/compose/interpolation/interpolation.go:102:4: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
^
cli/command/stack/loader/loader.go:30:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
^
cli/command/formatter/formatter.go:76:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/formatter/formatter.go:97:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/image/build.go:257:25: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("error checking context: '%s'.", err)
^
cli/command/volume/create.go:35:27: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
^
cli/command/container/create.go:160:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-27 15:13:03 -04:00
|
|
|
return nil, errors.Wrap(err, "failed to create the container ID file")
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return &cidFile{path: path, file: f}, nil
|
|
|
|
}
|
|
|
|
|
2022-07-13 06:29:49 -04:00
|
|
|
//nolint:gocyclo
|
2023-11-20 11:38:50 -05:00
|
|
|
func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *containerConfig, options *createOptions) (containerID string, err error) {
|
2023-01-18 11:39:35 -05:00
|
|
|
config := containerCfg.Config
|
|
|
|
hostConfig := containerCfg.HostConfig
|
|
|
|
networkingConfig := containerCfg.NetworkingConfig
|
2016-09-08 13:11:39 -04:00
|
|
|
|
2022-08-31 12:40:37 -04:00
|
|
|
warnOnOomKillDisable(*hostConfig, dockerCli.Err())
|
|
|
|
warnOnLocalhostDNS(*hostConfig, dockerCli.Err())
|
2018-12-13 06:59:35 -05:00
|
|
|
|
2017-01-11 16:54:52 -05:00
|
|
|
var (
|
2017-05-09 18:35:25 -04:00
|
|
|
trustedRef reference.Canonical
|
|
|
|
namedRef reference.Named
|
2017-01-11 16:54:52 -05:00
|
|
|
)
|
|
|
|
|
2017-05-09 18:35:25 -04:00
|
|
|
containerIDFile, err := newCIDFile(hostConfig.ContainerIDFile)
|
|
|
|
if err != nil {
|
2023-06-08 10:46:15 -04:00
|
|
|
return "", err
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2017-05-09 18:35:25 -04:00
|
|
|
defer containerIDFile.Close()
|
2016-09-08 13:11:39 -04:00
|
|
|
|
2017-01-11 16:54:52 -05:00
|
|
|
ref, err := reference.ParseAnyReference(config.Image)
|
2016-09-08 13:11:39 -04:00
|
|
|
if err != nil {
|
2023-06-08 10:46:15 -04:00
|
|
|
return "", err
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2017-01-11 16:54:52 -05:00
|
|
|
if named, ok := ref.(reference.Named); ok {
|
2017-01-25 19:54:18 -05:00
|
|
|
namedRef = reference.TagNameOnly(named)
|
2016-09-08 13:11:39 -04:00
|
|
|
|
2023-11-20 11:38:50 -05:00
|
|
|
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && !options.untrusted {
|
2016-09-08 13:11:39 -04:00
|
|
|
var err error
|
2023-03-15 11:21:02 -04:00
|
|
|
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef)
|
2016-09-08 13:11:39 -04:00
|
|
|
if err != nil {
|
2023-06-08 10:46:15 -04:00
|
|
|
return "", err
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2017-01-11 16:54:52 -05:00
|
|
|
config.Image = reference.FamiliarString(trustedRef)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-20 07:12:24 -05:00
|
|
|
pullAndTagImage := func() error {
|
2023-11-20 11:38:50 -05:00
|
|
|
if err := pullImage(ctx, dockerCli, config.Image, options); err != nil {
|
2019-02-20 07:12:24 -05:00
|
|
|
return err
|
2018-11-05 08:12:22 -05:00
|
|
|
}
|
|
|
|
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
|
2019-02-20 07:12:24 -05:00
|
|
|
return image.TagTrusted(ctx, dockerCli, trustedRef, taggedRef)
|
2018-11-05 08:12:22 -05:00
|
|
|
}
|
2019-02-20 07:12:24 -05:00
|
|
|
return nil
|
2019-02-16 18:41:56 -05:00
|
|
|
}
|
|
|
|
|
2020-05-27 14:32:22 -04:00
|
|
|
var platform *specs.Platform
|
|
|
|
// Engine API version 1.41 first introduced the option to specify platform on
|
|
|
|
// create. It will produce an error if you try to set a platform on older API
|
|
|
|
// versions, so check the API version here to maintain backwards
|
|
|
|
// compatibility for CLI users.
|
2023-11-20 11:38:50 -05:00
|
|
|
if options.platform != "" && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.41") {
|
|
|
|
p, err := platforms.Parse(options.platform)
|
2020-05-27 14:32:22 -04:00
|
|
|
if err != nil {
|
2024-05-26 05:50:13 -04:00
|
|
|
return "", errors.Wrap(errdefs.InvalidParameter(err), "error parsing specified platform")
|
2020-05-27 14:32:22 -04:00
|
|
|
}
|
|
|
|
platform = &p
|
|
|
|
}
|
|
|
|
|
2023-11-20 11:38:50 -05:00
|
|
|
if options.pull == PullImageAlways {
|
2019-02-20 07:12:24 -05:00
|
|
|
if err := pullAndTagImage(); err != nil {
|
2023-06-08 10:46:15 -04:00
|
|
|
return "", err
|
2019-02-20 07:12:24 -05:00
|
|
|
}
|
|
|
|
}
|
2018-11-05 08:12:22 -05:00
|
|
|
|
2022-05-18 06:48:02 -04:00
|
|
|
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.Out().GetTtySize()
|
|
|
|
|
2023-11-20 11:38:50 -05:00
|
|
|
response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, options.name)
|
2019-02-16 18:41:56 -05:00
|
|
|
if err != nil {
|
|
|
|
// Pull image if it does not exist locally and we have the PullImageMissing option. Default behavior.
|
2023-11-20 11:38:50 -05:00
|
|
|
if errdefs.IsNotFound(err) && namedRef != nil && options.pull == PullImageMissing {
|
|
|
|
if !options.quiet {
|
2021-12-03 11:51:01 -05:00
|
|
|
// we don't want to write to stdout anything apart from container.ID
|
2022-08-31 12:40:37 -04:00
|
|
|
fmt.Fprintf(dockerCli.Err(), "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
2021-12-03 11:51:01 -05:00
|
|
|
}
|
|
|
|
|
2019-02-20 07:12:24 -05:00
|
|
|
if err := pullAndTagImage(); err != nil {
|
2023-06-08 10:46:15 -04:00
|
|
|
return "", err
|
2019-02-16 18:41:56 -05:00
|
|
|
}
|
2018-11-05 08:12:22 -05:00
|
|
|
|
2019-02-16 18:41:56 -05:00
|
|
|
var retryErr error
|
2023-11-20 11:38:50 -05:00
|
|
|
response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, options.name)
|
2019-02-16 18:41:56 -05:00
|
|
|
if retryErr != nil {
|
2023-06-08 10:46:15 -04:00
|
|
|
return "", retryErr
|
2018-11-05 08:12:22 -05:00
|
|
|
}
|
2019-02-16 18:41:56 -05:00
|
|
|
} else {
|
2023-06-08 10:46:15 -04:00
|
|
|
return "", err
|
2018-11-05 08:12:22 -05:00
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2023-01-18 11:39:35 -05:00
|
|
|
for _, w := range response.Warnings {
|
2023-06-08 10:46:15 -04:00
|
|
|
_, _ = fmt.Fprintf(dockerCli.Err(), "WARNING: %s\n", w)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2017-05-09 18:35:25 -04:00
|
|
|
err = containerIDFile.Write(response.ID)
|
2023-06-08 10:46:15 -04:00
|
|
|
return response.ID, err
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2018-12-13 06:59:35 -05:00
|
|
|
|
|
|
|
func warnOnOomKillDisable(hostConfig container.HostConfig, stderr io.Writer) {
|
|
|
|
if hostConfig.OomKillDisable != nil && *hostConfig.OomKillDisable && hostConfig.Memory == 0 {
|
|
|
|
fmt.Fprintln(stderr, "WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check the DNS settings passed via --dns against localhost regexp to warn if
|
|
|
|
// they are trying to set a DNS to a localhost address
|
|
|
|
func warnOnLocalhostDNS(hostConfig container.HostConfig, stderr io.Writer) {
|
|
|
|
for _, dnsIP := range hostConfig.DNS {
|
|
|
|
if isLocalhost(dnsIP) {
|
|
|
|
fmt.Fprintf(stderr, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
|
|
|
|
const ipLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
|
|
|
|
|
|
|
|
var localhostIPRegexp = regexp.MustCompile(ipLocalhost)
|
|
|
|
|
|
|
|
// IsLocalhost returns true if ip matches the localhost IP regular expression.
|
|
|
|
// Used for determining if nameserver settings are being passed which are
|
|
|
|
// localhost addresses
|
|
|
|
func isLocalhost(ip string) bool {
|
|
|
|
return localhostIPRegexp.MatchString(ip)
|
|
|
|
}
|
2022-06-27 11:16:44 -04:00
|
|
|
|
|
|
|
func validatePullOpt(val string) error {
|
|
|
|
switch val {
|
|
|
|
case PullImageAlways, PullImageMissing, PullImageNever, "":
|
|
|
|
// valid option, but nothing to do yet
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return fmt.Errorf(
|
|
|
|
"invalid pull option: '%s': must be one of %q, %q or %q",
|
|
|
|
val,
|
|
|
|
PullImageAlways,
|
|
|
|
PullImageMissing,
|
|
|
|
PullImageNever,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|