diff --git a/chart/templates/clusterrole-operator-manager-role.yaml b/chart/templates/clusterrole-operator-manager-role.yaml index 4955b35..f72f3fb 100644 --- a/chart/templates/clusterrole-operator-manager-role.yaml +++ b/chart/templates/clusterrole-operator-manager-role.yaml @@ -49,6 +49,9 @@ rules: verbs: - create - get + - list + - update + - watch - apiGroups: - batch resources: diff --git a/chart/templates/serviceaccount-builder.yaml b/chart/templates/serviceaccount-builder.yaml new file mode 100644 index 0000000..7fa2c6b --- /dev/null +++ b/chart/templates/serviceaccount-builder.yaml @@ -0,0 +1,11 @@ +{{- if .Values.build.serviceAccount }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.build.serviceAccount }} + namespace: {{ .Release.Namespace }} + {{- if .Values.build.roleArn }} + annotations: + eks.amazonaws.com/role-arn: {{ .Values.build.roleArn | quote }} + {{- end }} +{{- end }} diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 728eecd..879d81d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -50,6 +50,9 @@ rules: verbs: - create - get + - list + - update + - watch - apiGroups: - batch resources: diff --git a/hack/helm-generator/main.go b/hack/helm-generator/main.go index a30b296..772db23 100644 --- a/hack/helm-generator/main.go +++ b/hack/helm-generator/main.go @@ -88,6 +88,11 @@ func main() { fmt.Fprintf(os.Stderr, "Warning: Could not add env vars to deployment: %v\n", err) } + // Add builder ServiceAccount template (conditional on build.serviceAccount) + if err := addBuilderServiceAccount(templatesDir); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Could not add builder service account: %v\n", err) + } + fmt.Printf("✓ Generated %d Helm templates\n\n", fileCount) fmt.Println("Test with:") fmt.Println(" make helm-lint") @@ -279,3 +284,19 @@ func addEnvVarsToDeployment(templatesDir string) error { return os.WriteFile(deploymentFile, []byte(contentStr), 0644) } + +func addBuilderServiceAccount(templatesDir string) error { + content := `{{- if .Values.build.serviceAccount }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.build.serviceAccount }} + namespace: {{ .Release.Namespace }} + {{- if .Values.build.roleArn }} + annotations: + eks.amazonaws.com/role-arn: {{ .Values.build.roleArn | quote }} + {{- end }} +{{- end }} +` + return os.WriteFile(filepath.Join(templatesDir, "serviceaccount-builder.yaml"), []byte(content), 0644) +} diff --git a/internal/controller/deco_controller.go b/internal/controller/deco_controller.go index 172d4b5..7a825fa 100644 --- a/internal/controller/deco_controller.go +++ b/internal/controller/deco_controller.go @@ -39,7 +39,7 @@ type DecoReconciler struct { // +kubebuilder:rbac:groups=deco.sites,resources=decos/status,verbs=get;update;patch // +kubebuilder:rbac:groups=deco.sites,resources=decos/finalizers,verbs=update // +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;delete -// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;create +// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update func (r *DecoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := logf.FromContext(ctx) @@ -252,7 +252,7 @@ func (r *DecoReconciler) createJob(ctx context.Context, deco *decositesv1alpha1. } if sa := job.Spec.Template.Spec.ServiceAccountName; sa != "" { - if err := r.ensureServiceAccount(ctx, deco.Namespace, sa, r.BuilderSAAnnotations); err != nil { + if err := ensureServiceAccount(ctx, r.Client, deco.Namespace, sa, r.BuilderSAAnnotations); err != nil { return fmt.Errorf("ensuring service account %q: %w", sa, err) } } @@ -267,25 +267,6 @@ func (r *DecoReconciler) createJob(ctx context.Context, deco *decositesv1alpha1. return nil } -// ensureServiceAccount creates or updates the ServiceAccount merging the provided annotations. -func (r *DecoReconciler) ensureServiceAccount(ctx context.Context, namespace, name string, annotations map[string]string) error { - sa := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, - } - _, err := controllerutil.CreateOrUpdate(ctx, r.Client, sa, func() error { - if len(annotations) > 0 { - if sa.Annotations == nil { - sa.Annotations = map[string]string{} - } - for k, v := range annotations { - sa.Annotations[k] = v - } - } - return nil - }) - return err -} - func buildPhaseFromJob(job *batchv1.Job) string { for _, c := range job.Status.Conditions { if c.Status != corev1.ConditionTrue { diff --git a/internal/controller/serviceaccount.go b/internal/controller/serviceaccount.go new file mode 100644 index 0000000..99fafeb --- /dev/null +++ b/internal/controller/serviceaccount.go @@ -0,0 +1,30 @@ +package controller + +import ( + "context" + + 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/controllerutil" +) + +// ensureServiceAccount creates or updates the named ServiceAccount in the given +// namespace, merging the provided annotations. It is idempotent. +func ensureServiceAccount(ctx context.Context, c client.Client, namespace, name string, annotations map[string]string) error { + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, + } + _, err := controllerutil.CreateOrUpdate(ctx, c, sa, func() error { + if len(annotations) > 0 { + if sa.Annotations == nil { + sa.Annotations = map[string]string{} + } + for k, v := range annotations { + sa.Annotations[k] = v + } + } + return nil + }) + return err +}