Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4622fc0
feat: support Ubuntu interim releases after end of maintenance
letFunny Aug 7, 2025
71ec1f5
pro archive spread is manual, restore
letFunny Aug 7, 2025
ec13945
docs
letFunny Aug 7, 2025
afbb8c3
restore spread folders
letFunny Mar 12, 2025
db59cb9
correct spread.yaml
letFunny Aug 12, 2025
3ecbf38
change ack flag for ignore=<condition>
letFunny Aug 22, 2025
a41e51d
use "maintenance" instead of a boolean
letFunny Aug 22, 2025
f0f4f06
change chisel.yaml in existing tests
letFunny Aug 25, 2025
53b7085
make optional
letFunny Aug 25, 2025
a0e7cf8
Revert "change chisel.yaml in existing tests"
letFunny Aug 25, 2025
71892d5
add maintenance support for tests
letFunny Aug 25, 2025
a65228e
default chisel yaml common to all tests
letFunny Aug 25, 2025
43d1c9e
fix spread
letFunny Aug 25, 2025
6fd6203
default dates
letFunny Aug 28, 2025
7e60de3
typo
letFunny Aug 28, 2025
c8b7ee0
spread, enable mantic
letFunny Aug 28, 2025
5782703
fix spread
letFunny Aug 28, 2025
e922c52
correct dates
letFunny Aug 28, 2025
d489010
spread, extract option into variable
letFunny Sep 1, 2025
0bcde22
change TODO to match agreement
letFunny Sep 1, 2025
cc6b74c
use unset instead of empty
letFunny Sep 9, 2025
e369edb
add support for unstable
letFunny Sep 9, 2025
17be3dd
make maintenance an enum for archives
letFunny Sep 9, 2025
02deb1d
change error messages to include urls
letFunny Sep 10, 2025
686e857
git forgot to add spread test
letFunny Sep 10, 2025
c2681a3
final scope of the PR
letFunny Sep 10, 2025
6a255a9
full stop
letFunny Sep 16, 2025
7783156
archive maintenance different than release
letFunny Sep 22, 2025
4f77c93
use different flags for maintenance vs oldRelease
letFunny Sep 23, 2025
b31ea6c
leftover from unmaintained
letFunny Sep 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions cmd/chisel/cmd_cut.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package main

import (
"fmt"
"time"

"github.com/jessevdk/go-flags"

"github.com/canonical/chisel/internal/archive"
Expand All @@ -22,12 +25,14 @@ var cutDescs = map[string]string{
"release": "Chisel release name or directory (e.g. ubuntu-22.04)",
"root": "Root for generated content",
"arch": "Package architecture",
"ignore": "Conditions to ignore (e.g. unmaintained, unstable)",
}

type cmdCut struct {
Release string `long:"release" value-name:"<dir>"`
RootDir string `long:"root" value-name:"<dir>" required:"yes"`
Arch string `long:"arch" value-name:"<arch>"`
Ignore string `long:"ignore" choice:"unmaintained" choice:"unstable" value-name:"<cond>"`

Positional struct {
SliceRefs []string `positional-arg-name:"<slice names>" required:"yes"`
Expand Down Expand Up @@ -57,6 +62,16 @@ func (cmd *cmdCut) Execute(args []string) error {
return err
}

if time.Now().Before(release.Maintenance.Standard) {
if cmd.Ignore == "unstable" {
logf(`Warning: This release is in the "unstable" maintenance status. ` +
`See https://documentation.ubuntu.com/chisel/en/latest/reference/chisel-releases/chisel.yaml/#maintenance to be safe`)
} else {
return fmt.Errorf(`this release is in the "unstable" maintenance status, ` +
`see https://documentation.ubuntu.com/chisel/en/latest/reference/chisel-releases/chisel.yaml/#maintenance for details`)
}
}

selection, err := setup.Select(release, sliceKeys)
if err != nil {
return err
Expand All @@ -73,6 +88,8 @@ func (cmd *cmdCut) Execute(args []string) error {
Pro: archiveInfo.Pro,
CacheDir: cache.DefaultDir("chisel"),
PubKeys: archiveInfo.PubKeys,
Maintained: archiveInfo.Maintained,
OldRelease: archiveInfo.OldRelease,
})
if err != nil {
if err == archive.ErrCredentialsNotFound {
Expand All @@ -84,6 +101,25 @@ func (cmd *cmdCut) Execute(args []string) error {
archives[archiveName] = openArchive
}

hasMaintainedArchive := false
for _, archive := range archives {
if archive.Options().Maintained {
hasMaintainedArchive = true
break
}
}
if !hasMaintainedArchive {
if cmd.Ignore == "unmaintained" {
logf(`Warning: No archive has "maintained" maintenance status. ` +
`Consider the different Ubuntu Pro subcriptions to be safe. ` +
`See https://documentation.ubuntu.com/chisel/en/latest/reference/chisel-releases/chisel.yaml/#maintenance for details.`)
} else {
return fmt.Errorf(`no archive has "maintained" maintenance status, ` +
`consider the different Ubuntu Pro subcriptions to be safe, ` +
`see https://documentation.ubuntu.com/chisel/en/latest/reference/chisel-releases/chisel.yaml/#maintenance for details`)
}
}

err = slicer.Run(&slicer.RunOptions{
Selection: selection,
Archives: archives,
Expand Down
2 changes: 2 additions & 0 deletions cmd/chisel/cmd_debug_check_release_archives.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ func (cmd *cmdDebugCheckReleaseArchives) Execute(args []string) error {
Pro: archiveInfo.Pro,
CacheDir: cache.DefaultDir("chisel"),
PubKeys: archiveInfo.PubKeys,
Maintained: archiveInfo.Maintained,
OldRelease: archiveInfo.OldRelease,
})
if err == archive.ErrCredentialsNotFound {
logf("Archive %q ignored: credentials not found\n", archiveName)
Expand Down
18 changes: 4 additions & 14 deletions cmd/chisel/cmd_debug_check_release_archives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,19 +277,7 @@ func (s *ChiselSuite) TestRun(c *C) {
// makeChiselYaml returns a valid chisel.yaml that contains the archives
// supplied.
func makeChiselYaml(archives []string) string {
archiveKey := testutil.PGPKeys["key-ubuntu-2018"]
rawChiselYaml := testutil.Reindent(`
format: v1
archives:
ubuntu:
version: 22.04
components: [main, universe]
suites: [jammy]
public-keys: [test-key]
public-keys:
test-key:
id: ` + archiveKey.ID + `
armor: |` + "\n" + testutil.PrefixEachLine(archiveKey.PubKeyArmor, "\t\t\t\t\t\t"))
rawChiselYaml := testutil.Reindent(testutil.DefaultChiselYaml)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Comment for reviewer]: I have taken the opportunity to finally unify all the defaultYaml strings which were copies of each other but needed to be updated individually every time we wanted to make a change.


chiselYaml := map[string]any{}
err := yaml.Unmarshal([]byte(rawChiselYaml), chiselYaml)
Expand All @@ -313,7 +301,9 @@ func makeChiselYaml(archives []string) string {
if err != nil {
panic(err)
}
return string(bs)
out := string(bs)
// Maintenance dates get marshaled as <date>T00:00:00Z by default.
return strings.ReplaceAll(out, "T00:00:00Z", "")
}

func deepCopyYAML(src map[string]any) map[string]any {
Expand Down
17 changes: 1 addition & 16 deletions cmd/chisel/cmd_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,8 @@ var infoTests = []infoTest{{
err: `no slice definitions found for: "foo_bar_foo", "a_b", "7_c", "a_b c", "a_b x_y"`,
}}

var testKey = testutil.PGPKeys["key1"]

var defaultChiselYaml = `
format: v1
archives:
ubuntu:
version: 22.04
components: [main, universe]
suites: [jammy]
public-keys: [test-key]
public-keys:
test-key:
id: ` + testKey.ID + `
armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t")

var infoRelease = map[string]string{
"chisel.yaml": string(defaultChiselYaml),
"chisel.yaml": string(testutil.DefaultChiselYaml),
"slices/mypkg1.yaml": `
package: mypkg1
essential:
Expand Down
14 changes: 12 additions & 2 deletions internal/archive/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ type Options struct {
Pro string
CacheDir string
PubKeys []*packet.PublicKey
// Maintained is set when the archive is still being updated.
Maintained bool
// OldRelease is set for Ubuntu releases which are moved from the regular
// archive which happens after the release's end of life date.
OldRelease bool
}

func Open(options *Options) (Archive, error) {
Expand Down Expand Up @@ -148,6 +153,7 @@ func (a *ubuntuArchive) Info(pkg string) (*PackageInfo, error) {
}

const ubuntuURL = "http://archive.ubuntu.com/ubuntu/"
const ubuntuOldReleasesURL = "http://old-releases.ubuntu.com/ubuntu/"
const ubuntuPortsURL = "http://ports.ubuntu.com/ubuntu-ports/"

const (
Expand Down Expand Up @@ -178,7 +184,7 @@ var proArchiveInfo = map[string]struct {
},
}

func archiveURL(pro, arch string) (string, *credentials, error) {
func archiveURL(pro, arch string, oldRelease bool) (string, *credentials, error) {
if pro != "" {
archiveInfo, ok := proArchiveInfo[pro]
if !ok {
Expand All @@ -192,6 +198,10 @@ func archiveURL(pro, arch string) (string, *credentials, error) {
return url, creds, nil
}

if oldRelease {
return ubuntuOldReleasesURL, nil, nil
}

if arch == "amd64" || arch == "i386" {
return ubuntuURL, nil, nil
}
Expand All @@ -209,7 +219,7 @@ func openUbuntu(options *Options) (Archive, error) {
return nil, fmt.Errorf("archive options missing version")
}

baseURL, creds, err := archiveURL(options.Pro, options.Arch)
baseURL, creds, err := archiveURL(options.Pro, options.Arch, options.OldRelease)
if err != nil {
return nil, err
}
Expand Down
39 changes: 39 additions & 0 deletions internal/archive/archive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,31 @@ func (s *httpSuite) TestProArchives(c *C) {
}
}

func (s *httpSuite) TestOpenUnmaintainedArchives(c *C) {
s.base = "http://old-releases.ubuntu.com/ubuntu/"
s.prepareArchive("jammy", "22.04", "amd64", []string{"main", "universe"})

options := archive.Options{
Label: "ubuntu",
Version: "22.04",
Arch: "amd64",
Suites: []string{"jammy"},
Components: []string{"main", "universe"},
CacheDir: c.MkDir(),
PubKeys: []*packet.PublicKey{s.pubKey},
OldRelease: false,
}

_, err := archive.Open(&options)
// Fails when OldRelease is not set because it attempts to contact the
// default ubuntu archive where the release is no longer available.
c.Assert(err, Not(IsNil))

options.OldRelease = true
_, err = archive.Open(&options)
c.Assert(err, IsNil)
}

type verifyArchiveReleaseTest struct {
summary string
pubKeys []*packet.PublicKey
Expand Down Expand Up @@ -642,6 +667,7 @@ type realArchiveTest struct {
suites []string
components []string
pro string
oldRelease bool
archivePubKeys []*packet.PublicKey
archs []string
pkg string
Expand All @@ -651,6 +677,7 @@ type realArchiveTest struct {
var realArchiveTests = []realArchiveTest{{
name: "focal",
version: "20.04",
oldRelease: false,
suites: []string{"focal"},
components: []string{"main", "universe"},
archivePubKeys: []*packet.PublicKey{keyUbuntu2018.PubKey},
Expand All @@ -659,6 +686,7 @@ var realArchiveTests = []realArchiveTest{{
}, {
name: "jammy",
version: "22.04",
oldRelease: false,
suites: []string{"jammy"},
components: []string{"main", "universe"},
archivePubKeys: []*packet.PublicKey{keyUbuntu2018.PubKey},
Expand All @@ -667,11 +695,21 @@ var realArchiveTests = []realArchiveTest{{
}, {
name: "noble",
version: "24.04",
oldRelease: false,
suites: []string{"noble"},
components: []string{"main", "universe"},
archivePubKeys: []*packet.PublicKey{keyUbuntu2018.PubKey},
pkg: "hostname",
path: "/usr/bin/hostname",
}, {
name: "mantic",
version: "23.10",
oldRelease: true,
suites: []string{"mantic"},
components: []string{"main", "universe"},
archivePubKeys: []*packet.PublicKey{keyUbuntu2018.PubKey},
pkg: "hostname",
path: "/bin/hostname",
}}

var proArchiveTests = []realArchiveTest{{
Expand Down Expand Up @@ -797,6 +835,7 @@ func (s *S) testOpenArchiveArch(c *C, test realArchiveTest, arch string) {
CacheDir: c.MkDir(),
Pro: test.pro,
PubKeys: test.archivePubKeys,
OldRelease: test.oldRelease,
}

testArchive, err := archive.Open(&options)
Expand Down
20 changes: 17 additions & 3 deletions internal/setup/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"golang.org/x/crypto/openpgp/packet"

Expand All @@ -16,9 +17,17 @@ import (
// Release is a collection of package slices targeting a particular
// distribution version.
type Release struct {
Path string
Packages map[string]*Package
Archives map[string]*Archive
Path string
Packages map[string]*Package
Archives map[string]*Archive
Maintenance *Maintenance
}

type Maintenance struct {
Standard time.Time
Expanded time.Time
Legacy time.Time
EndOfLife time.Time
}

// Archive is the location from which binary packages are obtained.
Expand All @@ -30,6 +39,11 @@ type Archive struct {
Priority int
Pro string
PubKeys []*packet.PublicKey
// Maintained is set when the archive is still being updated.
Maintained bool
// OldRelease is set for Ubuntu releases which are moved from the regular
// archive which happens after the release's end of life date.
OldRelease bool
}

// Package holds a collection of slices that represent parts of themselves.
Expand Down
Loading
Loading