Skip to content

Commit

Permalink
feat: allow to configure the deployment order of resources
Browse files Browse the repository at this point in the history
  • Loading branch information
lburgazzoli committed Feb 4, 2025
1 parent 23bdfde commit fb71033
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 8 deletions.
12 changes: 12 additions & 0 deletions controllers/components/kueue/kueue_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package kueue

import (
"cmp"
"context"

"github.com/blang/semver/v4"
Expand All @@ -27,6 +28,7 @@ import (
networkingv1 "k8s.io/api/networking/v1"
rbacv1 "k8s.io/api/rbac/v1"
extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
ctrl "sigs.k8s.io/controller-runtime"

componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1"
Expand All @@ -51,6 +53,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.
WatchesGVK(gvk.ValidatingAdmissionPolicyBinding). // "watch" VAPB, because we want it to be configable by user and it can be left behind when kueue is remov
WithAction(extraInitialize)
}

// customized Owns() for Component with new predicates
b.Owns(&corev1.ConfigMap{}).
Owns(&corev1.Secret{}).
Expand Down Expand Up @@ -84,6 +87,15 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.
WithAction(customizeResources).
WithAction(deploy.NewAction(
deploy.WithCache(),
// This ensures that CRDs are installed first, and deployment are
// applied as the latest step, to ensure every resource required
// would be up to date
deploy.WithSortFunction(func(a unstructured.Unstructured, b unstructured.Unstructured) int {
return cmp.Compare(
deployPriority[a.GroupVersionKind()],
deployPriority[b.GroupVersionKind()],
)
}),
)).
WithAction(updatestatus.NewAction()).
// must be the final action
Expand Down
7 changes: 7 additions & 0 deletions controllers/components/kueue/kueue_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package kueue

import (
conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"

componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1"
"github.com/opendatahub-io/opendatahub-operator/v2/controllers/status"
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk"
odhtypes "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/types"
odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy"
)
Expand All @@ -24,6 +26,11 @@ var (
imageParamMap = map[string]string{
"odh-kueue-controller-image": "RELATED_IMAGE_ODH_KUEUE_CONTROLLER_IMAGE",
}

deployPriority = map[schema.GroupVersionKind]int{
gvk.CustomResourceDefinition: -1, // Highest priority (comes first)
gvk.Deployment: 1, // Lowest priority (comes last)
}
)

func manifestsPath() odhtypes.ManifestInfo {
Expand Down
20 changes: 17 additions & 3 deletions pkg/controller/actions/deploy/action_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"slices"
"strconv"
"strings"

Expand Down Expand Up @@ -40,6 +41,7 @@ type Action struct {
labels map[string]string
annotations map[string]string
cache *Cache
sortFunc func(unstructured.Unstructured, unstructured.Unstructured) int
}

type ActionOpts func(*Action)
Expand Down Expand Up @@ -106,6 +108,12 @@ func WithCache(opts ...CacheOpt) ActionOpts {
}
}

func WithSortFunction(fn func(unstructured.Unstructured, unstructured.Unstructured) int) ActionOpts {
return func(action *Action) {
action.sortFunc = fn
}
}

func (a *Action) run(ctx context.Context, rr *odhTypes.ReconciliationRequest) error {
// cleanup old entries if needed
if a.cache != nil {
Expand All @@ -119,8 +127,14 @@ func (a *Action) run(ctx context.Context, rr *odhTypes.ReconciliationRequest) er

controllerName := strings.ToLower(kind)

for i := range rr.Resources {
res := rr.Resources[i]
items := rr.Resources
if a.sortFunc != nil {
items = slices.Clone(rr.Resources)
slices.SortFunc(items, a.sortFunc)
}

for i := range items {
res := items[i]
current := resources.GvkToUnstructured(res.GroupVersionKind())

lookupErr := rr.Client.Get(ctx, client.ObjectKeyFromObject(&res), current)
Expand Down Expand Up @@ -148,7 +162,7 @@ func (a *Action) run(ctx context.Context, rr *odhTypes.ReconciliationRequest) er
var ok bool
var err error

switch rr.Resources[i].GroupVersionKind() {
switch items[i].GroupVersionKind() {
case gvk.CustomResourceDefinition:
ok, err = a.deployCRD(ctx, rr, res, current)
default:
Expand Down
65 changes: 65 additions & 0 deletions pkg/controller/actions/deploy/action_deploy_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package deploy_test

import (
"cmp"
"context"
"path/filepath"
"strconv"
Expand All @@ -18,9 +19,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
apimachinery "k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/envtest"

Expand Down Expand Up @@ -635,3 +638,65 @@ func TestDeployOwnerRef(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(updatedCRD.GetOwnerReferences()).Should(BeEmpty())
}

func TestDeployActionWithSort(t *testing.T) {
g := NewWithT(t)

patchOrder := make([]string, 0)

ctx := context.Background()
ns := xid.New().String()
cl, err := fakeclient.NewWithInterceptors(
&interceptor.Funcs{
Patch: func(ctx context.Context, c client.WithWatch, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
patchOrder = append(patchOrder, obj.GetObjectKind().GroupVersionKind().Kind)
return nil
},
})

g.Expect(err).ShouldNot(HaveOccurred())

priority := map[schema.GroupVersionKind]int{
gvk.CustomResourceDefinition: -1, // Highest priority (comes first)
gvk.Deployment: 1, // Lowest priority (comes last)
}

action := deploy.NewAction(
deploy.WithSortFunction(func(a unstructured.Unstructured, b unstructured.Unstructured) int {
return cmp.Compare(
priority[a.GroupVersionKind()],
priority[b.GroupVersionKind()],
)
}),
)

rr := types.ReconciliationRequest{
Client: cl,
DSCI: &dsciv1.DSCInitialization{Spec: dsciv1.DSCInitializationSpec{ApplicationsNamespace: ns}},
Instance: &componentApi.Dashboard{
ObjectMeta: metav1.ObjectMeta{
Generation: 1,
},
},
Release: common.Release{
Name: cluster.OpenDataHub,
Version: version.OperatorVersion{Version: semver.Version{
Major: 1, Minor: 2, Patch: 3,
}}},
Resources: []unstructured.Unstructured{
*resources.GvkToUnstructured(gvk.Dashboard),
*resources.GvkToUnstructured(gvk.Deployment),
*resources.GvkToUnstructured(gvk.CustomResourceDefinition),
*resources.GvkToUnstructured(gvk.RoleBinding),
},
}

err = action(ctx, &rr)
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(patchOrder).To(HaveExactElements(
gvk.CustomResourceDefinition.Kind,
gvk.Dashboard.Kind,
gvk.RoleBinding.Kind,
gvk.Deployment.Kind,
))
}
20 changes: 15 additions & 5 deletions pkg/utils/test/fakeclient/fakeclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
k8sFake "k8s.io/client-go/kubernetes/fake"
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
clientFake "sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"

componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1"
dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1"
Expand All @@ -20,6 +21,10 @@ import (
)

func New(objs ...ctrlClient.Object) (*client.Client, error) {
return NewWithInterceptors(nil, objs...)
}

func NewWithInterceptors(interceptorFunc *interceptor.Funcs, objs ...ctrlClient.Object) (*client.Client, error) {
scheme := runtime.NewScheme()
utilruntime.Must(corev1.AddToScheme(scheme))
utilruntime.Must(appsv1.AddToScheme(scheme))
Expand Down Expand Up @@ -49,12 +54,17 @@ func New(objs ...ctrlClient.Object) (*client.Client, error) {
ro[i] = u
}

cb := clientFake.NewClientBuilder().
WithScheme(scheme).
WithRESTMapper(fakeMapper).
WithObjects(objs...)

if interceptorFunc != nil {
cb = cb.WithInterceptorFuncs(*interceptorFunc)
}

c := client.New(
clientFake.NewClientBuilder().
WithScheme(scheme).
WithRESTMapper(fakeMapper).
WithObjects(objs...).
Build(),
cb.Build(),
k8sFake.NewSimpleClientset(ro...),
dynamicFake.NewSimpleDynamicClient(scheme, ro...),
)
Expand Down

0 comments on commit fb71033

Please sign in to comment.