Skip to content

Commit 703f6aa

Browse files
authored
Record user name and id as AppWrapper labels (#115)
1 parent b87fa22 commit 703f6aa

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

internal/webhook/appwrapper_webhook.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
discovery "k8s.io/client-go/discovery"
3131
"k8s.io/client-go/kubernetes"
3232
authClientv1 "k8s.io/client-go/kubernetes/typed/authorization/v1"
33+
utilmaps "sigs.k8s.io/kueue/pkg/util/maps"
3334

3435
ctrl "sigs.k8s.io/controller-runtime"
3536
"sigs.k8s.io/controller-runtime/pkg/log"
@@ -44,6 +45,11 @@ import (
4445
"github.com/project-codeflare/appwrapper/pkg/utils"
4546
)
4647

48+
const (
49+
AppWrapperUsernameLabel = "workload.codeflare.dev/user"
50+
AppWrapperUserIDLabel = "workload.codeflare.dev/userid"
51+
)
52+
4753
type AppWrapperWebhook struct {
4854
Config *config.AppWrapperConfig
4955
SubjectAccessReviewer authClientv1.SubjectAccessReviewInterface
@@ -66,6 +72,14 @@ func (w *AppWrapperWebhook) Default(ctx context.Context, obj runtime.Object) err
6672
log.FromContext(ctx).Info("Error raised during podSet inference", "job", aw)
6773
return err
6874
}
75+
76+
// inject labels with user name and id
77+
request, err := admission.RequestFromContext(ctx)
78+
if err != nil {
79+
return err
80+
}
81+
userInfo := request.UserInfo
82+
aw.Labels = utilmaps.MergeKeepFirst(map[string]string{AppWrapperUsernameLabel: userInfo.Username, AppWrapperUserIDLabel: userInfo.UID}, aw.Labels)
6983
return nil
7084
}
7185

@@ -258,6 +272,14 @@ func (w *AppWrapperWebhook) validateAppWrapperUpdate(old *workloadv1beta2.AppWra
258272
}
259273
}
260274

275+
// ensure user name and id are not mutated
276+
if old.Labels[AppWrapperUsernameLabel] != new.Labels[AppWrapperUsernameLabel] {
277+
allErrors = append(allErrors, field.Forbidden(field.NewPath("metadata").Child("labels").Key(AppWrapperUsernameLabel), msg))
278+
}
279+
if old.Labels[AppWrapperUserIDLabel] != new.Labels[AppWrapperUserIDLabel] {
280+
allErrors = append(allErrors, field.Forbidden(field.NewPath("metadata").Child("labels").Key(AppWrapperUserIDLabel), msg))
281+
}
282+
261283
return allErrors
262284
}
263285

internal/webhook/appwrapper_webhook_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"k8s.io/apimachinery/pkg/runtime"
2828
"k8s.io/apimachinery/pkg/types"
2929
"k8s.io/utils/ptr"
30+
utilmaps "sigs.k8s.io/kueue/pkg/util/maps"
3031
)
3132

3233
var _ = Describe("AppWrapper Webhook Tests", func() {
@@ -39,6 +40,16 @@ var _ = Describe("AppWrapper Webhook Tests", func() {
3940
Expect(aw.Spec.Suspend).Should(BeTrue(), "aw.Spec.Suspend should have been changed to true")
4041
Expect(k8sClient.Delete(ctx, aw)).To(Succeed())
4142
})
43+
44+
It("User name and ID are set", func() {
45+
aw := toAppWrapper(pod(100))
46+
aw.Labels = utilmaps.MergeKeepFirst(map[string]string{AppWrapperUsernameLabel: "bad", AppWrapperUserIDLabel: "bad"}, aw.Labels)
47+
48+
Expect(k8sLimitedClient.Create(ctx, aw)).To(Succeed())
49+
Expect(aw.Labels[AppWrapperUsernameLabel]).Should(BeIdenticalTo(limitedUserName))
50+
Expect(aw.Labels[AppWrapperUserIDLabel]).Should(BeIdenticalTo(limitedUserID))
51+
Expect(k8sLimitedClient.Delete(ctx, aw)).To(Succeed())
52+
})
4253
})
4354

4455
Context("Validating Webhook", func() {
@@ -128,6 +139,36 @@ var _ = Describe("AppWrapper Webhook Tests", func() {
128139
Expect(k8sClient.Create(ctx, aw)).ShouldNot(Succeed())
129140
})
130141

142+
It("User name and ID are immutable", func() {
143+
aw := toAppWrapper(pod(100))
144+
awName := types.NamespacedName{Name: aw.Name, Namespace: aw.Namespace}
145+
Expect(k8sClient.Create(ctx, aw)).Should(Succeed())
146+
147+
aw = getAppWrapper(awName)
148+
aw.Labels[AppWrapperUsernameLabel] = "bad"
149+
Expect(k8sClient.Update(ctx, aw)).ShouldNot(Succeed())
150+
151+
aw = getAppWrapper(awName)
152+
aw.Labels[AppWrapperUserIDLabel] = "bad"
153+
Expect(k8sClient.Update(ctx, aw)).ShouldNot(Succeed())
154+
155+
Expect(k8sClient.Delete(ctx, aw)).To(Succeed())
156+
})
157+
158+
It("User name and ID should be preserved on updates", func() {
159+
aw := toAppWrapper(pod(100))
160+
awName := types.NamespacedName{Name: aw.Name, Namespace: aw.Namespace}
161+
Expect(k8sLimitedClient.Create(ctx, aw)).Should(Succeed())
162+
163+
aw = getAppWrapper(awName)
164+
Expect(k8sClient.Update(ctx, aw)).Should(Succeed())
165+
166+
aw = getAppWrapper(awName)
167+
Expect(aw.Labels[AppWrapperUsernameLabel]).Should(BeIdenticalTo(limitedUserName))
168+
Expect(aw.Labels[AppWrapperUserIDLabel]).Should(BeIdenticalTo(limitedUserID))
169+
Expect(k8sLimitedClient.Delete(ctx, aw)).To(Succeed())
170+
})
171+
131172
Context("aw.Spec.Components is immutable", func() {
132173
It("Updates to non-sensitive fields are allowed", func() {
133174
aw := toAppWrapper(pod(100), deployment(4, 100))

internal/webhook/suite_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ var testEnv *envtest.Environment
6060
var ctx context.Context
6161
var cancel context.CancelFunc
6262

63+
const limitedUserName = "limited-user"
64+
const limitedUserID = "8da0fcfe-6d7f-4f44-b433-d91d22cc1b8c"
65+
6366
func TestControllers(t *testing.T) {
6467
RegisterFailHandler(Fail)
6568

@@ -115,9 +118,8 @@ var _ = BeforeSuite(func() {
115118
Expect(k8sClient).NotTo(BeNil())
116119

117120
// configure a restricted rbac user who can create AppWrappers and Pods but not Deployments
118-
limitedUserName := "limited-user"
119121
limitedCfg := *cfg
120-
limitedCfg.Impersonate = rest.ImpersonationConfig{UserName: limitedUserName, Extra: map[string][]string{"xyzzy": {"plugh"}}}
122+
limitedCfg.Impersonate = rest.ImpersonationConfig{UserName: limitedUserName, UID: string(limitedUserID), Extra: map[string][]string{"xyzzy": {"plugh"}}}
121123
_, err = testEnv.AddUser(envtest.User{Name: limitedUserName, Groups: []string{}}, &limitedCfg)
122124
Expect(err).NotTo(HaveOccurred())
123125
clusterRole := &rbacv1.ClusterRole{

0 commit comments

Comments
 (0)