Skip to content

Commit

Permalink
Finalizers and OwnerRefs for PackageRevision (#3655)
Browse files Browse the repository at this point in the history
  • Loading branch information
mortent authored Nov 15, 2022
1 parent 946fa81 commit e0bcd34
Show file tree
Hide file tree
Showing 10 changed files with 479 additions and 143 deletions.
55 changes: 44 additions & 11 deletions porch/pkg/cache/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,30 @@ func (r *cachedRepository) DeletePackage(ctx context.Context, old repository.Pac

func (r *cachedRepository) Close() error {
r.cancel()

// Make sure that watch events are sent for packagerevisions that are
// removed as part of closing the repository.
for _, pr := range r.cachedPackageRevisions {
nn := types.NamespacedName{
Name: pr.KubeObjectName(),
Namespace: pr.KubeObjectNamespace(),
}
// There isn't really any correct way to handle finalizers here. We are removing
// the repository, so we have to just delete the PackageRevision regardless of any
// finalizers.
pkgRevMeta, err := r.metadataStore.Delete(context.TODO(), nn, true)
if err != nil {
// There isn't much use in returning an error here, so we just log it
// and create a PackageRevisionMeta with just name and namespace. This
// makes sure that the Delete event is sent.
klog.Warningf("Error looking up PackageRev CR for %s: %v")
pkgRevMeta = meta.PackageRevisionMeta{
Name: nn.Name,
Namespace: nn.Namespace,
}
}
r.objectNotifier.NotifyPackageRevisionChange(watch.Deleted, pr, pkgRevMeta)
}
return nil
}

Expand Down Expand Up @@ -370,18 +394,19 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re
}

newPackageRevisionMap := make(map[repository.PackageRevisionKey]*cachedPackageRevision, len(newPackageRevisions))
newPackageRevisionNames := make(map[string]bool)
newPackageRevisionNames := make(map[string]*cachedPackageRevision, len(newPackageRevisions))
for _, newPackage := range newPackageRevisions {
k := newPackage.Key()
if newPackageRevisionMap[k] != nil {
klog.Warningf("found duplicate packages with key %v", k)
}

newPackageRevisionMap[k] = &cachedPackageRevision{
pkgRev := &cachedPackageRevision{
PackageRevision: newPackage,
isLatestRevision: false,
}
newPackageRevisionNames[newPackage.KubeObjectName()] = true
newPackageRevisionMap[k] = pkgRev
newPackageRevisionNames[newPackage.KubeObjectName()] = pkgRev
}

identifyLatestRevisions(newPackageRevisionMap)
Expand Down Expand Up @@ -410,10 +435,10 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re
if _, err := r.metadataStore.Delete(ctx, types.NamespacedName{
Name: prm.Name,
Namespace: prm.Namespace,
}); err != nil {
}, true); err != nil {
if !apierrors.IsNotFound(err) {
// This will be retried the next time the sync runs.
klog.Warningf("unable to create PackageRev CR for %s/%s: %w",
klog.Warningf("unable to delete PackageRev CR for %s/%s: %w",
prm.Name, prm.Namespace, err)
}
}
Expand All @@ -422,13 +447,13 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re

// We go through all the PackageRevisions and make sure they have
// a corresponding PackageRev CR.
for pkgRevName := range newPackageRevisionNames {
for pkgRevName, pkgRev := range newPackageRevisionNames {
if _, found := existingPkgRevCRsMap[pkgRevName]; !found {
pkgRevMeta := meta.PackageRevisionMeta{
Name: pkgRevName,
Namespace: r.repoSpec.Namespace,
}
if _, err := r.metadataStore.Create(ctx, pkgRevMeta, r.repoSpec); err != nil {
if _, err := r.metadataStore.Create(ctx, pkgRevMeta, r.repoSpec.Name, pkgRev.UID()); err != nil {
// TODO: We should try to find a way to make these errors available through
// either the repository CR or the PackageRevision CR. This will be
// retried on the next sync.
Expand All @@ -455,11 +480,19 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re
}

for k, oldPackage := range oldPackageRevisions {
metaPackage, found := existingPkgRevCRsMap[oldPackage.KubeObjectName()]
if !found {
klog.Warningf("no PackageRev CR found for PackageRevision %s", oldPackage.KubeObjectName())
}
if newPackageRevisionMap[k] == nil {
nn := types.NamespacedName{
Name: oldPackage.KubeObjectName(),
Namespace: oldPackage.KubeObjectNamespace(),
}
metaPackage, err := r.metadataStore.Delete(ctx, nn, true)
if err != nil {
klog.Warningf("Error deleting PkgRevMeta %s: %v")
metaPackage = meta.PackageRevisionMeta{
Name: nn.Name,
Namespace: nn.Namespace,
}
}
r.objectNotifier.NotifyPackageRevisionChange(watch.Deleted, oldPackage, metaPackage)
}
}
Expand Down
137 changes: 92 additions & 45 deletions porch/pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ func (p *PackageRevision) GetPackageRevision(ctx context.Context) (*api.PackageR
repoPkgRev.Labels[api.LatestPackageRevisionKey] = api.LatestPackageRevisionValue
}
repoPkgRev.Annotations = p.packageRevisionMeta.Annotations
repoPkgRev.Finalizers = p.packageRevisionMeta.Finalizers
repoPkgRev.OwnerReferences = p.packageRevisionMeta.OwnerReferences
repoPkgRev.DeletionTimestamp = p.packageRevisionMeta.DeletionTimestamp
return repoPkgRev, nil
}

Expand Down Expand Up @@ -330,12 +333,14 @@ func (cad *cadEngine) CreatePackageRevision(ctx context.Context, repositoryObj *
return nil, err
}
pkgRevMeta := meta.PackageRevisionMeta{
Name: repoPkgRev.KubeObjectName(),
Namespace: repoPkgRev.KubeObjectNamespace(),
Labels: obj.Labels,
Annotations: obj.Annotations,
}
pkgRevMeta, err = cad.metadataStore.Create(ctx, pkgRevMeta, repositoryObj)
Name: repoPkgRev.KubeObjectName(),
Namespace: repoPkgRev.KubeObjectNamespace(),
Labels: obj.Labels,
Annotations: obj.Annotations,
Finalizers: obj.Finalizers,
OwnerReferences: obj.OwnerReferences,
}
pkgRevMeta, err = cad.metadataStore.Create(ctx, pkgRevMeta, repositoryObj.Name, repoPkgRev.UID())
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -534,6 +539,26 @@ func (cad *cadEngine) UpdatePackageRevision(ctx context.Context, repositoryObj *
return nil, err
}

// Check if the PackageRevision is in the terminating state and
// and this request removes the last finalizer.
repoPkgRev := oldPackage.repoPackageRevision
pkgRevMetaNN := types.NamespacedName{
Name: repoPkgRev.KubeObjectName(),
Namespace: repoPkgRev.KubeObjectNamespace(),
}
pkgRevMeta, err := cad.metadataStore.Get(ctx, pkgRevMetaNN)
if err != nil {
return nil, err
}
// If this is in the terminating state and we are removing the last finalizer,
// we delete the resource instead of updating it.
if pkgRevMeta.DeletionTimestamp != nil && len(newObj.Finalizers) == 0 {
if err := cad.deletePackageRevision(ctx, repo, repoPkgRev, pkgRevMeta); err != nil {
return nil, err
}
return ToPackageRevision(repoPkgRev, pkgRevMeta), nil
}

// Validate package lifecycle. Can only update a draft.
switch lifecycle := oldObj.Spec.Lifecycle; lifecycle {
default:
Expand All @@ -542,21 +567,13 @@ func (cad *cadEngine) UpdatePackageRevision(ctx context.Context, repositoryObj *
// Draft or proposed can be updated.
case api.PackageRevisionLifecyclePublished:
// Only metadata (currently labels and annotations) can be updated for published packages.
repoPkgRev := oldPackage.repoPackageRevision

pkgRevMeta := meta.PackageRevisionMeta{
Name: repoPkgRev.KubeObjectName(),
Namespace: repoPkgRev.KubeObjectNamespace(),
Labels: newObj.Labels,
Annotations: newObj.Annotations,
pkgRevMeta, err = cad.updatePkgRevMeta(ctx, repoPkgRev, newObj)
if err != nil {
return nil, err
}
cad.metadataStore.Update(ctx, pkgRevMeta)

cad.watcherManager.NotifyPackageRevisionChange(watch.Modified, repoPkgRev, pkgRevMeta)
return &PackageRevision{
repoPackageRevision: repoPkgRev,
packageRevisionMeta: pkgRevMeta,
}, nil
return ToPackageRevision(repoPkgRev, pkgRevMeta), nil
}
switch lifecycle := newObj.Spec.Lifecycle; lifecycle {
default:
Expand All @@ -575,19 +592,13 @@ func (cad *cadEngine) UpdatePackageRevision(ctx context.Context, repositoryObj *
return nil, err
}

pkgRevMeta := meta.PackageRevisionMeta{
Name: repoPkgRev.KubeObjectName(),
Namespace: repoPkgRev.KubeObjectNamespace(),
Labels: newObj.Labels,
Annotations: newObj.Annotations,
pkgRevMeta, err = cad.updatePkgRevMeta(ctx, repoPkgRev, newObj)
if err != nil {
return nil, err
}
cad.metadataStore.Update(ctx, pkgRevMeta)

cad.watcherManager.NotifyPackageRevisionChange(watch.Modified, repoPkgRev, pkgRevMeta)
return &PackageRevision{
repoPackageRevision: repoPkgRev,
packageRevisionMeta: pkgRevMeta,
}, nil
return ToPackageRevision(repoPkgRev, pkgRevMeta), nil
}

var mutations []mutation
Expand Down Expand Up @@ -676,24 +687,30 @@ func (cad *cadEngine) UpdatePackageRevision(ctx context.Context, repositoryObj *
}

// Updates are done.
repoPkgRev, err := draft.Close(ctx)
repoPkgRev, err = draft.Close(ctx)
if err != nil {
return nil, err
}

pkgRevMeta := meta.PackageRevisionMeta{
Name: repoPkgRev.KubeObjectName(),
Namespace: repoPkgRev.KubeObjectNamespace(),
Labels: newObj.Labels,
Annotations: newObj.Annotations,
pkgRevMeta, err = cad.updatePkgRevMeta(ctx, repoPkgRev, newObj)
if err != nil {
return nil, err
}
cad.metadataStore.Update(ctx, pkgRevMeta)

cad.watcherManager.NotifyPackageRevisionChange(watch.Modified, repoPkgRev, pkgRevMeta)
return &PackageRevision{
repoPackageRevision: repoPkgRev,
packageRevisionMeta: pkgRevMeta,
}, nil
return ToPackageRevision(repoPkgRev, pkgRevMeta), nil
}

func (cad *cadEngine) updatePkgRevMeta(ctx context.Context, repoPkgRev repository.PackageRevision, apiPkgRev *api.PackageRevision) (meta.PackageRevisionMeta, error) {
pkgRevMeta := meta.PackageRevisionMeta{
Name: repoPkgRev.KubeObjectName(),
Namespace: repoPkgRev.KubeObjectNamespace(),
Labels: apiPkgRev.Labels,
Annotations: apiPkgRev.Annotations,
Finalizers: apiPkgRev.Finalizers,
OwnerReferences: apiPkgRev.OwnerReferences,
}
return cad.metadataStore.Update(ctx, pkgRevMeta)
}

func createKptfilePatchTask(ctx context.Context, oldPackage repository.PackageRevision, newObj *api.PackageRevision) (*api.Task, bool, error) {
Expand Down Expand Up @@ -814,19 +831,49 @@ func (cad *cadEngine) DeletePackageRevision(ctx context.Context, repositoryObj *
return err
}

if err := repo.DeletePackageRevision(ctx, oldPackage.repoPackageRevision); err != nil {
return err
}

// We delete the PackageRev regardless of any finalizers, since it
// will always have the same finalizers as the PackageRevision. This
// will put the PackageRev, and therefore the PackageRevision in the
// terminating state.
// But we only delete the PackageRevision from the repo once all finalizers
// have been removed.
namespacedName := types.NamespacedName{
Name: oldPackage.repoPackageRevision.KubeObjectName(),
Namespace: oldPackage.repoPackageRevision.KubeObjectNamespace(),
}
if _, err := cad.metadataStore.Delete(ctx, namespacedName); err != nil {
pkgRevMeta, err := cad.metadataStore.Delete(ctx, namespacedName, false)
if err != nil {
return err
}

cad.watcherManager.NotifyPackageRevisionChange(watch.Deleted, oldPackage.repoPackageRevision, oldPackage.packageRevisionMeta)
if len(pkgRevMeta.Finalizers) > 0 {
klog.Infof("PackageRevision %s deleted, but still have finalizers: %s", oldPackage.KubeObjectName(), strings.Join(pkgRevMeta.Finalizers, ","))
cad.watcherManager.NotifyPackageRevisionChange(watch.Modified, oldPackage.repoPackageRevision, oldPackage.packageRevisionMeta)
return nil
}
klog.Infof("PackageRevision %s deleted for real since no finalizers", oldPackage.KubeObjectName())

return cad.deletePackageRevision(ctx, repo, oldPackage.repoPackageRevision, oldPackage.packageRevisionMeta)
}

func (cad *cadEngine) deletePackageRevision(ctx context.Context, repo repository.Repository, repoPkgRev repository.PackageRevision, pkgRevMeta meta.PackageRevisionMeta) error {
ctx, span := tracer.Start(ctx, "cadEngine::deletePackageRevision", trace.WithAttributes())
defer span.End()

if err := repo.DeletePackageRevision(ctx, repoPkgRev); err != nil {
return err
}

nn := types.NamespacedName{
Name: pkgRevMeta.Name,
Namespace: pkgRevMeta.Namespace,
}
if _, err := cad.metadataStore.Delete(ctx, nn, true); err != nil {
// If this fails, the CR will be cleaned up by the background job.
klog.Warningf("Error deleting PkgRevMeta %s: %v", nn.String(), err)
}

cad.watcherManager.NotifyPackageRevisionChange(watch.Deleted, repoPkgRev, pkgRevMeta)
return nil
}

Expand Down
6 changes: 6 additions & 0 deletions porch/pkg/engine/fake/packagerevision.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import (
kptfile "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
"github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1"
"github.com/GoogleContainerTools/kpt/porch/pkg/repository"
"k8s.io/apimachinery/pkg/types"
)

// Implementation of the repository.PackageRevision interface for testing.
type PackageRevision struct {
Name string
Namespace string
Uid types.UID
PackageRevisionKey repository.PackageRevisionKey
PackageLifecycle v1alpha1.PackageRevisionLifecycle
PackageRevision *v1alpha1.PackageRevision
Expand All @@ -41,6 +43,10 @@ func (pr *PackageRevision) KubeObjectNamespace() string {
return pr.Namespace
}

func (pr *PackageRevision) UID() types.UID {
return pr.Uid
}

func (pr *PackageRevision) Key() repository.PackageRevisionKey {
return pr.PackageRevisionKey
}
Expand Down
4 changes: 4 additions & 0 deletions porch/pkg/git/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ func (p *gitPackageRevision) KubeObjectNamespace() string {
return p.repo.namespace
}

func (p *gitPackageRevision) UID() types.UID {
return p.uid()
}

func (p *gitPackageRevision) Key() repository.PackageRevisionKey {
return repository.PackageRevisionKey{
Repository: p.repo.name,
Expand Down
4 changes: 2 additions & 2 deletions porch/pkg/meta/fake/memorystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (m *MemoryMetadataStore) List(ctx context.Context, repo *configapi.Reposito
return m.Metas, nil
}

func (m *MemoryMetadataStore) Create(ctx context.Context, pkgRevMeta meta.PackageRevisionMeta, repo *configapi.Repository) (meta.PackageRevisionMeta, error) {
func (m *MemoryMetadataStore) Create(ctx context.Context, pkgRevMeta meta.PackageRevisionMeta, repoName string, pkgRevUID types.UID) (meta.PackageRevisionMeta, error) {
for _, m := range m.Metas {
if m.Name == pkgRevMeta.Name && m.Namespace == pkgRevMeta.Namespace {
return m, apierrors.NewAlreadyExists(
Expand Down Expand Up @@ -78,7 +78,7 @@ func (m *MemoryMetadataStore) Update(ctx context.Context, pkgRevMeta meta.Packag
return pkgRevMeta, nil
}

func (m *MemoryMetadataStore) Delete(ctx context.Context, namespacedName types.NamespacedName) (meta.PackageRevisionMeta, error) {
func (m *MemoryMetadataStore) Delete(ctx context.Context, namespacedName types.NamespacedName, _ bool) (meta.PackageRevisionMeta, error) {
var metas []meta.PackageRevisionMeta
found := false
var deletedMeta meta.PackageRevisionMeta
Expand Down
Loading

0 comments on commit e0bcd34

Please sign in to comment.