Skip to content
Draft
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
4 changes: 4 additions & 0 deletions images/dvcr-artifact/cmd/dvcr-cleaner/cmd/gc.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ func annotateGarbageCollectionSecretOnCleanupDone(ctx context.Context, result ma
return err
}

if secret == nil {
return fmt.Errorf("garbage collection secret not found")
}

if secret.Annotations == nil {
secret.Annotations = make(map[string]string)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
"github.com/deckhouse/deckhouse/pkg/log"
"github.com/deckhouse/virtualization-controller/pkg/controller/cvi/internal"
"github.com/deckhouse/virtualization-controller/pkg/controller/cvi/internal/source"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponehandler"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponeimporter"
"github.com/deckhouse/virtualization-controller/pkg/controller/gc"
"github.com/deckhouse/virtualization-controller/pkg/controller/indexer"
"github.com/deckhouse/virtualization-controller/pkg/controller/service"
Expand Down Expand Up @@ -81,7 +81,7 @@ func NewController(

reconciler := NewReconciler(
mgr.GetClient(),
postponehandler.New[*v1alpha2.ClusterVirtualImage](dvcrService, recorder),
postponeimporter.NewHandler[*v1alpha2.ClusterVirtualImage](dvcrService, recorder),
internal.NewDatasourceReadyHandler(sources),
internal.NewLifeCycleHandler(sources, mgr.GetClient()),
internal.NewImagePresenceHandler(dvcr.NewImageChecker(mgr.GetClient(), dvcrSettings)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source"

"github.com/deckhouse/virtualization-controller/pkg/controller/cvi/internal/watcher"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponeimporter"
"github.com/deckhouse/virtualization-controller/pkg/controller/reconciler"
"github.com/deckhouse/virtualization-controller/pkg/controller/watchers"
"github.com/deckhouse/virtualization/api/core/v1alpha2"
Expand Down Expand Up @@ -102,6 +103,7 @@ func (r *Reconciler) SetupController(_ context.Context, mgr manager.Manager, ctr
watcher.NewVirtualMachineWatcher(),
watcher.NewVirtualDiskWatcher(mgrClient),
watcher.NewVirtualDiskSnapshotWatcher(mgrClient),
postponeimporter.NewWatcher[*v1alpha2.ClusterVirtualImage](mgrClient),
} {
err := w.Watch(mgr, ctr)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,25 @@ func (h LifeCycleHandler) Handle(ctx context.Context, req reconcile.Request, dep
}

if h.dvcrService.IsGarbageCollectionStarted(secret) {
hasCreationTimestamp := !secret.GetCreationTimestamp().Time.IsZero()
waitDuration := time.Since(secret.GetCreationTimestamp().Time)
if hasCreationTimestamp && waitDuration > dvcrtypes.WaitProvisionersTimeout {
dvcrcondition.UpdateGarbageCollectionCondition(deploy,
dvcrdeploymentcondition.Error,
"Wait for garbage collection more than %s timeout: %s elapsed, garbage collection canceled",
dvcrtypes.WaitProvisionersTimeout.String(),
waitDuration.String(),
)
annotations.AddAnnotation(deploy, annotations.AnnDVCRGarbageCollectionResult, "")
return reconcile.Result{}, h.dvcrService.DeleteGarbageCollectionSecret(ctx)
}

dvcrcondition.UpdateGarbageCollectionCondition(deploy,
dvcrdeploymentcondition.InProgress,
"Wait for garbage collection to finish.",
)
// Wait for done annotation appears on secret.
return reconcile.Result{}, nil
return reconcile.Result{RequeueAfter: time.Second * 20}, nil
}

// No special annotation, check for provisioners to finish.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"reflect"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/event"
Expand Down Expand Up @@ -50,16 +51,29 @@ func (w *DVCRGarbageCollectionSecretWatcher) Watch(mgr manager.Manager, ctr cont
mgr.GetCache(),
&corev1.Secret{},
handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, secret *corev1.Secret) []reconcile.Request {
if secret.GetNamespace() == dvcrtypes.ModuleNamespace && secret.GetName() == dvcrtypes.DVCRGarbageCollectionSecretName {
return []reconcile.Request{{NamespacedName: client.ObjectKeyFromObject(secret)}}
}
return nil
return []reconcile.Request{{NamespacedName: client.ObjectKeyFromObject(secret)}}
}),
predicate.TypedFuncs[*corev1.Secret]{
CreateFunc: func(e event.TypedCreateEvent[*corev1.Secret]) bool {
return IsDVCRGarbageCollectionSecret(e.Object)
},
UpdateFunc: func(e event.TypedUpdateEvent[*corev1.Secret]) bool {
if !IsDVCRGarbageCollectionSecret(e.ObjectNew) {
return false
}
return !reflect.DeepEqual(e.ObjectNew.GetAnnotations(), e.ObjectOld.GetAnnotations())
},
DeleteFunc: func(e event.TypedDeleteEvent[*corev1.Secret]) bool {
return IsDVCRGarbageCollectionSecret(e.Object)
},
},
),
)
}

func IsDVCRGarbageCollectionSecret(secret metav1.Object) bool {
if secret == nil {
return false
}
return secret.GetNamespace() == dvcrtypes.ModuleNamespace && secret.GetName() == dvcrtypes.DVCRGarbageCollectionSecretName
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package postponehandler
package postponeimporter

import (
"context"
Expand Down Expand Up @@ -42,13 +42,13 @@ type DVCRService interface {

var PostponePeriod = time.Second * 15

type Postpone[object client.Object] struct {
type PostponeHandler[object client.Object] struct {
dvcrService DVCRService
recorder eventrecord.EventRecorderLogger
}

func New[T client.Object](dvcrService DVCRService, recorder eventrecord.EventRecorderLogger) *Postpone[T] {
return &Postpone[T]{
func NewHandler[T client.Object](dvcrService DVCRService, recorder eventrecord.EventRecorderLogger) *PostponeHandler[T] {
return &PostponeHandler[T]{
dvcrService: dvcrService,
recorder: recorder,
}
Expand All @@ -57,7 +57,7 @@ func New[T client.Object](dvcrService DVCRService, recorder eventrecord.EventRec
// Handle sets Ready condition to Provisioning for newly created resources
// if dvcr is in the garbage collection mode.
// Applicable for ClusterVirtualImage, VirtualImage, and VirtualDisk.
func (p *Postpone[T]) Handle(ctx context.Context, obj T) (reconcile.Result, error) {
func (p *PostponeHandler[T]) Handle(ctx context.Context, obj T) (reconcile.Result, error) {
conditionsPtr := conditions.NewConditionsAccessor(obj).Conditions()

readyCondition, readyConditionPresent := conditions.GetCondition(getReadyType(obj), *conditionsPtr)
Expand Down Expand Up @@ -109,7 +109,7 @@ func (p *Postpone[T]) Handle(ctx context.Context, obj T) (reconcile.Result, erro
return reconcile.Result{RequeueAfter: PostponePeriod}, reconciler.ErrStopHandlerChain
}

func (p *Postpone[T]) Name() string {
func (p *PostponeHandler[T]) Name() string {
return "postpone-on-dvcr-garbage-collection-handler"
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
Copyright 2026 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package postponeimporter

import (
"context"
"fmt"
"reflect"

corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"

"github.com/deckhouse/virtualization-controller/pkg/controller/conditions"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/internal/watcher"
"github.com/deckhouse/virtualization/api/core/v1alpha2"
"github.com/deckhouse/virtualization/api/core/v1alpha2/cvicondition"
"github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition"
"github.com/deckhouse/virtualization/api/core/v1alpha2/vicondition"
)

type DVCRGarbageCollectionSecretWatcher[object client.Object] struct {
client client.Client
}

func NewWatcher[T client.Object](client client.Client) *DVCRGarbageCollectionSecretWatcher[T] {
return &DVCRGarbageCollectionSecretWatcher[T]{
client: client,
}
}

func (w *DVCRGarbageCollectionSecretWatcher[T]) Watch(mgr manager.Manager, ctr controller.Controller) error {
if err := ctr.Watch(
source.Kind(
mgr.GetCache(),
&corev1.Secret{},
handler.TypedEnqueueRequestsFromMapFunc(w.enqueuePostponedResources),
predicate.TypedFuncs[*corev1.Secret]{
CreateFunc: func(e event.TypedCreateEvent[*corev1.Secret]) bool {
return watcher.IsDVCRGarbageCollectionSecret(e.Object)
},
UpdateFunc: func(e event.TypedUpdateEvent[*corev1.Secret]) bool {
if !watcher.IsDVCRGarbageCollectionSecret(e.ObjectNew) {
return false
}
return !reflect.DeepEqual(e.ObjectNew.GetAnnotations(), e.ObjectOld.GetAnnotations())
},
DeleteFunc: func(e event.TypedDeleteEvent[*corev1.Secret]) bool {
return watcher.IsDVCRGarbageCollectionSecret(e.Object)
},
},
),
); err != nil {
return fmt.Errorf("error setting watch on DVCR garbage collection Secret: %w", err)
}
return nil
}

func (w *DVCRGarbageCollectionSecretWatcher[T]) enqueuePostponedResources(ctx context.Context, _ *corev1.Secret) []reconcile.Request {
var resList client.ObjectList

// Type switch for T should be done on an empty value for T.
var obj T
switch any(obj).(type) {
case *v1alpha2.ClusterVirtualImage:
resList = &v1alpha2.ClusterVirtualImageList{}
case *v1alpha2.VirtualImage:
resList = &v1alpha2.VirtualImageList{}
case *v1alpha2.VirtualDisk:
resList = &v1alpha2.VirtualDiskList{}
default:
return nil
}

if err := w.client.List(ctx, resList, &client.ListOptions{}); err != nil {
return nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do not ignore errors

}

requests := make([]reconcile.Request, 0)
switch obj := resList.(type) {
case *v1alpha2.ClusterVirtualImageList:
for _, item := range obj.Items {
if isClusterVirtualImagePostponed(&item) {
requests = append(requests, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(&item)})
}
}
case *v1alpha2.VirtualDiskList:
for _, item := range obj.Items {
if isVirtualDiskPostponed(&item) {
requests = append(requests, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(&item)})
}
}
case *v1alpha2.VirtualImageList:
for _, item := range obj.Items {
if isVirtualImagePostponed(&item) {
requests = append(requests, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(&item)})
}
}
}

return requests
}

func isClusterVirtualImagePostponed(cvi *v1alpha2.ClusterVirtualImage) bool {
cond, ok := conditions.GetCondition(cvicondition.ReadyType, cvi.Status.Conditions)
return ok && cond.Reason == ProvisioningPostponedReason.String()
}

func isVirtualImagePostponed(vi *v1alpha2.VirtualImage) bool {
cond, ok := conditions.GetCondition(vicondition.ReadyType, vi.Status.Conditions)
return ok && cond.Reason == ProvisioningPostponedReason.String()
}

func isVirtualDiskPostponed(vd *v1alpha2.VirtualDisk) bool {
cond, ok := conditions.GetCondition(vdcondition.ReadyType, vd.Status.Conditions)
return ok && cond.Reason == ProvisioningPostponedReason.String()
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ import (

"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponehandler"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponeimporter"
"github.com/deckhouse/virtualization-controller/pkg/logger"
"github.com/deckhouse/virtualization/api/core/v1alpha2"
)

const postponeFilterHandlerPrefix = "vd-"

type PostponeHandlerPreFilter struct {
postponeHandler *postponehandler.Postpone[*v1alpha2.VirtualDisk]
postponeHandler *postponeimporter.PostponeHandler[*v1alpha2.VirtualDisk]
}

// NewPostponeHandlerPreFilter runs postpone handler only if VirtualDisk is required to import/upload
// to DVCR first.
func NewPostponeHandlerPreFilter(postponeHandler *postponehandler.Postpone[*v1alpha2.VirtualDisk]) *PostponeHandlerPreFilter {
func NewPostponeHandlerPreFilter(postponeHandler *postponeimporter.PostponeHandler[*v1alpha2.VirtualDisk]) *PostponeHandlerPreFilter {
return &PostponeHandlerPreFilter{
postponeHandler: postponeHandler,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (

"github.com/deckhouse/deckhouse/pkg/log"
"github.com/deckhouse/virtualization-controller/pkg/config"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponehandler"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponeimporter"
"github.com/deckhouse/virtualization-controller/pkg/controller/service"
"github.com/deckhouse/virtualization-controller/pkg/controller/vd/internal"
intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vd/internal/service"
Expand Down Expand Up @@ -82,7 +82,7 @@ func NewController(

reconciler := NewReconciler(
mgr.GetClient(),
internal.NewPostponeHandlerPreFilter(postponehandler.New[*v1alpha2.VirtualDisk](dvcrService, recorder)),
internal.NewPostponeHandlerPreFilter(postponeimporter.NewHandler[*v1alpha2.VirtualDisk](dvcrService, recorder)),
internal.NewInitHandler(),
internal.NewStorageClassReadyHandler(scService),
internal.NewDatasourceReadyHandler(recorder, blank, sources),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponeimporter"
"github.com/deckhouse/virtualization-controller/pkg/controller/reconciler"
vdsupplements "github.com/deckhouse/virtualization-controller/pkg/controller/vd/internal/supplements"
"github.com/deckhouse/virtualization-controller/pkg/controller/vd/internal/watcher"
Expand Down Expand Up @@ -106,6 +107,7 @@ func (r *Reconciler) SetupController(_ context.Context, mgr manager.Manager, ctr
watcher.NewDataVolumeWatcher(),
watcher.NewVirtualMachineWatcher(),
watcher.NewResourceQuotaWatcher(mgrClient),
postponeimporter.NewWatcher[*v1alpha2.VirtualDisk](mgrClient),
} {
err := w.Watch(mgr, ctr)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (

"github.com/deckhouse/deckhouse/pkg/log"
"github.com/deckhouse/virtualization-controller/pkg/config"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponehandler"
"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponeimporter"
"github.com/deckhouse/virtualization-controller/pkg/controller/gc"
"github.com/deckhouse/virtualization-controller/pkg/controller/indexer"
"github.com/deckhouse/virtualization-controller/pkg/controller/service"
Expand Down Expand Up @@ -86,7 +86,7 @@ func NewController(

reconciler := NewReconciler(
mgr.GetClient(),
postponehandler.New[*v1alpha2.VirtualImage](dvcrService, recorder),
postponeimporter.NewHandler[*v1alpha2.VirtualImage](dvcrService, recorder),
internal.NewStorageClassReadyHandler(recorder, scService),
internal.NewDatasourceReadyHandler(sources),
internal.NewLifeCycleHandler(recorder, sources, mgr.GetClient()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"

"github.com/deckhouse/virtualization-controller/pkg/controller/dvcr-garbage-collection/postponeimporter"
"github.com/deckhouse/virtualization-controller/pkg/controller/reconciler"
"github.com/deckhouse/virtualization-controller/pkg/controller/vi/internal/watcher"
"github.com/deckhouse/virtualization-controller/pkg/controller/watchers"
Expand Down Expand Up @@ -117,6 +118,7 @@ func (r *Reconciler) SetupController(_ context.Context, mgr manager.Manager, ctr
watcher.NewDataVolumeWatcher(),
watcher.NewPersistentVolumeClaimWatcher(),
watcher.NewVirtualDiskWatcher(mgrClient),
postponeimporter.NewWatcher[*v1alpha2.VirtualImage](mgrClient),
} {
err := w.Watch(mgr, ctr)
if err != nil {
Expand Down
Loading