Browse Source

Improve bashbrew SharedTags handling (explicitly allowed for "bashbrew cat", etc)

Tianon Gravi 6 years ago
parent
commit
6930483624

+ 3 - 3
bashbrew/go/src/bashbrew/cmd-build.go

@@ -44,7 +44,7 @@ func cmdBuild(c *cli.Context) error {
 				continue
 			}
 
-			from, err := r.DockerFrom(&entry)
+			from, err := r.DockerFrom(entry)
 			if err != nil {
 				return cli.NewMultiError(fmt.Errorf(`failed fetching/scraping FROM for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
 			}
@@ -69,7 +69,7 @@ func cmdBuild(c *cli.Context) error {
 				}
 			}
 
-			cacheTag, err := r.DockerCacheName(&entry)
+			cacheTag, err := r.DockerCacheName(entry)
 			if err != nil {
 				return cli.NewMultiError(fmt.Errorf(`failed calculating "cache hash" for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
 			}
@@ -79,7 +79,7 @@ func cmdBuild(c *cli.Context) error {
 			if err != nil {
 				fmt.Printf("Building %s (%s)\n", cacheTag, r.EntryIdentifier(entry))
 				if !dryRun {
-					commit, err := r.fetchGitRepo(arch, &entry)
+					commit, err := r.fetchGitRepo(arch, entry)
 					if err != nil {
 						return cli.NewMultiError(fmt.Errorf(`failed fetching git repo for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
 					}

+ 11 - 2
bashbrew/go/src/bashbrew/cmd-cat.go

@@ -13,8 +13,17 @@ import (
 )
 
 var DefaultCatFormat = `
-{{- if i }}{{ "\n\n" }}{{ end -}}
-{{- .TagName | ternary (.Manifest.GetTag .TagName) .Manifest -}}
+{{- if i -}}
+	{{- "\n\n" -}}
+{{- end -}}
+{{- with .TagEntries -}}
+	{{- range $i, $e := . -}}
+		{{- if $i -}}{{- "\n\n" -}}{{- end -}}
+		{{- $e -}}
+	{{- end -}}
+{{- else -}}
+	{{- .Manifest -}}
+{{- end -}}
 `
 
 func cmdCat(c *cli.Context) error {

+ 1 - 1
bashbrew/go/src/bashbrew/cmd-deps.go

@@ -67,7 +67,7 @@ func cmdFamily(parents bool, c *cli.Context) error {
 				continue
 			}
 
-			from, err := r.DockerFrom(&entry)
+			from, err := r.DockerFrom(entry)
 			if err != nil {
 				return cli.NewMultiError(fmt.Errorf(`failed fetching/scraping FROM for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
 			}

+ 1 - 1
bashbrew/go/src/bashbrew/cmd-from.go

@@ -27,7 +27,7 @@ func cmdFrom(c *cli.Context) error {
 				continue
 			}
 
-			from, err := r.DockerFrom(&entry)
+			from, err := r.DockerFrom(entry)
 			if err != nil {
 				return cli.NewMultiError(fmt.Errorf(`failed fetching/scraping FROM for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
 			}

+ 2 - 2
bashbrew/go/src/bashbrew/cmd-list.go

@@ -36,14 +36,14 @@ func cmdList(c *cli.Context) error {
 			if r.TagEntry == nil {
 				fmt.Printf("%s\n", r.RepoName)
 			} else {
-				for _, tag := range r.Tags(namespace, uniq, *r.TagEntry) {
+				for _, tag := range r.Tags(namespace, uniq, r.TagEntry) {
 					fmt.Printf("%s\n", tag)
 				}
 			}
 			continue
 		}
 
-		var entries []manifest.Manifest2822Entry
+		var entries []*manifest.Manifest2822Entry
 		if buildOrder {
 			entries, err = r.SortedEntries(applyConstraints)
 			if err != nil {

+ 3 - 3
bashbrew/go/src/bashbrew/cmd-put-shared.go

@@ -19,7 +19,7 @@ func entriesToManifestToolYaml(singleArch bool, r Repo, entries ...*manifest.Man
 	expectedNumber := 0
 	entryIdentifiers := []string{}
 	for _, entry := range entries {
-		entryIdentifiers = append(entryIdentifiers, r.EntryIdentifier(*entry))
+		entryIdentifiers = append(entryIdentifiers, r.EntryIdentifier(entry))
 
 		for _, entryArch := range entry.Architectures {
 			if singleArch && entryArch != arch {
@@ -36,7 +36,7 @@ func entriesToManifestToolYaml(singleArch bool, r Repo, entries ...*manifest.Man
 
 			var archNamespace string
 			if archNamespace, ok = archNamespaces[entryArch]; !ok || archNamespace == "" {
-				fmt.Fprintf(os.Stderr, "warning: no arch-namespace specified for %q; skipping (%q)\n", entryArch, r.EntryIdentifier(*entry))
+				fmt.Fprintf(os.Stderr, "warning: no arch-namespace specified for %q; skipping (%q)\n", entryArch, r.EntryIdentifier(entry))
 				continue
 			}
 
@@ -106,7 +106,7 @@ func cmdPutShared(c *cli.Context) error {
 			// handle all multi-architecture tags first (regardless of whether they have SharedTags)
 			// turn them into SharedTagGroup objects so all manifest-tool invocations can be handled by a single process/loop
 			for _, entry := range r.Entries() {
-				entryCopy := entry
+				entryCopy := *entry
 				sharedTagGroups = append(sharedTagGroups, manifest.SharedTagGroup{
 					SharedTags: entry.Tags,
 					Entries:    []*manifest.Manifest2822Entry{&entryCopy},

+ 22 - 8
bashbrew/go/src/bashbrew/repo.go

@@ -51,16 +51,19 @@ type Repo struct {
 	TagName  string
 	Manifest *manifest.Manifest2822
 	TagEntry *manifest.Manifest2822Entry
+
+	// if "TagName" refers to a SharedTag, "TagEntry" will be the first match, this will contain all matches (otherwise it will be just "TagEntry")
+	TagEntries []*manifest.Manifest2822Entry
 }
 
 func (r Repo) Identifier() string {
 	if r.TagEntry != nil {
-		return r.EntryIdentifier(*r.TagEntry)
+		return r.EntryIdentifier(r.TagEntry)
 	}
 	return r.RepoName
 }
 
-func (r Repo) EntryIdentifier(entry manifest.Manifest2822Entry) string {
+func (r Repo) EntryIdentifier(entry *manifest.Manifest2822Entry) string {
 	return r.RepoName + ":" + entry.Tags[0]
 }
 
@@ -76,7 +79,7 @@ func (r Repo) EntryRepo(entry *manifest.Manifest2822Entry) *Repo {
 
 var haveOutputSkippedMessage = map[string]bool{}
 
-func (r Repo) SkipConstraints(entry manifest.Manifest2822Entry) bool {
+func (r Repo) SkipConstraints(entry *manifest.Manifest2822Entry) bool {
 	repoTag := r.RepoName + ":" + entry.Tags[0]
 
 	// TODO decide if "arch" and "constraints" should be handled separately (but probably not)
@@ -135,16 +138,19 @@ NextConstraint:
 	return false
 }
 
-func (r Repo) Entries() []manifest.Manifest2822Entry {
+func (r Repo) Entries() []*manifest.Manifest2822Entry {
 	if r.TagName == "" {
-		return r.Manifest.Entries
+		ret := []*manifest.Manifest2822Entry{}
+		for i := range r.Manifest.Entries {
+			ret = append(ret, &r.Manifest.Entries[i])
+		}
+		return ret
 	} else {
-		// TODO what if r.TagName isn't a single entry, but is a SharedTag ?
-		return []manifest.Manifest2822Entry{*r.Manifest.GetTag(r.TagName)}
+		return r.TagEntries
 	}
 }
 
-func (r Repo) Tags(namespace string, uniq bool, entry manifest.Manifest2822Entry) []string {
+func (r Repo) Tags(namespace string, uniq bool, entry *manifest.Manifest2822Entry) []string {
 	tagRepo := path.Join(namespace, r.RepoName)
 	ret := []string{}
 	tags := append([]string{}, entry.Tags...)
@@ -177,6 +183,14 @@ func fetch(repo string) (*Repo, error) {
 	}
 	if tagName != "" {
 		r.TagEntry = man.GetTag(tagName)
+		if r.TagEntry == nil {
+			// must be a SharedTag
+			r.TagEntries = man.GetSharedTag(tagName)
+			r.TagEntry = r.TagEntries[0]
+		} else {
+			// not a SharedTag, backfill TagEntries
+			r.TagEntries = []*manifest.Manifest2822Entry{r.TagEntry}
+		}
 	}
 	repoCache[repo] = r
 	return r, nil

+ 5 - 5
bashbrew/go/src/bashbrew/sort.go

@@ -38,7 +38,7 @@ func sortRepos(repos []string, applyConstraints bool) ([]string, error) {
 	return ret, nil
 }
 
-func (r Repo) SortedEntries(applyConstraints bool) ([]manifest.Manifest2822Entry, error) {
+func (r Repo) SortedEntries(applyConstraints bool) ([]*manifest.Manifest2822Entry, error) {
 	entries := r.Entries()
 
 	// short circuit if we don't have to go further
@@ -49,7 +49,7 @@ func (r Repo) SortedEntries(applyConstraints bool) ([]manifest.Manifest2822Entry
 	// create individual "Repo" objects for each entry in "r" so they can be sorted by the same "sortRepoObjects" function
 	rs := []*Repo{}
 	for i := range entries {
-		rs = append(rs, r.EntryRepo(&entries[i]))
+		rs = append(rs, r.EntryRepo(entries[i]))
 	}
 
 	rs, err := sortRepoObjects(rs, applyConstraints)
@@ -57,9 +57,9 @@ func (r Repo) SortedEntries(applyConstraints bool) ([]manifest.Manifest2822Entry
 		return nil, err
 	}
 
-	ret := []manifest.Manifest2822Entry{}
+	ret := []*manifest.Manifest2822Entry{}
 	for _, entryR := range rs {
-		ret = append(ret, *entryR.TagEntry)
+		ret = append(ret, entryR.TagEntries...)
 	}
 	return ret, nil
 }
@@ -101,7 +101,7 @@ func sortRepoObjects(rs []*Repo, applyConstraints bool) ([]*Repo, error) {
 				continue
 			}
 
-			from, err := r.DockerFrom(&entry)
+			from, err := r.DockerFrom(entry)
 			if err != nil {
 				return nil, err
 			}

+ 1 - 1
bashbrew/go/vendor/manifest

@@ -10,7 +10,7 @@
 		{
 			"importpath": "github.com/docker-library/go-dockerlibrary",
 			"repository": "https://github.com/docker-library/go-dockerlibrary",
-			"revision": "78186ac78fb8c1ce4e0f2f4847ae4d57a69cfcc3",
+			"revision": "7e50189a05d4ff8233197dc948cc8fb11a780e33",
 			"branch": "master"
 		},
 		{

+ 13 - 6
bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/fetch.go

@@ -9,6 +9,13 @@ import (
 	"strings"
 )
 
+func validateTagName(man *Manifest2822, repoName, tagName string) error {
+	if tagName != "" && (man.GetTag(tagName) == nil && len(man.GetSharedTag(tagName)) == 0) {
+		return fmt.Errorf("tag not found in manifest for %q: %q", repoName, tagName)
+	}
+	return nil
+}
+
 // "library" is the default "library directory"
 // returns the parsed version of (in order):
 //   if "repo" is a URL, the remote contents of that URL
@@ -33,10 +40,10 @@ func Fetch(library, repo string) (string, string, *Manifest2822, error) {
 		}
 		defer resp.Body.Close()
 		man, err := Parse(resp.Body)
-		if tagName != "" && man.GetTag(tagName) == nil {
-			return repoName, tagName, man, fmt.Errorf("tag not found in manifest for %q: %q", repoName, tagName)
+		if err != nil {
+			return repoName, tagName, man, err
 		}
-		return repoName, tagName, man, err
+		return repoName, tagName, man, validateTagName(man, repoName, tagName)
 	}
 
 	// try file paths
@@ -55,10 +62,10 @@ func Fetch(library, repo string) (string, string, *Manifest2822, error) {
 		if err == nil {
 			defer f.Close()
 			man, err := Parse(f)
-			if tagName != "" && man.GetTag(tagName) == nil {
-				return repoName, tagName, man, fmt.Errorf("tag not found in manifest for %q: %q", repoName, tagName)
+			if err != nil {
+				return repoName, tagName, man, err
 			}
-			return repoName, tagName, man, err
+			return repoName, tagName, man, validateTagName(man, repoName, tagName)
 		}
 	}
 

+ 6 - 6
bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/rfc2822.go

@@ -308,20 +308,20 @@ func (entry Manifest2822Entry) HasArchitecture(arch string) bool {
 }
 
 func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry {
-	for _, entry := range manifest.Entries {
+	for i, entry := range manifest.Entries {
 		if entry.HasTag(tag) {
-			return &entry
+			return &manifest.Entries[i]
 		}
 	}
 	return nil
 }
 
 // GetSharedTag returns a list of entries with the given tag in entry.SharedTags (or the empty list if there are no entries with the given tag).
-func (manifest Manifest2822) GetSharedTag(tag string) []Manifest2822Entry {
-	ret := []Manifest2822Entry{}
-	for _, entry := range manifest.Entries {
+func (manifest Manifest2822) GetSharedTag(tag string) []*Manifest2822Entry {
+	ret := []*Manifest2822Entry{}
+	for i, entry := range manifest.Entries {
 		if entry.HasSharedTag(tag) {
-			ret = append(ret, entry)
+			ret = append(ret, &manifest.Entries[i])
 		}
 	}
 	return ret

+ 61 - 23
naughty-from.sh

@@ -13,6 +13,52 @@ if [ "$#" -eq 0 ]; then
 	set -- '--all'
 fi
 
+_is_naughty() {
+	local from="$1"; shift
+
+	case "$BASHBREW_ARCH=$from" in
+		# a few explicitly permissible exceptions to Santa's naughty list
+		*=scratch \
+		| amd64=docker.elastic.co/elasticsearch/elasticsearch:* \
+		| amd64=docker.elastic.co/kibana/kibana:* \
+		| amd64=docker.elastic.co/logstash/logstash:* \
+		| windows-*=mcr.microsoft.com/windows/nanoserver:* \
+		| windows-*=mcr.microsoft.com/windows/servercore:* \
+		| windows-*=microsoft/nanoserver:* \
+		| windows-*=microsoft/windowsservercore:* \
+		) return 1 ;;
+
+		# "x/y" and not an approved exception
+		*/*) return 0 ;;
+	esac
+
+	# must be some other official image AND support our current architecture
+	local archSupported
+	if archSupported="$(bashbrew cat --format '{{ .TagEntry.HasArchitecture arch | ternary arch "" }}' "$from")" && [ -n "$archSupported" ]; then
+		return 1
+	fi
+
+	return 0
+}
+
+_arches() {
+	bashbrew cat --format '
+		{{- range .TagEntries -}}
+			{{- .Architectures | join "\n" -}}
+			{{- "\n" -}}
+		{{- end -}}
+	' "$@" | sort -u
+}
+
+_froms() {
+	bashbrew cat --format '
+		{{- range .TagEntries -}}
+			{{- $.DockerFrom . -}}
+			{{- "\n" -}}
+		{{- end -}}
+	' "$@" | sort -u
+}
+
 declare -A naughtyFromsArches=(
 	#[img:tag=from:tag]='arch arch ...'
 )
@@ -20,10 +66,11 @@ naughtyFroms=()
 
 tags="$(bashbrew list --uniq "$@" | sort -u)"
 for img in $tags; do
-	for BASHBREW_ARCH in $(bashbrew cat --format '{{ join " " .TagEntry.Architectures }}' "$img"); do
+	arches="$(_arches "$img")"
+	for BASHBREW_ARCH in $arches; do
 		export BASHBREW_ARCH
 
-		if ! from="$(bashbrew cat --format '{{ $.DockerFrom .TagEntry }}' "$img" 2>/dev/null)"; then
+		if ! froms="$(_froms "$img" 2>/dev/null)"; then
 			# if we can't fetch the tags from their real locations, let's try the warehouse
 			refsList="$(
 				bashbrew list --uniq "$img" \
@@ -37,30 +84,21 @@ for img in $tags; do
 				fetch --no-tags --quiet \
 				https://github.com/docker-library/commit-warehouse.git \
 				$refsList
-			from="$(bashbrew cat --format '{{ $.DockerFrom .TagEntry }}' "$img")"
+			froms="$(_froms "$img")"
 		fi
 
-		case "$BASHBREW_ARCH=$from" in
-			# a few explicitly permissible exceptions to Santa's naughty list
-			*=scratch \
-			| amd64=docker.elastic.co/elasticsearch/elasticsearch:* \
-			| amd64=docker.elastic.co/kibana/kibana:* \
-			| amd64=docker.elastic.co/logstash/logstash:* \
-			| windows-*=mcr.microsoft.com/windows/nanoserver:* \
-			| windows-*=mcr.microsoft.com/windows/servercore:* \
-			| windows-*=microsoft/nanoserver:* \
-			| windows-*=microsoft/windowsservercore:* \
-			) continue ;;
-		esac
-
-		if ! listOutput="$(bashbrew cat --format '{{ .TagEntry.HasArchitecture arch | ternary arch "" }}' "$from")" || [ -z "$listOutput" ]; then
-			if [ -z "${naughtyFromsArches["$img=$from"]:-}" ]; then
-				naughtyFroms+=( "$img=$from" )
-			else
-				naughtyFromsArches["$img=$from"]+=', '
+		[ -n "$froms" ] # rough sanity check
+
+		for from in $froms; do
+			if _is_naughty "$from"; then
+				if [ -z "${naughtyFromsArches["$img=$from"]:-}" ]; then
+					naughtyFroms+=( "$img=$from" )
+				else
+					naughtyFromsArches["$img=$from"]+=', '
+				fi
+				naughtyFromsArches["$img=$from"]+="$BASHBREW_ARCH"
 			fi
-			naughtyFromsArches["$img=$from"]+="$BASHBREW_ARCH"
-		fi
+		done
 	done
 done