diff --git a/manifest/rfc2822.go b/manifest/rfc2822.go index ad4484e..4b47dae 100644 --- a/manifest/rfc2822.go +++ b/manifest/rfc2822.go @@ -27,7 +27,8 @@ type Manifest2822Entry struct { Maintainers []string `delim:"," strip:"\n\r\t "` - Tags []string `delim:"," strip:"\n\r\t "` + Tags []string `delim:"," strip:"\n\r\t "` + ManifestListTags []string `delim:"," strip:"\n\r\t "` GitRepo string GitFetch string @@ -46,6 +47,7 @@ func (entry Manifest2822Entry) Clone() Manifest2822Entry { // SLICES! grr entry.Maintainers = append([]string{}, entry.Maintainers...) entry.Tags = append([]string{}, entry.Tags...) + entry.ManifestListTags = append([]string{}, entry.ManifestListTags...) entry.Constraints = append([]string{}, entry.Constraints...) return entry } @@ -60,6 +62,10 @@ func (entry Manifest2822Entry) TagsString() string { return strings.Join(entry.Tags, StringSeparator2822) } +func (entry Manifest2822Entry) ManifestListTagsString() string { + return strings.Join(entry.ManifestListTags, StringSeparator2822) +} + func (entry Manifest2822Entry) ConstraintsString() string { return strings.Join(entry.Constraints, StringSeparator2822) } @@ -77,6 +83,9 @@ func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifes if entry.TagsString() == defaults.TagsString() { entry.Tags = nil } + if entry.ManifestListTagsString() == defaults.ManifestListTagsString() { + entry.ManifestListTags = nil + } if entry.GitRepo == defaults.GitRepo { entry.GitRepo = "" } @@ -103,6 +112,9 @@ func (entry Manifest2822Entry) String() string { if str := entry.TagsString(); str != "" { ret = append(ret, "Tags: "+str) } + if str := entry.ManifestListTagsString(); str != "" { + ret = append(ret, "ManifestListTags: "+str) + } if str := entry.GitRepo; str != "" { ret = append(ret, "GitRepo: "+str) } @@ -145,6 +157,16 @@ func (entry Manifest2822Entry) HasTag(tag string) bool { return false } +// HasManifestListTag returns true if the given tag exists in entry.ManifestListTags. +func (entry Manifest2822Entry) HasManifestListTag(tag string) bool { + for _, existingTag := range entry.ManifestListTags { + if tag == existingTag { + return true + } + } + return false +} + func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry { for _, entry := range manifest.Entries { if entry.HasTag(tag) { @@ -154,6 +176,27 @@ func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry { return nil } +// GetManifestListTag returns a list of entries with the given tag in entry.ManifestListTags (or the empty list if there are no entries with the given tag). +func (manifest Manifest2822) GetManifestListTag(tag string) []Manifest2822Entry { + ret := []Manifest2822Entry{} + for _, entry := range manifest.Entries { + if entry.HasManifestListTag(tag) { + ret = append(ret, entry) + } + } + return ret +} + +// GetAllManifestListTags returns a list of the sum of all ManifesListTags in all entries of this image manifest (in the order they appear in the file). +func (manifest Manifest2822) GetAllManifestListTags() []string { + fakeEntry := Manifest2822Entry{} + for _, entry := range manifest.Entries { + fakeEntry.ManifestListTags = append(fakeEntry.ManifestListTags, entry.ManifestListTags...) + } + fakeEntry.DeduplicateManifestListTags() + return fakeEntry.ManifestListTags +} + func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error { if len(entry.Tags) < 1 { return fmt.Errorf("missing Tags") @@ -171,20 +214,36 @@ func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error { return fmt.Errorf("Tags %q has invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat) } + entry.DeduplicateManifestListTags() + seenTag := map[string]bool{} for _, tag := range entry.Tags { if otherEntry := manifest.GetTag(tag); otherEntry != nil { return fmt.Errorf("Tags %q includes duplicate tag: %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString()) } + if otherEntries := manifest.GetManifestListTag(tag); len(otherEntries) > 0 { + return fmt.Errorf("Tags %q includes tag conflicting with a manifest list tag: %q (manifest list tag in %q)", entry.TagsString(), tag, otherEntries[0].TagsString()) + } if seenTag[tag] { return fmt.Errorf("Tags %q includes duplicate tag: %q", entry.TagsString(), tag) } seenTag[tag] = true } + for _, tag := range entry.ManifestListTags { + if otherEntry := manifest.GetTag(tag); otherEntry != nil { + return fmt.Errorf("Tags %q includes conflicting manifest list tag: %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString()) + } + if seenTag[tag] { + return fmt.Errorf("Tags %q includes duplicate tag: %q (in ManifestListTags)", entry.TagsString(), tag) + } + seenTag[tag] = true + } for i, existingEntry := range manifest.Entries { if existingEntry.SameBuildArtifacts(entry) { manifest.Entries[i].Tags = append(existingEntry.Tags, entry.Tags...) + manifest.Entries[i].ManifestListTags = append(existingEntry.ManifestListTags, entry.ManifestListTags...) + manifest.Entries[i].DeduplicateManifestListTags() return nil } } @@ -216,6 +275,20 @@ func (entry Manifest2822Entry) InvalidMaintainers() []string { return invalid } +// DeduplicateManifestListTags will remove duplicate values from entry.ManifestListTags, preserving order. +func (entry *Manifest2822Entry) DeduplicateManifestListTags() { + aggregate := []string{} + seen := map[string]bool{} + for _, tag := range entry.ManifestListTags { + if seen[tag] { + continue + } + seen[tag] = true + aggregate = append(aggregate, tag) + } + entry.ManifestListTags = aggregate +} + type decoderWrapper struct { *control.Decoder } @@ -255,7 +328,7 @@ func Parse2822(readerIn io.Reader) (*Manifest2822, error) { if invalidMaintainers := manifest.Global.InvalidMaintainers(); len(invalidMaintainers) > 0 { return nil, fmt.Errorf("invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat) } - if len(manifest.Global.Tags) > 0 { + if len(manifest.Global.Tags) > 0 || len(manifest.Global.ManifestListTags) > 0 { return nil, fmt.Errorf("global Tags not permitted") }