func PrintCommitInfo(w io.Writer, commitInfo *pfs.CommitInfo) { fmt.Fprintf(w, "%s\t", commitInfo.Commit.Id) if commitInfo.ParentCommit != nil { fmt.Fprintf(w, "%s\t", commitInfo.ParentCommit.Id) } else { fmt.Fprint(w, "<none>\t") } if commitInfo.CommitType == pfs.CommitType_COMMIT_TYPE_WRITE { fmt.Fprint(w, "writeable\t") } else { fmt.Fprint(w, "read-only\t") } fmt.Fprintf( w, "%s ago\t", units.HumanDuration( time.Since( prototime.TimestampToTime( commitInfo.Started, ), ), ), ) finished := "\t" if commitInfo.Finished != nil { finished = fmt.Sprintf("%s ago\t", units.HumanDuration( time.Since( prototime.TimestampToTime( commitInfo.Finished, ), ), )) } fmt.Fprintf(w, finished) fmt.Fprintf(w, "%s\t\n", units.BytesSize(float64(commitInfo.SizeBytes))) }
// String returns a human-readable description of the state func (s *State) String() string { if s.Running { if s.Paused { return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) } if s.Restarting { return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) } return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) } if s.removalInProgress { return "Removal In Progress" } if s.Dead { return "Dead" } if s.StartedAt.IsZero() { return "Created" } if s.FinishedAt.IsZero() { return "" } return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) }
// String returns a human-readable description of the state func (s *State) String() string { s.RLock() defer s.RUnlock() if s.Running { if s.Paused { return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) } return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) } if s.FinishedAt.IsZero() { return "" } return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) }
func printPod(pod *api.Pod, w io.Writer) error { // TODO: remove me when pods are converted spec := &api.PodSpec{} if err := api.Scheme.Convert(&pod.Spec, spec); err != nil { glog.Errorf("Unable to convert pod manifest: %v", err) } containers := spec.Containers var firstContainer api.Container if len(containers) > 0 { firstContainer, containers = containers[0], containers[1:] } _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", pod.Name, pod.Status.PodIP, firstContainer.Name, firstContainer.Image, podHostString(pod.Status.Host, pod.Status.HostIP), formatLabels(pod.Labels), pod.Status.Phase, units.HumanDuration(time.Now().Sub(pod.CreationTimestamp.Time))) if err != nil { return err } // Lay out all the other containers on separate lines. for _, container := range containers { _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "", "", container.Name, container.Image, "", "", "", "") if err != nil { return err } } return nil }
// CmdHistory shows the history of an image. // // Usage: docker history [OPTIONS] IMAGE func (cli *DockerCli) CmdHistory(args ...string) error { cmd := Cli.Subcmd("history", []string{"IMAGE"}, Cli.DockerCommands["history"].Description, true) human := cmd.Bool([]string{"H", "-human"}, true, "Print sizes and dates in human readable format") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) serverResp, err := cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, nil) if err != nil { return err } defer serverResp.body.Close() history := []types.ImageHistory{} if err := json.NewDecoder(serverResp.body).Decode(&history); err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE\tCOMMENT") } for _, entry := range history { if *noTrunc { fmt.Fprintf(w, entry.ID) } else { fmt.Fprintf(w, stringid.TruncateID(entry.ID)) } if !*quiet { if *human { fmt.Fprintf(w, "\t%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(entry.Created, 0)))) } else { fmt.Fprintf(w, "\t%s\t", time.Unix(entry.Created, 0).Format(time.RFC3339)) } if *noTrunc { fmt.Fprintf(w, "%s\t", strings.Replace(entry.CreatedBy, "\t", " ", -1)) } else { fmt.Fprintf(w, "%s\t", stringutils.Truncate(strings.Replace(entry.CreatedBy, "\t", " ", -1), 45)) } if *human { fmt.Fprintf(w, "%s\t", units.HumanSize(float64(entry.Size))) } else { fmt.Fprintf(w, "%d\t", entry.Size) } fmt.Fprintf(w, "%s", entry.Comment) } fmt.Fprintf(w, "\n") } w.Flush() return nil }
// cancel cancels any deployment process in progress for config. func (o DeployOptions) cancel(config *deployapi.DeploymentConfig, out io.Writer) error { if config.Spec.Paused { return fmt.Errorf("cannot cancel a paused deployment config") } deployments, err := o.kubeClient.ReplicationControllers(config.Namespace).List(kapi.ListOptions{LabelSelector: deployutil.ConfigSelector(config.Name)}) if err != nil { return err } if len(deployments.Items) == 0 { fmt.Fprintf(out, "There have been no deployments for %s/%s\n", config.Namespace, config.Name) return nil } sort.Sort(deployutil.ByLatestVersionDesc(deployments.Items)) failedCancellations := []string{} anyCancelled := false for _, deployment := range deployments.Items { status := deployutil.DeploymentStatusFor(&deployment) switch status { case deployapi.DeploymentStatusNew, deployapi.DeploymentStatusPending, deployapi.DeploymentStatusRunning: if deployutil.IsDeploymentCancelled(&deployment) { continue } deployment.Annotations[deployapi.DeploymentCancelledAnnotation] = deployapi.DeploymentCancelledAnnotationValue deployment.Annotations[deployapi.DeploymentStatusReasonAnnotation] = deployapi.DeploymentCancelledByUser _, err := o.kubeClient.ReplicationControllers(deployment.Namespace).Update(&deployment) if err == nil { fmt.Fprintf(out, "Cancelled deployment #%d\n", config.Status.LatestVersion) anyCancelled = true } else { fmt.Fprintf(out, "Couldn't cancel deployment #%d (status: %s): %v\n", deployutil.DeploymentVersionFor(&deployment), status, err) failedCancellations = append(failedCancellations, strconv.FormatInt(deployutil.DeploymentVersionFor(&deployment), 10)) } } } if len(failedCancellations) > 0 { return fmt.Errorf("couldn't cancel deployment %s", strings.Join(failedCancellations, ", ")) } if !anyCancelled { latest := &deployments.Items[0] maybeCancelling := "" if deployutil.IsDeploymentCancelled(latest) && !deployutil.IsTerminatedDeployment(latest) { maybeCancelling = " (cancelling)" } timeAt := strings.ToLower(units.HumanDuration(time.Now().Sub(latest.CreationTimestamp.Time))) fmt.Fprintf(out, "No deployments are in progress (latest deployment #%d %s%s %s ago)\n", deployutil.DeploymentVersionFor(latest), strings.ToLower(string(deployutil.DeploymentStatusFor(latest))), maybeCancelling, timeAt) } return nil }
// CmdHistory shows the history of an image. // // Usage: docker history [OPTIONS] IMAGE func (cli *DockerCli) CmdHistory(args ...string) error { cmd := Cli.Subcmd("history", []string{"IMAGE"}, Cli.DockerCommands["history"].Description, true) human := cmd.Bool([]string{"H", "-human"}, true, "Print sizes and dates in human readable format") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Don't truncate output") cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) history, err := cli.client.ImageHistory(cmd.Arg(0)) if err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if *quiet { for _, entry := range history { if *noTrunc { fmt.Fprintf(w, "%s\n", entry.ID) } else { fmt.Fprintf(w, "%s\n", stringid.TruncateID(entry.ID)) } } w.Flush() return nil } var imageID string var createdBy string var created string var size string fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE\tCOMMENT") for _, entry := range history { imageID = entry.ID createdBy = strings.Replace(entry.CreatedBy, "\t", " ", -1) if *noTrunc == false { createdBy = stringutils.Truncate(createdBy, 45) imageID = stringid.TruncateID(entry.ID) } if *human { created = units.HumanDuration(time.Now().UTC().Sub(time.Unix(entry.Created, 0))) + " ago" size = units.HumanSize(float64(entry.Size)) } else { created = time.Unix(entry.Created, 0).Format(time.RFC3339) size = strconv.FormatInt(entry.Size, 10) } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", imageID, created, createdBy, size, entry.Comment) } w.Flush() return nil }
// RunBuildLogs contains all the necessary functionality for the OpenShift cli build-logs command func RunBuildLogs(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, opts api.BuildLogOptions, args []string) error { if len(args) != 1 { // maximum time to wait for a list of builds timeout := 800 * time.Millisecond // maximum number of builds to list maxBuildListLen := 10 ch := make(chan error) go func() { // TODO fetch via API no more than maxBuildListLen builds builds, err := getBuilds(f) if err != nil { return } if len(builds) == 0 { ch <- cmdutil.UsageError(cmd, "There are no builds in the current project") return } sort.Sort(sort.Reverse(api.ByCreationTimestamp(builds))) msg := "A build name is required. Most recent builds:" for i, b := range builds { if i == maxBuildListLen { break } msg += fmt.Sprintf("\n* %s\t%s\t%s ago", b.Name, b.Status.Phase, units.HumanDuration(time.Since(b.CreationTimestamp.Time))) } ch <- cmdutil.UsageError(cmd, msg) }() select { case <-time.After(timeout): return cmdutil.UsageError(cmd, "A build name is required") case err := <-ch: return err } } namespace, _, err := f.DefaultNamespace() if err != nil { return err } c, _, err := f.Clients() if err != nil { return err } readCloser, err := c.BuildLogs(namespace).Get(args[0], opts).Stream() if err != nil { return err } defer readCloser.Close() _, err = io.Copy(out, readCloser) return err }
// CmdHistory shows the history of an image. // // Usage: docker history [OPTIONS] IMAGE func (cli *DockerCli) CmdHistory(args ...string) error { cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image", true) quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") cmd.Require(flag.Exact, 1) utils.ParseFlags(cmd, args, true) body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, nil)) if err != nil { return err } outs := engine.NewTable("Created", 0) if _, err := outs.ReadListFrom(body); err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE") } for _, out := range outs.Data { outID := out.Get("Id") if !*quiet { if *noTrunc { fmt.Fprintf(w, "%s\t", outID) } else { fmt.Fprintf(w, "%s\t", stringid.TruncateID(outID)) } fmt.Fprintf(w, "%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0)))) if *noTrunc { fmt.Fprintf(w, "%s\t", out.Get("CreatedBy")) } else { fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45)) } fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("Size")))) } else { if *noTrunc { fmt.Fprintln(w, outID) } else { fmt.Fprintln(w, stringid.TruncateID(outID)) } } } w.Flush() return nil }
func PrintRepoInfo(w io.Writer, repoInfo *pfs.RepoInfo) { fmt.Fprintf(w, "%s\t", repoInfo.Repo.Name) fmt.Fprintf( w, "%s ago\t", units.HumanDuration( time.Since( prototime.TimestampToTime( repoInfo.Created, ), ), ), ) fmt.Fprintf(w, "%s\t\n", units.BytesSize(float64(repoInfo.SizeBytes))) }
func PrintBlockInfo(w io.Writer, blockInfo *drive.BlockInfo) { fmt.Fprintf(w, "%s\t", blockInfo.Block.Hash) fmt.Fprintf( w, "%s ago\t", units.HumanDuration( time.Since( prototime.TimestampToTime( blockInfo.Created, ), ), ), ) fmt.Fprintf(w, "%s\t\n", units.BytesSize(float64(blockInfo.SizeBytes))) }
// CmdHistory shows the history of an image. // // Usage: docker history [OPTIONS] IMAGE func (cli *DockerCli) CmdHistory(args ...string) error { cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image", true) quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) rdr, _, err := cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, nil) if err != nil { return err } history := []types.ImageHistory{} err = json.NewDecoder(rdr).Decode(&history) if err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE\tCOMMENT") } for _, entry := range history { if *noTrunc { fmt.Fprintf(w, entry.ID) } else { fmt.Fprintf(w, stringid.TruncateID(entry.ID)) } if !*quiet { fmt.Fprintf(w, "\t%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(entry.Created, 0)))) if *noTrunc { fmt.Fprintf(w, "%s\t", entry.CreatedBy) } else { fmt.Fprintf(w, "%s\t", utils.Trunc(entry.CreatedBy, 45)) } fmt.Fprintf(w, "%s\t", units.HumanSize(float64(entry.Size))) fmt.Fprintf(w, "%s", entry.Comment) } fmt.Fprintf(w, "\n") } w.Flush() return nil }
func PrintFileInfo(w io.Writer, fileInfo *pfs.FileInfo) { fmt.Fprintf(w, "%s\t", fileInfo.Path.Path) if fileInfo.FileType == pfs.FileType_FILE_TYPE_REGULAR { fmt.Fprint(w, "file\t") } else { fmt.Fprint(w, "dir\t") } fmt.Fprintf( w, "%s ago\t", units.HumanDuration( time.Since( prototime.TimestampToTime( fileInfo.LastModified, ), ), ), ) fmt.Fprint(w, "-\t") fmt.Fprintf(w, "%s\t", units.BytesSize(float64(fileInfo.SizeBytes))) fmt.Fprintf(w, "%4d\t\n", fileInfo.Perm) }
func runHistory(cmd *types.Command, args []string) { if historyHelp { cmd.PrintUsage() } if len(args) != 1 { cmd.PrintShortUsage() } imageID := cmd.API.GetImageID(args[0], true) image, err := cmd.API.GetImage(imageID) if err != nil { log.Fatalf("Cannot get image %s: %v", imageID, err) } if imagesQ { fmt.Println(imageID) return } w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0) defer w.Flush() fmt.Fprintf(w, "IMAGE\tCREATED\tCREATED BY\tSIZE\n") identifier := utils.TruncIf(image.Identifier, 8, !historyNoTrunc) creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", image.CreationDate) if err != nil { log.Fatalf("Unable to parse creation date from the Scaleway API: %v", err) } creationDateStr := units.HumanDuration(time.Now().UTC().Sub(creationDate)) volumeName := utils.TruncIf(image.RootVolume.Name, 25, !historyNoTrunc) size := units.HumanSize(float64(image.RootVolume.Size)) fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", identifier, creationDateStr, volumeName, size) }
// CmdImages lists the images in a specified repository, or all top-level images if no repository is specified. // // Usage: docker images [OPTIONS] [REPOSITORY] func (cli *DockerCli) CmdImages(args ...string) error { cmd := cli.Subcmd("images", "[REPOSITORY]", "List images", true) quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") showDigests := cmd.Bool([]string{"-digests"}, false, "Show digests") // FIXME: --viz and --tree are deprecated. Remove them in a future version. flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format") flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format") flFilter := opts.NewListOpts(nil) cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided") cmd.Require(flag.Max, 1) utils.ParseFlags(cmd, args, true) // Consolidate all filter flags, and sanity check them early. // They'll get process in the daemon/server. imageFilterArgs := filters.Args{} for _, f := range flFilter.GetAll() { var err error imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs) if err != nil { return err } } matchName := cmd.Arg(0) // FIXME: --viz and --tree are deprecated. Remove them in a future version. if *flViz || *flTree { v := url.Values{ "all": []string{"1"}, } if len(imageFilterArgs) > 0 { filterJSON, err := filters.ToParam(imageFilterArgs) if err != nil { return err } v.Set("filters", filterJSON) } body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil)) if err != nil { return err } outs := engine.NewTable("Created", 0) if _, err := outs.ReadListFrom(body); err != nil { return err } var ( printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string) startImage *engine.Env roots = engine.NewTable("Created", outs.Len()) byParent = make(map[string]*engine.Table) ) for _, image := range outs.Data { if image.Get("ParentId") == "" { roots.Add(image) } else { if children, exists := byParent[image.Get("ParentId")]; exists { children.Add(image) } else { byParent[image.Get("ParentId")] = engine.NewTable("Created", 1) byParent[image.Get("ParentId")].Add(image) } } if matchName != "" { if matchName == image.Get("Id") || matchName == stringid.TruncateID(image.Get("Id")) { startImage = image } for _, repotag := range image.GetList("RepoTags") { if repotag == matchName { startImage = image } } } } if *flViz { fmt.Fprintf(cli.out, "digraph docker {\n") printNode = (*DockerCli).printVizNode } else { printNode = (*DockerCli).printTreeNode } if startImage != nil { root := engine.NewTable("Created", 1) root.Add(startImage) cli.WalkTree(*noTrunc, root, byParent, "", printNode) } else if matchName == "" { cli.WalkTree(*noTrunc, roots, byParent, "", printNode) } if *flViz { fmt.Fprintf(cli.out, " base [style=invisible]\n}\n") } } else { v := url.Values{} if len(imageFilterArgs) > 0 { filterJSON, err := filters.ToParam(imageFilterArgs) if err != nil { return err } v.Set("filters", filterJSON) } if cmd.NArg() == 1 { // FIXME rename this parameter, to not be confused with the filters flag v.Set("filter", matchName) } if *all { v.Set("all", "1") } body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil)) if err != nil { return err } outs := engine.NewTable("Created", 0) if _, err := outs.ReadListFrom(body); err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { if *showDigests { fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tVIRTUAL SIZE") } else { fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE") } } for _, out := range outs.Data { outID := out.Get("Id") if !*noTrunc { outID = stringid.TruncateID(outID) } repoTags := out.GetList("RepoTags") repoDigests := out.GetList("RepoDigests") if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" { // dangling image - clear out either repoTags or repoDigsts so we only show it once below repoDigests = []string{} } // combine the tags and digests lists tagsAndDigests := append(repoTags, repoDigests...) for _, repoAndRef := range tagsAndDigests { repo, ref := parsers.ParseRepositoryTag(repoAndRef) // default tag and digest to none - if there's a value, it'll be set below tag := "<none>" digest := "<none>" if utils.DigestReference(ref) { digest = ref } else { tag = ref } if !*quiet { if *showDigests { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize")))) } else { fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize")))) } } else { fmt.Fprintln(w, outID) } } } if !*quiet { w.Flush() } } return nil }
func formatImageStreamTags(out *tabwriter.Writer, stream *imageapi.ImageStream) { if len(stream.Status.Tags) == 0 && len(stream.Spec.Tags) == 0 { fmt.Fprintf(out, "Tags:\t<none>\n") return } now := timeNowFn() images := make(map[string]string) for tag, tags := range stream.Status.Tags { for _, item := range tags.Items { switch { case len(item.Image) > 0: if _, ok := images[item.Image]; !ok { images[item.Image] = tag } case len(item.DockerImageReference) > 0: if _, ok := images[item.DockerImageReference]; !ok { images[item.Image] = item.DockerImageReference } } } } sortedTags := []string{} for k := range stream.Status.Tags { sortedTags = append(sortedTags, k) } var localReferences sets.String var referentialTags map[string]sets.String for k := range stream.Spec.Tags { if target, _, ok, multiple := imageapi.FollowTagReference(stream, k); ok && multiple { if referentialTags == nil { referentialTags = make(map[string]sets.String) } if localReferences == nil { localReferences = sets.NewString() } localReferences.Insert(k) v := referentialTags[target] if v == nil { v = sets.NewString() referentialTags[target] = v } v.Insert(k) } if _, ok := stream.Status.Tags[k]; !ok { sortedTags = append(sortedTags, k) } } fmt.Fprintf(out, "Unique Images:\t%d\nTags:\t%d\n\n", len(images), len(sortedTags)) first := true imageapi.PrioritizeTags(sortedTags) for _, tag := range sortedTags { if localReferences.Has(tag) { continue } if first { first = false } else { fmt.Fprintf(out, "\n") } taglist, _ := stream.Status.Tags[tag] tagRef, hasSpecTag := stream.Spec.Tags[tag] scheduled := false insecure := false importing := false var name string if hasSpecTag && tagRef.From != nil { if len(tagRef.From.Namespace) > 0 && tagRef.From.Namespace != stream.Namespace { name = fmt.Sprintf("%s/%s", tagRef.From.Namespace, tagRef.From.Name) } else { name = tagRef.From.Name } scheduled, insecure = tagRef.ImportPolicy.Scheduled, tagRef.ImportPolicy.Insecure gen := imageapi.LatestObservedTagGeneration(stream, tag) importing = !tagRef.Reference && tagRef.Generation != nil && *tagRef.Generation != gen } // updates whenever tag :5.2 is changed // :latest (30 minutes ago) -> 102.205.358.453/foo/bar@sha256:abcde734 // error: last import failed 20 minutes ago // updates automatically from index.docker.io/mysql/bar // will use insecure HTTPS connections or HTTP // // MySQL 5.5 // --------- // Describes a system for updating based on practical changes to a database system // with some other data involved // // 20 minutes ago <import failed> // Failed to locate the server in time // 30 minutes ago 102.205.358.453/foo/bar@sha256:abcdef // 1 hour ago 102.205.358.453/foo/bar@sha256:bfedfc //var shortErrors []string /* var internalReference *imageapi.DockerImageReference if value := stream.Status.DockerImageRepository; len(value) > 0 { ref, err := imageapi.ParseDockerImageReference(value) if err != nil { internalReference = &ref } } */ if referentialTags[tag].Len() > 0 { references := referentialTags[tag].List() imageapi.PrioritizeTags(references) fmt.Fprintf(out, "%s (%s)\n", tag, strings.Join(references, ", ")) } else { fmt.Fprintf(out, "%s\n", tag) } switch { case !hasSpecTag || tagRef.From == nil: fmt.Fprintf(out, " pushed image\n") case tagRef.From.Kind == "ImageStreamTag": switch { case tagRef.Reference: fmt.Fprintf(out, " reference to %s\n", name) case scheduled: fmt.Fprintf(out, " updates automatically from %s\n", name) default: fmt.Fprintf(out, " tagged from %s\n", name) } case tagRef.From.Kind == "DockerImage": switch { case tagRef.Reference: fmt.Fprintf(out, " reference to registry %s\n", name) case scheduled: fmt.Fprintf(out, " updates automatically from registry %s\n", name) default: fmt.Fprintf(out, " tagged from %s\n", name) } case tagRef.From.Kind == "ImageStreamImage": switch { case tagRef.Reference: fmt.Fprintf(out, " reference to image %s\n", name) default: fmt.Fprintf(out, " tagged from %s\n", name) } default: switch { case tagRef.Reference: fmt.Fprintf(out, " reference to %s %s\n", tagRef.From.Kind, name) default: fmt.Fprintf(out, " updates from %s %s\n", tagRef.From.Kind, name) } } if insecure { fmt.Fprintf(out, " will use insecure HTTPS or HTTP connections\n") } fmt.Fprintln(out) extraOutput := false if d := tagRef.Annotations["description"]; len(d) > 0 { fmt.Fprintf(out, " %s\n", d) extraOutput = true } if t := tagRef.Annotations["tags"]; len(t) > 0 { fmt.Fprintf(out, " Tags: %s\n", strings.Join(strings.Split(t, ","), ", ")) extraOutput = true } if t := tagRef.Annotations["supports"]; len(t) > 0 { fmt.Fprintf(out, " Supports: %s\n", strings.Join(strings.Split(t, ","), ", ")) extraOutput = true } if t := tagRef.Annotations["sampleRepo"]; len(t) > 0 { fmt.Fprintf(out, " Example Repo: %s\n", t) extraOutput = true } if extraOutput { fmt.Fprintln(out) } if importing { fmt.Fprintf(out, " ~ importing latest image ...\n") } for i := range taglist.Conditions { condition := &taglist.Conditions[i] switch condition.Type { case imageapi.ImportSuccess: if condition.Status == api.ConditionFalse { d := now.Sub(condition.LastTransitionTime.Time) fmt.Fprintf(out, " ! error: Import failed (%s): %s\n %s ago\n", condition.Reason, condition.Message, units.HumanDuration(d)) } } } if len(taglist.Items) == 0 { continue } for i, event := range taglist.Items { d := now.Sub(event.Created.Time) if i == 0 { fmt.Fprintf(out, " * %s\n", event.DockerImageReference) } else { fmt.Fprintf(out, " %s\n", event.DockerImageReference) } ref, err := imageapi.ParseDockerImageReference(event.DockerImageReference) id := event.Image if len(id) > 0 && err == nil && ref.ID != id { fmt.Fprintf(out, " %s ago\t%s\n", units.HumanDuration(d), id) } else { fmt.Fprintf(out, " %s ago\n", units.HumanDuration(d)) } } } }
func formatRelativeTime(t time.Time) string { return units.HumanDuration(timeNowFn().Sub(t)) }
func formatImageStreamTags(out *tabwriter.Writer, stream *imageapi.ImageStream) { if len(stream.Status.Tags) == 0 && len(stream.Spec.Tags) == 0 { fmt.Fprintf(out, "Tags:\t<none>\n") return } fmt.Fprint(out, "\nTag\tSpec\tCreated\tPullSpec\tImage\n") sortedTags := []string{} for k := range stream.Status.Tags { sortedTags = append(sortedTags, k) } for k := range stream.Spec.Tags { if _, ok := stream.Status.Tags[k]; !ok { sortedTags = append(sortedTags, k) } } hasScheduled, hasInsecure := false, false imageapi.PrioritizeTags(sortedTags) for _, tag := range sortedTags { tagRef, ok := stream.Spec.Tags[tag] specTag := "" scheduled := false insecure := false if ok { if tagRef.From != nil { namePair := "" if len(tagRef.From.Namespace) > 0 && tagRef.From.Namespace != stream.Namespace { namePair = fmt.Sprintf("%s/%s", tagRef.From.Namespace, tagRef.From.Name) } else { namePair = tagRef.From.Name } switch tagRef.From.Kind { case "ImageStreamTag", "ImageStreamImage": specTag = namePair case "DockerImage": specTag = tagRef.From.Name default: specTag = fmt.Sprintf("<unknown %s> %s", tagRef.From.Kind, namePair) } } scheduled, insecure = tagRef.ImportPolicy.Scheduled, tagRef.ImportPolicy.Insecure hasScheduled = hasScheduled || scheduled hasInsecure = hasScheduled || insecure } else { specTag = "<pushed>" } if taglist, ok := stream.Status.Tags[tag]; ok { if len(taglist.Conditions) > 0 { var lastTime time.Time summary := []string{} for _, condition := range taglist.Conditions { if condition.LastTransitionTime.After(lastTime) { lastTime = condition.LastTransitionTime.Time } switch condition.Type { case imageapi.ImportSuccess: if condition.Status == api.ConditionFalse { summary = append(summary, fmt.Sprintf("import failed: %s", condition.Message)) } default: summary = append(summary, string(condition.Type)) } } if len(summary) > 0 { description := strings.Join(summary, ", ") if len(description) > 70 { description = strings.TrimSpace(description[:70-3]) + "..." } d := timeNowFn().Sub(lastTime) fmt.Fprintf(out, "%s\t%s\t%s ago\t%s\t%v\n", tag, shortenImagePullSpec(specTag), units.HumanDuration(d), "", description) } } for i, event := range taglist.Items { d := timeNowFn().Sub(event.Created.Time) image := event.Image ref, err := imageapi.ParseDockerImageReference(event.DockerImageReference) if err == nil { if ref.ID == image { image = "<same>" } } pullSpec := event.DockerImageReference if pullSpec == specTag { pullSpec = "<same>" } else { pullSpec = shortenImagePullSpec(pullSpec) } specTag = shortenImagePullSpec(specTag) if i != 0 { tag, specTag = "", "" } else { extra := "" if scheduled { extra += "*" } if insecure { extra += "!" } if len(extra) > 0 { specTag += " " + extra } } fmt.Fprintf(out, "%s\t%s\t%s ago\t%s\t%v\n", tag, specTag, units.HumanDuration(d), pullSpec, image) } } else { fmt.Fprintf(out, "%s\t%s\t\t<not available>\t<not available>\n", tag, specTag) } } if hasInsecure || hasScheduled { fmt.Fprintln(out) if hasScheduled { fmt.Fprintf(out, " * tag is scheduled for periodic import\n") } if hasInsecure { fmt.Fprintf(out, " ! tag is insecure and can be imported over HTTP or self-signed HTTPS\n") } } }
k := parts[0] v := parts[1] if k == "FREIGHT_NAME" { cName = v continue } if k == "FREIGHT_VERSION" { cVersion = v continue } } cTime := units.HumanDuration(time.Now().UTC().Sub(time.Unix(cnt.Created, 0))) exposedPorts := []string{} for k, v := range cntInfo.NetworkSettings.Ports { for _, port := range v { exposedPorts = append(exposedPorts, fmt.Sprintf("%s:%s->%s", port.HostIp, port.HostPort, k)) } } portDisplay := strings.Join(exposedPorts, ",") if cName != "" && cVersion != "" { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", cId, cName, cVersion, cTime, cnt.Status, portDisplay) } }
func (c *containerContext) RunningFor() string { c.addHeader(runningForHeader) createdAt := time.Unix(int64(c.c.Created), 0) return units.HumanDuration(time.Now().UTC().Sub(createdAt)) }
// Receives a time.Duration and returns Docker go-utils' // human-readable output func formatToHumanDuration(dur time.Duration) string { return units.HumanDuration(dur) }
func runImages(cmd *types.Command, args []string) { if imagesHelp { cmd.PrintUsage() } if len(args) != 0 { cmd.PrintShortUsage() } wg := sync.WaitGroup{} chEntries := make(chan api.ScalewayImageInterface) var entries = []api.ScalewayImageInterface{} wg.Add(1) go func() { defer wg.Done() images, err := cmd.API.GetImages() if err != nil { log.Fatalf("unable to fetch images from the Scaleway API: %v", err) } for _, val := range *images { creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate) if err != nil { log.Fatalf("unable to parse creation date from the Scaleway API: %v", err) } chEntries <- api.ScalewayImageInterface{ Type: "image", CreationDate: creationDate, Identifier: val.Identifier, Name: val.Name, Public: val.Public, Tag: "latest", VirtualSize: float64(val.RootVolume.Size), } } }() if imagesA { wg.Add(1) go func() { defer wg.Done() snapshots, err := cmd.API.GetSnapshots() if err != nil { log.Fatalf("unable to fetch snapshots from the Scaleway API: %v", err) } for _, val := range *snapshots { creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate) if err != nil { log.Fatalf("unable to parse creation date from the Scaleway API: %v", err) } chEntries <- api.ScalewayImageInterface{ Type: "snapshot", CreationDate: creationDate, Identifier: val.Identifier, Name: val.Name, Tag: "<snapshot>", VirtualSize: float64(val.Size), Public: false, } } }() wg.Add(1) go func() { defer wg.Done() bootscripts, err := cmd.API.GetBootscripts() if err != nil { log.Fatalf("unable to fetch bootscripts from the Scaleway API: %v", err) } for _, val := range *bootscripts { chEntries <- api.ScalewayImageInterface{ Type: "bootscript", Identifier: val.Identifier, Name: val.Title, Tag: "<bootscript>", Public: false, } } }() wg.Add(1) go func() { defer wg.Done() volumes, err := cmd.API.GetVolumes() if err != nil { log.Fatalf("unable to fetch volumes from the Scaleway API: %v", err) } for _, val := range *volumes { creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate) if err != nil { log.Fatalf("unable to parse creation date from the Scaleway API: %v", err) } chEntries <- api.ScalewayImageInterface{ Type: "volume", CreationDate: creationDate, Identifier: val.Identifier, Name: val.Name, Tag: "<volume>", VirtualSize: float64(val.Size), Public: false, } } }() } go func() { wg.Wait() close(chEntries) }() done := false for { select { case entry, ok := <-chEntries: if !ok { done = true break } entries = append(entries, entry) } if done { break } } w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) defer w.Flush() if !imagesQ { fmt.Fprintf(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE\n") } sort.Sort(api.ByCreationDate(entries)) for _, image := range entries { if imagesQ { fmt.Fprintf(w, "%s\n", image.Identifier) } else { tag := image.Tag shortID := utils.TruncIf(image.Identifier, 8, !imagesNoTrunc) name := utils.Wordify(image.Name) if !image.Public && image.Type == "image" { name = "user/" + name } shortName := utils.TruncIf(name, 25, !imagesNoTrunc) var creationDate, virtualSize string if image.CreationDate.IsZero() { creationDate = "n/a" } else { creationDate = units.HumanDuration(time.Now().UTC().Sub(image.CreationDate)) } if image.VirtualSize == 0 { virtualSize = "n/a" } else { virtualSize = units.HumanSize(image.VirtualSize) } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", shortName, tag, shortID, creationDate, virtualSize) } } }
func (cli *KraneCli) CmdPs(args ...string) error { cmd := cli.Subcmd("ps", "[OPTIONS]", "List containers") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") size := cmd.Bool([]string{"s", "-size"}, false, "Display sizes") all := cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.") since := cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.") before := cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.") last := cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.") if err := cmd.Parse(args); err != nil { return nil } v := url.Values{} if *last == -1 && *nLatest { *last = 1 } if *all { v.Set("all", "1") } if *last != -1 { v.Set("limit", strconv.Itoa(*last)) } if *since != "" { v.Set("since", *since) } if *before != "" { v.Set("before", *before) } if *size { v.Set("size", "1") } body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false)) if err != nil { return err } ships := engine.NewTable("Created", 0) if _, err := ships.ReadListFrom(body); err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES\tSHIP") if *size { fmt.Fprintln(w, "\tSIZE") } else { fmt.Fprint(w, "\n") } } for _, outShip := range ships.Data { var ( outShipFQDN = outShip.Get("fqdn") outContainers = outShip.Get("Containers") ) outs := engine.NewTable("Created", 0) if _, err := outs.ReadListFrom([]byte(outContainers)); err != nil { return err } for _, out := range outs.Data { var ( outID = out.Get("Id") outNames = out.GetList("Names") ) if !*noTrunc { outID = utils.TruncateID(outID) } // Remove the leading / from the names for i := 0; i < len(outNames); i++ { outNames[i] = outNames[i][1:] } if !*quiet { var ( outCommand = out.Get("Command") ports = engine.NewTable("", 0) ) outCommand = strconv.Quote(outCommand) if !*noTrunc { outCommand = utils.Trunc(outCommand, 20) } ports.ReadListFrom([]byte(out.Get("Ports"))) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, out.Get("Image"), outCommand, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","), outShipFQDN) if *size { if out.GetInt("SizeRootFs") > 0 { fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(out.GetInt64("SizeRw")), units.HumanSize(out.GetInt64("SizeRootFs"))) } else { fmt.Fprintf(w, "%s\n", units.HumanSize(out.GetInt64("SizeRw"))) } } else { fmt.Fprint(w, "\n") } } else { fmt.Fprintln(w, outID) } } } if !*quiet { w.Flush() } return nil }
// CmdImages lists the images in a specified repository, or all top-level images if no repository is specified. // // Usage: docker images [OPTIONS] [REPOSITORY] func (cli *DockerCli) CmdImages(args ...string) error { cmd := Cli.Subcmd("images", []string{"[REPOSITORY[:TAG]]"}, Cli.DockerCommands["images"].Description, true) quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") showDigests := cmd.Bool([]string{"-digests"}, false, "Show digests") flFilter := opts.NewListOpts(nil) cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided") cmd.Require(flag.Max, 1) cmd.ParseFlags(args, true) // Consolidate all filter flags, and sanity check them early. // They'll get process in the daemon/server. imageFilterArgs := filters.Args{} for _, f := range flFilter.GetAll() { var err error imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs) if err != nil { return err } } matchName := cmd.Arg(0) v := url.Values{} if len(imageFilterArgs) > 0 { filterJSON, err := filters.ToParam(imageFilterArgs) if err != nil { return err } v.Set("filters", filterJSON) } if cmd.NArg() == 1 { // FIXME rename this parameter, to not be confused with the filters flag v.Set("filter", matchName) } if *all { v.Set("all", "1") } serverResp, err := cli.call("GET", "/images/json?"+v.Encode(), nil, nil) if err != nil { return err } defer serverResp.body.Close() images := []types.Image{} if err := json.NewDecoder(serverResp.body).Decode(&images); err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { if *showDigests { fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tVIRTUAL SIZE") } else { fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE") } } for _, image := range images { ID := image.ID if !*noTrunc { ID = stringid.TruncateID(ID) } repoTags := image.RepoTags repoDigests := image.RepoDigests if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" { // dangling image - clear out either repoTags or repoDigsts so we only show it once below repoDigests = []string{} } // combine the tags and digests lists tagsAndDigests := append(repoTags, repoDigests...) for _, repoAndRef := range tagsAndDigests { repo, ref := parsers.ParseRepositoryTag(repoAndRef) // default tag and digest to none - if there's a value, it'll be set below tag := "<none>" digest := "<none>" if utils.DigestReference(ref) { digest = ref } else { tag = ref } if !*quiet { if *showDigests { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize))) } else { fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize))) } } else { fmt.Fprintln(w, ID) } } } if !*quiet { w.Flush() } return nil }
// Returns a string that contains a description of how much has passed since // the given timestamp until now. func timeAgo(ts int64) string { created, now := time.Unix(ts, 0), time.Now().UTC() return units.HumanDuration(now.Sub(created)) }
// CmdPs outputs a list of Docker containers. // // Usage: docker ps [OPTIONS] func (cli *DockerCli) CmdPs(args ...string) error { var ( err error psFilterArgs = filters.Args{} v = url.Values{} cmd = cli.Subcmd("ps", nil, "List containers", true) quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes") all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers (default shows just running)") noTrunc = cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") nLatest = cmd.Bool([]string{"l", "-latest"}, false, "Show the latest created container, include non-running") since = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show created since Id or Name, include non-running") before = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name") last = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running") flFilter = opts.NewListOpts(nil) ) cmd.Require(flag.Exact, 0) cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided") cmd.ParseFlags(args, true) if *last == -1 && *nLatest { *last = 1 } if *all { v.Set("all", "1") } if *last != -1 { v.Set("limit", strconv.Itoa(*last)) } if *since != "" { v.Set("since", *since) } if *before != "" { v.Set("before", *before) } if *size { v.Set("size", "1") } // Consolidate all filter flags, and sanity check them. // They'll get processed in the daemon/server. for _, f := range flFilter.GetAll() { if psFilterArgs, err = filters.ParseFlag(f, psFilterArgs); err != nil { return err } } if len(psFilterArgs) > 0 { filterJSON, err := filters.ToParam(psFilterArgs) if err != nil { return err } v.Set("filters", filterJSON) } serverResp, err := cli.call("GET", "/containers/json?"+v.Encode(), nil, nil) if err != nil { return err } defer serverResp.body.Close() containers := []types.Container{} if err := json.NewDecoder(serverResp.body).Decode(&containers); err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") if *size { fmt.Fprintln(w, "\tSIZE") } else { fmt.Fprint(w, "\n") } } stripNamePrefix := func(ss []string) []string { for i, s := range ss { ss[i] = s[1:] } return ss } for _, container := range containers { ID := container.ID if !*noTrunc { ID = stringid.TruncateID(ID) } if *quiet { fmt.Fprintln(w, ID) continue } var ( names = stripNamePrefix(container.Names) command = strconv.Quote(container.Command) displayPort string ) if !*noTrunc { command = stringutils.Truncate(command, 20) // only display the default name for the container with notrunc is passed for _, name := range names { if len(strings.Split(name, "/")) == 1 { names = []string{name} break } } } image := container.Image if image == "" { image = "<no image>" } if container.HostConfig.NetworkMode == "host" { displayPort = "*/tcp, */udp" } else { displayPort = api.DisplayablePorts(container.Ports) } fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", ID, image, command, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(container.Created), 0))), container.Status, displayPort, strings.Join(names, ",")) if *size { if container.SizeRootFs > 0 { fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(container.SizeRw)), units.HumanSize(float64(container.SizeRootFs))) } else { fmt.Fprintf(w, "%s\n", units.HumanSize(float64(container.SizeRw))) } continue } fmt.Fprint(w, "\n") } if !*quiet { w.Flush() } return nil }
func formatImageStreamTags(out *tabwriter.Writer, stream *imageapi.ImageStream) { if len(stream.Status.Tags) == 0 { fmt.Fprintf(out, "Tags:\t<none>\n") return } fmt.Fprint(out, "\nTag\tSpec\tCreated\tPullSpec\tImage\n") sortedTags := []string{} for k := range stream.Status.Tags { sortedTags = append(sortedTags, k) } for k := range stream.Spec.Tags { if _, ok := stream.Status.Tags[k]; !ok { sortedTags = append(sortedTags, k) } } sort.Strings(sortedTags) for _, tag := range sortedTags { tagRef, ok := stream.Spec.Tags[tag] specTag := "" if ok { if tagRef.From != nil { namePair := "" if len(tagRef.From.Namespace) > 0 && tagRef.From.Namespace != stream.Namespace { namePair = fmt.Sprintf("%s/%s", tagRef.From.Namespace, tagRef.From.Name) } else { namePair = tagRef.From.Name } switch tagRef.From.Kind { case "ImageStreamTag", "ImageStreamImage": specTag = namePair case "DockerImage": specTag = tagRef.From.Name default: specTag = fmt.Sprintf("<unknown %s> %s", tagRef.From.Kind, namePair) } } } else { specTag = "<pushed>" } if taglist, ok := stream.Status.Tags[tag]; ok { for _, event := range taglist.Items { d := timeNowFn().Sub(event.Created.Time) image := event.Image ref, err := imageapi.ParseDockerImageReference(event.DockerImageReference) if err == nil { if ref.ID == image { image = "" } } fmt.Fprintf(out, "%s\t%s\t%s ago\t%s\t%v\n", tag, specTag, units.HumanDuration(d), event.DockerImageReference, image) if tag != "" { tag = "" } if specTag != "" { specTag = "" } } } else { fmt.Fprintf(out, "%s\t%s\t\t<not available>\t<not available>\n", tag, specTag) } } }
// CmdImages lists the images in a specified repository, or all top-level images if no repository is specified. // // Usage: docker images [OPTIONS] [REPOSITORY] func (cli *DockerCli) CmdImages(args ...string) error { cmd := Cli.Subcmd("images", []string{"[REPOSITORY[:TAG]]"}, Cli.DockerCommands["images"].Description, true) quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)") noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Don't truncate output") showDigests := cmd.Bool([]string{"-digests"}, false, "Show digests") flFilter := opts.NewListOpts(nil) cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided") cmd.Require(flag.Max, 1) cmd.ParseFlags(args, true) // Consolidate all filter flags, and sanity check them early. // They'll get process in the daemon/server. imageFilterArgs := filters.NewArgs() for _, f := range flFilter.GetAll() { var err error imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs) if err != nil { return err } } var matchName string if cmd.NArg() == 1 { matchName = cmd.Arg(0) } options := types.ImageListOptions{ MatchName: matchName, All: *all, Filters: imageFilterArgs, } images, err := cli.client.ImageList(options) if err != nil { return err } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { if *showDigests { fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tSIZE") } else { fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE") } } for _, image := range images { ID := image.ID if !*noTrunc { ID = stringid.TruncateID(ID) } repoTags := image.RepoTags repoDigests := image.RepoDigests if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" { // dangling image - clear out either repoTags or repoDigsts so we only show it once below repoDigests = []string{} } // combine the tags and digests lists tagsAndDigests := append(repoTags, repoDigests...) for _, repoAndRef := range tagsAndDigests { // default repo, tag, and digest to none - if there's a value, it'll be set below repo := "<none>" tag := "<none>" digest := "<none>" if !strings.HasPrefix(repoAndRef, "<none>") { ref, err := reference.ParseNamed(repoAndRef) if err != nil { return err } repo = ref.Name() switch x := ref.(type) { case reference.Digested: digest = x.Digest().String() case reference.Tagged: tag = x.Tag() } } if !*quiet { if *showDigests { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.Size))) } else { fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.Size))) } } else { fmt.Fprintln(w, ID) } } } if !*quiet { w.Flush() } return nil }
// translateTimestamp returns the elapsed time since timestamp in // human-readable approximation. func translateTimestamp(timestamp util.Time) string { return units.HumanDuration(time.Now().Sub(timestamp.Time)) }