Major change: published repo now supports multiple components <> snapshots (local repos). #36

This commit is contained in:
Andrey Smirnov
2014-06-03 14:34:26 +04:00
parent 43ee735aa4
commit ee71b93669
2 changed files with 562 additions and 324 deletions
+463 -272
View File
@@ -17,6 +17,15 @@ import (
"time"
)
type repoSourceItem struct {
// Pointer to snapshot if SourceKind == "snapshot"
snapshot *Snapshot
// Pointer to local repo if SourceKind == "local"
localRepo *LocalRepo
// Package references is SourceKind == "local"
packageRefs *PackageRefList
}
// PublishedRepo is a published for http/ftp representation of snapshot as Debian repository
type PublishedRepo struct {
// Internal unique ID
@@ -24,55 +33,169 @@ type PublishedRepo struct {
// Prefix & distribution should be unique across all published repositories
Prefix string
Distribution string
Component string
Origin string
Label string
// Architectures is a list of all architectures published
Architectures []string
// SourceKind is "local"/"repo"
SourceKind string
// Map of sources by each component: component name -> source UUID
Sources map[string]string
// Legacy fields for compatibily with old published repositories (< 0.6)
Component string
// SourceUUID is UUID of either snapshot or local repo
SourceUUID string `codec:"SnapshotUUID"`
// Pointer to snapshot if SourceKind == "snapshot"
snapshot *Snapshot
// Pointer to local repo if SourceKind == "local"
localRepo *LocalRepo
// Package references is SourceKind == "local"
packageRefs *PackageRefList
// Map of component to source items
sourceItems map[string]repoSourceItem
// True if repo is being re-published
rePublishing bool
}
// walkUpTree goes from source in the tree of source snapshots/mirrors/local repos
// gathering information about declared components and distributions
func walkUpTree(source interface{}, collectionFactory *CollectionFactory) (rootDistributions []string, rootComponents []string) {
var (
head interface{}
current = []interface{}{source}
)
rootComponents = []string{}
rootDistributions = []string{}
// walk up the tree from current source up to roots (local or remote repos)
// and collect information about distribution and components
for len(current) > 0 {
head, current = current[0], current[1:]
if snapshot, ok := head.(*Snapshot); ok {
for _, uuid := range snapshot.SourceIDs {
if snapshot.SourceKind == "repo" {
remoteRepo, err := collectionFactory.RemoteRepoCollection().ByUUID(uuid)
if err != nil {
continue
}
current = append(current, remoteRepo)
} else if snapshot.SourceKind == "local" {
localRepo, err := collectionFactory.LocalRepoCollection().ByUUID(uuid)
if err != nil {
continue
}
current = append(current, localRepo)
} else if snapshot.SourceKind == "snapshot" {
snap, err := collectionFactory.SnapshotCollection().ByUUID(uuid)
if err != nil {
continue
}
current = append(current, snap)
}
}
} else if localRepo, ok := head.(*LocalRepo); ok {
if localRepo.DefaultDistribution != "" {
rootDistributions = append(rootDistributions, localRepo.DefaultDistribution)
}
if localRepo.DefaultComponent != "" {
rootComponents = append(rootComponents, localRepo.DefaultComponent)
}
} else if remoteRepo, ok := head.(*RemoteRepo); ok {
if remoteRepo.Distribution != "" {
rootDistributions = append(rootDistributions, remoteRepo.Distribution)
}
rootComponents = append(rootComponents, remoteRepo.Components...)
} else {
panic("unknown type")
}
}
return
}
// NewPublishedRepo creates new published repository
//
// prefix specifies publishing prefix
// distribution, component and architectures are user-defined properties
// source could either be *Snapshot or *LocalRepo
func NewPublishedRepo(prefix string, distribution string, component string, architectures []string, source interface{}, collectionFactory *CollectionFactory) (*PublishedRepo, error) {
var ok bool
// distribution and architectures are user-defined properties
// components & sources are lists of component to source mapping (*Snapshot or *LocalRepo)
func NewPublishedRepo(prefix string, distribution string, architectures []string,
components []string, sources []interface{}, collectionFactory *CollectionFactory) (*PublishedRepo, error) {
result := &PublishedRepo{
UUID: uuid.New(),
Architectures: architectures,
Sources: make(map[string]string),
sourceItems: make(map[string]repoSourceItem),
}
// figure out source
result.snapshot, ok = source.(*Snapshot)
if len(sources) == 0 {
panic("publish with empty sources")
}
if len(sources) != len(components) {
panic("sources and components should be equal in size")
}
var (
discoveredDistributions = []string{}
source interface{}
component string
snapshot *Snapshot
localRepo *LocalRepo
ok bool
)
// get first source
source = sources[0]
// figure out source kind
snapshot, ok = source.(*Snapshot)
if ok {
result.SourceKind = "snapshot"
result.SourceUUID = result.snapshot.UUID
} else {
result.localRepo, ok = source.(*LocalRepo)
localRepo, ok = source.(*LocalRepo)
if ok {
result.SourceKind = "local"
result.SourceUUID = result.localRepo.UUID
result.packageRefs = result.localRepo.RefList()
} else {
panic("unknown source kind")
}
}
for i := range sources {
component, source = components[i], sources[i]
if distribution == "" || component == "" {
rootDistributions, rootComponents := walkUpTree(source, collectionFactory)
if distribution == "" {
discoveredDistributions = append(discoveredDistributions, rootDistributions...)
}
if component == "" {
sort.Strings(rootComponents)
if len(rootComponents) > 0 && rootComponents[0] == rootComponents[len(rootComponents)-1] {
component = rootComponents[0]
} else if len(sources) == 1 {
// only if going from one source, assume default component "main"
component = "main"
} else {
return nil, fmt.Errorf("unable to figure out component name for %s", source)
}
}
}
_, exists := result.Sources[component]
if exists {
return nil, fmt.Errorf("duplicate component name: %s", component)
}
if result.SourceKind == "snapshot" {
snapshot = source.(*Snapshot)
result.Sources[component] = snapshot.UUID
result.sourceItems[component] = repoSourceItem{snapshot: snapshot}
} else if result.SourceKind == "local" {
localRepo = source.(*LocalRepo)
result.Sources[component] = localRepo.UUID
result.sourceItems[component] = repoSourceItem{localRepo: localRepo, packageRefs: localRepo.RefList()}
}
}
// clean & verify prefix
prefix = filepath.Clean(prefix)
if strings.HasPrefix(prefix, "/") {
@@ -91,106 +214,58 @@ func NewPublishedRepo(prefix string, distribution string, component string, arch
result.Prefix = prefix
// guessing distribution & component
if component == "" || distribution == "" {
var (
head interface{}
current = []interface{}{source}
rootComponents = []string{}
rootDistributions = []string{}
)
// walk up the tree from current source up to roots (local or remote repos)
// and collect information about distribution and components
for len(current) > 0 {
head, current = current[0], current[1:]
if snapshot, ok := head.(*Snapshot); ok {
for _, uuid := range snapshot.SourceIDs {
if snapshot.SourceKind == "repo" {
remoteRepo, err := collectionFactory.RemoteRepoCollection().ByUUID(uuid)
if err != nil {
continue
}
current = append(current, remoteRepo)
} else if snapshot.SourceKind == "local" {
localRepo, err := collectionFactory.LocalRepoCollection().ByUUID(uuid)
if err != nil {
continue
}
current = append(current, localRepo)
} else if snapshot.SourceKind == "snapshot" {
snap, err := collectionFactory.SnapshotCollection().ByUUID(uuid)
if err != nil {
continue
}
current = append(current, snap)
}
}
} else if localRepo, ok := head.(*LocalRepo); ok {
if localRepo.DefaultDistribution != "" {
rootDistributions = append(rootDistributions, localRepo.DefaultDistribution)
}
if localRepo.DefaultComponent != "" {
rootComponents = append(rootComponents, localRepo.DefaultComponent)
}
} else if remoteRepo, ok := head.(*RemoteRepo); ok {
if remoteRepo.Distribution != "" {
rootDistributions = append(rootDistributions, remoteRepo.Distribution)
}
rootComponents = append(rootComponents, remoteRepo.Components...)
} else {
panic("unknown type")
}
}
if distribution == "" {
sort.Strings(rootDistributions)
if len(rootDistributions) > 0 && rootDistributions[0] == rootDistributions[len(rootDistributions)-1] {
distribution = rootDistributions[0]
} else {
return nil, fmt.Errorf("unable to guess distribution name, please specify explicitly")
}
}
if component == "" {
sort.Strings(rootComponents)
if len(rootComponents) > 0 && rootComponents[0] == rootComponents[len(rootComponents)-1] {
component = rootComponents[0]
} else {
component = "main"
}
// guessing distribution
if distribution == "" {
sort.Strings(discoveredDistributions)
if len(discoveredDistributions) > 0 && discoveredDistributions[0] == discoveredDistributions[len(discoveredDistributions)-1] {
distribution = discoveredDistributions[0]
} else {
return nil, fmt.Errorf("unable to guess distribution name, please specify explicitly")
}
}
result.Distribution, result.Component = distribution, component
result.Distribution = distribution
return result, nil
}
// String returns human-readable represenation of PublishedRepo
func (p *PublishedRepo) String() string {
var source string
var sources = []string{}
if p.snapshot != nil {
source = p.snapshot.String()
} else if p.localRepo != nil {
source = p.localRepo.String()
} else {
panic("no snapshot/localRepo")
for component, item := range p.sourceItems {
var source string
if item.snapshot != nil {
source = item.snapshot.String()
} else if item.localRepo != nil {
source = item.localRepo.String()
} else {
panic("no snapshot/localRepo")
}
sources = append(sources, fmt.Sprintf("{%s: %s}", component, source))
}
var extra string
if p.Origin != "" {
extra += fmt.Sprintf(", origin: %s", p.Origin)
extra += fmt.Sprintf("origin: %s", p.Origin)
}
if p.Label != "" {
extra += fmt.Sprintf(", label: %s", p.Label)
if extra != "" {
extra += ", "
}
extra += fmt.Sprintf("label: %s", p.Label)
}
return fmt.Sprintf("%s/%s (%s%s) [%s] publishes %s", p.Prefix, p.Distribution, p.Component, extra, strings.Join(p.Architectures, ", "), source)
if extra != "" {
extra = " (" + extra + ")"
}
return fmt.Sprintf("%s/%s%s [%s] publishes %s", p.Prefix, p.Distribution, extra, strings.Join(p.Architectures, ", "),
strings.Join(sources, ", "))
}
// Key returns unique key identifying PublishedRepo
@@ -199,37 +274,53 @@ func (p *PublishedRepo) Key() []byte {
}
// RefKey is a unique id for package reference list
func (p *PublishedRepo) RefKey() []byte {
return []byte("E" + p.UUID)
func (p *PublishedRepo) RefKey(component string) []byte {
return []byte("E" + p.UUID + component)
}
// RefList returns list of package refs in local repo
func (p *PublishedRepo) RefList() *PackageRefList {
func (p *PublishedRepo) RefList(component string) *PackageRefList {
item := p.sourceItems[component]
if p.SourceKind == "local" {
return p.packageRefs
return item.packageRefs
}
if p.SourceKind == "snapshot" {
return p.snapshot.RefList()
return item.snapshot.RefList()
}
panic("unknown source")
}
func (p *PublishedRepo) UpdateLocalRepo() {
// Components returns sorted list of published repo components
func (p *PublishedRepo) Components() []string {
result := make([]string, 0, len(p.Sources))
for component := range p.Sources {
result = append(result, component)
}
sort.Strings(result)
return result
}
// UpdateLocalRepo updates content from local repo in component
func (p *PublishedRepo) UpdateLocalRepo(component string) {
item := p.sourceItems[component]
if p.SourceKind != "local" {
panic("not local repo publish")
}
p.packageRefs = p.localRepo.RefList()
item.packageRefs = item.localRepo.RefList()
p.rePublishing = true
}
func (p *PublishedRepo) UpdateSnapshot(snapshot *Snapshot) {
// UpdateSnapshot switches snapshot for component
func (p *PublishedRepo) UpdateSnapshot(component string, snapshot *Snapshot) {
item := p.sourceItems[component]
if p.SourceKind != "snapshot" {
panic("not snapshot publish")
}
p.snapshot = snapshot
p.SourceUUID = snapshot.UUID
item.snapshot = snapshot
p.Sources[component] = snapshot.UUID
p.rePublishing = true
}
@@ -256,6 +347,13 @@ func (p *PublishedRepo) Decode(input []byte) error {
p.SourceKind = "snapshot"
}
// <0.6 aptly used single SourceUUID + Component instead of Sources
if p.Component != "" && p.SourceUUID != "" && len(p.Sources) == 0 {
p.Sources = map[string]string{p.Component: p.SourceUUID}
p.Component = ""
p.SourceUUID = ""
}
return nil
}
@@ -276,15 +374,21 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
progress.Printf("Loading packages...\n")
}
// Load all packages
list, err := NewPackageListFromRefList(p.RefList(), collectionFactory.PackageCollection(), progress)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
lists := map[string]*PackageList{}
for component := range p.sourceItems {
// Load all packages
lists[component], err = NewPackageListFromRefList(p.RefList(component), collectionFactory.PackageCollection(), progress)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
}
if !p.rePublishing {
if len(p.Architectures) == 0 {
p.Architectures = list.Architectures(true)
for _, list := range lists {
p.Architectures = append(p.Architectures, list.Architectures(true)...)
}
}
if len(p.Architectures) == 0 {
@@ -306,105 +410,107 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
progress.Printf("Generating metadata files and linking package files...\n")
}
// For all architectures, generate release file
for _, arch := range p.Architectures {
if progress != nil {
progress.InitBar(int64(list.Len()), false)
}
var relativePath string
if arch == "source" {
relativePath = filepath.Join(p.Component, "source", "Sources")
} else {
relativePath = filepath.Join(p.Component, fmt.Sprintf("binary-%s", arch), "Packages")
}
err = publishedStorage.MkDir(filepath.Dir(filepath.Join(basePath, relativePath)))
if err != nil {
return err
}
var packagesFile *os.File
packagesFile, err = publishedStorage.CreateFile(filepath.Join(basePath, relativePath+suffix))
if err != nil {
return fmt.Errorf("unable to creates Packages file: %s", err)
}
if suffix != "" {
renameMap[filepath.Join(basePath, relativePath+suffix)] = filepath.Join(basePath, relativePath)
}
bufWriter := bufio.NewWriter(packagesFile)
err = list.ForEach(func(pkg *Package) error {
for component, list := range lists {
// For all architectures, generate packages/sources files
for _, arch := range p.Architectures {
if progress != nil {
progress.AddBar(1)
}
if pkg.MatchesArchitecture(arch) {
err = pkg.LinkFromPool(publishedStorage, packagePool, p.Prefix, p.Component)
if err != nil {
return err
}
err = pkg.Stanza().WriteTo(bufWriter)
if err != nil {
return err
}
err = bufWriter.WriteByte('\n')
if err != nil {
return err
}
pkg.files = nil
pkg.deps = nil
pkg.extra = nil
progress.InitBar(int64(list.Len()), false)
}
return nil
})
var relativePath string
if arch == "source" {
relativePath = filepath.Join(component, "source", "Sources")
} else {
relativePath = filepath.Join(component, fmt.Sprintf("binary-%s", arch), "Packages")
}
err = publishedStorage.MkDir(filepath.Dir(filepath.Join(basePath, relativePath)))
if err != nil {
return err
}
if err != nil {
return fmt.Errorf("unable to process packages: %s", err)
}
var packagesFile *os.File
packagesFile, err = publishedStorage.CreateFile(filepath.Join(basePath, relativePath+suffix))
if err != nil {
return fmt.Errorf("unable to creates Packages file: %s", err)
}
err = bufWriter.Flush()
if err != nil {
return fmt.Errorf("unable to write Packages file: %s", err)
}
if suffix != "" {
renameMap[filepath.Join(basePath, relativePath+suffix)] = filepath.Join(basePath, relativePath)
}
err = utils.CompressFile(packagesFile)
if err != nil {
return fmt.Errorf("unable to compress Packages files: %s", err)
}
bufWriter := bufio.NewWriter(packagesFile)
if suffix != "" {
renameMap[filepath.Join(basePath, relativePath+suffix+".gz")] = filepath.Join(basePath, relativePath+".gz")
renameMap[filepath.Join(basePath, relativePath+suffix+".bz2")] = filepath.Join(basePath, relativePath+".bz2")
}
err = list.ForEach(func(pkg *Package) error {
if progress != nil {
progress.AddBar(1)
}
if pkg.MatchesArchitecture(arch) {
err = pkg.LinkFromPool(publishedStorage, packagePool, p.Prefix, component)
if err != nil {
return err
}
packagesFile.Close()
err = pkg.Stanza().WriteTo(bufWriter)
if err != nil {
return err
}
err = bufWriter.WriteByte('\n')
if err != nil {
return err
}
var checksumInfo utils.ChecksumInfo
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath] = checksumInfo
pkg.files = nil
pkg.deps = nil
pkg.extra = nil
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix+".gz"))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath+".gz"] = checksumInfo
}
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix+".bz2"))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath+".bz2"] = checksumInfo
return nil
})
if progress != nil {
progress.ShutdownBar()
if err != nil {
return fmt.Errorf("unable to process packages: %s", err)
}
err = bufWriter.Flush()
if err != nil {
return fmt.Errorf("unable to write Packages file: %s", err)
}
err = utils.CompressFile(packagesFile)
if err != nil {
return fmt.Errorf("unable to compress Packages files: %s", err)
}
if suffix != "" {
renameMap[filepath.Join(basePath, relativePath+suffix+".gz")] = filepath.Join(basePath, relativePath+".gz")
renameMap[filepath.Join(basePath, relativePath+suffix+".bz2")] = filepath.Join(basePath, relativePath+".bz2")
}
packagesFile.Close()
var checksumInfo utils.ChecksumInfo
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath] = checksumInfo
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix+".gz"))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath+".gz"] = checksumInfo
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix+".bz2"))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath+".bz2"] = checksumInfo
if progress != nil {
progress.ShutdownBar()
}
}
}
@@ -421,13 +527,14 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
}
release["Codename"] = p.Distribution
release["Date"] = time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST")
release["Components"] = p.Component
release["Architectures"] = strings.Join(utils.StrSlicesSubstract(p.Architectures, []string{"source"}), " ")
release["Description"] = " Generated by aptly\n"
release["MD5Sum"] = "\n"
release["SHA1"] = "\n"
release["SHA256"] = "\n"
release["Components"] = strings.Join(p.Components(), " ")
for path, info := range generatedFiles {
release["MD5Sum"] += fmt.Sprintf(" %s %8d %s\n", info.MD5, info.Size, path)
release["SHA1"] += fmt.Sprintf(" %s %8d %s\n", info.SHA1, info.Size, path)
@@ -494,7 +601,8 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
// RemoveFiles removes files that were created by Publish
//
// It can remove prefix fully, and part of pool (for specific component)
func (p *PublishedRepo) RemoveFiles(publishedStorage aptly.PublishedStorage, removePrefix, removePoolComponent bool, progress aptly.Progress) error {
func (p *PublishedRepo) RemoveFiles(publishedStorage aptly.PublishedStorage, removePrefix bool,
removePoolComponents []string, progress aptly.Progress) error {
// I. Easy: remove whole prefix (meta+packages)
if removePrefix {
err := publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "dists"), progress)
@@ -512,15 +620,13 @@ func (p *PublishedRepo) RemoveFiles(publishedStorage aptly.PublishedStorage, rem
}
// III. Complex: there are no other publishes with the same prefix + component
if removePoolComponent {
err = publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "pool", p.Component), progress)
for _, component := range removePoolComponents {
err = publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "pool", component), progress)
if err != nil {
return err
}
} else {
/// IV: Hard: should have removed published files from the pool + component
/// that are unique to this published repo
}
return nil
}
@@ -585,42 +691,74 @@ func (collection *PublishedRepoCollection) Update(repo *PublishedRepo) (err erro
}
if repo.SourceKind == "local" {
err = collection.db.Put(repo.RefKey(), repo.packageRefs.Encode())
for component, item := range repo.sourceItems {
err = collection.db.Put(repo.RefKey(component), item.packageRefs.Encode())
if err != nil {
return
}
}
}
return
}
// LoadComplete loads additional information for remote repo
func (collection *PublishedRepoCollection) LoadComplete(repo *PublishedRepo, collectionFactory *CollectionFactory) (err error) {
repo.sourceItems = make(map[string]repoSourceItem)
if repo.SourceKind == "snapshot" {
repo.snapshot, err = collectionFactory.SnapshotCollection().ByUUID(repo.SourceUUID)
if err != nil {
return
for component, sourceUUID := range repo.Sources {
item := repoSourceItem{}
item.snapshot, err = collectionFactory.SnapshotCollection().ByUUID(sourceUUID)
if err != nil {
return
}
err = collectionFactory.SnapshotCollection().LoadComplete(item.snapshot)
if err != nil {
return
}
repo.sourceItems[component] = item
}
err = collectionFactory.SnapshotCollection().LoadComplete(repo.snapshot)
} else if repo.SourceKind == "local" {
repo.localRepo, err = collectionFactory.LocalRepoCollection().ByUUID(repo.SourceUUID)
if err != nil {
return
}
err = collectionFactory.LocalRepoCollection().LoadComplete(repo.localRepo)
if err != nil {
return
}
for component, sourceUUID := range repo.Sources {
item := repoSourceItem{}
var encoded []byte
encoded, err = collection.db.Get(repo.RefKey())
if err != nil {
return err
}
item.localRepo, err = collectionFactory.LocalRepoCollection().ByUUID(sourceUUID)
if err != nil {
return
}
err = collectionFactory.LocalRepoCollection().LoadComplete(item.localRepo)
if err != nil {
return
}
repo.packageRefs = &PackageRefList{}
err = repo.packageRefs.Decode(encoded)
var encoded []byte
encoded, err = collection.db.Get(repo.RefKey(component))
if err != nil {
// < 0.6 saving w/o component name
if err == database.ErrNotFound && len(repo.Sources) == 1 {
encoded, err = collection.db.Get(repo.RefKey(""))
}
if err != nil {
return
}
}
item.packageRefs = &PackageRefList{}
err = item.packageRefs.Decode(encoded)
if err != nil {
return
}
repo.sourceItems[component] = item
}
} else {
panic("unknown SourceKind")
}
return err
return
}
// ByPrefixDistribution looks up repository by prefix & distribution
@@ -647,8 +785,17 @@ func (collection *PublishedRepoCollection) ByUUID(uuid string) (*PublishedRepo,
func (collection *PublishedRepoCollection) BySnapshot(snapshot *Snapshot) []*PublishedRepo {
result := make([]*PublishedRepo, 0)
for _, r := range collection.list {
if r.SourceKind == "snapshot" && r.SourceUUID == snapshot.UUID {
result = append(result, r)
if r.SourceKind == "snapshot" {
if r.SourceUUID == snapshot.UUID {
result = append(result, r)
}
for _, sourceUUID := range r.Sources {
if sourceUUID == snapshot.UUID {
result = append(result, r)
break
}
}
}
}
return result
@@ -658,8 +805,17 @@ func (collection *PublishedRepoCollection) BySnapshot(snapshot *Snapshot) []*Pub
func (collection *PublishedRepoCollection) ByLocalRepo(repo *LocalRepo) []*PublishedRepo {
result := make([]*PublishedRepo, 0)
for _, r := range collection.list {
if r.SourceKind == "local" && r.SourceUUID == repo.UUID {
result = append(result, r)
if r.SourceKind == "local" {
if r.SourceUUID == repo.UUID {
result = append(result, r)
}
for _, sourceUUID := range r.Sources {
if sourceUUID == repo.UUID {
result = append(result, r)
break
}
}
}
}
return result
@@ -683,60 +839,81 @@ func (collection *PublishedRepoCollection) Len() int {
}
// CleanupPrefixComponentFiles removes all unreferenced files in published storage under prefix/component pair
func (collection *PublishedRepoCollection) CleanupPrefixComponentFiles(prefix, component string,
func (collection *PublishedRepoCollection) CleanupPrefixComponentFiles(prefix string, components []string,
publishedStorage aptly.PublishedStorage, collectionFactory *CollectionFactory, progress aptly.Progress) error {
var err error
referencedFiles := []string{}
referencedFiles := map[string][]string{}
if progress != nil {
progress.Printf("Cleaning up prefix %#v component %#v...\n", prefix, component)
progress.Printf("Cleaning up prefix %#v components %#v...\n", prefix, components)
}
for _, r := range collection.list {
if r.Prefix == prefix && r.Component == component {
if r.Prefix == prefix {
matches := false
repoComponents := r.Components()
for _, component := range components {
if utils.StrSliceHasItem(repoComponents, component) {
matches = true
break
}
}
if !matches {
continue
}
err = collection.LoadComplete(r, collectionFactory)
if err != nil {
return err
}
packageList, err := NewPackageListFromRefList(r.RefList(), collectionFactory.PackageCollection(), progress)
if err != nil {
return err
for _, component := range components {
if utils.StrSliceHasItem(repoComponents, component) {
packageList, err := NewPackageListFromRefList(r.RefList(component), collectionFactory.PackageCollection(), progress)
if err != nil {
return err
}
packageList.ForEach(func(p *Package) error {
poolDir, err := p.PoolDirectory()
if err != nil {
return err
}
for _, f := range p.Files() {
referencedFiles[component] = append(referencedFiles[component], filepath.Join(poolDir, f.Filename))
}
return nil
})
}
}
packageList.ForEach(func(p *Package) error {
poolDir, err := p.PoolDirectory()
if err != nil {
return err
}
for _, f := range p.Files() {
referencedFiles = append(referencedFiles, filepath.Join(poolDir, f.Filename))
}
return nil
})
}
}
sort.Strings(referencedFiles)
for _, component := range components {
sort.Strings(referencedFiles[component])
rootPath := filepath.Join(prefix, "pool", component)
existingFiles, err := publishedStorage.Filelist(rootPath)
if err != nil {
return err
}
sort.Strings(existingFiles)
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles)
for _, file := range filesToDelete {
err = publishedStorage.Remove(filepath.Join(rootPath, file))
rootPath := filepath.Join(prefix, "pool", component)
existingFiles, err := publishedStorage.Filelist(rootPath)
if err != nil {
return err
}
sort.Strings(existingFiles)
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles[component])
for _, file := range filesToDelete {
err = publishedStorage.Remove(filepath.Join(rootPath, file))
if err != nil {
return err
}
}
}
return nil
@@ -751,7 +928,8 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
}
removePrefix := true
removePoolComponent := true
removePoolComponents := repo.Components()
cleanComponents := []string{}
repoPosition := -1
for i, r := range collection.list {
@@ -761,13 +939,18 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
}
if r.Prefix == repo.Prefix {
removePrefix = false
if r.Component == repo.Component {
removePoolComponent = false
rComponents := r.Components()
for _, component := range rComponents {
if utils.StrSliceHasItem(removePoolComponents, component) {
removePoolComponents = utils.StrSlicesSubstract(removePoolComponents, []string{component})
cleanComponents = append(cleanComponents, component)
}
}
}
}
err = repo.RemoveFiles(publishedStorage, removePrefix, removePoolComponent, progress)
err = repo.RemoveFiles(publishedStorage, removePrefix, removePoolComponents, progress)
if err != nil {
return err
}
@@ -775,8 +958,8 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
collection.list[len(collection.list)-1], collection.list[repoPosition], collection.list =
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
if !removePrefix && !removePoolComponent {
err = collection.CleanupPrefixComponentFiles(repo.Prefix, repo.Component, publishedStorage, collectionFactory, progress)
if len(cleanComponents) > 0 {
err = collection.CleanupPrefixComponentFiles(repo.Prefix, cleanComponents, publishedStorage, collectionFactory, progress)
if err != nil {
return err
}
@@ -786,5 +969,13 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
if err != nil {
return err
}
return collection.db.Delete(repo.RefKey())
for _, component := range repo.Components() {
err = collection.db.Delete(repo.RefKey(component))
if err != nil {
return err
}
}
return nil
}