mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-11-13 14:37:18 +00:00
Acquire-By-Hash entries are written for MD5Sum, SHA1, SHA256 and SHA512,
hardcoded in two places in deb/index_files.go. Every algorithm is a full
set of index copies on each publish, and on S3-like storage each of those
is a network round trip rather than a link.
Measured on an R2-backed archive of 25 packages across three suites, one
`aptly publish update` per suite:
Acquire-By-Hash off 38 S3 requests
Acquire-By-Hash on 222 S3 requests (88 HeadObject, 83 CopyObject,
35 DeleteObject, 13 PutObject)
Modern apt verifies with SHA256; MD5Sum and SHA1 are deprecated and
Debian has been phasing them out of Release files for years. Publishers
who do not need them are paying three quarters of that for nothing, with
no way to say so.
acquireByHashAlgorithms in aptly.conf now selects the set. An empty or
absent value keeps all four, so existing configurations are unaffected.
The order entries are written in comes from the code rather than the
config, so the directories created in one loop and the entries written in
another cannot disagree. An unrecognised name is an error rather than
something to skip: skipping a typo would produce "no by-hash entries for
that algorithm", which looks exactly like having configured it away.
Set per publish invocation from the config, the way SkipBz2 already is,
rather than persisted on the publish point.
Pre-existing test failures on this tree are unchanged by this commit:
deb goes 173->177 passed with the same 11 failures, utils keeps its one
(TestCompress), s3 stays green.
183 lines
6.5 KiB
Go
183 lines
6.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/aptly-dev/aptly/deb"
|
|
"github.com/aptly-dev/aptly/utils"
|
|
"github.com/smira/commander"
|
|
"github.com/smira/flag"
|
|
)
|
|
|
|
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|
var (
|
|
err error
|
|
names []string
|
|
)
|
|
|
|
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
|
|
|
|
if len(args) < len(components)+1 || len(args) > len(components)+2 {
|
|
cmd.Usage()
|
|
return commander.ErrCommandError
|
|
}
|
|
|
|
distribution := args[0]
|
|
param := "."
|
|
|
|
if len(args) == len(components)+2 {
|
|
param = args[1]
|
|
names = args[2:]
|
|
} else {
|
|
names = args[1:]
|
|
}
|
|
|
|
storage, prefix := deb.ParsePrefix(param)
|
|
|
|
var published *deb.PublishedRepo
|
|
|
|
collectionFactory := context.NewCollectionFactory()
|
|
published, err = collectionFactory.PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to switch: %s", err)
|
|
}
|
|
|
|
if published.SourceKind != deb.SourceSnapshot {
|
|
return fmt.Errorf("unable to switch: not a published snapshot repository")
|
|
}
|
|
|
|
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to switch: %s", err)
|
|
}
|
|
|
|
publishedComponents := published.Components()
|
|
if len(components) == 1 && len(publishedComponents) == 1 && components[0] == "" {
|
|
components = publishedComponents
|
|
}
|
|
|
|
if len(names) != len(components) {
|
|
return fmt.Errorf("mismatch in number of components (%d) and snapshots (%d)", len(components), len(names))
|
|
}
|
|
|
|
snapshotCollection := collectionFactory.SnapshotCollection()
|
|
for i, component := range components {
|
|
if !utils.StrSliceHasItem(publishedComponents, component) {
|
|
return fmt.Errorf("unable to switch: component %s does not exist in published repository", component)
|
|
}
|
|
|
|
snapshot, err := snapshotCollection.ByName(names[i])
|
|
if err != nil {
|
|
return fmt.Errorf("unable to switch: %s", err)
|
|
}
|
|
|
|
err = snapshotCollection.LoadComplete(snapshot)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to switch: %s", err)
|
|
}
|
|
|
|
published.UpdateSnapshot(component, snapshot)
|
|
}
|
|
|
|
signer, err := getSigner(context.Flags())
|
|
if err != nil {
|
|
return fmt.Errorf("unable to initialize GPG signer: %s", err)
|
|
}
|
|
|
|
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
|
|
if forceOverwrite {
|
|
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
|
|
"the same package pool.\n")
|
|
}
|
|
|
|
if context.Flags().IsSet("skip-contents") {
|
|
published.SkipContents = context.Flags().Lookup("skip-contents").Value.Get().(bool)
|
|
}
|
|
|
|
if context.Flags().IsSet("skip-bz2") {
|
|
published.SkipBz2 = context.Flags().Lookup("skip-bz2").Value.Get().(bool)
|
|
}
|
|
|
|
if context.Flags().IsSet("signed-by") {
|
|
published.SignedBy = context.Flags().Lookup("signed-by").Value.String()
|
|
}
|
|
|
|
if context.Flags().IsSet("version") {
|
|
published.Version = context.Flags().Lookup("version").Value.String()
|
|
}
|
|
|
|
if context.Flags().IsSet("multi-dist") {
|
|
published.MultiDist = context.Flags().Lookup("multi-dist").Value.Get().(bool)
|
|
}
|
|
|
|
// Which hash algorithms get Acquire-By-Hash entries. Each one is a full
|
|
// set of index copies per publish, which is felt most on S3-like storage.
|
|
published.AcquireByHashAlgorithms = context.Config().AcquireByHashAlgorithms
|
|
err = published.Publish(context.PackagePool(), context, collectionFactory, signer, context.Progress(), forceOverwrite, context.SkelPath())
|
|
if err != nil {
|
|
return fmt.Errorf("unable to publish: %s", err)
|
|
}
|
|
|
|
err = collectionFactory.PublishedRepoCollection().Update(published)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to save to DB: %s", err)
|
|
}
|
|
|
|
skipCleanup := context.Flags().Lookup("skip-cleanup").Value.Get().(bool)
|
|
if !skipCleanup {
|
|
err = collectionFactory.PublishedRepoCollection().CleanupPrefixComponentFiles(context, published, components, collectionFactory, context.Progress())
|
|
if err != nil {
|
|
return fmt.Errorf("unable to switch: %s", err)
|
|
}
|
|
}
|
|
|
|
context.Progress().Printf("\nPublished %s repository %s has been successfully switched to new source.\n", published.SourceKind, published.String())
|
|
|
|
return err
|
|
}
|
|
|
|
func makeCmdPublishSwitch() *commander.Command {
|
|
cmd := &commander.Command{
|
|
Run: aptlyPublishSwitch,
|
|
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-source>",
|
|
Short: "update published repository by switching to new source",
|
|
Long: `
|
|
Command switches in-place published snapshots with new source contents. All
|
|
publishing parameters are preserved (architecture list, distribution,
|
|
component).
|
|
|
|
For multiple component repositories, flag -component should be given with
|
|
list of components to update. Corresponding sources should be given in the
|
|
same order, e.g.:
|
|
|
|
aptly publish switch -component=main,contrib wheezy wh-main wh-contrib
|
|
|
|
Example:
|
|
|
|
$ aptly publish switch wheezy ppa wheezy-7.5
|
|
|
|
This command would switch published repository (with one component) named ppa/wheezy
|
|
(prefix ppa, dsitribution wheezy to new snapshot wheezy-7.5).
|
|
`,
|
|
Flag: *flag.NewFlagSet("aptly-publish-switch", flag.ExitOnError),
|
|
}
|
|
cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (flag is repeatable, can be specified multiple times)")
|
|
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
|
|
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
|
|
cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)")
|
|
cmd.Flag.String("passphrase-file", "", "GPG passphrase-file for the key (warning: could be insecure)")
|
|
cmd.Flag.Bool("batch", false, "run GPG with detached tty")
|
|
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
|
|
cmd.Flag.Bool("skip-contents", false, "don't generate Contents indexes")
|
|
cmd.Flag.Bool("skip-bz2", false, "don't generate bzipped indexes")
|
|
cmd.Flag.String("component", "", "component names to update (for multi-component publishing, separate components with commas)")
|
|
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
|
|
cmd.Flag.String("signed-by", "", "an optional field containing a comma separated list of OpenPGP key fingerprints to be used for validating the next Release file")
|
|
cmd.Flag.String("version", "", "version of the release")
|
|
cmd.Flag.Bool("skip-cleanup", false, "don't remove unreferenced files in prefix/component")
|
|
cmd.Flag.Bool("multi-dist", false, "enable multiple packages with the same filename in different distributions")
|
|
|
|
return cmd
|
|
}
|