Add initial Architectures support (and the beginnings of a "put-shared" subcommand for bringing all the SharedTags and arch-specific images together with manifest lists/indexes)
This commit is contained in:
parent
362855e06f
commit
fdf3501d49
|
|
@ -81,7 +81,7 @@ func cmdBuild(c *cli.Context) error {
|
||||||
return cli.NewMultiError(fmt.Errorf(`failed fetching git repo for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
|
return cli.NewMultiError(fmt.Errorf(`failed fetching git repo for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
archive, err := gitArchive(commit, entry.Directory)
|
archive, err := gitArchive(commit, entry.ArchDirectory(arch))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewMultiError(fmt.Errorf(`failed generating git archive for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
|
return cli.NewMultiError(fmt.Errorf(`failed generating git archive for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func cmdPutShared(c *cli.Context) error {
|
||||||
|
repos, err := repos(c.Bool("all"), c.Args()...)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewMultiError(fmt.Errorf(`failed gathering repo list`), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace := c.String("namespace")
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
return fmt.Errorf(`"--namespace" is a required flag for "put-shared"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "warning: this subcommand is still a big WIP -- it doesn't do anything yet!\n")
|
||||||
|
|
||||||
|
for _, repo := range repos {
|
||||||
|
r, err := fetch(repo)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO handle all multi-architecture tags first (regardless of whether they have SharedTags)
|
||||||
|
|
||||||
|
targetRepo := path.Join(namespace, r.RepoName)
|
||||||
|
for _, group := range r.Manifest.GetSharedTagGroups() {
|
||||||
|
// TODO build up a YAML file
|
||||||
|
entryTags := []string{}
|
||||||
|
for _, entry := range group.Entries {
|
||||||
|
entryTags = append(entryTags, entry.Tags[0])
|
||||||
|
}
|
||||||
|
fmt.Printf("Putting %s (tags %s) <= %s\n", targetRepo, strings.Join(group.SharedTags, ", "), strings.Join(entryTags, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,8 @@ type FlagsConfigEntry struct {
|
||||||
|
|
||||||
Commands []string `delim:"," strip:"\n\r\t "`
|
Commands []string `delim:"," strip:"\n\r\t "`
|
||||||
|
|
||||||
|
// TODO arch namespace mappings (for intermediate pushing before put-shared, and for put-shared to pull from to join together in one big happy family)
|
||||||
|
|
||||||
Library string
|
Library string
|
||||||
Cache string
|
Cache string
|
||||||
Debug string
|
Debug string
|
||||||
|
|
@ -26,6 +28,7 @@ type FlagsConfigEntry struct {
|
||||||
BuildOrder string
|
BuildOrder string
|
||||||
Pull string
|
Pull string
|
||||||
|
|
||||||
|
Arch string
|
||||||
Constraints []string `delim:"," strip:"\n\r\t "`
|
Constraints []string `delim:"," strip:"\n\r\t "`
|
||||||
ExclusiveConstraints string
|
ExclusiveConstraints string
|
||||||
ApplyConstraints string
|
ApplyConstraints string
|
||||||
|
|
@ -55,6 +58,9 @@ func (dst *FlagsConfigEntry) Apply(src FlagsConfigEntry) {
|
||||||
if src.Pull != "" {
|
if src.Pull != "" {
|
||||||
dst.Pull = src.Pull
|
dst.Pull = src.Pull
|
||||||
}
|
}
|
||||||
|
if src.Arch != "" {
|
||||||
|
dst.Arch = src.Arch
|
||||||
|
}
|
||||||
if len(src.Constraints) > 0 {
|
if len(src.Constraints) > 0 {
|
||||||
dst.Constraints = src.Constraints[:]
|
dst.Constraints = src.Constraints[:]
|
||||||
}
|
}
|
||||||
|
|
@ -73,6 +79,7 @@ func (config FlagsConfigEntry) Vars() map[string]map[string]interface{} {
|
||||||
"cache": config.Cache,
|
"cache": config.Cache,
|
||||||
"debug": config.Debug,
|
"debug": config.Debug,
|
||||||
|
|
||||||
|
"arch": config.Arch,
|
||||||
"constraint": config.Constraints,
|
"constraint": config.Constraints,
|
||||||
"exclusive-constraints": config.ExclusiveConstraints,
|
"exclusive-constraints": config.ExclusiveConstraints,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ func (r Repo) DockerFrom(entry *manifest.Manifest2822Entry) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
dockerfileFile := path.Join(entry.Directory, "Dockerfile")
|
dockerfileFile := path.Join(entry.ArchDirectory(arch), "Dockerfile")
|
||||||
|
|
||||||
cacheKey := strings.Join([]string{
|
cacheKey := strings.Join([]string{
|
||||||
commit,
|
commit,
|
||||||
|
|
@ -130,9 +130,9 @@ func (r Repo) dockerBuildUniqueBits(entry *manifest.Manifest2822Entry) ([]string
|
||||||
dockerFromIdCache[from] = fromId
|
dockerFromIdCache[from] = fromId
|
||||||
}
|
}
|
||||||
return []string{
|
return []string{
|
||||||
entry.GitRepo,
|
entry.ArchGitRepo(arch),
|
||||||
entry.GitCommit,
|
entry.ArchGitCommit(arch),
|
||||||
entry.Directory,
|
entry.ArchDirectory(arch),
|
||||||
fromId,
|
fromId,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,12 +102,12 @@ var gitRepoCache = map[string]string{}
|
||||||
|
|
||||||
func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) {
|
func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) {
|
||||||
cacheKey := strings.Join([]string{
|
cacheKey := strings.Join([]string{
|
||||||
entry.GitRepo,
|
entry.ArchGitRepo(arch),
|
||||||
entry.GitFetch,
|
entry.ArchGitFetch(arch),
|
||||||
entry.GitCommit,
|
entry.ArchGitCommit(arch),
|
||||||
}, "\n")
|
}, "\n")
|
||||||
if commit, ok := gitRepoCache[cacheKey]; ok {
|
if commit, ok := gitRepoCache[cacheKey]; ok {
|
||||||
entry.GitCommit = commit
|
entry.SetGitCommit(arch, commit)
|
||||||
return commit, nil
|
return commit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,27 +116,27 @@ func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if manifest.GitCommitRegex.MatchString(entry.GitCommit) {
|
if manifest.GitCommitRegex.MatchString(entry.ArchGitCommit(arch)) {
|
||||||
commit, err := getGitCommit(entry.GitCommit)
|
commit, err := getGitCommit(entry.ArchGitCommit(arch))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
gitRepoCache[cacheKey] = commit
|
gitRepoCache[cacheKey] = commit
|
||||||
entry.GitCommit = commit
|
entry.SetGitCommit(arch, commit)
|
||||||
return commit, nil
|
return commit, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchString := entry.GitFetch + ":"
|
fetchString := entry.ArchGitFetch(arch) + ":"
|
||||||
if entry.GitCommit == "FETCH_HEAD" {
|
if entry.ArchGitCommit(arch) == "FETCH_HEAD" {
|
||||||
// fetch remote tag references to a local tag ref so that we can cache them and not re-fetch every time
|
// fetch remote tag references to a local tag ref so that we can cache them and not re-fetch every time
|
||||||
localRef := "refs/tags/" + gitNormalizeForTagUsage(cacheKey)
|
localRef := "refs/tags/" + gitNormalizeForTagUsage(cacheKey)
|
||||||
commit, err := getGitCommit(localRef)
|
commit, err := getGitCommit(localRef)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
gitRepoCache[cacheKey] = commit
|
gitRepoCache[cacheKey] = commit
|
||||||
entry.GitCommit = commit
|
entry.SetGitCommit(arch, commit)
|
||||||
return commit, nil
|
return commit, nil
|
||||||
}
|
}
|
||||||
fetchString += localRef
|
fetchString += localRef
|
||||||
} else if entry.GitFetch == manifest.DefaultLineBasedFetch {
|
} else if entry.ArchGitFetch(arch) == manifest.DefaultLineBasedFetch {
|
||||||
// backwards compat (see manifest/line-based.go in go-dockerlibrary)
|
// backwards compat (see manifest/line-based.go in go-dockerlibrary)
|
||||||
refBase := "refs/remotes"
|
refBase := "refs/remotes"
|
||||||
refBaseDir := filepath.Join(gitCache(), refBase)
|
refBaseDir := filepath.Join(gitCache(), refBase)
|
||||||
|
|
@ -154,17 +154,17 @@ func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) {
|
||||||
// we create a temporary remote dir so that we can clean it up completely afterwards
|
// we create a temporary remote dir so that we can clean it up completely afterwards
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(entry.GitRepo, "git://github.com/") {
|
if strings.HasPrefix(entry.ArchGitRepo(arch), "git://github.com/") {
|
||||||
fmt.Fprintf(os.Stderr, "warning: insecure protocol git:// detected: %s\n", entry.GitRepo)
|
fmt.Fprintf(os.Stderr, "warning: insecure protocol git:// detected: %s\n", entry.ArchGitRepo(arch))
|
||||||
entry.GitRepo = strings.Replace(entry.GitRepo, "git://", "https://", 1)
|
entry.SetGitRepo(arch, strings.Replace(entry.ArchGitRepo(arch), "git://", "https://", 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = git("fetch", "--quiet", "--no-tags", entry.GitRepo, fetchString)
|
_, err = git("fetch", "--quiet", "--no-tags", entry.ArchGitRepo(arch), fetchString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
commit, err := getGitCommit(entry.GitCommit)
|
commit, err := getGitCommit(entry.ArchGitCommit(arch))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -175,6 +175,6 @@ func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
gitRepoCache[cacheKey] = commit
|
gitRepoCache[cacheKey] = commit
|
||||||
entry.GitCommit = commit
|
entry.SetGitCommit(arch, commit)
|
||||||
return commit, nil
|
return commit, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
|
||||||
|
"github.com/docker-library/go-dockerlibrary/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO somewhere, ensure that the Docker engine we're talking to is API version 1.22+ (Docker 1.10+)
|
// TODO somewhere, ensure that the Docker engine we're talking to is API version 1.22+ (Docker 1.10+)
|
||||||
|
|
@ -18,6 +20,7 @@ var (
|
||||||
defaultLibrary string
|
defaultLibrary string
|
||||||
defaultCache string
|
defaultCache string
|
||||||
|
|
||||||
|
arch string
|
||||||
constraints []string
|
constraints []string
|
||||||
exclusiveConstraints bool
|
exclusiveConstraints bool
|
||||||
|
|
||||||
|
|
@ -27,6 +30,7 @@ var (
|
||||||
// separated so that FlagsConfig.ApplyTo can access them
|
// separated so that FlagsConfig.ApplyTo can access them
|
||||||
flagEnvVars = map[string]string{
|
flagEnvVars = map[string]string{
|
||||||
"debug": "BASHBREW_DEBUG",
|
"debug": "BASHBREW_DEBUG",
|
||||||
|
"arch": "BASHBREW_ARCH",
|
||||||
"config": "BASHBREW_CONFIG",
|
"config": "BASHBREW_CONFIG",
|
||||||
"library": "BASHBREW_LIBRARY",
|
"library": "BASHBREW_LIBRARY",
|
||||||
"cache": "BASHBREW_CACHE",
|
"cache": "BASHBREW_CACHE",
|
||||||
|
|
@ -72,6 +76,12 @@ func main() {
|
||||||
Usage: "do not apply any sorting, even via --build-order",
|
Usage: "do not apply any sorting, even via --build-order",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "arch",
|
||||||
|
Value: manifest.DefaultArchitecture,
|
||||||
|
EnvVar: flagEnvVars["arch"],
|
||||||
|
Usage: "the current platform architecture",
|
||||||
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "constraint",
|
Name: "constraint",
|
||||||
Usage: "build constraints (see Constraints in Manifest2822Entry)",
|
Usage: "build constraints (see Constraints in Manifest2822Entry)",
|
||||||
|
|
@ -127,6 +137,7 @@ func main() {
|
||||||
debugFlag = c.GlobalBool("debug")
|
debugFlag = c.GlobalBool("debug")
|
||||||
noSortFlag = c.GlobalBool("no-sort")
|
noSortFlag = c.GlobalBool("no-sort")
|
||||||
|
|
||||||
|
arch = c.GlobalString("arch")
|
||||||
constraints = c.GlobalStringSlice("constraint")
|
constraints = c.GlobalStringSlice("constraint")
|
||||||
exclusiveConstraints = c.GlobalBool("exclusive-constraints")
|
exclusiveConstraints = c.GlobalBool("exclusive-constraints")
|
||||||
|
|
||||||
|
|
@ -222,7 +233,18 @@ func main() {
|
||||||
Before: subcommandBeforeFactory("push"),
|
Before: subcommandBeforeFactory("push"),
|
||||||
Action: cmdPush,
|
Action: cmdPush,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "put-shared",
|
||||||
|
Usage: `updated shared tags in the registry`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
commonFlags["all"],
|
||||||
|
commonFlags["namespace"],
|
||||||
|
},
|
||||||
|
Before: subcommandBeforeFactory("put-shared"),
|
||||||
|
Action: cmdPutShared,
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO --depth flag for children and parents
|
||||||
{
|
{
|
||||||
Name: "children",
|
Name: "children",
|
||||||
Aliases: []string{
|
Aliases: []string{
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,15 @@ 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]
|
repoTag := r.RepoName + ":" + entry.Tags[0]
|
||||||
|
|
||||||
|
// TODO decide if "arch" and "constraints" should be handled separately (but probably not)
|
||||||
|
if !entry.HasArchitecture(arch) {
|
||||||
|
if !haveOutputSkippedMessage[repoTag] {
|
||||||
|
fmt.Fprintf(os.Stderr, "skipping %q (due to architecture %q; only %q supported)\n", repoTag, arch, entry.ArchitecturesString())
|
||||||
|
haveOutputSkippedMessage[repoTag] = true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if len(entry.Constraints) == 0 {
|
if len(entry.Constraints) == 0 {
|
||||||
if exclusiveConstraints {
|
if exclusiveConstraints {
|
||||||
if !haveOutputSkippedMessage[repoTag] {
|
if !haveOutputSkippedMessage[repoTag] {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
{
|
{
|
||||||
"importpath": "github.com/docker-library/go-dockerlibrary",
|
"importpath": "github.com/docker-library/go-dockerlibrary",
|
||||||
"repository": "https://github.com/docker-library/go-dockerlibrary",
|
"repository": "https://github.com/docker-library/go-dockerlibrary",
|
||||||
"revision": "08ef5a968ebdd83dcc42998a96b6528837b55273",
|
"revision": "663a091da13fc848e27a16048fb39c4e4067056e",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# `import "github.com/docker-library/go-dockerlibrary/manifest"`
|
||||||
|
|
||||||
|
[](https://travis-ci.org/docker-library/go-dockerlibrary) [](https://godoc.org/github.com/docker-library/go-dockerlibrary) [](https://codecov.io/gh/docker-library/go-dockerlibrary)
|
||||||
|
|
||||||
|
This package contains the core parsing elements of [the `bashbrew` tool used by the Docker Official Images](https://github.com/docker-library/official-images/tree/master/bashbrew).
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker-library/go-dockerlibrary/manifest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// TODO comment parsing
|
|
||||||
man, err := manifest.Parse(bufio.NewReader(strings.NewReader(`# RFC 2822
|
|
||||||
|
|
||||||
# I LOVE CAKE
|
|
||||||
|
|
||||||
Maintainers: InfoSiftr <github@infosiftr.com> (@infosiftr),
|
|
||||||
Johan Euphrosine <proppy@google.com> (@proppy)
|
|
||||||
GitRepo: https://github.com/docker-library/golang.git
|
|
||||||
GitFetch: refs/heads/master
|
|
||||||
|
|
||||||
|
|
||||||
# hi
|
|
||||||
|
|
||||||
|
|
||||||
# blasphemer
|
|
||||||
|
|
||||||
|
|
||||||
# Go 1.6
|
|
||||||
Tags: 1.6.1, 1.6, 1, latest
|
|
||||||
GitCommit: 0ce80411b9f41e9c3a21fc0a1bffba6ae761825a
|
|
||||||
Directory: 1.6
|
|
||||||
|
|
||||||
|
|
||||||
# Go 1.5
|
|
||||||
Tags: 1.5.3
|
|
||||||
GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19
|
|
||||||
Directory: 1.5
|
|
||||||
|
|
||||||
|
|
||||||
Tags: 1.5
|
|
||||||
GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19
|
|
||||||
Directory: 1.5
|
|
||||||
|
|
||||||
|
|
||||||
`)))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("-------------\n2822:\n%s\n", man)
|
|
||||||
|
|
||||||
man, err = manifest.Parse(bufio.NewReader(strings.NewReader(`
|
|
||||||
# first set
|
|
||||||
a: b@c d
|
|
||||||
e: b@c d
|
|
||||||
|
|
||||||
# second set
|
|
||||||
f: g@h
|
|
||||||
i: g@h j
|
|
||||||
`)))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("-------------\nline-based:\n%v\n", man)
|
|
||||||
}
|
|
||||||
192
go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/example_test.go
vendored
Normal file
192
go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/example_test.go
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
package manifest_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker-library/go-dockerlibrary/manifest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
man, err := manifest.Parse(bufio.NewReader(strings.NewReader(`# RFC 2822
|
||||||
|
|
||||||
|
# I LOVE CAKE
|
||||||
|
|
||||||
|
Maintainers: InfoSiftr <github@infosiftr.com> (@infosiftr),
|
||||||
|
Johan Euphrosine <proppy@google.com> (@proppy)
|
||||||
|
GitFetch: refs/heads/master
|
||||||
|
GitRepo: https://github.com/docker-library/golang.git
|
||||||
|
SharedTags: latest
|
||||||
|
arm64v8-GitRepo: https://github.com/docker-library/golang.git
|
||||||
|
Architectures: amd64
|
||||||
|
|
||||||
|
|
||||||
|
# hi
|
||||||
|
|
||||||
|
|
||||||
|
# blasphemer
|
||||||
|
|
||||||
|
|
||||||
|
# Go 1.6
|
||||||
|
Tags: 1.6.1, 1.6, 1
|
||||||
|
arm64v8-GitRepo: https://github.com/docker-library/golang.git
|
||||||
|
Directory: 1.6
|
||||||
|
GitCommit: 0ce80411b9f41e9c3a21fc0a1bffba6ae761825a
|
||||||
|
Constraints: some-random-build-server
|
||||||
|
|
||||||
|
|
||||||
|
# Go 1.5
|
||||||
|
Tags: 1.5.3
|
||||||
|
GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19
|
||||||
|
SharedTags: 1.5.3-debian, 1.5-debian
|
||||||
|
Directory: 1.5
|
||||||
|
s390x-GitCommit: b6c460e7cd79b595267870a98013ec3078b490df
|
||||||
|
i386-GitFetch: refs/heads/i386
|
||||||
|
ppc64le-Directory: 1.5/ppc64le
|
||||||
|
|
||||||
|
|
||||||
|
Tags: 1.5
|
||||||
|
SharedTags: 1.5-debian
|
||||||
|
GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19
|
||||||
|
Directory: 1.5
|
||||||
|
s390x-GitCommit: b6c460e7cd79b595267870a98013ec3078b490df
|
||||||
|
i386-GitFetch: refs/heads/i386
|
||||||
|
ppc64le-Directory: 1.5/ppc64le
|
||||||
|
|
||||||
|
SharedTags: raspbian
|
||||||
|
GitCommit: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
|
||||||
|
Tags: raspbian-s390x
|
||||||
|
Architectures: s390x
|
||||||
|
|
||||||
|
|
||||||
|
`)))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("-------------\n2822:\n%s\n", man)
|
||||||
|
|
||||||
|
fmt.Printf("\nShared Tag Groups:\n")
|
||||||
|
for _, group := range man.GetSharedTagGroups() {
|
||||||
|
fmt.Printf("\n - %s\n", strings.Join(group.SharedTags, ", "))
|
||||||
|
for _, entry := range group.Entries {
|
||||||
|
fmt.Printf(" - %s\n", entry.TagsString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
man, err = manifest.Parse(bufio.NewReader(strings.NewReader(`
|
||||||
|
# maintainer: InfoSiftr <github@infosiftr.com> (@infosiftr)
|
||||||
|
# maintainer: John Smith <jsmith@example.com> (@example-jsmith)
|
||||||
|
|
||||||
|
# first set
|
||||||
|
a: b@c d
|
||||||
|
e: b@c d
|
||||||
|
|
||||||
|
# second set
|
||||||
|
f: g@h
|
||||||
|
i: g@h j
|
||||||
|
`)))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("-------------\nline-based:\n%v\n", man)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// -------------
|
||||||
|
// 2822:
|
||||||
|
// Maintainers: InfoSiftr <github@infosiftr.com> (@infosiftr), Johan Euphrosine <proppy@google.com> (@proppy)
|
||||||
|
// SharedTags: latest
|
||||||
|
// GitRepo: https://github.com/docker-library/golang.git
|
||||||
|
// arm64v8-GitRepo: https://github.com/docker-library/golang.git
|
||||||
|
//
|
||||||
|
// Tags: 1.6.1, 1.6, 1
|
||||||
|
// GitCommit: 0ce80411b9f41e9c3a21fc0a1bffba6ae761825a
|
||||||
|
// Directory: 1.6
|
||||||
|
// Constraints: some-random-build-server
|
||||||
|
//
|
||||||
|
// Tags: 1.5.3, 1.5
|
||||||
|
// SharedTags: 1.5.3-debian, 1.5-debian
|
||||||
|
// GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19
|
||||||
|
// Directory: 1.5
|
||||||
|
// i386-GitFetch: refs/heads/i386
|
||||||
|
// ppc64le-Directory: 1.5/ppc64le
|
||||||
|
// s390x-GitCommit: b6c460e7cd79b595267870a98013ec3078b490df
|
||||||
|
//
|
||||||
|
// Tags: raspbian-s390x
|
||||||
|
// SharedTags: raspbian
|
||||||
|
// Architectures: s390x
|
||||||
|
// GitCommit: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
|
||||||
|
//
|
||||||
|
// Shared Tag Groups:
|
||||||
|
//
|
||||||
|
// - latest
|
||||||
|
// - 1.6.1, 1.6, 1
|
||||||
|
//
|
||||||
|
// - 1.5.3-debian, 1.5-debian
|
||||||
|
// - 1.5.3, 1.5
|
||||||
|
//
|
||||||
|
// - raspbian
|
||||||
|
// - raspbian-s390x
|
||||||
|
//
|
||||||
|
// -------------
|
||||||
|
// line-based:
|
||||||
|
// Maintainers: InfoSiftr <github@infosiftr.com> (@infosiftr), John Smith <jsmith@example.com> (@example-jsmith)
|
||||||
|
// GitFetch: refs/heads/*
|
||||||
|
//
|
||||||
|
// Tags: a, e
|
||||||
|
// GitRepo: b
|
||||||
|
// GitCommit: c
|
||||||
|
// Directory: d
|
||||||
|
//
|
||||||
|
// Tags: f
|
||||||
|
// GitRepo: g
|
||||||
|
// GitFetch: refs/tags/h
|
||||||
|
// GitCommit: FETCH_HEAD
|
||||||
|
//
|
||||||
|
// Tags: i
|
||||||
|
// GitRepo: g
|
||||||
|
// GitFetch: refs/tags/h
|
||||||
|
// GitCommit: FETCH_HEAD
|
||||||
|
// Directory: j
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFetch_local() {
|
||||||
|
repoName, tagName, man, err := manifest.Fetch("testdata", "bash:4.4")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s:%s\n\n", repoName, tagName)
|
||||||
|
|
||||||
|
fmt.Println(man.GetTag(tagName).ClearDefaults(manifest.DefaultManifestEntry).String())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// bash:4.4
|
||||||
|
//
|
||||||
|
// Maintainers: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||||
|
// Tags: 4.4.12, 4.4, 4, latest
|
||||||
|
// GitRepo: https://github.com/tianon/docker-bash.git
|
||||||
|
// GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
// Directory: 4.4
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFetch_remote() {
|
||||||
|
repoName, tagName, man, err := manifest.Fetch("/home/jsmith/docker/official-images/library", "https://github.com/docker-library/official-images/raw/1a3c4cd6d5cd53bd538a6f56a69f94c5b35325a7/library/bash:4.4")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s:%s\n\n", repoName, tagName)
|
||||||
|
|
||||||
|
fmt.Println(man.GetTag(tagName).ClearDefaults(manifest.DefaultManifestEntry).String())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// bash:4.4
|
||||||
|
//
|
||||||
|
// Maintainers: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||||
|
// Tags: 4.4.12, 4.4, 4, latest
|
||||||
|
// GitRepo: https://github.com/tianon/docker-bash.git
|
||||||
|
// GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
// Directory: 4.4
|
||||||
|
}
|
||||||
20
go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/parse_test.go
vendored
Normal file
20
go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/parse_test.go
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package manifest_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker-library/go-dockerlibrary/manifest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseError(t *testing.T) {
|
||||||
|
invalidManifest := `this is just completely bogus and invalid no matter how you slice it`
|
||||||
|
|
||||||
|
man, err := manifest.Parse(strings.NewReader(invalidManifest))
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error, got valid manifest instead:\n%s", man)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(err.Error(), "cannot parse manifest in either format:") {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker-library/go-dockerlibrary/pkg/stripper"
|
"github.com/docker-library/go-dockerlibrary/pkg/stripper"
|
||||||
|
|
@ -27,25 +28,37 @@ type Manifest2822Entry struct {
|
||||||
|
|
||||||
Maintainers []string `delim:"," strip:"\n\r\t "`
|
Maintainers []string `delim:"," strip:"\n\r\t "`
|
||||||
|
|
||||||
Tags []string `delim:"," strip:"\n\r\t "`
|
Tags []string `delim:"," strip:"\n\r\t "`
|
||||||
|
SharedTags []string `delim:"," strip:"\n\r\t "`
|
||||||
|
|
||||||
|
Architectures []string `delim:"," strip:"\n\r\t "`
|
||||||
|
|
||||||
GitRepo string
|
GitRepo string
|
||||||
GitFetch string
|
GitFetch string
|
||||||
GitCommit string
|
GitCommit string
|
||||||
Directory string
|
Directory string
|
||||||
|
// architecture-specific versions of the above fields are in Paragraph.Values as ARCH-Field, ala s390x-Directory
|
||||||
|
|
||||||
Constraints []string `delim:"," strip:"\n\r\t "`
|
Constraints []string `delim:"," strip:"\n\r\t "`
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultManifestEntry = Manifest2822Entry{
|
var (
|
||||||
GitFetch: "refs/heads/master",
|
DefaultArchitecture = "amd64"
|
||||||
Directory: ".",
|
|
||||||
}
|
DefaultManifestEntry = Manifest2822Entry{
|
||||||
|
Architectures: []string{DefaultArchitecture},
|
||||||
|
|
||||||
|
GitFetch: "refs/heads/master",
|
||||||
|
Directory: ".",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func (entry Manifest2822Entry) Clone() Manifest2822Entry {
|
func (entry Manifest2822Entry) Clone() Manifest2822Entry {
|
||||||
// SLICES! grr
|
// SLICES! grr
|
||||||
entry.Maintainers = append([]string{}, entry.Maintainers...)
|
entry.Maintainers = append([]string{}, entry.Maintainers...)
|
||||||
entry.Tags = append([]string{}, entry.Tags...)
|
entry.Tags = append([]string{}, entry.Tags...)
|
||||||
|
entry.SharedTags = append([]string{}, entry.SharedTags...)
|
||||||
|
entry.Architectures = append([]string{}, entry.Architectures...)
|
||||||
entry.Constraints = append([]string{}, entry.Constraints...)
|
entry.Constraints = append([]string{}, entry.Constraints...)
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
@ -60,23 +73,60 @@ func (entry Manifest2822Entry) TagsString() string {
|
||||||
return strings.Join(entry.Tags, StringSeparator2822)
|
return strings.Join(entry.Tags, StringSeparator2822)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (entry Manifest2822Entry) SharedTagsString() string {
|
||||||
|
return strings.Join(entry.SharedTags, StringSeparator2822)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry Manifest2822Entry) ArchitecturesString() string {
|
||||||
|
return strings.Join(entry.Architectures, StringSeparator2822)
|
||||||
|
}
|
||||||
|
|
||||||
func (entry Manifest2822Entry) ConstraintsString() string {
|
func (entry Manifest2822Entry) ConstraintsString() string {
|
||||||
return strings.Join(entry.Constraints, StringSeparator2822)
|
return strings.Join(entry.Constraints, StringSeparator2822)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this method returns "true", then a.Tags and b.Tags can safely be combined (for the purposes of building)
|
// if this method returns "true", then a.Tags and b.Tags can safely be combined (for the purposes of building)
|
||||||
func (a Manifest2822Entry) SameBuildArtifacts(b Manifest2822Entry) bool {
|
func (a Manifest2822Entry) SameBuildArtifacts(b Manifest2822Entry) bool {
|
||||||
return a.GitRepo == b.GitRepo && a.GitFetch == b.GitFetch && a.GitCommit == b.GitCommit && a.Directory == b.Directory && a.ConstraintsString() == b.ConstraintsString()
|
// check xxxarch-GitRepo, etc. fields for sameness first
|
||||||
|
for _, key := range append(a.archFields(), b.archFields()...) {
|
||||||
|
if a.Paragraph.Values[key] != b.Paragraph.Values[key] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.ArchitecturesString() == b.ArchitecturesString() && a.GitRepo == b.GitRepo && a.GitFetch == b.GitFetch && a.GitCommit == b.GitCommit && a.Directory == b.Directory && a.ConstraintsString() == b.ConstraintsString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func isArchField(field string) bool {
|
||||||
|
return strings.HasSuffix(field, "-GitRepo") || strings.HasSuffix(field, "-GitFetch") || strings.HasSuffix(field, "-GitCommit") || strings.HasSuffix(field, "-Directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a list of architecture-specific fields in an Entry
|
||||||
|
func (entry Manifest2822Entry) archFields() []string {
|
||||||
|
ret := []string{}
|
||||||
|
for key, val := range entry.Paragraph.Values {
|
||||||
|
if isArchField(key) && val != "" {
|
||||||
|
ret = append(ret, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a new Entry with any of the values that are equal to the values in "defaults" cleared
|
// returns a new Entry with any of the values that are equal to the values in "defaults" cleared
|
||||||
func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifest2822Entry {
|
func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifest2822Entry {
|
||||||
|
entry = entry.Clone() // make absolutely certain we have a deep clone
|
||||||
if entry.MaintainersString() == defaults.MaintainersString() {
|
if entry.MaintainersString() == defaults.MaintainersString() {
|
||||||
entry.Maintainers = nil
|
entry.Maintainers = nil
|
||||||
}
|
}
|
||||||
if entry.TagsString() == defaults.TagsString() {
|
if entry.TagsString() == defaults.TagsString() {
|
||||||
entry.Tags = nil
|
entry.Tags = nil
|
||||||
}
|
}
|
||||||
|
if entry.SharedTagsString() == defaults.SharedTagsString() {
|
||||||
|
entry.SharedTags = nil
|
||||||
|
}
|
||||||
|
if entry.ArchitecturesString() == defaults.ArchitecturesString() {
|
||||||
|
entry.Architectures = nil
|
||||||
|
}
|
||||||
if entry.GitRepo == defaults.GitRepo {
|
if entry.GitRepo == defaults.GitRepo {
|
||||||
entry.GitRepo = ""
|
entry.GitRepo = ""
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +139,11 @@ func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifes
|
||||||
if entry.Directory == defaults.Directory {
|
if entry.Directory == defaults.Directory {
|
||||||
entry.Directory = ""
|
entry.Directory = ""
|
||||||
}
|
}
|
||||||
|
for _, key := range defaults.archFields() {
|
||||||
|
if defaults.Paragraph.Values[key] == entry.Paragraph.Values[key] {
|
||||||
|
delete(entry.Paragraph.Values, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
if entry.ConstraintsString() == defaults.ConstraintsString() {
|
if entry.ConstraintsString() == defaults.ConstraintsString() {
|
||||||
entry.Constraints = nil
|
entry.Constraints = nil
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +158,12 @@ func (entry Manifest2822Entry) String() string {
|
||||||
if str := entry.TagsString(); str != "" {
|
if str := entry.TagsString(); str != "" {
|
||||||
ret = append(ret, "Tags: "+str)
|
ret = append(ret, "Tags: "+str)
|
||||||
}
|
}
|
||||||
|
if str := entry.SharedTagsString(); str != "" {
|
||||||
|
ret = append(ret, "SharedTags: "+str)
|
||||||
|
}
|
||||||
|
if str := entry.ArchitecturesString(); str != "" {
|
||||||
|
ret = append(ret, "Architectures: "+str)
|
||||||
|
}
|
||||||
if str := entry.GitRepo; str != "" {
|
if str := entry.GitRepo; str != "" {
|
||||||
ret = append(ret, "GitRepo: "+str)
|
ret = append(ret, "GitRepo: "+str)
|
||||||
}
|
}
|
||||||
|
|
@ -115,6 +176,11 @@ func (entry Manifest2822Entry) String() string {
|
||||||
if str := entry.Directory; str != "" {
|
if str := entry.Directory; str != "" {
|
||||||
ret = append(ret, "Directory: "+str)
|
ret = append(ret, "Directory: "+str)
|
||||||
}
|
}
|
||||||
|
archFields := entry.archFields()
|
||||||
|
sort.Strings(archFields) // consistent ordering
|
||||||
|
for _, key := range archFields {
|
||||||
|
ret = append(ret, key+": "+entry.Paragraph.Values[key])
|
||||||
|
}
|
||||||
if str := entry.ConstraintsString(); str != "" {
|
if str := entry.ConstraintsString(); str != "" {
|
||||||
ret = append(ret, "Constraints: "+str)
|
ret = append(ret, "Constraints: "+str)
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +202,48 @@ func (manifest Manifest2822) String() string {
|
||||||
return strings.Join(ret, "\n\n")
|
return strings.Join(ret, "\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (entry *Manifest2822Entry) SetGitRepo(arch string, repo string) {
|
||||||
|
if entry.Paragraph.Values == nil {
|
||||||
|
entry.Paragraph.Values = map[string]string{}
|
||||||
|
}
|
||||||
|
entry.Paragraph.Values[arch+"-GitRepo"] = repo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry Manifest2822Entry) ArchGitRepo(arch string) string {
|
||||||
|
if val, ok := entry.Paragraph.Values[arch+"-GitRepo"]; ok && val != "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return entry.GitRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry Manifest2822Entry) ArchGitFetch(arch string) string {
|
||||||
|
if val, ok := entry.Paragraph.Values[arch+"-GitFetch"]; ok && val != "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return entry.GitFetch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Manifest2822Entry) SetGitCommit(arch string, commit string) {
|
||||||
|
if entry.Paragraph.Values == nil {
|
||||||
|
entry.Paragraph.Values = map[string]string{}
|
||||||
|
}
|
||||||
|
entry.Paragraph.Values[arch+"-GitCommit"] = commit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry Manifest2822Entry) ArchGitCommit(arch string) string {
|
||||||
|
if val, ok := entry.Paragraph.Values[arch+"-GitCommit"]; ok && val != "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return entry.GitCommit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry Manifest2822Entry) ArchDirectory(arch string) string {
|
||||||
|
if val, ok := entry.Paragraph.Values[arch+"-Directory"]; ok && val != "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return entry.Directory
|
||||||
|
}
|
||||||
|
|
||||||
func (entry Manifest2822Entry) HasTag(tag string) bool {
|
func (entry Manifest2822Entry) HasTag(tag string) bool {
|
||||||
for _, existingTag := range entry.Tags {
|
for _, existingTag := range entry.Tags {
|
||||||
if tag == existingTag {
|
if tag == existingTag {
|
||||||
|
|
@ -145,6 +253,26 @@ func (entry Manifest2822Entry) HasTag(tag string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasSharedTag returns true if the given tag exists in entry.SharedTags.
|
||||||
|
func (entry Manifest2822Entry) HasSharedTag(tag string) bool {
|
||||||
|
for _, existingTag := range entry.SharedTags {
|
||||||
|
if tag == existingTag {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasArchitecture returns true if the given architecture exists in entry.Architectures
|
||||||
|
func (entry Manifest2822Entry) HasArchitecture(arch string) bool {
|
||||||
|
for _, existingArch := range entry.Architectures {
|
||||||
|
if arch == existingArch {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry {
|
func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry {
|
||||||
for _, entry := range manifest.Entries {
|
for _, entry := range manifest.Entries {
|
||||||
if entry.HasTag(tag) {
|
if entry.HasTag(tag) {
|
||||||
|
|
@ -154,6 +282,62 @@ func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry {
|
||||||
return nil
|
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 {
|
||||||
|
if entry.HasSharedTag(tag) {
|
||||||
|
ret = append(ret, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllSharedTags returns a list of the sum of all SharedTags in all entries of this image manifest (in the order they appear in the file).
|
||||||
|
func (manifest Manifest2822) GetAllSharedTags() []string {
|
||||||
|
fakeEntry := Manifest2822Entry{}
|
||||||
|
for _, entry := range manifest.Entries {
|
||||||
|
fakeEntry.SharedTags = append(fakeEntry.SharedTags, entry.SharedTags...)
|
||||||
|
}
|
||||||
|
fakeEntry.DeduplicateSharedTags()
|
||||||
|
return fakeEntry.SharedTags
|
||||||
|
}
|
||||||
|
|
||||||
|
type SharedTagGroup struct {
|
||||||
|
SharedTags []string
|
||||||
|
Entries []*Manifest2822Entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSharedTagGroups returns a map of shared tag groups to the list of entries they share (as described in https://github.com/docker-library/go-dockerlibrary/pull/2#issuecomment-277853597).
|
||||||
|
func (manifest Manifest2822) GetSharedTagGroups() []SharedTagGroup {
|
||||||
|
inter := map[string][]string{}
|
||||||
|
interOrder := []string{} // order matters, and maps randomize order
|
||||||
|
interKeySep := ","
|
||||||
|
for _, sharedTag := range manifest.GetAllSharedTags() {
|
||||||
|
interKeyParts := []string{}
|
||||||
|
for _, entry := range manifest.GetSharedTag(sharedTag) {
|
||||||
|
interKeyParts = append(interKeyParts, entry.Tags[0])
|
||||||
|
}
|
||||||
|
interKey := strings.Join(interKeyParts, interKeySep)
|
||||||
|
if _, ok := inter[interKey]; !ok {
|
||||||
|
interOrder = append(interOrder, interKey)
|
||||||
|
}
|
||||||
|
inter[interKey] = append(inter[interKey], sharedTag)
|
||||||
|
}
|
||||||
|
ret := []SharedTagGroup{}
|
||||||
|
for _, tags := range interOrder {
|
||||||
|
group := SharedTagGroup{
|
||||||
|
SharedTags: inter[tags],
|
||||||
|
Entries: []*Manifest2822Entry{},
|
||||||
|
}
|
||||||
|
for _, tag := range strings.Split(tags, interKeySep) {
|
||||||
|
group.Entries = append(group.Entries, manifest.GetTag(tag))
|
||||||
|
}
|
||||||
|
ret = append(ret, group)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error {
|
func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error {
|
||||||
if len(entry.Tags) < 1 {
|
if len(entry.Tags) < 1 {
|
||||||
return fmt.Errorf("missing Tags")
|
return fmt.Errorf("missing Tags")
|
||||||
|
|
@ -165,20 +349,36 @@ func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error {
|
||||||
return fmt.Errorf("Tags %q has invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat)
|
return fmt.Errorf("Tags %q has invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry.DeduplicateSharedTags()
|
||||||
|
|
||||||
seenTag := map[string]bool{}
|
seenTag := map[string]bool{}
|
||||||
for _, tag := range entry.Tags {
|
for _, tag := range entry.Tags {
|
||||||
if otherEntry := manifest.GetTag(tag); otherEntry != nil {
|
if otherEntry := manifest.GetTag(tag); otherEntry != nil {
|
||||||
return fmt.Errorf("Tags %q includes duplicate tag: %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString())
|
return fmt.Errorf("Tags %q includes duplicate tag: %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString())
|
||||||
}
|
}
|
||||||
|
if otherEntries := manifest.GetSharedTag(tag); len(otherEntries) > 0 {
|
||||||
|
return fmt.Errorf("Tags %q includes tag conflicting with a shared tag: %q (shared tag in %q)", entry.TagsString(), tag, otherEntries[0].TagsString())
|
||||||
|
}
|
||||||
if seenTag[tag] {
|
if seenTag[tag] {
|
||||||
return fmt.Errorf("Tags %q includes duplicate tag: %q", entry.TagsString(), tag)
|
return fmt.Errorf("Tags %q includes duplicate tag: %q", entry.TagsString(), tag)
|
||||||
}
|
}
|
||||||
seenTag[tag] = true
|
seenTag[tag] = true
|
||||||
}
|
}
|
||||||
|
for _, tag := range entry.SharedTags {
|
||||||
|
if otherEntry := manifest.GetTag(tag); otherEntry != nil {
|
||||||
|
return fmt.Errorf("Tags %q includes conflicting shared tag: %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString())
|
||||||
|
}
|
||||||
|
if seenTag[tag] {
|
||||||
|
return fmt.Errorf("Tags %q includes duplicate tag: %q (in SharedTags)", entry.TagsString(), tag)
|
||||||
|
}
|
||||||
|
seenTag[tag] = true
|
||||||
|
}
|
||||||
|
|
||||||
for i, existingEntry := range manifest.Entries {
|
for i, existingEntry := range manifest.Entries {
|
||||||
if existingEntry.SameBuildArtifacts(entry) {
|
if existingEntry.SameBuildArtifacts(entry) {
|
||||||
manifest.Entries[i].Tags = append(existingEntry.Tags, entry.Tags...)
|
manifest.Entries[i].Tags = append(existingEntry.Tags, entry.Tags...)
|
||||||
|
manifest.Entries[i].SharedTags = append(existingEntry.SharedTags, entry.SharedTags...)
|
||||||
|
manifest.Entries[i].DeduplicateSharedTags()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -210,20 +410,51 @@ func (entry Manifest2822Entry) InvalidMaintainers() []string {
|
||||||
return invalid
|
return invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeduplicateSharedTags will remove duplicate values from entry.SharedTags, preserving order.
|
||||||
|
func (entry *Manifest2822Entry) DeduplicateSharedTags() {
|
||||||
|
aggregate := []string{}
|
||||||
|
seen := map[string]bool{}
|
||||||
|
for _, tag := range entry.SharedTags {
|
||||||
|
if seen[tag] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[tag] = true
|
||||||
|
aggregate = append(aggregate, tag)
|
||||||
|
}
|
||||||
|
entry.SharedTags = aggregate
|
||||||
|
}
|
||||||
|
|
||||||
type decoderWrapper struct {
|
type decoderWrapper struct {
|
||||||
*control.Decoder
|
*control.Decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (decoder *decoderWrapper) Decode(entry *Manifest2822Entry) error {
|
func (decoder *decoderWrapper) Decode(entry *Manifest2822Entry) error {
|
||||||
|
// reset Architectures and SharedTags so that they can be either inherited or replaced, not additive
|
||||||
|
sharedTags := entry.SharedTags
|
||||||
|
entry.SharedTags = nil
|
||||||
|
arches := entry.Architectures
|
||||||
|
entry.Architectures = nil
|
||||||
|
|
||||||
for {
|
for {
|
||||||
err := decoder.Decoder.Decode(entry)
|
err := decoder.Decoder.Decode(entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore empty paragraphs (blank lines at the start, excess blank lines between paragraphs, excess blank lines at EOF)
|
// ignore empty paragraphs (blank lines at the start, excess blank lines between paragraphs, excess blank lines at EOF)
|
||||||
if len(entry.Paragraph.Order) > 0 {
|
if len(entry.Paragraph.Order) == 0 {
|
||||||
return nil
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we had no SharedTags or Architectures, restore our "default" (original) values
|
||||||
|
if len(entry.SharedTags) == 0 {
|
||||||
|
entry.SharedTags = sharedTags
|
||||||
|
}
|
||||||
|
if len(entry.Architectures) == 0 {
|
||||||
|
entry.Architectures = arches
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
38
go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/testdata/bash
vendored
Normal file
38
go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/testdata/bash
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# this is a snapshot of https://github.com/docker-library/official-images/raw/1a3c4cd6d5cd53bd538a6f56a69f94c5b35325a7/library/bash
|
||||||
|
|
||||||
|
# this file is generated via https://github.com/tianon/docker-bash/blob/cd1de3dfc885b3395cd354ddb988922350b092a7/generate-stackbrew-library.sh
|
||||||
|
|
||||||
|
Maintainers: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||||
|
GitRepo: https://github.com/tianon/docker-bash.git
|
||||||
|
|
||||||
|
Tags: 4.4.12, 4.4, 4, latest
|
||||||
|
GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
Directory: 4.4
|
||||||
|
|
||||||
|
Tags: 4.3.48, 4.3
|
||||||
|
GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
Directory: 4.3
|
||||||
|
|
||||||
|
Tags: 4.2.53, 4.2
|
||||||
|
GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
Directory: 4.2
|
||||||
|
|
||||||
|
Tags: 4.1.17, 4.1
|
||||||
|
GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
Directory: 4.1
|
||||||
|
|
||||||
|
Tags: 4.0.44, 4.0
|
||||||
|
GitCommit: 4438745d601d10d300e363f24205a3ca75307803
|
||||||
|
Directory: 4.0
|
||||||
|
|
||||||
|
Tags: 3.2.57, 3.2, 3
|
||||||
|
GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
Directory: 3.2
|
||||||
|
|
||||||
|
Tags: 3.1.23, 3.1
|
||||||
|
GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
Directory: 3.1
|
||||||
|
|
||||||
|
Tags: 3.0.22, 3.0
|
||||||
|
GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e
|
||||||
|
Directory: 3.0
|
||||||
26
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_example_test.go
vendored
Normal file
26
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_example_test.go
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package execpipe_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker-library/go-dockerlibrary/pkg/execpipe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
pipe, err := execpipe.RunCommand("go", "version")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer pipe.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, pipe)
|
||||||
|
|
||||||
|
fmt.Println(strings.SplitN(buf.String(), " version ", 2)[0])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// go
|
||||||
|
}
|
||||||
31
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_test.go
vendored
Normal file
31
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_test.go
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package execpipe_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker-library/go-dockerlibrary/pkg/execpipe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStdoutPipeError(t *testing.T) {
|
||||||
|
cmd := exec.Command("nothing", "really", "matters", "in", "the", "end")
|
||||||
|
|
||||||
|
// set "Stdout" so that "cmd.StdoutPipe" fails
|
||||||
|
// https://golang.org/src/os/exec/exec.go?s=16834:16883#L587
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
|
||||||
|
_, err := execpipe.Run(cmd)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected execpipe.Run to fail -- it did not")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStartError(t *testing.T) {
|
||||||
|
// craft a definitely-invalid command so that "cmd.Start" fails
|
||||||
|
// https://golang.org/src/os/exec/exec.go?s=8739:8766#L303
|
||||||
|
_, err := execpipe.RunCommand("nothing-really-matters-in-the-end--bogus-command")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected execpipe.RunCommand to fail -- it did not")
|
||||||
|
}
|
||||||
|
}
|
||||||
32
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/stripper/comments_example_test.go
vendored
Normal file
32
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/stripper/comments_example_test.go
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
package stripper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker-library/go-dockerlibrary/pkg/stripper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleCommentStripper() {
|
||||||
|
r := strings.NewReader(`
|
||||||
|
# opening comment
|
||||||
|
a: b
|
||||||
|
# comment!
|
||||||
|
c: d # not a comment
|
||||||
|
|
||||||
|
# another cheeky comment
|
||||||
|
e: f
|
||||||
|
`)
|
||||||
|
|
||||||
|
comStrip := stripper.NewCommentStripper(r)
|
||||||
|
|
||||||
|
// using CopyBuffer to force smaller Read sizes (better testing coverage that way)
|
||||||
|
io.CopyBuffer(os.Stdout, comStrip, make([]byte, 32))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// a: b
|
||||||
|
// c: d # not a comment
|
||||||
|
//
|
||||||
|
// e: f
|
||||||
|
}
|
||||||
8
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/doc.go
vendored
Normal file
8
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/doc.go
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
Package templatelib implements a group of useful functions for use with the stdlib text/template package.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
tmpl, err := template.New("some-template").Funcs(templatelib.FuncMap).Parse("Hi, {{ join " " .Names }}")
|
||||||
|
*/
|
||||||
|
package templatelib
|
||||||
|
|
@ -67,12 +67,15 @@ func stringsModifierActionFactory(a func(string, string) string) func([]string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO write some tests for these
|
|
||||||
|
|
||||||
var FuncMap = template.FuncMap{
|
var FuncMap = template.FuncMap{
|
||||||
|
// {{- $isGitHub := hasPrefix "https://github.com/" $url -}}
|
||||||
|
// {{- $isHtml := hasSuffix ".html" $url -}}
|
||||||
"hasPrefix": swapStringsFuncBoolArgsOrder(strings.HasPrefix),
|
"hasPrefix": swapStringsFuncBoolArgsOrder(strings.HasPrefix),
|
||||||
"hasSuffix": swapStringsFuncBoolArgsOrder(strings.HasSuffix),
|
"hasSuffix": swapStringsFuncBoolArgsOrder(strings.HasSuffix),
|
||||||
|
|
||||||
|
// {{- $hugeIfTrue := .SomeValue | ternary "HUGE" "not so huge" -}}
|
||||||
|
// if .SomeValue is truthy, $hugeIfTrue will be "HUGE"
|
||||||
|
// (otherwise, "not so huge")
|
||||||
"ternary": func(truthy interface{}, falsey interface{}, val interface{}) interface{} {
|
"ternary": func(truthy interface{}, falsey interface{}, val interface{}) interface{} {
|
||||||
if t, ok := template.IsTrue(val); !ok {
|
if t, ok := template.IsTrue(val); !ok {
|
||||||
panic(fmt.Sprintf(`template.IsTrue(%+v) says things are NOT OK`, val))
|
panic(fmt.Sprintf(`template.IsTrue(%+v) says things are NOT OK`, val))
|
||||||
|
|
@ -83,14 +86,26 @@ var FuncMap = template.FuncMap{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// First Tag: {{- .Tags | first -}}
|
||||||
|
// Last Tag: {{- .Tags | last -}}
|
||||||
"first": thingsActionFactory("first", true, func(args []interface{}, arg interface{}) interface{} { return arg }),
|
"first": thingsActionFactory("first", true, func(args []interface{}, arg interface{}) interface{} { return arg }),
|
||||||
"last": thingsActionFactory("last", false, func(args []interface{}, arg interface{}) interface{} { return arg }),
|
"last": thingsActionFactory("last", false, func(args []interface{}, arg interface{}) interface{} { return arg }),
|
||||||
|
|
||||||
|
// JSON data dump: {{ json . }}
|
||||||
|
// (especially nice for taking data and piping it to "jq")
|
||||||
|
// (ie "some-tool inspect --format '{{ json . }}' some-things | jq .")
|
||||||
"json": func(v interface{}) (string, error) {
|
"json": func(v interface{}) (string, error) {
|
||||||
j, err := json.Marshal(v)
|
j, err := json.Marshal(v)
|
||||||
return string(j), err
|
return string(j), err
|
||||||
},
|
},
|
||||||
"join": stringsActionFactory("join", true, strings.Join),
|
|
||||||
|
// Everybody: {{- join ", " .Names -}}
|
||||||
|
// Concat: {{- join "/" "https://github.com" "jsmith" "some-repo" -}}
|
||||||
|
"join": stringsActionFactory("join", true, strings.Join),
|
||||||
|
|
||||||
|
// {{- $mungedUrl := $url | replace "git://" "https://" | trimSuffixes ".git" -}}
|
||||||
|
// turns: git://github.com/jsmith/some-repo.git
|
||||||
|
// into: https://github.com/jsmith/some-repo
|
||||||
"trimPrefixes": stringsActionFactory("trimPrefixes", false, stringsModifierActionFactory(strings.TrimPrefix)),
|
"trimPrefixes": stringsActionFactory("trimPrefixes", false, stringsModifierActionFactory(strings.TrimPrefix)),
|
||||||
"trimSuffixes": stringsActionFactory("trimSuffixes", false, stringsModifierActionFactory(strings.TrimSuffix)),
|
"trimSuffixes": stringsActionFactory("trimSuffixes", false, stringsModifierActionFactory(strings.TrimSuffix)),
|
||||||
"replace": stringsActionFactory("replace", false, func(strs []string, str string) string {
|
"replace": stringsActionFactory("replace", false, func(strs []string, str string) string {
|
||||||
|
|
|
||||||
193
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_example_test.go
vendored
Normal file
193
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_example_test.go
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
package templatelib_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/docker-library/go-dockerlibrary/pkg/templatelib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_prefixSuffix() {
|
||||||
|
tmpl, err := template.New("github-or-html").Funcs(templatelib.FuncMap).Parse(`
|
||||||
|
{{- . -}}
|
||||||
|
|
||||||
|
{{- if hasPrefix "https://github.com/" . -}}
|
||||||
|
{{- " " -}} GitHub
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- if hasSuffix ".html" . -}}
|
||||||
|
{{- " " -}} HTML
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- "\n" -}}
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, "https://github.com/example/example")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, "https://example.com/test.html")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, "https://example.com")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, "https://github.com/example/example/raw/master/test.html")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// https://github.com/example/example GitHub
|
||||||
|
// https://example.com/test.html HTML
|
||||||
|
// https://example.com
|
||||||
|
// https://github.com/example/example/raw/master/test.html GitHub HTML
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_ternary() {
|
||||||
|
tmpl, err := template.New("huge-if-true").Funcs(templatelib.FuncMap).Parse(`
|
||||||
|
{{- range $a := . -}}
|
||||||
|
{{ printf "%#v: %s\n" $a (ternary "HUGE" "not so huge" $a) }}
|
||||||
|
{{- end -}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, []interface{}{
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
"true",
|
||||||
|
"false",
|
||||||
|
"",
|
||||||
|
nil,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
9001,
|
||||||
|
[]bool{},
|
||||||
|
[]bool{false},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true: HUGE
|
||||||
|
// false: not so huge
|
||||||
|
// "true": HUGE
|
||||||
|
// "false": HUGE
|
||||||
|
// "": not so huge
|
||||||
|
// <nil>: not so huge
|
||||||
|
// 1: HUGE
|
||||||
|
// 0: not so huge
|
||||||
|
// 9001: HUGE
|
||||||
|
// []bool{}: not so huge
|
||||||
|
// []bool{false}: HUGE
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_firstLast() {
|
||||||
|
tmpl, err := template.New("first-and-last").Funcs(templatelib.FuncMap).Parse(`First: {{ . | first }}, Last: {{ . | last }}`)
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, []interface{}{
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// First: a, Last: c
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_json() {
|
||||||
|
tmpl, err := template.New("json").Funcs(templatelib.FuncMap).Parse(`
|
||||||
|
{{- json . -}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, map[string]interface{}{
|
||||||
|
"a": []string{"1", "2", "3"},
|
||||||
|
"b": map[string]bool{"1": true, "2": false, "3": true},
|
||||||
|
"c": nil,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {"a":["1","2","3"],"b":{"1":true,"2":false,"3":true},"c":null}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_join() {
|
||||||
|
tmpl, err := template.New("join").Funcs(templatelib.FuncMap).Parse(`
|
||||||
|
Array: {{ . | join ", " }}{{ "\n" -}}
|
||||||
|
Args: {{ join ", " "a" "b" "c" -}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, []string{
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Array: 1, 2, 3
|
||||||
|
// Args: a, b, c
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_trimReplaceGitToHttps() {
|
||||||
|
tmpl, err := template.New("git-to-https").Funcs(templatelib.FuncMap).Parse(`
|
||||||
|
{{- range . -}}
|
||||||
|
{{- . | replace "git://" "https://" | trimSuffixes ".git" }}{{ "\n" -}}
|
||||||
|
{{- end -}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, []string{
|
||||||
|
"git://github.com/jsmith/some-repo.git",
|
||||||
|
"https://github.com/jsmith/some-repo.git",
|
||||||
|
"https://github.com/jsmith/some-repo",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// https://github.com/jsmith/some-repo
|
||||||
|
// https://github.com/jsmith/some-repo
|
||||||
|
// https://github.com/jsmith/some-repo
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_trimReplaceGitToGo() {
|
||||||
|
tmpl, err := template.New("git-to-go").Funcs(templatelib.FuncMap).Parse(`
|
||||||
|
{{- range . -}}
|
||||||
|
{{- . | trimPrefixes "git://" "http://" "https://" "ssh://" | trimSuffixes ".git" }}{{ "\n" -}}
|
||||||
|
{{- end -}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
err = tmpl.Execute(os.Stdout, []string{
|
||||||
|
"git://github.com/jsmith/some-repo.git",
|
||||||
|
"https://github.com/jsmith/some-repo.git",
|
||||||
|
"https://github.com/jsmith/some-repo",
|
||||||
|
"ssh://github.com/jsmith/some-repo.git",
|
||||||
|
"github.com/jsmith/some-repo",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// github.com/jsmith/some-repo
|
||||||
|
// github.com/jsmith/some-repo
|
||||||
|
// github.com/jsmith/some-repo
|
||||||
|
// github.com/jsmith/some-repo
|
||||||
|
// github.com/jsmith/some-repo
|
||||||
|
}
|
||||||
45
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_test.go
vendored
Normal file
45
go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_test.go
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
package templatelib_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/docker-library/go-dockerlibrary/pkg/templatelib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTernaryPanic(t *testing.T) {
|
||||||
|
// one of the only places template.IsTrue will return "false" for the "ok" value is an UnsafePointer (hence this test)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Errorf("Expected panic, executed successfully instead")
|
||||||
|
} else if errText, ok := r.(string); !ok || errText != `template.IsTrue(<nil>) says things are NOT OK` {
|
||||||
|
t.Errorf("Unexpected panic: %v", errText)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
tmpl, err := template.New("unsafe-pointer").Funcs(templatelib.FuncMap).Parse(`{{ ternary "true" "false" . }}`)
|
||||||
|
|
||||||
|
err = tmpl.Execute(nil, unsafe.Pointer(uintptr(0)))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected panic, got error instead: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJoinPanic(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Errorf("Expected panic, executed successfully instead")
|
||||||
|
} else if errText, ok := r.(string); !ok || errText != `"join" requires at least one argument` {
|
||||||
|
t.Errorf("Unexpected panic: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
tmpl, err := template.New("join-no-arg").Funcs(templatelib.FuncMap).Parse(`{{ join }}`)
|
||||||
|
|
||||||
|
err = tmpl.Execute(nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected panic, got error instead: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue