@@ -3,13 +3,11 @@ package controller
33import (
44 "context"
55 "encoding/json"
6- "errors"
76 "fmt"
87 "maps"
98 "net/http"
109 "slices"
1110 "strings"
12- "time"
1311
1412 rm "cloud.google.com/go/resourcemanager/apiv3"
1513 rmpb "cloud.google.com/go/resourcemanager/apiv3/resourcemanagerpb"
@@ -70,164 +68,130 @@ func (m *StatefulsetMutator) Handle(ctx context.Context, req admission.Request)
7068 return admission .Allowed ("statefulset is being deleted" )
7169 }
7270
73- if sfs .Annotations [enabledssbucketeerAnnotation ] != "true" {
71+ group , ok := sfs .Annotations [impersonateGroupAnnotation ]
72+ if ! ok {
7473 return admission .Allowed ("skipping ssbucketeer mutation" )
7574 }
7675
7776 ensurePodAnnotations (& sfs .Spec .Template )
7877
79- // 1. Check if we want to impersonate a group SA
80- group , hasGroupAnnotation := sfs .Annotations [impersonateGroupAnnotation ]
81- if hasGroupAnnotation {
82- saAnnotations := map [string ]string {
83- impersonateGroupAnnotation : group ,
84- }
78+ // Find the service container, so we can add volumeMounts
79+ serviceContainer := getServiceContainer (& sfs .Spec .Template , sfs .Annotations [serviceContainerAnnotation ])
80+ if serviceContainer == nil {
81+ err := fmt .Errorf ("could not find container with name %q" , sfs .Annotations [serviceContainerAnnotation ])
82+ log .Error (err , "could not find service container" )
83+ return admission .Errored (http .StatusBadRequest , err )
84+ }
8585
86- groupConfig := m .GroupConfigs .GetConfig (group )
87- if groupConfig == nil {
88- return admission .Denied (fmt .Sprintf ("no configuration found for group: %s" , group ))
89- }
86+ bucketMounts := getExtraBucketMounts (sfs .Annotations )
9087
91- if groupConfig .ReasonRequired {
92- reason , hasReason := sfs .Annotations [accessReasonAnnotation ]
93- if ! hasReason || reason == "" {
94- return admission .Denied (fmt .Sprintf ("reason is required for access group: %q" , group ))
95- }
96- saAnnotations [accessReasonAnnotation ] = reason
97- log .Info ("Reason provided for access group" , "group" , group , "reason" , reason )
98- }
88+ saAnnotations := map [string ]string {
89+ impersonateGroupAnnotation : group ,
90+ requestedServiceDurationAnnotation : sfs .Annotations [requestedServiceDurationAnnotation ],
91+ accessReasonAnnotation : sfs .Annotations [accessReasonAnnotation ],
92+ }
9993
100- if groupConfig .MaxDuration > 0 {
101- durationString , ok := sfs .Annotations [requestedServiceDurationAnnotation ]
102- if ! ok {
103- return admission .Denied (fmt .Sprintf ("access duration required for group %q" , group ))
104- }
105-
106- duration , err := time .ParseDuration (durationString )
107- if err != nil {
108- log .Error (err , "failed to parse requested-duration" , "duration" , durationString )
109- return admission .Denied (fmt .Sprintf ("invalid requested duration %q" , durationString ))
110- }
111-
112- if duration > groupConfig .MaxDuration {
113- log .Info ("attempt to start service with longer than max duration, defaulting to max" ,
114- "duration" , duration ,
115- "group" , group ,
116- "maxDuration" , groupConfig .MaxDuration )
117- duration = groupConfig .MaxDuration
118- sfs .Annotations [requestedServiceDurationAnnotation ] = duration .String ()
119- }
120-
121- saAnnotations [requestedServiceDurationAnnotation ] = duration .String ()
94+ groupConfig := m .GroupConfigs .GetConfig (group )
95+ if groupConfig == nil {
96+ return admission .Denied (fmt .Sprintf ("no configuration found for group: %s" , group ))
97+ }
12298
123- }
99+ // Handle IAM bindings and k8s SA annotations
100+ if err := m .handleServiceAccount (ctx , req .Namespace , sfs .Spec .Template .Spec .ServiceAccountName , saAnnotations ); err != nil {
101+ log .Error (err , "handle serviceaccount" )
102+ return admission .Denied ("error handling service account" )
103+ }
124104
125- // Handle IAM bindings and k8s SA annotations
126- if err := m .handleServiceAccount (ctx , req .Namespace , sfs .Spec .Template .Spec .ServiceAccountName , saAnnotations ); err != nil {
127- log .Error (err , "handle serviceaccount" )
128- return admission .Denied ("error handling service account" )
105+ if m .ADCGroupEnvName != "" {
106+ if ! slices .ContainsFunc (serviceContainer .Env , func (e corev1.EnvVar ) bool {
107+ return e .Name == m .ADCGroupEnvName
108+ }) {
109+ serviceContainer .Env = append (serviceContainer .Env , corev1.EnvVar {Name : m .ADCGroupEnvName , Value : group })
129110 }
130111 }
131112
132- // 2. Mount buckets
133- var bucketNames []string
134- if annotationBuckets , ok := sfs .Annotations [mountBucketsAnnotation ]; ok {
135- bucketNames = strings .Split (annotationBuckets , "," )
113+ // TODO: Use Dapla Team API for this?
114+ if sfs .Annotations [mountStandardBucketsAnnotation ] == "true" {
115+ team := groupConfig .ToTeam (group )
116+ if err := m .addStandardBuckets (ctx , team , * groupConfig , bucketMounts ); err != nil {
117+ log .Error (err , "failed to add standard buckets" )
118+ }
136119 }
137120
138- // Find the service container, so we can add volumeMounts
139- containerIndex := slices .IndexFunc (sfs .Spec .Template .Spec .Containers , func (c corev1.Container ) bool {
140- return c .Name == sfs .Annotations [serviceContainerAnnotation ]
141- })
121+ addBucketsToPodSpec (& sfs .Spec .Template .Spec , serviceContainer , bucketMounts , m .PrecreatorImage )
142122
143- if containerIndex == - 1 {
144- err := fmt . Errorf ( "could not find container with name %q" , sfs . Annotations [ serviceContainerAnnotation ])
145- log .Error (err , "could not find service container " )
146- return admission . Errored ( http . StatusBadRequest , err )
123+ if m . IamProbeImage != "" && sfs . Annotations [ iamProbeStatus ] != iamProbeDone {
124+ if err := m . launchIamProbe ( ctx , sfs ); err != nil {
125+ log .Error (err , "could not start iam probe " )
126+ }
147127 }
148128
149- if hasGroupAnnotation && m .ADCGroupEnvName != "" {
150- containerEnv := & sfs .Spec .Template .Spec .Containers [containerIndex ].Env
151- if ! slices .ContainsFunc (* containerEnv , func (e corev1.EnvVar ) bool {
152- return e .Name == m .ADCGroupEnvName
153- }) {
154- * containerEnv = append (* containerEnv , corev1.EnvVar {Name : m .ADCGroupEnvName , Value : group })
155- }
129+ marshaledStatefulSet , err := json .Marshal (sfs )
130+ if err != nil {
131+ return admission .Errored (http .StatusInternalServerError , err )
132+ }
133+ return admission .PatchResponseFromRaw (req .Object .Raw , marshaledStatefulSet )
134+ }
135+
136+ func getExtraBucketMounts (annotations map [string ]string ) map [string ]string {
137+ var bucketNames []string
138+ if annotationBuckets , ok := annotations [mountBucketsAnnotation ]; ok {
139+ bucketNames = strings .Split (annotationBuckets , "," )
156140 }
157141
158142 bucketMounts := make (map [string ]string , len (bucketNames ))
159143 for _ , bucket := range bucketNames {
160144 bucketMounts [bucket ] = bucket
161145 }
146+ return bucketMounts
147+ }
162148
163- // TODO: Use Dapla Team API for this?
164- if sfs .Annotations [mountStandardBucketsAnnotation ] == "true" {
165- team := group
166- var gc * AccessGroupConfig
167- for _ , config := range m .GroupConfigs {
168- if team = strings .TrimSuffix (group , config .Name ); team != group {
169- team = strings .TrimSuffix (team , "-" )
170- gc = & config
171- break
172- }
173- }
174-
175- if team != group {
176- if err := m .addStandardBuckets (ctx , team , * gc , bucketMounts ); err != nil {
177- log .Error (err , "failed to add standard buckets" )
178- }
179- } else {
180- log .Error (errors .New ("could not deduce team from group" ), "team could not be deduced from group" , "group" , group )
181- }
149+ func getServiceContainer (pod * corev1.PodTemplateSpec , name string ) * corev1.Container {
150+ idx := slices .IndexFunc (pod .Spec .Containers , func (c corev1.Container ) bool {
151+ return c .Name == name
152+ })
153+ if idx == - 1 {
154+ return nil
182155 }
156+ return & pod .Spec .Containers [idx ]
157+ }
183158
184- addBucketsToPodSpec (& sfs .Spec .Template .Spec , & sfs .Spec .Template .Spec .Containers [containerIndex ], bucketMounts , m .PrecreatorImage )
159+ func (m * StatefulsetMutator ) launchIamProbe (ctx context.Context , sfs * appsv1.StatefulSet ) error {
160+ sfs .Annotations [iamProbeStatus ] = fmt .Sprintf ("%s%d" , iamProbeRunningPrefix , * sfs .Spec .Replicas )
161+ sfs .Spec .Replicas = ptr [int32 ](0 )
185162
186- if m .IamProbeImage != "" && sfs .Annotations [iamProbeStatus ] != iamProbeDone {
187- sfs .Annotations [iamProbeStatus ] = fmt .Sprintf ("%s%d" , iamProbeRunningPrefix , * sfs .Spec .Replicas )
188- sfs .Spec .Replicas = ptr [int32 ](0 )
189-
190- probeJob := & batchv1.Job {
191- ObjectMeta : metav1.ObjectMeta {
192- Name : fmt .Sprintf ("%s-iam-probe" , sfs .Name ),
193- Namespace : sfs .Namespace ,
194- Annotations : map [string ]string {
195- probeJobStatefulsetAnnotation : sfs .Name ,
196- },
163+ probeJob := & batchv1.Job {
164+ ObjectMeta : metav1.ObjectMeta {
165+ Name : fmt .Sprintf ("%s-iam-probe" , sfs .Name ),
166+ Namespace : sfs .Namespace ,
167+ Annotations : map [string ]string {
168+ probeJobStatefulsetAnnotation : sfs .Name ,
197169 },
198- Spec : batchv1. JobSpec {
199- ActiveDeadlineSeconds : ptr [ int64 ]( 300 ),
200- TTLSecondsAfterFinished : ptr [int32 ]( 0 ),
201- Template : corev1. PodTemplateSpec {
202- ObjectMeta : metav1. ObjectMeta {
203- Annotations : map [ string ] string {
204- istioExcludedIpRangesAnnotation : gcsfuseOutboundIPRange ,
205- } ,
170+ },
171+ Spec : batchv1. JobSpec {
172+ ActiveDeadlineSeconds : ptr [int64 ]( 300 ),
173+ TTLSecondsAfterFinished : ptr [ int32 ]( 0 ),
174+ Template : corev1. PodTemplateSpec {
175+ ObjectMeta : metav1. ObjectMeta {
176+ Annotations : map [ string ] string {
177+ istioExcludedIpRangesAnnotation : gcsfuseOutboundIPRange ,
206178 },
207- Spec : corev1. PodSpec {
208- ServiceAccountName : sfs . Spec . Template . Spec . ServiceAccountName ,
209- Containers : []corev1. Container {
210- {
211- Image : m . IamProbeImage ,
212- Name : "iam-probe" ,
213- } ,
179+ },
180+ Spec : corev1. PodSpec {
181+ ServiceAccountName : sfs . Spec . Template . Spec . ServiceAccountName ,
182+ Containers : []corev1. Container {
183+ {
184+ Image : m . IamProbeImage ,
185+ Name : "iam-probe" ,
214186 },
215- RestartPolicy : corev1 .RestartPolicyNever ,
216187 },
188+ RestartPolicy : corev1 .RestartPolicyNever ,
217189 },
218190 },
219- }
220-
221- if err := m .Client .Create (ctx , probeJob ); err != nil {
222- log .Error (err , "could not create probe job" )
223- }
191+ },
224192 }
225193
226- marshaledStatefulSet , err := json .Marshal (sfs )
227- if err != nil {
228- return admission .Errored (http .StatusInternalServerError , err )
229- }
230- return admission .PatchResponseFromRaw (req .Object .Raw , marshaledStatefulSet )
194+ return m .Client .Create (ctx , probeJob )
231195}
232196
233197func (a * StatefulsetMutator ) handleServiceAccount (ctx context.Context , namespace , name string , saAnnotations map [string ]string ) error {
0 commit comments