func runRemove(dockerCli *command.DockerCli, opts removeOptions, images []string) error { client := dockerCli.Client() ctx := context.Background() options := types.ImageRemoveOptions{ Force: opts.force, PruneChildren: !opts.noPrune, } var errs []string for _, image := range images { dels, err := client.ImageRemove(ctx, image, options) if err != nil { errs = append(errs, err.Error()) } else { for _, del := range dels { if del.Deleted != "" { fmt.Fprintf(dockerCli.Out(), "Deleted: %s\n", del.Deleted) } else { fmt.Fprintf(dockerCli.Out(), "Untagged: %s\n", del.Untagged) } } } } if len(errs) > 0 { return fmt.Errorf("%s", strings.Join(errs, "\n")) } return nil }
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 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 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 }
func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) { pruneFilters := opts.filter.Value() pruneFilters.Add("dangling", fmt.Sprintf("%v", !opts.all)) warning := danglingWarning if opts.all { warning = allImageWarning } if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) { return } report, err := dockerCli.Client().ImagesPrune(context.Background(), pruneFilters) if err != nil { return } if len(report.ImagesDeleted) > 0 { output = "Deleted Images:\n" for _, st := range report.ImagesDeleted { if st.Untagged != "" { output += fmt.Sprintln("untagged:", st.Untagged) } else { output += fmt.Sprintln("deleted:", st.Deleted) } } spaceReclaimed = report.SpaceReclaimed } return }
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 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 runPs(dockerCli *command.DockerCli, opts *psOptions) error { ctx := context.Background() listOptions, err := buildContainerListOptions(opts) if err != nil { return err } containers, err := dockerCli.Client().ContainerList(ctx, *listOptions) if err != nil { return err } format := opts.format if len(format) == 0 { if len(dockerCli.ConfigFile().PsFormat) > 0 && !opts.quiet { format = dockerCli.ConfigFile().PsFormat } else { format = formatter.TableFormatKey } } containerCtx := formatter.Context{ Output: dockerCli.Out(), Format: formatter.NewContainerFormat(format, opts.quiet, listOptions.Size), Trunc: !opts.noTrunc, } return formatter.ContainerWrite(containerCtx, containers) }
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 }
// 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 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 }
// MonitorTtySize updates the container tty size when the terminal tty changes size func MonitorTtySize(ctx context.Context, cli *command.DockerCli, id string, isExec bool) error { resizeTty := func() { height, width := cli.Out().GetTtySize() resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) } resizeTty() if runtime.GOOS == "windows" { go func() { prevH, prevW := cli.Out().GetTtySize() for { time.Sleep(time.Millisecond * 250) h, w := cli.Out().GetTtySize() if prevW != w || prevH != h { resizeTty() } prevH = h prevW = w } }() } else { sigchan := make(chan os.Signal, 1) gosignal.Notify(sigchan, signal.SIGWINCH) go func() { for range sigchan { resizeTty() } }() } 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 }
func runSecretRemove(dockerCli *command.DockerCli, opts removeOptions) error { client := dockerCli.Client() ctx := context.Background() ids, err := getCliRequestedSecretIDs(ctx, client, opts.names) if err != nil { return err } var errs []string for _, id := range ids { if err := client.SecretRemove(ctx, id); err != nil { errs = append(errs, err.Error()) continue } fmt.Fprintln(dockerCli.Out(), id) } if len(errs) > 0 { return fmt.Errorf("%s", strings.Join(errs, "\n")) } return nil }
func runSecretCreate(dockerCli *command.DockerCli, options createOptions) error { client := dockerCli.Client() ctx := context.Background() var in io.Reader = dockerCli.In() if options.file != "-" { file, err := system.OpenSequential(options.file) if err != nil { return err } in = file defer file.Close() } secretData, err := ioutil.ReadAll(in) if err != nil { return fmt.Errorf("Error reading content from %q: %v", options.file, 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 }
func runList(dockerCli *command.DockerCli, opts listOptions) error { client := dockerCli.Client() options := types.NetworkListOptions{Filters: opts.filter.Value()} networkResources, err := client.NetworkList(context.Background(), options) if err != nil { return err } format := opts.format if len(format) == 0 { if len(dockerCli.ConfigFile().NetworksFormat) > 0 && !opts.quiet { format = dockerCli.ConfigFile().NetworksFormat } else { format = formatter.TableFormatKey } } sort.Sort(byNetworkName(networkResources)) networksCtx := formatter.Context{ Output: dockerCli.Out(), Format: formatter.NewNetworkFormat(format, opts.quiet), Trunc: !opts.noTrunc, } return formatter.NetworkWrite(networksCtx, networkResources) }
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) }
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 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 }
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 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 := dockerCli.ResolveAuthConfig(ctx, 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 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, } client := dockerCli.Client() responseBody, err := client.ServiceLogs(ctx, opts.service, options) if err != nil { return err } defer responseBody.Close() resolver := idresolver.New(client, opts.noResolve) stdout := &logWriter{ctx: ctx, opts: opts, r: resolver, w: dockerCli.Out()} stderr := &logWriter{ctx: ctx, opts: opts, r: resolver, w: dockerCli.Err()} // TODO(aluzzardi): Do an io.Copy for services with TTY enabled. _, err = stdcopy.StdCopy(stdout, stderr, responseBody) return err }
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 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 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 }
func runImages(dockerCli *command.DockerCli, opts imagesOptions) error { ctx := context.Background() options := types.ImageListOptions{ MatchName: opts.matchName, All: opts.all, Filters: opts.filter.Value(), } images, err := dockerCli.Client().ImageList(ctx, options) if err != nil { return err } format := opts.format if len(format) == 0 { if len(dockerCli.ConfigFile().ImagesFormat) > 0 && !opts.quiet { format = dockerCli.ConfigFile().ImagesFormat } else { format = formatter.TableFormatKey } } imageCtx := formatter.ImageContext{ Context: formatter.Context{ Output: dockerCli.Out(), Format: formatter.NewImageFormat(format, opts.quiet, opts.showDigests), Trunc: !opts.noTrunc, }, Digest: opts.showDigests, } return formatter.ImageWrite(imageCtx, images) }
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 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 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 runLoad(dockerCli *command.DockerCli, opts loadOptions) error { var input io.Reader = dockerCli.In() if opts.input != "" { file, err := os.Open(opts.input) if err != nil { return err } defer file.Close() input = file } // To avoid getting stuck, verify that a tar file is given either in // the input flag or through stdin and if not display an error message and exit. if opts.input == "" && dockerCli.In().IsTerminal() { return fmt.Errorf("requested load from stdin, but stdin is empty") } if !dockerCli.Out().IsTerminal() { opts.quiet = true } response, err := dockerCli.Client().ImageLoad(context.Background(), input, opts.quiet) if err != nil { return err } defer response.Body.Close() if response.Body != nil && response.JSON { return jsonmessage.DisplayJSONMessagesToStream(response.Body, dockerCli.Out(), nil) } _, err = io.Copy(dockerCli.Out(), response.Body) return err }