/
cmd-parents.go
96 lines (86 loc) · 2.92 KB
/
cmd-parents.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package main
import (
"errors"
"fmt"
"github.com/docker-library/bashbrew/manifest"
"github.com/urfave/cli"
)
func cmdParents(c *cli.Context) error {
repos := c.Args()
if len(repos) < 1 {
return fmt.Errorf(`need at least one repo`)
}
uniq := c.Bool("uniq")
applyConstraints := c.Bool("apply-constraints")
archFilter := c.Bool("arch-filter")
depth := c.Int("depth")
// used in conjunction with "uniq" to make sure we print a given tag once and only once when enabled
seen := map[string]struct{}{}
for _, repo := range repos {
lookup := []string{repo}
lookupArches := dedupeSlice[string]{} // this gets filled with the Architectures of the entries of the specified "repo" (such that we can then filter the architectures of the parents of the parents appropriately to prevent "orientdb" from having "mcr.microsoft.com/windows/servercore" as a parent due to being "FROM eclipse-temurin:8-jdk" but with a Linux-limited set of supported architectures)
for d := depth; len(lookup) > 0 && (depth == 0 || d > 0); d-- {
nextLookup := dedupeSlice[string]{}
for _, repo := range lookup {
r, err := fetch(repo)
if err != nil {
var (
manifestNotFoundErr manifest.ManifestNotFoundError
tagNotFoundErr manifest.TagNotFoundError
)
if d != depth && (errors.As(err, &manifestNotFoundErr) || errors.As(err, &tagNotFoundErr)) {
// if this repo isn't one of the original top-level arguments and our error is just that it's not a supported tag, walk no further ("FROM mcr.microsoft.com/...", etc)
continue
}
return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err)
}
for _, entry := range r.Entries() {
if applyConstraints && r.SkipConstraints(entry) {
continue
}
if archFilter && !entry.HasArchitecture(arch) {
continue
}
if d == depth {
if !applyConstraints && !archFilter {
for _, entryArch := range entry.Architectures {
lookupArches.add(entryArch)
}
} else {
lookupArches.add(arch)
}
}
entryFroms := dedupeSlice[string]{}
for _, lookupArch := range lookupArches.slice() {
if !entry.HasArchitecture(lookupArch) {
continue
}
froms, err := r.ArchDockerFroms(lookupArch, entry)
if err != nil {
return cli.NewMultiError(fmt.Errorf(`failed fetching/scraping FROM for %q (tags %q, arch %q)`, r.RepoName, entry.TagsString(), lookupArch), err)
}
for _, from := range froms {
if from == "scratch" {
// "scratch" isn't really anyone's actual parent (it's a special-case built-in)
continue
}
entryFroms.add(from)
}
}
for _, from := range entryFroms.slice() {
nextLookup.add(from)
if uniq {
if _, ok := seen[from]; ok {
continue
}
seen[from] = struct{}{}
}
fmt.Println(from)
}
}
}
lookup = nextLookup.slice()
}
}
return nil
}