Skip to content
Merged
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
1 change: 0 additions & 1 deletion slice/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ run:
linters:
enable:
- copyloopvar
- dupl
- dupword
- durationcheck
- fatcontext
Expand Down
4 changes: 2 additions & 2 deletions slice/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,8 @@ func setupControllers(mgr ctrl.Manager, certsReady chan struct{}) {
os.Exit(1)
}

if err := controller.NewWorkloadReconciler(mgr.GetClient()).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Workload")
if failedCtrl, err := controller.SetupControllers(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", failedCtrl)
os.Exit(1)
}

Expand Down
26 changes: 26 additions & 0 deletions slice/config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ kind: ClusterRole
metadata:
name: manager-role
rules:
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- update
- watch
- apiGroups:
- ""
resources:
Expand All @@ -22,6 +31,23 @@ rules:
- list
- update
- watch
- apiGroups:
- kueue.x-k8s.io
resources:
- admissionchecks
verbs:
- get
- list
- watch
- apiGroups:
- kueue.x-k8s.io
resources:
- admissionchecks/status
- workloads/status
verbs:
- get
- patch
- update
- apiGroups:
- kueue.x-k8s.io
resources:
Expand Down
2 changes: 1 addition & 1 deletion slice/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ require (
k8s.io/apiserver v0.33.2 // indirect
k8s.io/component-base v0.33.2 // indirect
k8s.io/component-helpers v0.33.2 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/klog/v2 v2.130.1
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
Expand Down
78 changes: 78 additions & 0 deletions slice/internal/controller/admissioncheck_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright The Kubernetes Authors.

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 controller

import (
"context"

apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
)

type AdmissionCheckReconciler struct {
client client.Client
}

var _ reconcile.Reconciler = (*AdmissionCheckReconciler)(nil)

func NewAdmissionCheckReconciler(cl client.Client) *AdmissionCheckReconciler {
return &AdmissionCheckReconciler{
client: cl,
}
}

// +kubebuilder:rbac:groups=kueue.x-k8s.io,resources=admissionchecks,verbs=get;list;watch
// +kubebuilder:rbac:groups=kueue.x-k8s.io,resources=admissionchecks/status,verbs=get;update;patch

func (r *AdmissionCheckReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
ac := &kueue.AdmissionCheck{}
if err := r.client.Get(ctx, req.NamespacedName, ac); err != nil || ac.Spec.ControllerName != SliceControllerName {
return reconcile.Result{}, client.IgnoreNotFound(err)
}

log := ctrl.LoggerFrom(ctx)
log.V(2).Info("Reconcile AdmissionCheck")

currentCondition := ptr.Deref(apimeta.FindStatusCondition(ac.Status.Conditions, kueue.AdmissionCheckActive), metav1.Condition{})
newCondition := metav1.Condition{
Type: kueue.AdmissionCheckActive,
Status: metav1.ConditionTrue,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a mechanism (in Kueue?) which eventually transitions this to false if this controller fails?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As I see in the provisioning request controller, we set it to False only when there is a validation error in the admission check config (see here). We are not using the admission check config, so we don’t need it. Therefore, I think we should just set it to true.

Reason: "Active",
Message: "The admission check is active",
ObservedGeneration: ac.Generation,
}

if currentCondition.Status != newCondition.Status {
apimeta.SetStatusCondition(&ac.Status.Conditions, newCondition)
return reconcile.Result{}, client.IgnoreNotFound(r.client.Status().Update(ctx, ac))
}

return reconcile.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *AdmissionCheckReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&kueue.AdmissionCheck{}).
Named("admissioncheck_controller").
Complete(r)
}
95 changes: 95 additions & 0 deletions slice/internal/controller/admissioncheck_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
Copyright The Kubernetes Authors.

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 controller

import (
"testing"

"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"

utiltesting "tpu-slice-controller/internal/util/testing"
)

func TestAdmissionCheckReconciler(t *testing.T) {
baseAdmissionCheckName := "ac"
baseGeneration := int64(1)
baseRequest := types.NamespacedName{Name: baseAdmissionCheckName, Namespace: corev1.NamespaceDefault}
baseAdmissionCheckWrapper := utiltesting.MakeAdmissionCheck(baseAdmissionCheckName).
Generation(baseGeneration).
ControllerName(SliceControllerName)

testCases := map[string]struct {
request types.NamespacedName
admissionCheck *kueue.AdmissionCheck
wantAdmissionChecks []kueue.AdmissionCheck
wantErr error
}{
"unrelated check": {
request: baseRequest,
admissionCheck: baseAdmissionCheckWrapper.Clone().ControllerName("other-controller").Obj(),
wantAdmissionChecks: []kueue.AdmissionCheck{
*baseAdmissionCheckWrapper.Clone().ControllerName("other-controller").Obj(),
},
},
"should set Active status": {
request: baseRequest,
admissionCheck: baseAdmissionCheckWrapper.DeepCopy(),
wantAdmissionChecks: []kueue.AdmissionCheck{
*baseAdmissionCheckWrapper.Clone().
Condition(metav1.Condition{
Type: kueue.AdmissionCheckActive,
Status: metav1.ConditionTrue,
Reason: "Active",
Message: "The admission check is active",
ObservedGeneration: baseGeneration,
}).
Obj(),
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
scheme := runtime.NewScheme()
utilruntime.Must(kueue.AddToScheme(scheme))
utilruntime.Must(kueue.AddToScheme(scheme))

clientBuilder := fake.NewClientBuilder().WithScheme(scheme)

if tc.admissionCheck != nil {
clientBuilder = clientBuilder.WithObjects(tc.admissionCheck)
}

kClient := clientBuilder.Build()
reconciler := NewAdmissionCheckReconciler(kClient)

ctx, _ := utiltesting.ContextWithLog(t)

_, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: tc.request})
if diff := cmp.Diff(tc.wantErr, err); diff != "" {
t.Errorf("Error after reconcile (-want,+got):\n%s", diff)
}
})
}
}
37 changes: 37 additions & 0 deletions slice/internal/controller/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Copyright The Kubernetes Authors.

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 controller

import (
ctrl "sigs.k8s.io/controller-runtime"
)

const (
SliceWorkloadControllerName = "slice-workload-controller"
)

func SetupControllers(mgr ctrl.Manager) (string, error) {
wlRec := NewWorkloadReconciler(mgr.GetClient(), mgr.GetEventRecorderFor(SliceWorkloadControllerName))
if err := wlRec.SetupWithManager(mgr); err != nil {
return "Workload", err
}
acRec := NewAdmissionCheckReconciler(mgr.GetClient())
if err := acRec.SetupWithManager(mgr); err != nil {
return "AdmissionCheck", err
}
return "", nil
}
Loading
Loading