Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion cmd/quarry-client/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func serveSHA256SUMS(rw http.ResponseWriter, req *http.Request) (Err error) {
}
// Extension targets are consumed by quarry-sysupdate itself, not by
// systemd-sysupdate.
if strings.HasPrefix(target.Path, xsysupdate.ExtensionTargetPrefix) {
if _, _, ok := xsysupdate.CutExtensionTargetPrefix(target.Path); ok {
slog.Debug("Stripped quarry extension target from generated SHA256SUMS",
"target", target.Path)
continue
Expand Down
52 changes: 44 additions & 8 deletions cmd/quarry-sysupdate/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import (
"errors"
"fmt"
"log/slog"
"strings"

"github.com/urfave/cli/v3"

"go.amutable.dev/quarry/internal/third_party/assert"
"go.amutable.dev/quarry/internal/tufclient"
"go.amutable.dev/quarry/internal/xsysupdate"
)

Expand All @@ -38,20 +39,55 @@ var updateCommand = withExtensionDirFlag(withQuarryUserFlag(withQuarryProxyFlag(
}
slog.Info("Initialised extensions.",
"extensions", exts.String())
for target, err := range unprivIterTargetFiles(ctx) {
if err != nil {
return fmt.Errorf("error while scanning repos: %w", err)
}
if !strings.HasPrefix(target.Path, xsysupdate.ExtensionTargetPrefix) {
continue
}
// Legacy-prefixed targets are only applied after all other targets so
// we can allow them to be overridden by the non-legacy prefix.
// TODO: Drop this.
var (
applied = map[string]struct{}{} // uses the stripped name
legacyTargets []*tufclient.TargetInfo
)
applyTarget := func(target *tufclient.TargetInfo) error {
slog.Info("Extension target found.",
"target", target.Path, "repository", target.Repo.Name)
name, legacy, ok := xsysupdate.CutExtensionTargetPrefix(target.Path)
assert.Assertf(ok, "extension target path %q must have extension prefix", target.Path)
if _, ok := applied[name]; ok {
slog.Info("Skipping legacy extension target superseded by new prefix.",
"target", target.Path, "repository", target.Repo.Name)
assert.Assertf(!legacy, "extension target path %q must not be applied after legacy paths", target.Path)
return nil
}
if err := exts.DoApplyTarget(ctx, target); err != nil {
return fmt.Errorf("apply extension target %s: %w", target.Path, err)
}
applied[name] = struct{}{}
slog.Info("Extension target applied.",
"target", target.Path, "repository", target.Repo.Name)
return nil
}
for target, err := range unprivIterTargetFiles(ctx) {
if err != nil {
return fmt.Errorf("error while scanning repos: %w", err)
}
if _, legacy, ok := xsysupdate.CutExtensionTargetPrefix(target.Path); !ok {
// Not an extension target file.
continue
} else if legacy {
// Defer application of legacy-prefixed extensions until we've
// done everything else so we can skip them if there was a
// non-legacy version of the same file.
legacyTargets = append(legacyTargets, target)
continue
}
// Otherwise, apply the extension target file.
if err := applyTarget(target); err != nil {
return err
}
}
for _, target := range legacyTargets {
if err := applyTarget(target); err != nil {
return err
}
}
if err := exts.DoBeforeUpdate(ctx); err != nil {
return fmt.Errorf("before update extensions: %w", err)
Expand Down
4 changes: 2 additions & 2 deletions hack/quarry-bakery
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ SHA256SUMS="$(mktemp "quarry-bakery-$sysext.SHA256SUMS.XXXXXX")"
trap 'rm -f "$SHA256SUMS"' EXIT
curl -sSL -o "$SHA256SUMS" "https://github.com/flatcar/sysext-bakery/releases/download/$sysext/SHA256SUMS"

# Pre-compute the special .zzz-quarry-special/sysupdate.FOO.d/* entries for the
# Pre-compute the special .quarry/sysupdate.FOO.d/* entries for the
# repository to insert it into the targets.json.
sysext_transfer_url="https://github.com/flatcar/sysext-bakery/releases/download/$sysext/$sysext.conf"
sysext_transfer="$(curl -sSL -o- "$sysext_transfer_url")"
Expand All @@ -51,7 +51,7 @@ sysext_transfer_entry="$(cat <<-EOF
"version": 1,
"spec_version": "1.0.31",
"targets": {
".zzz-quarry-special/sysupdate.$sysext.d/$sysext.transfer": {
".quarry/sysupdate.$sysext.d/$sysext.transfer": {
"hashes": {
"sha256": "$(sha256sum <<<"$sysext_transfer" | awk '{ print $1 }')"
},
Expand Down
30 changes: 27 additions & 3 deletions internal/xsysupdate/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,37 @@ import (
"errors"
"fmt"
"log/slog"
"strings"

"go.amutable.dev/quarry/internal/tufclient"
)

// ExtensionTargetPrefix is the path prefix used by quarry extension target
// files to indicate they are a special kind of repository metadata.
const ExtensionTargetPrefix = ".zzz-quarry-special/"
const (
// ExtensionTargetPrefix is the path prefix used by quarry extension target
// files to indicate they are a special kind of repository metadata.
ExtensionTargetPrefix = ".quarry/"

// legacyExtensionTargetPrefix is the old name for [ExtensionTargetPrefix],
// still accepted for backward compatibility.
// TODO: Drop this.
legacyExtensionTargetPrefix = ".zzz-quarry-special/"
)

// CutExtensionTargetPrefix returns path without its [ExtensionTargetPrefix]
// with similar semantics to [strings.CutPrefix]. legacy is true if the prefix
// is actually the legacy (to-be-removed) prefix and should be used when
// deciding which version of an extension path should have priority.
func CutExtensionTargetPrefix(path string) (name string, legacy, ok bool) {
for prefix, isLegacy := range map[string]bool{
ExtensionTargetPrefix: false,
legacyExtensionTargetPrefix: true,
} {
if name, ok := strings.CutPrefix(path, prefix); ok {
return name, isLegacy, ok
}
}
return path, false, false
}

// Extension represents a Quarry extension that deal with special files in the
// repository.
Expand Down
10 changes: 7 additions & 3 deletions internal/xsysupdate/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ const (
// The filename suffix is the tag name, and the target file contents are
// the tag value. If the file is zero-length then the tag is a "sentinel"
// and thus is just of the form "foo.bar" rather than "foo.bar=baz".
TagsPrefix = ExtensionTargetPrefix + "machine-tags/"
TagsPrefix = ExtensionTargetPrefix + tagsName

// tagsName is [TagsPrefix] without the [ExtensionTargetPrefix].
tagsName = "machine-tags/"

// maxTagFileSize is the maximum tag target file size we are willing to
// read. hostnamed has a restriction of 255 characters but lets use
Expand Down Expand Up @@ -132,10 +135,11 @@ func (ext *TagsExtension) Init(ctx context.Context) (context.Context, error) {
// ApplyTarget collects the machine tag described by the target file, to be
// applied in BeforeUpdate.
func (ext *TagsExtension) ApplyTarget(ctx context.Context, info *tufclient.TargetInfo) (_ bool, Err error) {
if !strings.HasPrefix(info.Path, TagsPrefix) {
name, _, ok := CutExtensionTargetPrefix(info.Path)
if !ok || !strings.HasPrefix(name, tagsName) {
return false, nil // not for this extension
}
tagName := strings.TrimPrefix(info.Path, TagsPrefix)
tagName := strings.TrimPrefix(name, tagsName)

slog.Info("[xsysupdate tags target] Collecting machine tag file.",
"target", info.Path, "repository", info.Repo.Name, "tagName", tagName)
Expand Down
4 changes: 2 additions & 2 deletions internal/xsysupdate/tags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ func TestTagsApplyTarget_NotForUs(t *testing.T) {

for _, targetPath := range []string{
"regular/target.bin",
".zzz-quarry-special/sysupdate.d/foo.transfer",
".zzz-quarry-special/machine-tags", // no trailing slash: not a tag file
".quarry/sysupdate.d/foo.transfer",
".quarry/machine-tags", // no trailing slash: not a tag file
} {
info := &tufclient.TargetInfo{
TargetFiles: &tufmetadata.TargetFiles{Path: targetPath},
Expand Down
14 changes: 7 additions & 7 deletions internal/xsysupdate/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ const (
// extension to sysupdate. Any target file in a repository with this prefix
// will be installed as /etc/sysupdate.* (with the [Source] Path= adjusted to
// point to the local quarry-http-client server).
TransferFilePrefix = ExtensionTargetPrefix + "sysupdate."
TransferFilePrefix = ExtensionTargetPrefix + transferFileName

// transferFileName is [TransferFilePrefix] without the
// [ExtensionTargetPrefix].
transferFileName = "sysupdate."

liveLink = "live"
lastLink = "last"
Expand Down Expand Up @@ -227,14 +231,10 @@ func (ext *TransferFileExtension) patchTransferFile(wtr io.Writer, rdr io.Reader
// ApplyTarget saves the transfer file from the repository to the per-update
// transfer directory.
func (ext *TransferFileExtension) ApplyTarget(ctx context.Context, info *tufclient.TargetInfo) (_ bool, Err error) {
if !strings.HasPrefix(info.Path, TransferFilePrefix) {
transferPath, _, ok := CutExtensionTargetPrefix(info.Path)
if !ok || !strings.HasPrefix(transferPath, transferFileName) {
return false, nil // not for this extension
}
transferPath, ok := strings.CutPrefix(info.Path, ExtensionTargetPrefix)
if !ok {
// Should never happen.
return false, fmt.Errorf("extension transfer file %s does not have %s prefix", info.Path, ExtensionTargetPrefix)
}

slog.Info("[xsysupdate transfers target] Applying transfer file.",
"target", info.Path, "repository", info.Repo.Name)
Expand Down
4 changes: 2 additions & 2 deletions internal/xsysupdate/transfer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ Type=partition
MatchPattern=foo_@v
`) + "\n")

const targetPath = ".zzz-quarry-special/sysupdate.d/foo.transfer"
const targetPath = ".quarry/sysupdate.d/foo.transfer"
srv := testrepo.New(t)
target := srv.WriteTarget(t, targetPath, bytes.NewReader(body))

Expand Down Expand Up @@ -626,7 +626,7 @@ func TestApplyTarget_NestedPath(t *testing.T) {

body := []byte("dummy=value\n")

const targetPath = ".zzz-quarry-special/sysupdate.d/foo.transfer.d/x.conf"
const targetPath = ".quarry/sysupdate.d/foo.transfer.d/x.conf"
srv := testrepo.New(t)
target := srv.WriteTarget(t, targetPath, bytes.NewReader(body))

Expand Down
Loading