mirror of
https://github.com/storytold/irsa-manager.git
synced 2026-10-09 00:09:43 +00:00
irsa Status Management
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ReadyCondition indicates the resource is ready and fully reconciled.
|
||||||
|
// If the Condition is False, the resource SHOULD be considered to be in the process of reconciling and not a
|
||||||
|
// representation of actual state.
|
||||||
|
ReadyCondition string = "Ready"
|
||||||
|
)
|
||||||
@@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,12 +56,47 @@ type IamRole struct {
|
|||||||
|
|
||||||
// IRSAStatus defines the observed state of IRSA
|
// IRSAStatus defines the observed state of IRSA
|
||||||
type IRSAStatus struct {
|
type IRSAStatus struct {
|
||||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
// Important: Run "make" to regenerate code after modifying this file
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetIRSAStatusConditions returns a pointer to the Status.Conditions slice
|
||||||
|
func (in *IRSA) GetIRSAStatusConditions() *[]metav1.Condition {
|
||||||
|
return &in.Status.Conditions
|
||||||
|
}
|
||||||
|
|
||||||
|
func IRSAStatusReady(irsa IRSA, reason, message string) IRSA {
|
||||||
|
newCondition := metav1.Condition{
|
||||||
|
Type: ReadyCondition,
|
||||||
|
Status: metav1.ConditionTrue,
|
||||||
|
Reason: reason,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
apimeta.SetStatusCondition(irsa.GetIRSAStatusConditions(), newCondition)
|
||||||
|
return irsa
|
||||||
|
}
|
||||||
|
|
||||||
|
func IRSAStatusNotReady(irsa IRSA, reason, message string) IRSA {
|
||||||
|
newCondition := metav1.Condition{
|
||||||
|
Type: ReadyCondition,
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
Reason: reason,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
apimeta.SetStatusCondition(irsa.GetIRSAStatusConditions(), newCondition)
|
||||||
|
return irsa
|
||||||
|
}
|
||||||
|
|
||||||
|
type IRSAReason string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IRSAReasonFailedRoleUpdate IRSAReason = "IRSAFailedRoleUpdate"
|
||||||
|
IRSAReasonFailedK8sApply IRSAReason = "IRSAFailedApplyingResources"
|
||||||
|
IRSAReasonReady IRSAReason = "IRSAReady"
|
||||||
|
)
|
||||||
|
|
||||||
//+kubebuilder:object:root=true
|
//+kubebuilder:object:root=true
|
||||||
//+kubebuilder:subresource:status
|
//+kubebuilder:subresource:status
|
||||||
|
//+kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||||
|
|
||||||
// IRSA is the Schema for the irsas API
|
// IRSA is the Schema for the irsas API
|
||||||
type IRSA struct {
|
type IRSA struct {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
@@ -81,14 +80,14 @@ type IRSASetupStatus struct {
|
|||||||
SelfHostedSetup []metav1.Condition `json:"selfHostedSetup,omitempty"`
|
SelfHostedSetup []metav1.Condition `json:"selfHostedSetup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStatusConditions returns a pointer to the Status.Conditions slice
|
// GetSelfhostedStatusConditions returns a pointer to the Status.Conditions slice
|
||||||
func (in *IRSASetup) GetSelfhostedStatusConditions() *[]metav1.Condition {
|
func (in *IRSASetup) GetSelfhostedStatusConditions() *[]metav1.Condition {
|
||||||
return &in.Status.SelfHostedSetup
|
return &in.Status.SelfHostedSetup
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupSelfHostedStatusReady(irsa IRSASetup, reason, message string) IRSASetup {
|
func SetupSelfHostedStatusReady(irsa IRSASetup, reason, message string) IRSASetup {
|
||||||
newCondition := metav1.Condition{
|
newCondition := metav1.Condition{
|
||||||
Type: meta.ReadyCondition,
|
Type: ReadyCondition,
|
||||||
Status: metav1.ConditionTrue,
|
Status: metav1.ConditionTrue,
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
Message: message,
|
Message: message,
|
||||||
@@ -99,7 +98,7 @@ func SetupSelfHostedStatusReady(irsa IRSASetup, reason, message string) IRSASetu
|
|||||||
|
|
||||||
func SelfHostedStatusNotReady(irsa IRSASetup, reason, message string) IRSASetup {
|
func SelfHostedStatusNotReady(irsa IRSASetup, reason, message string) IRSASetup {
|
||||||
newCondition := metav1.Condition{
|
newCondition := metav1.Condition{
|
||||||
Type: meta.ReadyCondition,
|
Type: ReadyCondition,
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
Message: message,
|
Message: message,
|
||||||
@@ -110,7 +109,7 @@ func SelfHostedStatusNotReady(irsa IRSASetup, reason, message string) IRSASetup
|
|||||||
|
|
||||||
// SelfHostedReadyStatus
|
// SelfHostedReadyStatus
|
||||||
func SelfHostedReadyStatus(irsa IRSASetup) *metav1.Condition {
|
func SelfHostedReadyStatus(irsa IRSASetup) *metav1.Condition {
|
||||||
if c := apimeta.FindStatusCondition(irsa.Status.SelfHostedSetup, meta.ReadyCondition); c != nil {
|
if c := apimeta.FindStatusCondition(irsa.Status.SelfHostedSetup, ReadyCondition); c != nil {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -130,7 +129,7 @@ func HasConditionReason(cond *metav1.Condition, reasons ...string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsSelfHostedReadyConditionTrue(irsa IRSASetup) bool {
|
func IsSelfHostedReadyConditionTrue(irsa IRSASetup) bool {
|
||||||
return apimeta.IsStatusConditionTrue(irsa.Status.SelfHostedSetup, meta.ReadyCondition)
|
return apimeta.IsStatusConditionTrue(irsa.Status.SelfHostedSetup, ReadyCondition)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelfHostedReason string
|
type SelfHostedReason string
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (in *IRSA) DeepCopyInto(out *IRSA) {
|
|||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
in.Spec.DeepCopyInto(&out.Spec)
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
out.Status = in.Status
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSA.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSA.
|
||||||
@@ -259,6 +259,13 @@ func (in *IRSASpec) DeepCopy() *IRSASpec {
|
|||||||
// 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 *IRSAStatus) DeepCopyInto(out *IRSAStatus) {
|
func (in *IRSAStatus) DeepCopyInto(out *IRSAStatus) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Conditions != nil {
|
||||||
|
in, out := &in.Conditions, &out.Conditions
|
||||||
|
*out = make([]v1.Condition, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSAStatus.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSAStatus.
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ spec:
|
|||||||
singular: irsa
|
singular: irsa
|
||||||
scope: Namespaced
|
scope: Namespaced
|
||||||
versions:
|
versions:
|
||||||
- name: v1alpha1
|
- additionalPrinterColumns:
|
||||||
|
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||||
|
name: Ready
|
||||||
|
type: string
|
||||||
|
name: v1alpha1
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
description: IRSA is the Schema for the irsas API
|
description: IRSA is the Schema for the irsas API
|
||||||
@@ -78,6 +82,76 @@ spec:
|
|||||||
type: object
|
type: object
|
||||||
status:
|
status:
|
||||||
description: IRSAStatus defines the observed state of IRSA
|
description: IRSAStatus defines the observed state of IRSA
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
items:
|
||||||
|
description: "Condition contains details for one aspect of the current
|
||||||
|
state of this API Resource.\n---\nThis struct is intended for
|
||||||
|
direct use as an array at the field path .status.conditions. For
|
||||||
|
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
|
||||||
|
observations of a foo's current state.\n\t // Known .status.conditions.type
|
||||||
|
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
|
||||||
|
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
|
||||||
|
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||||
|
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
|
||||||
|
\ // other fields\n\t}"
|
||||||
|
properties:
|
||||||
|
lastTransitionTime:
|
||||||
|
description: |-
|
||||||
|
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||||
|
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
description: |-
|
||||||
|
message is a human readable message indicating details about the transition.
|
||||||
|
This may be an empty string.
|
||||||
|
maxLength: 32768
|
||||||
|
type: string
|
||||||
|
observedGeneration:
|
||||||
|
description: |-
|
||||||
|
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||||
|
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||||
|
with respect to the current state of the instance.
|
||||||
|
format: int64
|
||||||
|
minimum: 0
|
||||||
|
type: integer
|
||||||
|
reason:
|
||||||
|
description: |-
|
||||||
|
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||||
|
Producers of specific condition types may define expected values and meanings for this field,
|
||||||
|
and whether the values are considered a guaranteed API.
|
||||||
|
The value should be a CamelCase string.
|
||||||
|
This field may not be empty.
|
||||||
|
maxLength: 1024
|
||||||
|
minLength: 1
|
||||||
|
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
description: status of the condition, one of True, False, Unknown.
|
||||||
|
enum:
|
||||||
|
- "True"
|
||||||
|
- "False"
|
||||||
|
- Unknown
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: |-
|
||||||
|
type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||||
|
---
|
||||||
|
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
|
||||||
|
useful (see .node.status.conditions), the ability to deconflict is important.
|
||||||
|
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||||
|
maxLength: 316
|
||||||
|
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- lastTransitionTime
|
||||||
|
- message
|
||||||
|
- reason
|
||||||
|
- status
|
||||||
|
- type
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
served: true
|
served: true
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ IRSA is the Schema for the irsas API
|
|||||||
| `spec` _[IRSASpec](#irsaspec)_ | | | |
|
| `spec` _[IRSASpec](#irsaspec)_ | | | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### IRSAServiceAccount
|
#### IRSAServiceAccount
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -159,10 +159,23 @@ func (r *IRSAReconciler) reconcile(ctx context.Context, obj *irsav1alpha1.IRSA,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
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)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// e is set only when an error occurs in an external dependency process and is reflected in the CRs status
|
||||||
|
var e error
|
||||||
|
var reason irsav1alpha1.IRSAReason
|
||||||
|
defer func() {
|
||||||
|
if e != nil {
|
||||||
|
*obj = irsav1alpha1.IRSAStatusNotReady(*obj, string(reason), e.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
accountId, err := r.AwsClient.StsClient().GetAccountId()
|
accountId, err := r.AwsClient.StsClient().GetAccountId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
e = err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
roleManager := awsclient.RoleManager{
|
roleManager := awsclient.RoleManager{
|
||||||
@@ -171,18 +184,17 @@ func (r *IRSAReconciler) reconcile(ctx context.Context, obj *irsav1alpha1.IRSA,
|
|||||||
Policies: obj.Spec.IamPolicies,
|
Policies: obj.Spec.IamPolicies,
|
||||||
AccountId: accountId,
|
AccountId: accountId,
|
||||||
}
|
}
|
||||||
issuerMeta, err := issuer.NewS3IssuerMeta(&irsaSetup.Spec.Discovery.S3)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = r.AwsClient.IamClient().CreateIRSARole(
|
err = r.AwsClient.IamClient().CreateIRSARole(
|
||||||
ctx,
|
ctx,
|
||||||
issuerMeta,
|
issuerMeta,
|
||||||
roleManager,
|
roleManager,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
e = err
|
||||||
|
reason = irsav1alpha1.IRSAReasonFailedRoleUpdate
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeHandler := handler.NewKubernetesHandler(kubeClient)
|
kubeHandler := handler.NewKubernetesHandler(kubeClient)
|
||||||
|
|
||||||
for _, ns := range serviceAccount.Namespaces {
|
for _, ns := range serviceAccount.Namespaces {
|
||||||
@@ -192,7 +204,14 @@ func (r *IRSAReconciler) reconcile(ctx context.Context, obj *irsav1alpha1.IRSA,
|
|||||||
})
|
})
|
||||||
kubeHandler.Append(sa)
|
kubeHandler.Append(sa)
|
||||||
}
|
}
|
||||||
return kubeHandler.ApplyAll(ctx)
|
err = kubeHandler.ApplyAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
e = err
|
||||||
|
reason = irsav1alpha1.IRSAReasonFailedK8sApply
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*obj = irsav1alpha1.IRSAStatusReady(*obj, string(irsav1alpha1.IRSAReasonReady), "successfully setup resources")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupWithManager sets up the controller with the Manager.
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ func reconcileSelfhosted(ctx context.Context, obj *irsav1alpha1.IRSASetup, awsCl
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.SelfHostedReason
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user