func pullImage(ctx context.Context, dockerCli *command.DockerCli, image string, out io.Writer) error { ref, err := reference.ParseNamed(image) if err != nil { return err } // Resolve the Repository name from fqn to RepositoryInfo repoInfo, err := registry.ParseRepositoryInfo(ref) if err != nil { return err } authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) encodedAuth, err := command.EncodeAuthToBase64(authConfig) if err != nil { return err } options := types.ImageCreateOptions{ RegistryAuth: encodedAuth, } responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options) if err != nil { return err } defer responseBody.Close() return jsonmessage.DisplayJSONMessagesStream( responseBody, out, dockerCli.Out().FD(), dockerCli.Out().IsTerminal(), nil) }
func runPush(dockerCli *command.DockerCli, remote string) error { ref, err := reference.ParseNamed(remote) if err != nil { return err } // Resolve the Repository name from fqn to RepositoryInfo repoInfo, err := registry.ParseRepositoryInfo(ref) if err != nil { return err } ctx := context.Background() // Resolve the Auth config relevant for this server authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push") if command.IsTrusted() { return trustedPush(ctx, dockerCli, repoInfo, ref, authConfig, requestPrivilege) } responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref.String(), requestPrivilege) if err != nil { return err } defer responseBody.Close() return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil) }
// NewPruneCommand returns a new cobra prune command for networks func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command { opts := pruneOptions{filter: opts.NewFilterOpt()} cmd := &cobra.Command{ Use: "prune [OPTIONS]", Short: "Remove all unused networks", Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { output, err := runPrune(dockerCli, opts) if err != nil { return err } if output != "" { fmt.Fprintln(dockerCli.Out(), output) } return nil }, Tags: map[string]string{"version": "1.25"}, } flags := cmd.Flags() flags.BoolVarP(&opts.force, "force", "f", false, "Do not prompt for confirmation") flags.Var(&opts.filter, "filter", "Provide filter values (e.g. 'until=<timestamp>')") return cmd }
func runEvents(dockerCli *command.DockerCli, opts *eventsOptions) error { tmpl, err := makeTemplate(opts.format) if err != nil { return cli.StatusError{ StatusCode: 64, Status: "Error parsing format: " + err.Error()} } options := types.EventsOptions{ Since: opts.since, Until: opts.until, Filters: opts.filter.Value(), } ctx, cancel := context.WithCancel(context.Background()) events, errs := dockerCli.Client().Events(ctx, options) defer cancel() out := dockerCli.Out() for { select { case event := <-events: if err := handleEvent(out, event, tmpl); err != nil { return err } case err := <-errs: if err == io.EOF { return nil } return err } } }
func runDisable(dockerCli *command.DockerCli, name string, force bool) error { if err := dockerCli.Client().PluginDisable(context.Background(), name, types.PluginDisableOptions{Force: force}); err != nil { return err } fmt.Fprintln(dockerCli.Out(), name) return nil }
func runStop(dockerCli *command.DockerCli, opts *stopOptions) error { ctx := context.Background() var timeout *time.Duration if opts.timeChanged { timeoutValue := time.Duration(opts.time) * time.Second timeout = &timeoutValue } var errs []string errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, id string) error { return dockerCli.Client().ContainerStop(ctx, id, timeout) }) for _, container := range opts.containers { if err := <-errChan; err != nil { errs = append(errs, err.Error()) } else { fmt.Fprintf(dockerCli.Out(), "%s\n", container) } } if len(errs) > 0 { return fmt.Errorf("%s", strings.Join(errs, "\n")) } return nil }
func runCreate(dockerCli *command.DockerCli, opts *serviceOptions) error { apiClient := dockerCli.Client() createOpts := types.ServiceCreateOptions{} service, err := opts.ToService() if err != nil { return err } ctx := context.Background() // only send auth if flag was set if opts.registryAuth { // Retrieve encoded auth token from the image reference encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, opts.image) if err != nil { return err } createOpts.EncodedRegistryAuth = encodedAuth } response, err := apiClient.ServiceCreate(ctx, service, createOpts) if err != nil { return err } fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID) return nil }
// NewStartCommand creates a new cobra.Command for `docker start` func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command { var opts startOptions cmd := &cobra.Command{ Use: "start [OPTIONS] CONTAINER [CONTAINER...]", Short: "Start one or more stopped containers", Args: cli.RequiresMinArgs(1), RunE: func(cmd *cobra.Command, args []string) error { opts.containers = args return runStart(dockerCli, &opts) }, } flags := cmd.Flags() flags.BoolVarP(&opts.attach, "attach", "a", false, "Attach STDOUT/STDERR and forward signals") flags.BoolVarP(&opts.openStdin, "interactive", "i", false, "Attach container's STDIN") flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container") if dockerCli.HasExperimental() { flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint") flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory") } return cmd }
func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command { opts := createOptions{ driverOpts: *opts.NewMapOpts(nil, nil), labels: opts.NewListOpts(opts.ValidateEnv), } cmd := &cobra.Command{ Use: "create [OPTIONS] [VOLUME]", Short: "Create a volume", Args: cli.RequiresMaxArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 1 { if opts.name != "" { fmt.Fprint(dockerCli.Err(), "Conflicting options: either specify --name or provide positional arg, not both\n") return cli.StatusError{StatusCode: 1} } opts.name = args[0] } return runCreate(dockerCli, opts) }, } flags := cmd.Flags() flags.StringVarP(&opts.driver, "driver", "d", "local", "Specify volume driver name") flags.StringVar(&opts.name, "name", "", "Specify volume name") flags.Lookup("name").Hidden = true flags.VarP(&opts.driverOpts, "opt", "o", "Set driver specific options") flags.Var(&opts.labels, "label", "Set metadata for a volume") return cmd }
func runCreate(dockerCli *command.DockerCli, opts createOptions) error { client := dockerCli.Client() ipamCfg, err := consolidateIpam(opts.ipamSubnet, opts.ipamIPRange, opts.ipamGateway, opts.ipamAux.GetAll()) if err != nil { return err } // Construct network create request body nc := types.NetworkCreate{ Driver: opts.driver, Options: opts.driverOpts.GetAll(), IPAM: &network.IPAM{ Driver: opts.ipamDriver, Config: ipamCfg, Options: opts.ipamOpt.GetAll(), }, CheckDuplicate: true, Internal: opts.internal, EnableIPv6: opts.ipv6, Attachable: opts.attachable, Labels: runconfigopts.ConvertKVStringsToMap(opts.labels), } resp, err := client.NetworkCreate(context.Background(), opts.name, nc) if err != nil { return err } fmt.Fprintf(dockerCli.Out(), "%s\n", resp.ID) return nil }
func copyFromContainer(ctx context.Context, dockerCli *command.DockerCli, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) { if dstPath != "-" { // Get an absolute destination path. dstPath, err = resolveLocalPath(dstPath) if err != nil { return err } } // if client requests to follow symbol link, then must decide target file to be copied var rebaseName string if cpParam.followLink { srcStat, err := statContainerPath(ctx, dockerCli, srcContainer, srcPath) // If the destination is a symbolic link, we should follow it. if err == nil && srcStat.Mode&os.ModeSymlink != 0 { linkTarget := srcStat.LinkTarget if !system.IsAbs(linkTarget) { // Join with the parent directory. srcParent, _ := archive.SplitPathDirEntry(srcPath) linkTarget = filepath.Join(srcParent, linkTarget) } linkTarget, rebaseName = archive.GetRebaseName(srcPath, linkTarget) srcPath = linkTarget } } content, stat, err := dockerCli.Client().CopyFromContainer(ctx, srcContainer, srcPath) if err != nil { return err } defer content.Close() if dstPath == "-" { // Send the response to STDOUT. _, err = io.Copy(os.Stdout, content) return err } // Prepare source copy info. srcInfo := archive.CopyInfo{ Path: srcPath, Exists: true, IsDir: stat.Mode.IsDir(), RebaseName: rebaseName, } preArchive := content if len(srcInfo.RebaseName) != 0 { _, srcBase := archive.SplitPathDirEntry(srcInfo.Path) preArchive = archive.RebaseArchiveEntries(content, srcBase, srcInfo.RebaseName) } // See comments in the implementation of `archive.CopyTo` for exactly what // goes into deciding how and whether the source archive needs to be // altered for the correct copy behavior. return archive.CopyTo(preArchive, srcInfo, dstPath) }
func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts swarmOptions) error { client := dockerCli.Client() ctx := context.Background() var updateFlags swarm.UpdateFlags swarm, err := client.SwarmInspect(ctx) if err != nil { return err } prevAutoLock := swarm.Spec.EncryptionConfig.AutoLockManagers opts.mergeSwarmSpec(&swarm.Spec, flags) curAutoLock := swarm.Spec.EncryptionConfig.AutoLockManagers err = client.SwarmUpdate(ctx, swarm.Version, swarm.Spec, updateFlags) if err != nil { return err } fmt.Fprintln(dockerCli.Out(), "Swarm updated.") if curAutoLock && !prevAutoLock { unlockKeyResp, err := client.SwarmGetUnlockKey(ctx) if err != nil { return errors.Wrap(err, "could not fetch unlock key") } printUnlockCommand(ctx, dockerCli, unlockKeyResp.UnlockKey) } return nil }
func runRm(dockerCli *command.DockerCli, opts *rmOptions) error { ctx := context.Background() var errs []string options := types.ContainerRemoveOptions{ RemoveVolumes: opts.rmVolumes, RemoveLinks: opts.rmLink, Force: opts.force, } errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error { if container == "" { return fmt.Errorf("Container name cannot be empty") } container = strings.Trim(container, "/") return dockerCli.Client().ContainerRemove(ctx, container, options) }) for _, name := range opts.containers { if err := <-errChan; err != nil { errs = append(errs, err.Error()) } else { fmt.Fprintf(dockerCli.Out(), "%s\n", name) } } if len(errs) > 0 { return fmt.Errorf("%s", strings.Join(errs, "\n")) } return nil }
// ForwardAllSignals forwards signals to the container func ForwardAllSignals(ctx context.Context, cli *command.DockerCli, cid string) chan os.Signal { sigc := make(chan os.Signal, 128) signal.CatchAll(sigc) go func() { for s := range sigc { if s == signal.SIGCHLD || s == signal.SIGPIPE { continue } var sig string for sigStr, sigN := range signal.SignalMap { if sigN == s { sig = sigStr break } } if sig == "" { fmt.Fprintf(cli.Err(), "Unsupported signal: %v. Discarding.\n", s) continue } if err := cli.Client().ContainerKill(ctx, cid, sig); err != nil { logrus.Debugf("Error sending signal: %s", err) } } }() return sigc }
func runServiceScale(dockerCli *command.DockerCli, serviceID string, scale string) error { client := dockerCli.Client() ctx := context.Background() service, _, err := client.ServiceInspectWithRaw(ctx, serviceID) if err != nil { return err } serviceMode := &service.Spec.Mode if serviceMode.Replicated == nil { return fmt.Errorf("scale can only be used with replicated mode") } uintScale, err := strconv.ParseUint(scale, 10, 64) if err != nil { return fmt.Errorf("invalid replicas value %s: %s", scale, err.Error()) } serviceMode.Replicated.Replicas = &uintScale err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{}) if err != nil { return err } fmt.Fprintf(dockerCli.Out(), "%s scaled to %s\n", serviceID, scale) return nil }
func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error { ctx := context.Background() options := types.ContainerLogsOptions{ ShowStdout: true, ShowStderr: true, Since: opts.since, Timestamps: opts.timestamps, Follow: opts.follow, Tail: opts.tail, Details: opts.details, } responseBody, err := dockerCli.Client().ContainerLogs(ctx, opts.container, options) if err != nil { return err } defer responseBody.Close() c, err := dockerCli.Client().ContainerInspect(ctx, opts.container) if err != nil { return err } if c.Config.Tty { _, err = io.Copy(dockerCli.Out(), responseBody) } else { _, err = stdcopy.StdCopy(dockerCli.Out(), dockerCli.Err(), responseBody) } return err }
func runDiff(dockerCli *command.DockerCli, opts *diffOptions) error { if opts.container == "" { return errors.New("Container name cannot be empty") } ctx := context.Background() changes, err := dockerCli.Client().ContainerDiff(ctx, opts.container) if err != nil { return err } for _, change := range changes { var kind string switch change.Kind { case archive.ChangeModify: kind = "C" case archive.ChangeAdd: kind = "A" case archive.ChangeDelete: kind = "D" } fmt.Fprintln(dockerCli.Out(), kind, change.Path) } return nil }
// TrustedReference returns the canonical trusted reference for an image reference func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (reference.Canonical, error) { repoInfo, err := registry.ParseRepositoryInfo(ref) if err != nil { return nil, err } // Resolve the Auth config relevant for this server authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index) notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull") if err != nil { fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err) return nil, err } t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole) if err != nil { return nil, err } // Only list tags in the top level targets role or the releases delegation role - ignore // all other delegation roles if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole { return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag())) } r, err := convertTarget(t.Target) if err != nil { return nil, err } return reference.WithDigest(reference.TrimNamed(ref), r.digest) }
// NewPruneCommand returns a new cobra prune command for images func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command { opts := pruneOptions{filter: opts.NewFilterOpt()} cmd := &cobra.Command{ Use: "prune [OPTIONS]", Short: "Remove unused images", Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { spaceReclaimed, output, err := runPrune(dockerCli, opts) if err != nil { return err } if output != "" { fmt.Fprintln(dockerCli.Out(), output) } fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed))) return nil }, Tags: map[string]string{"version": "1.25"}, } flags := cmd.Flags() flags.BoolVarP(&opts.force, "force", "f", false, "Do not prompt for confirmation") flags.BoolVarP(&opts.all, "all", "a", false, "Remove all unused images, not just dangling ones") flags.Var(&opts.filter, "filter", "Provide filter values (e.g. 'until=<timestamp>')") return cmd }
func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error { ctx := context.Background() c, err := dockerCli.Client().ContainerInspect(ctx, opts.container) if err != nil { return err } if !validDrivers[c.HostConfig.LogConfig.Type] { return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got: %s)", c.HostConfig.LogConfig.Type) } options := types.ContainerLogsOptions{ ShowStdout: true, ShowStderr: true, Since: opts.since, Timestamps: opts.timestamps, Follow: opts.follow, Tail: opts.tail, Details: opts.details, } responseBody, err := dockerCli.Client().ContainerLogs(ctx, opts.container, options) if err != nil { return err } defer responseBody.Close() if c.Config.Tty { _, err = io.Copy(dockerCli.Out(), responseBody) } else { _, err = stdcopy.StdCopy(dockerCli.Out(), dockerCli.Err(), responseBody) } return err }
func runPS(dockerCli *command.DockerCli, opts psOptions) error { client := dockerCli.Client() ctx := context.Background() service, _, err := client.ServiceInspectWithRaw(ctx, opts.serviceID) if err != nil { return err } filter := opts.filter.Value() filter.Add("service", service.ID) if filter.Include("node") { nodeFilters := filter.Get("node") for _, nodeFilter := range nodeFilters { nodeReference, err := node.Reference(ctx, client, nodeFilter) if err != nil { return err } filter.Del("node", nodeFilter) filter.Add("node", nodeReference) } } tasks, err := client.TaskList(ctx, types.TaskListOptions{Filter: filter}) if err != nil { return err } return task.Print(dockerCli, ctx, tasks, idresolver.New(client, opts.noResolve), opts.noTrunc) }
func runPush(dockerCli *command.DockerCli, name string) error { named, err := reference.ParseNamed(name) // FIXME: validate if err != nil { return err } if reference.IsNameOnly(named) { named = reference.WithDefaultTag(named) } ref, ok := named.(reference.NamedTagged) if !ok { return fmt.Errorf("invalid name: %s", named.String()) } ctx := context.Background() repoInfo, err := registry.ParseRepositoryInfo(named) if err != nil { return err } authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) encodedAuth, err := command.EncodeAuthToBase64(authConfig) if err != nil { return err } return dockerCli.Client().PluginPush(ctx, ref.String(), encodedAuth) }
// NewPruneCommand returns a new cobra prune command for volumes func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command { var opts pruneOptions cmd := &cobra.Command{ Use: "prune [OPTIONS]", Short: "Remove all unused volumes", Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { spaceReclaimed, output, err := runPrune(dockerCli, opts) if err != nil { return err } if output != "" { fmt.Fprintln(dockerCli.Out(), output) } fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed))) return nil }, } flags := cmd.Flags() flags.BoolVarP(&opts.force, "force", "f", false, "Do not prompt for confirmation") return cmd }
func runSecretList(dockerCli *command.DockerCli, opts listOptions) error { client := dockerCli.Client() ctx := context.Background() secrets, err := client.SecretList(ctx, types.SecretListOptions{}) if err != nil { return err } w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0) if opts.quiet { for _, s := range secrets { fmt.Fprintf(w, "%s\n", s.ID) } } else { fmt.Fprintf(w, "ID\tNAME\tCREATED\tUPDATED\tSIZE") fmt.Fprintf(w, "\n") for _, s := range secrets { created := units.HumanDuration(time.Now().UTC().Sub(s.Meta.CreatedAt)) + " ago" updated := units.HumanDuration(time.Now().UTC().Sub(s.Meta.UpdatedAt)) + " ago" size := units.HumanSizeWithPrecision(float64(s.SecretSize), 3) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", s.ID, s.Spec.Annotations.Name, created, updated, size) } } w.Flush() return nil }
func runConfig(dockerCli *command.DockerCli, opts configOptions) error { bundle, err := loadBundlefile(dockerCli.Err(), opts.namespace, opts.bundlefile) if err != nil { return err } return bundlefile.Print(dockerCli.Out(), bundle) }
func runList(dockerCli *command.DockerCli, opts listOptions) error { ctx := context.Background() client := dockerCli.Client() services, err := client.ServiceList(ctx, types.ServiceListOptions{Filter: opts.filter.Value()}) if err != nil { return err } out := dockerCli.Out() if opts.quiet { PrintQuiet(out, services) } else { taskFilter := filters.NewArgs() for _, service := range services { taskFilter.Add("service", service.ID) } tasks, err := client.TaskList(ctx, types.TaskListOptions{Filter: taskFilter}) if err != nil { return err } nodes, err := client.NodeList(ctx, types.NodeListOptions{}) if err != nil { return err } PrintNotQuiet(out, services, nodes, tasks) } return nil }
// NewImageCommand returns a cobra command for `image` subcommands func NewImageCommand(dockerCli *command.DockerCli) *cobra.Command { cmd := &cobra.Command{ Use: "image", Short: "Manage images", Args: cli.NoArgs, Run: func(cmd *cobra.Command, args []string) { cmd.SetOutput(dockerCli.Err()) cmd.HelpFunc()(cmd, args) }, } cmd.AddCommand( NewBuildCommand(dockerCli), NewHistoryCommand(dockerCli), NewImportCommand(dockerCli), NewLoadCommand(dockerCli), NewPullCommand(dockerCli), NewPushCommand(dockerCli), NewSaveCommand(dockerCli), NewTagCommand(dockerCli), newListCommand(dockerCli), newRemoveCommand(dockerCli), newInspectCommand(dockerCli), NewPruneCommand(dockerCli), ) return cmd }
// NewPruneCommand returns a new cobra prune command for networks func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command { var opts pruneOptions cmd := &cobra.Command{ Use: "prune [OPTIONS]", Short: "Remove all unused networks", Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { output, err := runPrune(dockerCli, opts) if err != nil { return err } if output != "" { fmt.Fprintln(dockerCli.Out(), output) } return nil }, Tags: map[string]string{"version": "1.25"}, } flags := cmd.Flags() flags.BoolVarP(&opts.force, "force", "f", false, "Do not prompt for confirmation") return cmd }
func runRemove(dockerCli *command.DockerCli, opts *rmOptions) error { ctx := context.Background() var errs cli.Errors for _, name := range opts.plugins { named, err := reference.ParseNamed(name) // FIXME: validate if err != nil { return err } if reference.IsNameOnly(named) { named = reference.WithDefaultTag(named) } ref, ok := named.(reference.NamedTagged) if !ok { return fmt.Errorf("invalid name: %s", named.String()) } // TODO: pass names to api instead of making multiple api calls if err := dockerCli.Client().PluginRemove(ctx, ref.String(), types.PluginRemoveOptions{Force: opts.force}); err != nil { errs = append(errs, err) continue } fmt.Fprintln(dockerCli.Out(), name) } // Do not simplify to `return errs` because even if errs == nil, it is not a nil-error interface value. if errs != nil { return errs } return nil }
func runSecretCreate(dockerCli *command.DockerCli, options createOptions) error { client := dockerCli.Client() ctx := context.Background() secretData, err := ioutil.ReadAll(os.Stdin) if err != nil { return fmt.Errorf("Error reading content from STDIN: %v", err) } spec := swarm.SecretSpec{ Annotations: swarm.Annotations{ Name: options.name, Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()), }, Data: secretData, } r, err := client.SecretCreate(ctx, spec) if err != nil { return err } fmt.Fprintln(dockerCli.Out(), r.ID) return nil }