diff --git a/README.md b/README.md index 4d75400..5529453 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,14 @@ This project is built using the Kubernetes [operator SDK](https://sdk.operatorfr ## Example -This CRD will allow any pod using the `serviceAccount` named `s3put` to `Get` and `List` all objects in the s3 bucket with ARN `arn:aws:s3:::test-irsa-4gkut9fl` +This CRD will allow any pod using the `serviceAccount` named `s3-get-lister` to `Get` and `List` all objects in the s3 bucket with ARN `arn:aws:s3:::test-irsa-4gkut9fl` ``` apiVersion: irsa.voodoo.io/v1alpha1 kind: IamRoleServiceAccount metadata: - name: iamroleserviceaccount-test-sample + name: s3-get-lister spec: - serviceAccountName: s3put policy: statement: - resource: "arn:aws:s3:::test-irsa-4gkut9fl" @@ -33,8 +32,30 @@ What this operator does (from a user point of view) : - create an IAM Role with this policy attached to it - create a serviceAccount named as specified with the IAM Role capabilities -_NB :_ -- the name of the resource (`metadata.name`) is mandatory (because of k8s) and has no particular utility for the end-user +you can use the serviceAccount created by the irsa-operator by simply setting its name in your pods `spec.serviceAccountName` + +``` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: irsa-test + name: irsa-test +spec: + selector: + matchLabels: + app: irsa-test + template: + metadata: + labels: + app: irsa-test + spec: + serviceAccountName: s3-get-lister # <- HERE + containers: + - image: amazon/aws-cli + name: aws-cli + command: ["aws", "s3", "ls", "arn:aws:s3:::test-irsa-4gkut9fl"] +``` ## (manual) installation of the operator diff --git a/api/v1alpha1/iamroleserviceaccount_types.go b/api/v1alpha1/iamroleserviceaccount_types.go index 6bf42cd..30c911b 100644 --- a/api/v1alpha1/iamroleserviceaccount_types.go +++ b/api/v1alpha1/iamroleserviceaccount_types.go @@ -1,14 +1,13 @@ package v1alpha1 import ( - "errors" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // NewIamRoleServiceAccount is the IamRoleServiceAccount constructor -func NewIamRoleServiceAccount(name, ns, saName string, policyspec PolicySpec) *IamRoleServiceAccount { +func NewIamRoleServiceAccount(name, ns string, policyspec PolicySpec) *IamRoleServiceAccount { return &IamRoleServiceAccount{ TypeMeta: metav1.TypeMeta{ APIVersion: "irsa.voodoo.io/v1alpha1", @@ -19,8 +18,7 @@ func NewIamRoleServiceAccount(name, ns, saName string, policyspec PolicySpec) *I Namespace: ns, }, Spec: IamRoleServiceAccountSpec{ - ServiceAccountName: saName, - Policy: policyspec, + Policy: policyspec, }, } } @@ -37,16 +35,12 @@ func (irsa IamRoleServiceAccount) IsPendingDeletion() bool { // Validate returns an error if the IamRoleServiceAccountSpec is not valid func (irsa IamRoleServiceAccount) Validate() error { - if irsa.Spec.ServiceAccountName == "" { - return errors.New("empty serviceAccountName") - } return irsa.Spec.Policy.Validate() } // IamRoleServiceAccountSpec defines the desired state of IamRoleServiceAccount type IamRoleServiceAccountSpec struct { - ServiceAccountName string `json:"serviceAccountName"` - Policy PolicySpec `json:"policy"` + Policy PolicySpec `json:"policy"` } // IamRoleServiceAccountStatus defines the observed state of IamRoleServiceAccount diff --git a/api/v1alpha1/role_types.go b/api/v1alpha1/role_types.go index a2d3ba8..128da13 100644 --- a/api/v1alpha1/role_types.go +++ b/api/v1alpha1/role_types.go @@ -8,7 +8,7 @@ import ( ) // NewRole constructs a Role, setting mandatory fields for us -func NewRole(name, ns, serviceAccountName string) *Role { +func NewRole(name, ns string) *Role { return &Role{ TypeMeta: metav1.TypeMeta{ APIVersion: "irsa.voodoo.io/v1alpha1", @@ -19,7 +19,7 @@ func NewRole(name, ns, serviceAccountName string) *Role { Namespace: ns, }, Spec: RoleSpec{ - ServiceAccountName: serviceAccountName, + ServiceAccountName: name, }, } } diff --git a/aws/aws_test.go b/aws/aws_test.go index 6fbc9a3..42801d4 100644 --- a/aws/aws_test.go +++ b/aws/aws_test.go @@ -38,7 +38,7 @@ var _ = Describe("policy", func() { }) var _ = Describe("role", func() { - role := api.NewRole("name", "testns", "serviceaccountname") + role := api.NewRole("name", "testns") Context("given a valid role", func() { It("doesn't exist yet", func() { exists, err := awsmngr.RoleExists(role.AwsName(clusterName)) diff --git a/config/crd/bases/irsa.voodoo.io_iamroleserviceaccounts.yaml b/config/crd/bases/irsa.voodoo.io_iamroleserviceaccounts.yaml index 74f41a6..f8f9f24 100644 --- a/config/crd/bases/irsa.voodoo.io_iamroleserviceaccounts.yaml +++ b/config/crd/bases/irsa.voodoo.io_iamroleserviceaccounts.yaml @@ -62,11 +62,8 @@ spec: required: - statement type: object - serviceAccountName: - type: string required: - policy - - serviceAccountName type: object status: description: IamRoleServiceAccountStatus defines the observed state of diff --git a/config/helm/irsa/templates/irsa-operator.yml b/config/helm/irsa/templates/irsa-operator.yml index 57c8a4c..73be7b5 100644 --- a/config/helm/irsa/templates/irsa-operator.yml +++ b/config/helm/irsa/templates/irsa-operator.yml @@ -60,11 +60,8 @@ spec: required: - statement type: object - serviceAccountName: - type: string required: - policy - - serviceAccountName type: object status: description: IamRoleServiceAccountStatus defines the observed state of IamRoleServiceAccount diff --git a/controllers/iamroleserviceaccount_controller.go b/controllers/iamroleserviceaccount_controller.go index 23cf3ac..a15a6d8 100644 --- a/controllers/iamroleserviceaccount_controller.go +++ b/controllers/iamroleserviceaccount_controller.go @@ -124,7 +124,7 @@ func (r *IamRoleServiceAccountReconciler) admissionStep(ctx context.Context, irs } { // we check the serviceAccountName doesn't conflict with an existing one - if r.saWithNameExistsInNs(ctx, irsa.Spec.ServiceAccountName, irsa.ObjectMeta.Namespace) { + if r.saWithNameExistsInNs(ctx, irsa.ObjectMeta.Name, irsa.ObjectMeta.Namespace) { e := errors.New("service_account already exists") r.log.Info(e.Error()) @@ -190,7 +190,7 @@ func (r *IamRoleServiceAccountReconciler) reconcilerRoutine(ctx context.Context, { // service_account creation var ok completed - saAlreadyExists, ok = r.saAlreadyExists(ctx, irsa.Spec.ServiceAccountName, irsa.ObjectMeta.Namespace) + saAlreadyExists, ok = r.saAlreadyExists(ctx, irsa.ObjectMeta.Name, irsa.ObjectMeta.Namespace) if !ok { return ctrl.Result{Requeue: true}, nil } @@ -226,7 +226,7 @@ func (r *IamRoleServiceAccountReconciler) executeFinalizerIfPresent(ctx context. { // we delete the service account we created, we first need to ensure it is not owned by another operator (since it's a serviceaccount) sa := &corev1.ServiceAccount{} - if err := r.Get(ctx, types.NamespacedName{Namespace: irsa.Namespace, Name: irsa.Spec.ServiceAccountName}, sa); err != nil { + if err := r.Get(ctx, types.NamespacedName{Namespace: irsa.ObjectMeta.Namespace, Name: irsa.ObjectMeta.Name}, sa); err != nil { if !k8serrors.IsNotFound(err) { r.log.Error(err, "get resource failed") return false @@ -376,7 +376,6 @@ func (r *IamRoleServiceAccountReconciler) createRole(ctx context.Context, irsa * role := api.NewRole( irsa.ObjectMeta.Name, irsa.ObjectMeta.Namespace, - irsa.Spec.ServiceAccountName, ) // set this irsa instance as the owner of this role @@ -419,7 +418,7 @@ func (r *IamRoleServiceAccountReconciler) createServiceAccount(ctx context.Conte Kind: "ServiceAccount", }, ObjectMeta: metav1.ObjectMeta{ - Name: irsa.Spec.ServiceAccountName, + Name: irsa.ObjectMeta.Name, Namespace: irsa.ObjectMeta.Namespace, Annotations: map[string]string{ "eks.amazonaws.com/role-arn": role.Spec.RoleARN, diff --git a/controllers/iamroleserviceaccount_controller_test.go b/controllers/iamroleserviceaccount_controller_test.go index 8a3ede7..3078d48 100644 --- a/controllers/iamroleserviceaccount_controller_test.go +++ b/controllers/iamroleserviceaccount_controller_test.go @@ -12,7 +12,7 @@ var _ = Describe("IamRoleServiceAccount validity check", func() { It("fails at submission", func() { Expect( - api.NewIamRoleServiceAccount(validName(), testns, randString(), invalidPolicySpec).Validate(), + api.NewIamRoleServiceAccount(validName(), testns, invalidPolicySpec).Validate(), ).ShouldNot(Succeed()) }) }) @@ -23,18 +23,9 @@ var _ = Describe("IamRoleServiceAccount validity check", func() { {Resource: "arn:aws:s3:::my_corporate_bucket/exampleobject.png", Action: []string{"act1"}}, }, } - name := validName() - - Context("if the spec.serviceAccountName is an empty string", func() { - invalidSaName := "" - It("doesnt pass validation", func() { - irsa := api.NewIamRoleServiceAccount(name, testns, invalidSaName, validPolicy) - Expect(irsa.Validate()).ShouldNot(Succeed()) - }) - }) Context("if everything else is also ok", func() { - irsa := api.NewIamRoleServiceAccount(name, testns, validName(), validPolicy) + irsa := api.NewIamRoleServiceAccount(validName(), testns, validPolicy) It("it passes validation", func() { Expect(irsa.Validate()).Should(Succeed()) }) diff --git a/controllers/model_test.go b/controllers/model_test.go index 93274c3..23767ef 100644 --- a/controllers/model_test.go +++ b/controllers/model_test.go @@ -71,18 +71,17 @@ func run(irsaName string, wg *sync.WaitGroup) { } { // k8s - saName := validName() { // we submit the iamroleserviceaccount Spec to k8s createResource( - api.NewIamRoleServiceAccount(irsaName, testns, saName, submittedPolicy), + api.NewIamRoleServiceAccount(irsaName, testns, submittedPolicy), ).Should(Succeed()) } { // every CR must eventually reach an OK status & serviceAccount has been created foundPolicyInCondition(irsaName, testns, api.CrOK).Should(BeTrue()) foundRoleInCondition(irsaName, testns, api.CrOK).Should(BeTrue()) foundIrsaInCondition(irsaName, testns, api.IrsaOK).Should(BeTrue()) - findSa(saName, testns).Should(BeTrue()) + findSa(irsaName, testns).Should(BeTrue()) } }