From 82bb84ff200f752d20fec7e5746c5a779ee02ff2 Mon Sep 17 00:00:00 2001 From: Alberto Carretero Date: Fri, 13 Dec 2024 18:43:58 +0100 Subject: [PATCH] feat: add support for "v2-archives" in "v1" format (#181) "v2-archives" is used for backwards compatibility with Chisel <= 1.0.0 since it will be ignored. In new versions, it will be parsed with the new fields that break said compatibility, e.g. "pro" archives. Co-authored-by: Rafid Bin Mostofa --- internal/setup/setup_test.go | 109 +++++++++++++++++- internal/setup/yaml.go | 26 ++++- internal/slicer/slicer_test.go | 22 +++- .../pro-archives/chisel-releases/chisel.yaml | 2 +- 4 files changed, 152 insertions(+), 7 deletions(-) diff --git a/internal/setup/setup_test.go b/internal/setup/setup_test.go index a5d16613..2131038f 100644 --- a/internal/setup/setup_test.go +++ b/internal/setup/setup_test.go @@ -1933,6 +1933,93 @@ var setupTests = []setupTest{{ `, }, relerror: `chisel.yaml: more than one default archive: bar, foo`, +}, { + summary: "Additional v2-archives are merged with regular archives", + input: map[string]string{ + "chisel.yaml": ` + format: v1 + archives: + ubuntu: + version: 20.04 + components: [main] + suites: [focal] + priority: 10 + public-keys: [test-key] + v2-archives: + fips: + version: 20.04 + components: [main] + suites: [focal] + pro: fips + priority: 20 + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + `, + "slices/mydir/mypkg.yaml": ` + package: mypkg + `, + }, + release: &setup.Release{ + Archives: map[string]*setup.Archive{ + "ubuntu": { + Name: "ubuntu", + Version: "20.04", + Suites: []string{"focal"}, + Components: []string{"main"}, + Priority: 10, + PubKeys: []*packet.PublicKey{testKey.PubKey}, + }, + "fips": { + Name: "fips", + Version: "20.04", + Suites: []string{"focal"}, + Components: []string{"main"}, + Pro: "fips", + Priority: 20, + PubKeys: []*packet.PublicKey{testKey.PubKey}, + }, + }, + Packages: map[string]*setup.Package{ + "mypkg": { + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, + }, + }, + }, +}, { + summary: "Cannot define same archive name in archives and v2-archives", + input: map[string]string{ + "chisel.yaml": ` + format: v1 + archives: + ubuntu: + version: 20.04 + components: [main] + suites: [focal] + priority: 10 + public-keys: [test-key] + v2-archives: + ubuntu: + version: 20.04 + components: [main] + suites: [focal] + priority: 20 + pro: fips + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + `, + "slices/mydir/mypkg.yaml": ` + package: mypkg + `, + }, + relerror: `chisel.yaml: archive "ubuntu" defined twice`, }} var defaultChiselYaml = ` @@ -1950,7 +2037,27 @@ var defaultChiselYaml = ` ` func (s *S) TestParseRelease(c *C) { - for _, test := range setupTests { + // Run tests for "archives" field in "v1" format. + runParseReleaseTests(c, setupTests) + + // Run tests for "v2-archives" field in "v1" format. + v2ArchiveTests := make([]setupTest, 0, len(setupTests)) + for _, t := range setupTests { + m := make(map[string]string) + for k, v := range t.input { + if !strings.Contains(v, "v2-archives:") { + v = strings.Replace(v, "archives:", "v2-archives:", -1) + } + m[k] = v + } + t.input = m + v2ArchiveTests = append(v2ArchiveTests, t) + } + runParseReleaseTests(c, v2ArchiveTests) +} + +func runParseReleaseTests(c *C, tests []setupTest) { + for _, test := range tests { c.Logf("Summary: %s", test.summary) if _, ok := test.input["chisel.yaml"]; !ok { diff --git a/internal/setup/yaml.go b/internal/setup/yaml.go index f2dbe127..ee6407a9 100644 --- a/internal/setup/yaml.go +++ b/internal/setup/yaml.go @@ -25,6 +25,12 @@ type yamlRelease struct { Format string `yaml:"format"` Archives map[string]yamlArchive `yaml:"archives"` PubKeys map[string]yamlPubKey `yaml:"public-keys"` + // "v2-archives" is used for backwards compatibility with Chisel <= 1.0.0 + // since it will be ignored. In new versions, it will be parsed with the new + // fields that break said compatibility, e.g. "pro" archives. + // + // Note: "archives" and "v2-archives" are merged together while parsing. + V2Archives map[string]yamlArchive `yaml:"v2-archives"` } const ( @@ -161,7 +167,7 @@ func parseRelease(baseDir, filePath string, data []byte) (*Release, error) { if yamlVar.Format != "v1" { return nil, fmt.Errorf("%s: unknown format %q", fileName, yamlVar.Format) } - if len(yamlVar.Archives) == 0 { + if len(yamlVar.Archives)+len(yamlVar.V2Archives) == 0 { return nil, fmt.Errorf("%s: no archives defined", fileName) } @@ -178,12 +184,24 @@ func parseRelease(baseDir, filePath string, data []byte) (*Release, error) { pubKeys[keyName] = key } + // Merge all archive definitions. + yamlArchives := make(map[string]yamlArchive, len(yamlVar.Archives)+len(yamlVar.V2Archives)) + for archiveName, details := range yamlVar.Archives { + yamlArchives[archiveName] = details + } + for archiveName, details := range yamlVar.V2Archives { + if _, ok := yamlArchives[archiveName]; ok { + return nil, fmt.Errorf("%s: archive %q defined twice", fileName, archiveName) + } + yamlArchives[archiveName] = details + } + // For compatibility if there is a default archive set and priorities are // not being used, we will revert back to the default archive behaviour. hasPriority := false var defaultArchive string var archiveNoPriority string - for archiveName, details := range yamlVar.Archives { + for archiveName, details := range yamlArchives { if details.Version == "" { return nil, fmt.Errorf("%s: archive %q missing version field", fileName, archiveName) } @@ -243,7 +261,7 @@ func parseRelease(baseDir, filePath string, data []byte) (*Release, error) { } } if (hasPriority && archiveNoPriority != "") || - (!hasPriority && defaultArchive == "" && len(yamlVar.Archives) > 1) { + (!hasPriority && defaultArchive == "" && len(yamlArchives) > 1) { return nil, fmt.Errorf("%s: archive %q is missing the priority setting", fileName, archiveNoPriority) } if defaultArchive != "" && !hasPriority { @@ -251,7 +269,7 @@ func parseRelease(baseDir, filePath string, data []byte) (*Release, error) { // negative priorities to all but the default one, which means all // others will be ignored unless pinned. var archiveNames []string - for archiveName := range yamlVar.Archives { + for archiveName := range yamlArchives { archiveNames = append(archiveNames, archiveName) } // Make it deterministic. diff --git a/internal/slicer/slicer_test.go b/internal/slicer/slicer_test.go index a49fdd97..51738367 100644 --- a/internal/slicer/slicer_test.go +++ b/internal/slicer/slicer_test.go @@ -1495,7 +1495,27 @@ var defaultChiselYaml = ` ` func (s *S) TestRun(c *C) { - for _, test := range slicerTests { + // Run tests for "archives" field in "v1" format. + runSlicerTests(c, slicerTests) + + // Run tests for "v2-archives" field in "v1" format. + v2ArchiveTests := make([]slicerTest, 0, len(slicerTests)) + for _, t := range slicerTests { + m := make(map[string]string) + for k, v := range t.release { + if !strings.Contains(v, "v2-archives:") { + v = strings.Replace(v, "archives:", "v2-archives:", -1) + } + m[k] = v + } + t.release = m + v2ArchiveTests = append(v2ArchiveTests, t) + } + runSlicerTests(c, v2ArchiveTests) +} + +func runSlicerTests(c *C, tests []slicerTest) { + for _, test := range tests { for _, testSlices := range testutil.Permutations(test.slices) { c.Logf("Summary: %s", test.summary) diff --git a/tests/pro-archives/chisel-releases/chisel.yaml b/tests/pro-archives/chisel-releases/chisel.yaml index 5beca3a9..f5d6ec6c 100644 --- a/tests/pro-archives/chisel-releases/chisel.yaml +++ b/tests/pro-archives/chisel-releases/chisel.yaml @@ -1,6 +1,6 @@ format: v1 -archives: +v2-archives: ubuntu: version: 24.04 pro: esm-infra