mirror of
https://github.com/storytold/irsa-manager.git
synced 2026-10-09 00:09:43 +00:00
@@ -33,17 +33,34 @@ type IRSASetupSpec struct {
|
|||||||
// +required
|
// +required
|
||||||
Cleanup bool `json:"cleanup"`
|
Cleanup bool `json:"cleanup"`
|
||||||
|
|
||||||
// Mode (Optional, Future Feature) Defines how the controller will operate once this feature is enabled.
|
// Mode specifies the operation mode of the controller.
|
||||||
// Currently unused. Planned values:
|
// Possible values:
|
||||||
// - "selfhosted": For self-managed Kubernetes clusters.
|
// - "selfhosted": For self-managed Kubernetes clusters.
|
||||||
// - "eks": For Amazon EKS environments.
|
// - "eks": For Amazon EKS environments.
|
||||||
Mode string `json:"mode,omitempty"`
|
// Default: "selfhosted"
|
||||||
|
Mode SetupMode `json:"mode,omitempty"`
|
||||||
|
|
||||||
// Discovery configures the IdP Discovery process, essential for setting up IRSA by locating
|
// Discovery configures the IdP Discovery process, essential for setting up IRSA by locating
|
||||||
// the OIDC provider information.
|
// the OIDC provider information.
|
||||||
Discovery Discovery `json:"discovery"`
|
// Only applicable when Mode is "selfhosted".
|
||||||
|
// +optional
|
||||||
|
Discovery Discovery `json:"discovery,omitempty"`
|
||||||
|
|
||||||
|
// IamOIDCProvider configures IAM OIDC IamOIDCProvider Name
|
||||||
|
// Only applicable when Mode is "eks".
|
||||||
|
IamOIDCProvider string `json:"iamOIDCProvider,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:default=selfhosted
|
||||||
|
// +kubebuilder:validation:Enum=selfhosted;eks
|
||||||
|
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
|
||||||
|
type SetupMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ModeSelfhosted = SetupMode("selfhosted")
|
||||||
|
ModeEks = SetupMode("eks")
|
||||||
|
)
|
||||||
|
|
||||||
// Discovery holds the configuration for IdP Discovery, which is crucial for locating
|
// Discovery holds the configuration for IdP Discovery, which is crucial for locating
|
||||||
// the OIDC provider in a self-hosted environment.
|
// the OIDC provider in a self-hosted environment.
|
||||||
type Discovery struct {
|
type Discovery struct {
|
||||||
@@ -62,39 +79,39 @@ type S3Discovery struct {
|
|||||||
|
|
||||||
// IRSASetupStatus defines the observed state of IRSASetup
|
// IRSASetupStatus defines the observed state of IRSASetup
|
||||||
type IRSASetupStatus struct {
|
type IRSASetupStatus struct {
|
||||||
SelfHostedSetup []metav1.Condition `json:"selfHostedSetup,omitempty"`
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSelfhostedStatusConditions returns a pointer to the Status.Conditions slice
|
// GetStatusConditions returns a pointer to the Status.Conditions slice
|
||||||
func (in *IRSASetup) GetSelfhostedStatusConditions() *[]metav1.Condition {
|
func (in *IRSASetup) GetStatusConditions() *[]metav1.Condition {
|
||||||
return &in.Status.SelfHostedSetup
|
return &in.Status.Conditions
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupSelfHostedStatusReady(irsa IRSASetup, reason, message string) IRSASetup {
|
func SetupStatusReady(irsa IRSASetup, reason, message string) IRSASetup {
|
||||||
newCondition := metav1.Condition{
|
newCondition := metav1.Condition{
|
||||||
Type: ReadyCondition,
|
Type: ReadyCondition,
|
||||||
Status: metav1.ConditionTrue,
|
Status: metav1.ConditionTrue,
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
Message: message,
|
Message: message,
|
||||||
}
|
}
|
||||||
apimeta.SetStatusCondition(irsa.GetSelfhostedStatusConditions(), newCondition)
|
apimeta.SetStatusCondition(irsa.GetStatusConditions(), newCondition)
|
||||||
return irsa
|
return irsa
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelfHostedStatusNotReady(irsa IRSASetup, reason, message string) IRSASetup {
|
func StatusNotReady(irsa IRSASetup, reason, message string) IRSASetup {
|
||||||
newCondition := metav1.Condition{
|
newCondition := metav1.Condition{
|
||||||
Type: ReadyCondition,
|
Type: ReadyCondition,
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
Message: message,
|
Message: message,
|
||||||
}
|
}
|
||||||
apimeta.SetStatusCondition(irsa.GetSelfhostedStatusConditions(), newCondition)
|
apimeta.SetStatusCondition(irsa.GetStatusConditions(), newCondition)
|
||||||
return irsa
|
return irsa
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelfHostedReadyStatus
|
// ReadyStatus
|
||||||
func SelfHostedReadyStatus(irsa IRSASetup) *metav1.Condition {
|
func ReadyStatus(irsa IRSASetup) *metav1.Condition {
|
||||||
if c := apimeta.FindStatusCondition(irsa.Status.SelfHostedSetup, ReadyCondition); c != nil {
|
if c := apimeta.FindStatusCondition(irsa.Status.Conditions, ReadyCondition); c != nil {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -113,22 +130,30 @@ func HasConditionReason(cond *metav1.Condition, reasons ...string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsSelfHostedReadyConditionTrue(irsa IRSASetup) bool {
|
func IsReadyConditionTrue(irsa IRSASetup) bool {
|
||||||
return apimeta.IsStatusConditionTrue(irsa.Status.SelfHostedSetup, ReadyCondition)
|
return apimeta.IsStatusConditionTrue(irsa.Status.Conditions, ReadyCondition)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelfHostedReason string
|
type SelfhostedConditionReason string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SelfHostedReasonFailedWebhook SelfHostedReason = "SelfHostedSetupFailedWebhookCreation"
|
SelfHostedReasonFailedWebhook SelfhostedConditionReason = "SelfHostedSetupFailedWebhookCreation"
|
||||||
SelfHostedReasonFailedOidc SelfHostedReason = "SelfHostedSetupFailedOidcCreation"
|
SelfHostedReasonFailedOidc SelfhostedConditionReason = "SelfHostedSetupFailedOidcCreation"
|
||||||
SelfHostedReasonFailedKeys SelfHostedReason = "SelfHostedSetupFailedKeysCreation"
|
SelfHostedReasonFailedIssuer SelfhostedConditionReason = "SelfHostedSetupFailedIssuer"
|
||||||
SelfHostedReasonReady SelfHostedReason = "SelfHostedSetupReady"
|
SelfHostedReasonFailedKeys SelfhostedConditionReason = "SelfHostedSetupFailedKeysCreation"
|
||||||
|
SelfHostedReasonReady SelfhostedConditionReason = "SelfHostedSetupReady"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EksConditionReason string
|
||||||
|
|
||||||
|
const (
|
||||||
|
EksNotReady EksConditionReason = "EksOIDCNotReady"
|
||||||
|
EksReasonReady EksConditionReason = "EksOIDCSetupReady"
|
||||||
)
|
)
|
||||||
|
|
||||||
//+kubebuilder:object:root=true
|
//+kubebuilder:object:root=true
|
||||||
//+kubebuilder:subresource:status
|
//+kubebuilder:subresource:status
|
||||||
//+kubebuilder:printcolumn:name="SelfHostedReady",type="string",JSONPath=".status.selfHostedSetup[?(@.type==\"Ready\")].status",description=""
|
//+kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||||
|
|
||||||
// IRSASetup represents a configuration for setting up IAM Roles for Service Accounts (IRSA) in a Kubernetes cluster.
|
// IRSASetup represents a configuration for setting up IAM Roles for Service Accounts (IRSA) in a Kubernetes cluster.
|
||||||
type IRSASetup struct {
|
type IRSASetup struct {
|
||||||
|
|||||||
@@ -213,8 +213,8 @@ func (in *IRSASetupSpec) DeepCopy() *IRSASetupSpec {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *IRSASetupStatus) DeepCopyInto(out *IRSASetupStatus) {
|
func (in *IRSASetupStatus) DeepCopyInto(out *IRSASetupStatus) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.SelfHostedSetup != nil {
|
if in.Conditions != nil {
|
||||||
in, out := &in.SelfHostedSetup, &out.SelfHostedSetup
|
in, out := &in.Conditions, &out.Conditions
|
||||||
*out = make([]v1.Condition, len(*in))
|
*out = make([]v1.Condition, len(*in))
|
||||||
for i := range *in {
|
for i := range *in {
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ spec:
|
|||||||
scope: Namespaced
|
scope: Namespaced
|
||||||
versions:
|
versions:
|
||||||
- additionalPrinterColumns:
|
- additionalPrinterColumns:
|
||||||
- jsonPath: .status.selfHostedSetup[?(@.type=="Ready")].status
|
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||||
name: SelfHostedReady
|
name: Ready
|
||||||
type: string
|
type: string
|
||||||
name: v1alpha1
|
name: v1alpha1
|
||||||
schema:
|
schema:
|
||||||
@@ -52,6 +52,7 @@ spec:
|
|||||||
description: |-
|
description: |-
|
||||||
Discovery configures the IdP Discovery process, essential for setting up IRSA by locating
|
Discovery configures the IdP Discovery process, essential for setting up IRSA by locating
|
||||||
the OIDC provider information.
|
the OIDC provider information.
|
||||||
|
Only applicable when Mode is "selfhosted".
|
||||||
properties:
|
properties:
|
||||||
s3:
|
s3:
|
||||||
description: S3 specifies the AWS S3 bucket details where the
|
description: S3 specifies the AWS S3 bucket details where the
|
||||||
@@ -70,21 +71,32 @@ spec:
|
|||||||
- region
|
- region
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
iamOIDCProvider:
|
||||||
|
description: |-
|
||||||
|
IamOIDCProvider configures IAM OIDC IamOIDCProvider Name
|
||||||
|
Only applicable when Mode is "eks".
|
||||||
|
type: string
|
||||||
mode:
|
mode:
|
||||||
description: |-
|
description: |-
|
||||||
Mode (Optional, Future Feature) Defines how the controller will operate once this feature is enabled.
|
Mode specifies the operation mode of the controller.
|
||||||
Currently unused. Planned values:
|
Possible values:
|
||||||
- "selfhosted": For self-managed Kubernetes clusters.
|
- "selfhosted": For self-managed Kubernetes clusters.
|
||||||
- "eks": For Amazon EKS environments.
|
- "eks": For Amazon EKS environments.
|
||||||
|
Default: "selfhosted"
|
||||||
|
enum:
|
||||||
|
- selfhosted
|
||||||
|
- eks
|
||||||
type: string
|
type: string
|
||||||
|
x-kubernetes-validations:
|
||||||
|
- message: Value is immutable
|
||||||
|
rule: self == oldSelf
|
||||||
required:
|
required:
|
||||||
- cleanup
|
- cleanup
|
||||||
- discovery
|
|
||||||
type: object
|
type: object
|
||||||
status:
|
status:
|
||||||
description: IRSASetupStatus defines the observed state of IRSASetup
|
description: IRSASetupStatus defines the observed state of IRSASetup
|
||||||
properties:
|
properties:
|
||||||
selfHostedSetup:
|
conditions:
|
||||||
items:
|
items:
|
||||||
description: "Condition contains details for one aspect of the current
|
description: "Condition contains details for one aspect of the current
|
||||||
state of this API Resource.\n---\nThis struct is intended for
|
state of this API Resource.\n---\nThis struct is intended for
|
||||||
|
|||||||
@@ -71,5 +71,7 @@ spec:
|
|||||||
| nindent 10 }}
|
| nindent 10 }}
|
||||||
securityContext:
|
securityContext:
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
serviceAccountName: {{ include "irsa-manager.fullname" . }}-controller-manager
|
serviceAccountName: {{ include "irsa-manager.fullname" . }}-controller-manager
|
||||||
terminationGracePeriodSeconds: 10
|
terminationGracePeriodSeconds: 10
|
||||||
@@ -15,8 +15,8 @@ spec:
|
|||||||
scope: Namespaced
|
scope: Namespaced
|
||||||
versions:
|
versions:
|
||||||
- additionalPrinterColumns:
|
- additionalPrinterColumns:
|
||||||
- jsonPath: .status.selfHostedSetup[?(@.type=="Ready")].status
|
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||||
name: SelfHostedReady
|
name: Ready
|
||||||
type: string
|
type: string
|
||||||
name: v1alpha1
|
name: v1alpha1
|
||||||
schema:
|
schema:
|
||||||
@@ -53,6 +53,7 @@ spec:
|
|||||||
description: |-
|
description: |-
|
||||||
Discovery configures the IdP Discovery process, essential for setting up IRSA by locating
|
Discovery configures the IdP Discovery process, essential for setting up IRSA by locating
|
||||||
the OIDC provider information.
|
the OIDC provider information.
|
||||||
|
Only applicable when Mode is "selfhosted".
|
||||||
properties:
|
properties:
|
||||||
s3:
|
s3:
|
||||||
description: S3 specifies the AWS S3 bucket details where the
|
description: S3 specifies the AWS S3 bucket details where the
|
||||||
@@ -71,21 +72,32 @@ spec:
|
|||||||
- region
|
- region
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
iamOIDCProvider:
|
||||||
|
description: |-
|
||||||
|
IamOIDCProvider configures IAM OIDC IamOIDCProvider Name
|
||||||
|
Only applicable when Mode is "eks".
|
||||||
|
type: string
|
||||||
mode:
|
mode:
|
||||||
description: |-
|
description: |-
|
||||||
Mode (Optional, Future Feature) Defines how the controller will operate once this feature is enabled.
|
Mode specifies the operation mode of the controller.
|
||||||
Currently unused. Planned values:
|
Possible values:
|
||||||
- "selfhosted": For self-managed Kubernetes clusters.
|
- "selfhosted": For self-managed Kubernetes clusters.
|
||||||
- "eks": For Amazon EKS environments.
|
- "eks": For Amazon EKS environments.
|
||||||
|
Default: "selfhosted"
|
||||||
|
enum:
|
||||||
|
- selfhosted
|
||||||
|
- eks
|
||||||
type: string
|
type: string
|
||||||
|
x-kubernetes-validations:
|
||||||
|
- message: Value is immutable
|
||||||
|
rule: self == oldSelf
|
||||||
required:
|
required:
|
||||||
- cleanup
|
- cleanup
|
||||||
- discovery
|
|
||||||
type: object
|
type: object
|
||||||
status:
|
status:
|
||||||
description: IRSASetupStatus defines the observed state of IRSASetup
|
description: IRSASetupStatus defines the observed state of IRSASetup
|
||||||
properties:
|
properties:
|
||||||
selfHostedSetup:
|
conditions:
|
||||||
items:
|
items:
|
||||||
description: "Condition contains details for one aspect of the current
|
description: "Condition contains details for one aspect of the current
|
||||||
state of this API Resource.\n---\nThis struct is intended for
|
state of this API Resource.\n---\nThis struct is intended for
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
resources:
|
resources:
|
||||||
- manager.yaml
|
- manager.yaml
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
images:
|
||||||
|
- name: controller
|
||||||
|
newName: ghcr.io/kkb0318/irsa-manager
|
||||||
|
newTag: latest
|
||||||
|
|||||||
+19
-2
@@ -31,6 +31,8 @@ _Appears in:_
|
|||||||
| `s3` _[S3Discovery](#s3discovery)_ | S3 specifies the AWS S3 bucket details where the OIDC provider's discovery information is hosted. | | |
|
| `s3` _[S3Discovery](#s3discovery)_ | S3 specifies the AWS S3 bucket details where the OIDC provider's discovery information is hosted. | | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### IRSA
|
#### IRSA
|
||||||
|
|
||||||
|
|
||||||
@@ -102,8 +104,9 @@ _Appears in:_
|
|||||||
| Field | Description | Default | Validation |
|
| Field | Description | Default | Validation |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `cleanup` _boolean_ | Cleanup, when enabled, allows the IRSASetup to perform garbage collection<br />of resources that are no longer needed or managed. | | |
|
| `cleanup` _boolean_ | Cleanup, when enabled, allows the IRSASetup to perform garbage collection<br />of resources that are no longer needed or managed. | | |
|
||||||
| `mode` _string_ | Mode (Optional, Future Feature) Defines how the controller will operate once this feature is enabled.<br />Currently unused. Planned values:<br /> - "selfhosted": For self-managed Kubernetes clusters.<br /> - "eks": For Amazon EKS environments. | | |
|
| `mode` _[SetupMode](#setupmode)_ | Mode specifies the operation mode of the controller.<br />Possible values:<br /> - "selfhosted": For self-managed Kubernetes clusters.<br /> - "eks": For Amazon EKS environments.<br />Default: "selfhosted" | | Enum: [selfhosted eks] <br /> |
|
||||||
| `discovery` _[Discovery](#discovery)_ | Discovery configures the IdP Discovery process, essential for setting up IRSA by locating<br />the OIDC provider information. | | |
|
| `discovery` _[Discovery](#discovery)_ | Discovery configures the IdP Discovery process, essential for setting up IRSA by locating<br />the OIDC provider information.<br />Only applicable when Mode is "selfhosted". | | |
|
||||||
|
| `iamOIDCProvider` _string_ | IamOIDCProvider configures IAM OIDC IamOIDCProvider Name<br />Only applicable when Mode is "eks". | | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -164,3 +167,17 @@ _Appears in:_
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### SetupMode
|
||||||
|
|
||||||
|
_Underlying type:_ _string_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_Validation:_
|
||||||
|
- Enum: [selfhosted eks]
|
||||||
|
|
||||||
|
_Appears in:_
|
||||||
|
- [IRSASetupSpec](#irsasetupspec)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: irsa-manager.kkb0318.github.io/v1alpha1
|
||||||
|
kind: IRSASetup
|
||||||
|
metadata:
|
||||||
|
name: irsa-init
|
||||||
|
namespace: irsa-manager-system
|
||||||
|
spec:
|
||||||
|
mode: eks
|
||||||
|
cleanup: true
|
||||||
|
iamOIDCProvider: "oidc.eks.<region>.amazonaws.com/id/<id>"
|
||||||
+2
-2
@@ -6,11 +6,11 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
cleanup: true
|
cleanup: true
|
||||||
serviceAccount:
|
serviceAccount:
|
||||||
name: irsa1-sa
|
name: irsa111-sa
|
||||||
namespaces:
|
namespaces:
|
||||||
- kube-system
|
- kube-system
|
||||||
- default
|
- default
|
||||||
iamRole:
|
iamRole:
|
||||||
name: irsa1-role
|
name: irsa111-role
|
||||||
iamPolicies:
|
iamPolicies:
|
||||||
- AmazonS3FullAccess
|
- AmazonS3FullAccess
|
||||||
|
|||||||
@@ -51,13 +51,6 @@ type IRSAReconciler struct {
|
|||||||
|
|
||||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
// move the current state of the cluster closer to the desired state.
|
// move the current state of the cluster closer to the desired state.
|
||||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
|
||||||
// the IRSA object against the actual cluster state, and then
|
|
||||||
// perform operations to make the cluster state reflect the state specified by
|
|
||||||
// the user.
|
|
||||||
//
|
|
||||||
// For more details, check Reconcile and its Result here:
|
|
||||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcile
|
|
||||||
func (r *IRSAReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
func (r *IRSAReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
log := ctrllog.FromContext(ctx)
|
log := ctrllog.FromContext(ctx)
|
||||||
obj := &irsav1alpha1.IRSA{}
|
obj := &irsav1alpha1.IRSA{}
|
||||||
@@ -152,7 +145,7 @@ func (r *IRSAReconciler) reconcile(ctx context.Context, obj *irsav1alpha1.IRSA,
|
|||||||
return fmt.Errorf("error converting to IRSASetup for %s: %v", list.Items[0].GetName(), err)
|
return fmt.Errorf("error converting to IRSASetup for %s: %v", list.Items[0].GetName(), err)
|
||||||
}
|
}
|
||||||
serviceAccount := obj.Spec.ServiceAccount
|
serviceAccount := obj.Spec.ServiceAccount
|
||||||
issuerMeta, err := issuer.NewS3IssuerMeta(&irsaSetup.Spec.Discovery.S3)
|
issuerMeta, err := issuer.NewOIDCIssuerMeta(irsaSetup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ var _ = Describe("IRSA Controller", func() {
|
|||||||
f: newServiceAccount,
|
f: newServiceAccount,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
f := createCallBack(ctx, r, typeNamespacedName, obj)
|
f := createCallBackForFixingNamespace(ctx, r, typeNamespacedName, obj)
|
||||||
|
|
||||||
By("Add Namespace 'default'")
|
By("Add Namespace 'default'")
|
||||||
f(obj.Spec.ServiceAccount.Name, []string{"default", "kube-system"})
|
f(obj.Spec.ServiceAccount.Name, []string{"default", "kube-system"})
|
||||||
@@ -222,6 +222,75 @@ var _ = Describe("IRSA Controller", func() {
|
|||||||
f: newServiceAccount,
|
f: newServiceAccount,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
By("removing the custom resource for the Kind")
|
||||||
|
Eventually(func() error {
|
||||||
|
return k8sClient.Delete(ctx, obj)
|
||||||
|
}, timeout).Should(Succeed())
|
||||||
|
_, err = r.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).To(Not(HaveOccurred()))
|
||||||
|
for _, expect := range expected {
|
||||||
|
checkNoExist(expect)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should update serviceaccount successfully with EKS mode",
|
||||||
|
obj: &irsav1alpha1.IRSA{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-resource-eks-1",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: irsav1alpha1.IRSASpec{
|
||||||
|
Cleanup: true,
|
||||||
|
ServiceAccount: irsav1alpha1.IRSAServiceAccount{
|
||||||
|
Name: "sa-eks-1",
|
||||||
|
Namespaces: []string{
|
||||||
|
"kube-system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
irsaSetupObj: newMockIRSASetupForEKS(),
|
||||||
|
f: func(r *IRSAReconciler, obj *irsav1alpha1.IRSA) {
|
||||||
|
expected := []expectedResource{
|
||||||
|
{
|
||||||
|
NamespacedName: types.NamespacedName{Name: "sa-eks-1", Namespace: "kube-system"},
|
||||||
|
f: newServiceAccount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NamespacedName: types.NamespacedName{Name: "sa-eks-1", Namespace: "default"},
|
||||||
|
f: newServiceAccount,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
By("Reconciling the created resource")
|
||||||
|
typeNamespacedName := types.NamespacedName{
|
||||||
|
Name: obj.Name,
|
||||||
|
Namespace: obj.Namespace,
|
||||||
|
}
|
||||||
|
_, err := r.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
checkExist(
|
||||||
|
expectedResource{
|
||||||
|
NamespacedName: types.NamespacedName{Name: "sa-eks-1", Namespace: "kube-system"},
|
||||||
|
f: newServiceAccount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
f := createCallBackForFixingNamespace(ctx, r, typeNamespacedName, obj)
|
||||||
|
|
||||||
|
By("Add Namespace 'default'")
|
||||||
|
f(obj.Spec.ServiceAccount.Name, []string{"default", "kube-system"})
|
||||||
|
By("Remove Namespace 'kube-system'")
|
||||||
|
f(obj.Spec.ServiceAccount.Name, []string{"default"})
|
||||||
|
checkNoExist(expectedResource{
|
||||||
|
NamespacedName: types.NamespacedName{Name: "sa-eks-1", Namespace: "kube-system"},
|
||||||
|
f: newServiceAccount,
|
||||||
|
})
|
||||||
|
|
||||||
By("removing the custom resource for the Kind")
|
By("removing the custom resource for the Kind")
|
||||||
Eventually(func() error {
|
Eventually(func() error {
|
||||||
return k8sClient.Delete(ctx, obj)
|
return k8sClient.Delete(ctx, obj)
|
||||||
@@ -298,7 +367,20 @@ func newMockIRSASetup() *irsav1alpha1.IRSASetup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCallBack(ctx context.Context, r *IRSAReconciler, typeNamespacedName types.NamespacedName, obj *irsav1alpha1.IRSA) func(name string, namespaces []string) {
|
func newMockIRSASetupForEKS() *irsav1alpha1.IRSASetup {
|
||||||
|
return &irsav1alpha1.IRSASetup{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: irsav1alpha1.IRSASetupSpec{
|
||||||
|
Mode: irsav1alpha1.ModeEks,
|
||||||
|
IamOIDCProvider: "oidc.example",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createCallBackForFixingNamespace(ctx context.Context, r *IRSAReconciler, typeNamespacedName types.NamespacedName, obj *irsav1alpha1.IRSA) func(name string, namespaces []string) {
|
||||||
return func(name string, namespaces []string) {
|
return func(name string, namespaces []string) {
|
||||||
fixNamespacesAndReconcile(ctx, r, typeNamespacedName, obj, name, namespaces)
|
fixNamespacesAndReconcile(ctx, r, typeNamespacedName, obj, name, namespaces)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
|
|
||||||
irsav1alpha1 "github.com/kkb0318/irsa-manager/api/v1alpha1"
|
irsav1alpha1 "github.com/kkb0318/irsa-manager/api/v1alpha1"
|
||||||
awsclient "github.com/kkb0318/irsa-manager/internal/aws"
|
awsclient "github.com/kkb0318/irsa-manager/internal/aws"
|
||||||
|
"github.com/kkb0318/irsa-manager/internal/eks"
|
||||||
"github.com/kkb0318/irsa-manager/internal/handler"
|
"github.com/kkb0318/irsa-manager/internal/handler"
|
||||||
"github.com/kkb0318/irsa-manager/internal/issuer"
|
"github.com/kkb0318/irsa-manager/internal/issuer"
|
||||||
"github.com/kkb0318/irsa-manager/internal/kubernetes"
|
"github.com/kkb0318/irsa-manager/internal/kubernetes"
|
||||||
@@ -59,13 +60,6 @@ type IRSASetupReconciler struct {
|
|||||||
|
|
||||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
// move the current state of the cluster closer to the desired state.
|
// move the current state of the cluster closer to the desired state.
|
||||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
|
||||||
// the IRSASetup object against the actual cluster state, and then
|
|
||||||
// perform operations to make the cluster state reflect the state specified by
|
|
||||||
// the user.
|
|
||||||
//
|
|
||||||
// For more details, check Reconcile and its Result here:
|
|
||||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcile
|
|
||||||
func (r *IRSASetupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
func (r *IRSASetupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
log := ctrllog.FromContext(ctx)
|
log := ctrllog.FromContext(ctx)
|
||||||
obj := &irsav1alpha1.IRSASetup{}
|
obj := &irsav1alpha1.IRSASetup{}
|
||||||
@@ -104,7 +98,11 @@ func (r *IRSASetupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
if !obj.DeletionTimestamp.IsZero() {
|
if !obj.DeletionTimestamp.IsZero() {
|
||||||
err = r.reconcileDelete(ctx, obj, kubeClient)
|
if obj.Spec.Mode == irsav1alpha1.ModeEks {
|
||||||
|
err = r.reconcileDeleteEks()
|
||||||
|
} else {
|
||||||
|
err = r.reconcileDeleteSelfhosted(ctx, obj, kubeClient)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
@@ -125,11 +123,17 @@ func (r *IRSASetupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *IRSASetupReconciler) reconcile(ctx context.Context, obj *irsav1alpha1.IRSASetup, kubeClient *kubernetes.KubernetesClient) error {
|
func (r *IRSASetupReconciler) reconcile(ctx context.Context, obj *irsav1alpha1.IRSASetup, kubeClient *kubernetes.KubernetesClient) error {
|
||||||
err := reconcileSelfhosted(ctx, obj, r.AwsClient, kubeClient)
|
if obj.Spec.Mode == irsav1alpha1.ModeEks {
|
||||||
return err
|
return reconcileEks(ctx, obj)
|
||||||
|
}
|
||||||
|
return reconcileSelfhosted(ctx, obj, r.AwsClient, kubeClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *IRSASetupReconciler) reconcileDelete(ctx context.Context, obj *irsav1alpha1.IRSASetup, kubeClient *kubernetes.KubernetesClient) error {
|
func (r *IRSASetupReconciler) reconcileDeleteEks() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *IRSASetupReconciler) reconcileDeleteSelfhosted(ctx context.Context, obj *irsav1alpha1.IRSASetup, kubeClient *kubernetes.KubernetesClient) error {
|
||||||
if !obj.Spec.Cleanup {
|
if !obj.Spec.Cleanup {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -154,7 +158,7 @@ func (r *IRSASetupReconciler) reconcileDelete(ctx context.Context, obj *irsav1al
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
issuerMeta, err := issuer.NewS3IssuerMeta(&obj.Spec.Discovery.S3)
|
issuerMeta, err := issuer.NewOIDCIssuerMeta(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -172,7 +176,7 @@ func (r *IRSASetupReconciler) reconcileDelete(ctx context.Context, obj *irsav1al
|
|||||||
// - The function enforces a 'force update' strategy in case of failures related to kubernetes Secrets creation or OIDC setup. This means it starts from scratch to ensure all components are correctly configured.
|
// - The function enforces a 'force update' strategy in case of failures related to kubernetes Secrets creation or OIDC setup. This means it starts from scratch to ensure all components are correctly configured.
|
||||||
func reconcileSelfhosted(ctx context.Context, obj *irsav1alpha1.IRSASetup, awsClient awsclient.AwsClient, kubeClient *kubernetes.KubernetesClient) error {
|
func reconcileSelfhosted(ctx context.Context, obj *irsav1alpha1.IRSASetup, awsClient awsclient.AwsClient, kubeClient *kubernetes.KubernetesClient) error {
|
||||||
log := ctrllog.FromContext(ctx)
|
log := ctrllog.FromContext(ctx)
|
||||||
if irsav1alpha1.IsSelfHostedReadyConditionTrue(*obj) {
|
if irsav1alpha1.IsReadyConditionTrue(*obj) {
|
||||||
// Selfhosted Setup have already succeeded
|
// Selfhosted Setup have already succeeded
|
||||||
log.Info("the self-hosted resources have already set up")
|
log.Info("the self-hosted resources have already set up")
|
||||||
return nil
|
return nil
|
||||||
@@ -205,20 +209,22 @@ func reconcileSelfhosted(ctx context.Context, obj *irsav1alpha1.IRSASetup, awsCl
|
|||||||
|
|
||||||
// e is set only when an error occurs in an external dependency process and is reflected in the CRs status
|
// e is set only when an error occurs in an external dependency process and is reflected in the CRs status
|
||||||
var e error
|
var e error
|
||||||
var reason irsav1alpha1.SelfHostedReason
|
var reason irsav1alpha1.SelfhostedConditionReason
|
||||||
defer func() {
|
defer func() {
|
||||||
if e != nil {
|
if e != nil {
|
||||||
*obj = irsav1alpha1.SelfHostedStatusNotReady(*obj, string(reason), e.Error())
|
*obj = irsav1alpha1.StatusNotReady(*obj, string(reason), e.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
forceUpdate := irsav1alpha1.HasConditionReason(
|
forceUpdate := irsav1alpha1.HasConditionReason(
|
||||||
irsav1alpha1.SelfHostedReadyStatus(*obj),
|
irsav1alpha1.ReadyStatus(*obj),
|
||||||
string(irsav1alpha1.SelfHostedReasonFailedKeys),
|
string(irsav1alpha1.SelfHostedReasonFailedKeys),
|
||||||
string(irsav1alpha1.SelfHostedReasonFailedOidc),
|
string(irsav1alpha1.SelfHostedReasonFailedOidc),
|
||||||
)
|
)
|
||||||
issuerMeta, err := issuer.NewS3IssuerMeta(&obj.Spec.Discovery.S3)
|
issuerMeta, err := issuer.NewOIDCIssuerMeta(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
e = err
|
||||||
|
reason = irsav1alpha1.SelfHostedReasonFailedIssuer
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = selfhosted.Execute(
|
err = selfhosted.Execute(
|
||||||
@@ -253,11 +259,34 @@ func reconcileSelfhosted(ctx context.Context, obj *irsav1alpha1.IRSASetup, awsCl
|
|||||||
reason = irsav1alpha1.SelfHostedReasonFailedWebhook
|
reason = irsav1alpha1.SelfHostedReasonFailedWebhook
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*obj = irsav1alpha1.SetupSelfHostedStatusReady(*obj, string(irsav1alpha1.SelfHostedReasonReady), "successfully setup resources for self-hosted")
|
*obj = irsav1alpha1.SetupStatusReady(*obj, string(irsav1alpha1.SelfHostedReasonReady), "successfully setup resources for self-hosted")
|
||||||
log.Info("the self-hosted resources have successfully set up")
|
log.Info("the self-hosted resources have successfully set up")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reconcileEks iterates tasks for EKS mode.
|
||||||
|
func reconcileEks(ctx context.Context, obj *irsav1alpha1.IRSASetup) error {
|
||||||
|
log := ctrllog.FromContext(ctx)
|
||||||
|
var reason irsav1alpha1.EksConditionReason
|
||||||
|
|
||||||
|
// e is set only when an error occurs in an external dependency process and is reflected in the CRs status
|
||||||
|
var e error
|
||||||
|
defer func() {
|
||||||
|
if e != nil {
|
||||||
|
*obj = irsav1alpha1.StatusNotReady(*obj, string(reason), e.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err := eks.Validate(obj)
|
||||||
|
if err != nil {
|
||||||
|
e = err
|
||||||
|
reason = irsav1alpha1.EksNotReady
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*obj = irsav1alpha1.SetupStatusReady(*obj, string(irsav1alpha1.EksReasonReady), "successfully setup for eks")
|
||||||
|
log.Info("The OIDC for EKS has been successfully set up")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func newOIDCIdpFactory(ctx context.Context, obj *irsav1alpha1.IRSASetup, jwk *selfhosted.JWK, awsClient awsclient.AwsClient) (selfhosted.OIDCIdPFactory, error) {
|
func newOIDCIdpFactory(ctx context.Context, obj *irsav1alpha1.IRSASetup, jwk *selfhosted.JWK, awsClient awsclient.AwsClient) (selfhosted.OIDCIdPFactory, error) {
|
||||||
region := obj.Spec.Discovery.S3.Region
|
region := obj.Spec.Discovery.S3.Region
|
||||||
bucketName := obj.Spec.Discovery.S3.BucketName
|
bucketName := obj.Spec.Discovery.S3.BucketName
|
||||||
|
|||||||
@@ -288,6 +288,42 @@ var _ = Describe("IRSASetup Controller", func() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "EKS mode",
|
||||||
|
obj: &irsav1alpha1.IRSASetup{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-resource-eks1",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: irsav1alpha1.IRSASetupSpec{
|
||||||
|
Cleanup: false,
|
||||||
|
Mode: irsav1alpha1.ModeEks,
|
||||||
|
IamOIDCProvider: "oidc.example",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
f: func(r *IRSASetupReconciler, obj *irsav1alpha1.IRSASetup) {
|
||||||
|
typeNamespacedName := types.NamespacedName{
|
||||||
|
Name: obj.Name,
|
||||||
|
Namespace: obj.Namespace,
|
||||||
|
}
|
||||||
|
_, err := r.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
_, err = r.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).To(Not(HaveOccurred()))
|
||||||
|
By("removing the custom resource (not cleanup)")
|
||||||
|
Eventually(func() error {
|
||||||
|
return k8sClient.Delete(ctx, obj)
|
||||||
|
}, timeout).Should(Succeed())
|
||||||
|
_, err = r.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).To(Not(HaveOccurred()))
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
It(tt.name, func() {
|
It(tt.name, func() {
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package eks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
irsav1alpha1 "github.com/kkb0318/irsa-manager/api/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Validate(obj *irsav1alpha1.IRSASetup) error {
|
||||||
|
if obj.Spec.IamOIDCProvider == "" {
|
||||||
|
return fmt.Errorf("IamOIDCProvider parameter must be set when Mode is 'eks'")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -11,27 +11,52 @@ type OIDCIssuerMeta interface {
|
|||||||
IssuerUrl() string
|
IssuerUrl() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type S3IssuerMeta struct {
|
type s3IssuerMeta struct {
|
||||||
region string
|
region string
|
||||||
bucketName string
|
bucketName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewS3IssuerMeta(s3 *irsav1alpha1.S3Discovery) (*S3IssuerMeta, error) {
|
func NewOIDCIssuerMeta(i *irsav1alpha1.IRSASetup) (OIDCIssuerMeta, error) {
|
||||||
|
if i.Spec.Mode == irsav1alpha1.ModeEks {
|
||||||
|
return newIamOIDCProviderIssuerMeta(i.Spec.IamOIDCProvider)
|
||||||
|
}
|
||||||
|
return newS3IssuerMeta(&i.Spec.Discovery.S3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newS3IssuerMeta(s3 *irsav1alpha1.S3Discovery) (*s3IssuerMeta, error) {
|
||||||
region := s3.Region
|
region := s3.Region
|
||||||
bucketName := s3.BucketName
|
bucketName := s3.BucketName
|
||||||
if region == "" || bucketName == "" {
|
if region == "" || bucketName == "" {
|
||||||
return nil, fmt.Errorf("s3 region and bucket name must not be empty. region: %s, bucketName: %s", region, bucketName)
|
return nil, fmt.Errorf("s3 region and bucket name must not be empty. region: %s, bucketName: %s", region, bucketName)
|
||||||
}
|
}
|
||||||
return &S3IssuerMeta{region, bucketName}, nil
|
return &s3IssuerMeta{region, bucketName}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *S3IssuerMeta) IssuerHostPath() string {
|
func (i *s3IssuerMeta) IssuerHostPath() string {
|
||||||
return fmt.Sprintf("s3-%s.amazonaws.com/%s", i.region, i.bucketName)
|
return fmt.Sprintf("s3-%s.amazonaws.com/%s", i.region, i.bucketName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssuerUrl constructs the URL path for the OIDC issuer based on the provided AWS region and bucket name.
|
// IssuerUrl constructs the URL path for the OIDC issuer based on the provided AWS region and bucket name.
|
||||||
// This utility function generates the expected host path for accessing the OIDC configuration stored in an S3 bucket.
|
// This utility function generates the expected host path for accessing the OIDC configuration stored in an S3 bucket.
|
||||||
func (i *S3IssuerMeta) IssuerUrl() string {
|
func (i *s3IssuerMeta) IssuerUrl() string {
|
||||||
return fmt.Sprintf("https://%s", i.
|
return fmt.Sprintf("https://%s", i.IssuerHostPath())
|
||||||
IssuerHostPath())
|
}
|
||||||
|
|
||||||
|
func newIamOIDCProviderIssuerMeta(providerName string) (*iamOIDCProviderIssuerMeta, error) {
|
||||||
|
if providerName == "" {
|
||||||
|
return nil, fmt.Errorf("IAM OIDC Provider Name must not be empty")
|
||||||
|
}
|
||||||
|
return &iamOIDCProviderIssuerMeta{providerName}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type iamOIDCProviderIssuerMeta struct {
|
||||||
|
providerName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iamOIDCProviderIssuerMeta) IssuerHostPath() string {
|
||||||
|
return i.providerName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iamOIDCProviderIssuerMeta) IssuerUrl() string {
|
||||||
|
return fmt.Sprintf("https://%s", i.IssuerHostPath())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user