diff --git a/docs/operator-manual/resource_actions_builtin.md b/docs/operator-manual/resource_actions_builtin.md index af6bd6d10dee0..8fc6c82ed3e9f 100644 --- a/docs/operator-manual/resource_actions_builtin.md +++ b/docs/operator-manual/resource_actions_builtin.md @@ -22,6 +22,8 @@ - [batch/Job/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/batch/Job/actions/resume/action.lua) - [batch/Job/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/batch/Job/actions/suspend/action.lua) - [batch/Job/terminate](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/batch/Job/actions/terminate/action.lua) +- [capsule.clastix.io/Tenant/cordon](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/capsule.clastix.io/Tenant/actions/cordon/action.lua) +- [capsule.clastix.io/Tenant/uncordon](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/capsule.clastix.io/Tenant/actions/uncordon/action.lua) - [core.strimzi.io/StrimziPodSet/rolling-update](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/core.strimzi.io/StrimziPodSet/actions/rolling-update/action.lua) - [external-secrets.io/ExternalSecret/refresh](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/external-secrets.io/ExternalSecret/actions/refresh/action.lua) - [external-secrets.io/PushSecret/push](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/external-secrets.io/PushSecret/actions/push/action.lua) diff --git a/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/health.lua b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/health.lua new file mode 100644 index 0000000000000..a8857b6a94226 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/health.lua @@ -0,0 +1,48 @@ +-- GlobalSopsSecret is a cluster-scoped resource that holds an encrypted secret +-- and replicates the decrypted result into namespaces across multiple tenants. +-- +-- Documentation: +-- https://github.com/peak-scale/sops-operator +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- Ready=Unknown => Progressing +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" then + if condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end + if condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.status == "Unknown" then + hs.status = "Progressing" + hs.message = condition.message + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/health_test.yaml b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/health_test.yaml new file mode 100644 index 0000000000000..4bb38c0cbf283 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/health_test.yaml @@ -0,0 +1,21 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Progressing + message: "Decryption and replication in progress" + inputPath: testdata/progressing_unknown.yaml +- healthStatus: + status: Healthy + message: "All managed secrets have been successfully decrypted and replicated into their target namespaces" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "Failed to replicate secret into target namespaces" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/degraded.yaml b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/degraded.yaml new file mode 100644 index 0000000000000..1fa61e37bfbd3 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/degraded.yaml @@ -0,0 +1,21 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: GlobalSopsSecret +metadata: + name: global-tls-ca + generation: 1 +spec: + namespaceSelector: + matchLabels: + capsule.clastix.io/tenant: solar + secretTemplates: + - name: tls-ca + stringData: + ca.crt: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Failed to replicate secret into target namespaces + reason: ReplicationError diff --git a/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/healthy.yaml b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/healthy.yaml new file mode 100644 index 0000000000000..5c8788e457cc1 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/healthy.yaml @@ -0,0 +1,21 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: GlobalSopsSecret +metadata: + name: global-tls-ca + generation: 1 +spec: + namespaceSelector: + matchLabels: + capsule.clastix.io/tenant: solar + secretTemplates: + - name: tls-ca + stringData: + ca.crt: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: All managed secrets have been successfully decrypted and replicated into their target namespaces + reason: Ready diff --git a/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_generation_mismatch.yaml b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..9259a52e450b7 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,21 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: GlobalSopsSecret +metadata: + name: global-tls-ca + generation: 2 +spec: + namespaceSelector: + matchLabels: + capsule.clastix.io/tenant: solar + secretTemplates: + - name: tls-ca + stringData: + ca.crt: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: All managed secrets have been successfully decrypted and replicated into their target namespaces + reason: Ready diff --git a/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_no_status.yaml b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..bb244fd65f916 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_no_status.yaml @@ -0,0 +1,12 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: GlobalSopsSecret +metadata: + name: global-tls-ca +spec: + namespaceSelector: + matchLabels: + capsule.clastix.io/tenant: solar + secretTemplates: + - name: tls-ca + stringData: + ca.crt: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] diff --git a/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_unknown.yaml b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_unknown.yaml new file mode 100644 index 0000000000000..654a45f4a3516 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/GlobalSopsSecret/testdata/progressing_unknown.yaml @@ -0,0 +1,21 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: GlobalSopsSecret +metadata: + name: global-tls-ca + generation: 1 +spec: + namespaceSelector: + matchLabels: + capsule.clastix.io/tenant: solar + secretTemplates: + - name: tls-ca + stringData: + ca.crt: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "Unknown" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Decryption and replication in progress + reason: Reconciling diff --git a/resource_customizations/addons.projectcapsule.dev/SopsProvider/health.lua b/resource_customizations/addons.projectcapsule.dev/SopsProvider/health.lua new file mode 100644 index 0000000000000..380b72390320b --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsProvider/health.lua @@ -0,0 +1,48 @@ +-- SopsProvider is a cluster-scoped resource that configures a decryption provider +-- (e.g. AWS KMS, GCP KMS, age) for the SOPS Operator. +-- +-- Documentation: +-- https://github.com/peak-scale/sops-operator +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- Ready=Unknown => Progressing +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" then + if condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end + if condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.status == "Unknown" then + hs.status = "Progressing" + hs.message = condition.message + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/addons.projectcapsule.dev/SopsProvider/health_test.yaml b/resource_customizations/addons.projectcapsule.dev/SopsProvider/health_test.yaml new file mode 100644 index 0000000000000..97e31dba38e70 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsProvider/health_test.yaml @@ -0,0 +1,21 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Progressing + message: "Validating provider configuration" + inputPath: testdata/progressing_unknown.yaml +- healthStatus: + status: Healthy + message: "All configured providers are available" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "Failed to load decryption provider" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/degraded.yaml b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/degraded.yaml new file mode 100644 index 0000000000000..2676fbd4d7284 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/degraded.yaml @@ -0,0 +1,17 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsProvider +metadata: + name: aws-kms-provider + generation: 1 +spec: + provider: + awsKms: + keyArn: arn:aws:kms:us-east-1:111122223333:key/example-key-id +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Failed to load decryption provider + reason: ProviderError diff --git a/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/healthy.yaml b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/healthy.yaml new file mode 100644 index 0000000000000..38920cabe2d58 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/healthy.yaml @@ -0,0 +1,17 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsProvider +metadata: + name: aws-kms-provider + generation: 1 +spec: + provider: + awsKms: + keyArn: arn:aws:kms:us-east-1:111122223333:key/example-key-id +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: All configured providers are available + reason: Ready diff --git a/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_generation_mismatch.yaml b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..5a6e14fb68bd0 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,17 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsProvider +metadata: + name: aws-kms-provider + generation: 2 +spec: + provider: + awsKms: + keyArn: arn:aws:kms:us-east-1:111122223333:key/example-key-id +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: All configured providers are available + reason: Ready diff --git a/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_no_status.yaml b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..335ba9fd69b7c --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_no_status.yaml @@ -0,0 +1,8 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsProvider +metadata: + name: aws-kms-provider +spec: + provider: + awsKms: + keyArn: arn:aws:kms:us-east-1:111122223333:key/example-key-id diff --git a/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_unknown.yaml b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_unknown.yaml new file mode 100644 index 0000000000000..6feac7d1f98f0 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsProvider/testdata/progressing_unknown.yaml @@ -0,0 +1,17 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsProvider +metadata: + name: aws-kms-provider + generation: 1 +spec: + provider: + awsKms: + keyArn: arn:aws:kms:us-east-1:111122223333:key/example-key-id +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "Unknown" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Validating provider configuration + reason: Reconciling diff --git a/resource_customizations/addons.projectcapsule.dev/SopsSecret/health.lua b/resource_customizations/addons.projectcapsule.dev/SopsSecret/health.lua new file mode 100644 index 0000000000000..e6f6b52f41b9c --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsSecret/health.lua @@ -0,0 +1,49 @@ +-- SopsSecret is a namespace-scoped resource managed by the SOPS Operator +-- (a Capsule addon) that holds an encrypted secret and decrypts it into +-- one or more Kubernetes Secrets. +-- +-- Documentation: +-- https://github.com/peak-scale/sops-operator +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- Ready=Unknown => Progressing (decryption/replication in progress) +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" then + if condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end + if condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.status == "Unknown" then + hs.status = "Progressing" + hs.message = condition.message + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/addons.projectcapsule.dev/SopsSecret/health_test.yaml b/resource_customizations/addons.projectcapsule.dev/SopsSecret/health_test.yaml new file mode 100644 index 0000000000000..73bdbf74a777f --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsSecret/health_test.yaml @@ -0,0 +1,21 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Progressing + message: "Decryption in progress" + inputPath: testdata/progressing_unknown.yaml +- healthStatus: + status: Healthy + message: "All managed secrets have been successfully decrypted and replicated" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "Failed to decrypt secret" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/degraded.yaml b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/degraded.yaml new file mode 100644 index 0000000000000..95291edc3514e --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/degraded.yaml @@ -0,0 +1,19 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsSecret +metadata: + name: solar-db-credentials + namespace: solar-production + generation: 1 +spec: + secretTemplates: + - name: db-password + stringData: + password: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Failed to decrypt secret + reason: DecryptionError diff --git a/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/healthy.yaml b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/healthy.yaml new file mode 100644 index 0000000000000..269e0fff807d6 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/healthy.yaml @@ -0,0 +1,19 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsSecret +metadata: + name: solar-db-credentials + namespace: solar-production + generation: 1 +spec: + secretTemplates: + - name: db-password + stringData: + password: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: All managed secrets have been successfully decrypted and replicated + reason: Ready diff --git a/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_generation_mismatch.yaml b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..68ec8833a8721 --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,19 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsSecret +metadata: + name: solar-db-credentials + namespace: solar-production + generation: 2 +spec: + secretTemplates: + - name: db-password + stringData: + password: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: All managed secrets have been successfully decrypted and replicated + reason: Ready diff --git a/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_no_status.yaml b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..c12b3558e067f --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_no_status.yaml @@ -0,0 +1,10 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsSecret +metadata: + name: solar-db-credentials + namespace: solar-production +spec: + secretTemplates: + - name: db-password + stringData: + password: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] diff --git a/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_unknown.yaml b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_unknown.yaml new file mode 100644 index 0000000000000..0246752487e1f --- /dev/null +++ b/resource_customizations/addons.projectcapsule.dev/SopsSecret/testdata/progressing_unknown.yaml @@ -0,0 +1,19 @@ +apiVersion: addons.projectcapsule.dev/v1alpha1 +kind: SopsSecret +metadata: + name: solar-db-credentials + namespace: solar-production + generation: 1 +spec: + secretTemplates: + - name: db-password + stringData: + password: ENC[AES256_GCM,data:encrypted==,tag:tag==,type:str] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "Unknown" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Decryption in progress + reason: Reconciling diff --git a/resource_customizations/capsule.clastix.io/CapsuleConfiguration/health.lua b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/health.lua new file mode 100644 index 0000000000000..64794147829ec --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/health.lua @@ -0,0 +1,40 @@ +-- CapsuleConfiguration is a cluster-scoped resource that holds the global +-- configuration for the Capsule operator. +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/CapsuleConfiguration/health_test.yaml b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/health_test.yaml new file mode 100644 index 0000000000000..52139ff0ac0ca --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "CapsuleConfiguration has been successfully reconciled" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "CapsuleConfiguration failed to reconcile" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/degraded.yaml new file mode 100644 index 0000000000000..2cfb72af43cd7 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/degraded.yaml @@ -0,0 +1,16 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: CapsuleConfiguration +metadata: + name: default + generation: 1 +spec: + userGroups: + - capsule.clastix.io +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: CapsuleConfiguration failed to reconcile + reason: ReconcileError diff --git a/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/healthy.yaml new file mode 100644 index 0000000000000..6bfa9d2173266 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/healthy.yaml @@ -0,0 +1,16 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: CapsuleConfiguration +metadata: + name: default + generation: 1 +spec: + userGroups: + - capsule.clastix.io +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: CapsuleConfiguration has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..0f22a5c847b61 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,16 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: CapsuleConfiguration +metadata: + name: default + generation: 2 +spec: + userGroups: + - capsule.clastix.io +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: CapsuleConfiguration has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..584696c29aed8 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CapsuleConfiguration/testdata/progressing_no_status.yaml @@ -0,0 +1,7 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: CapsuleConfiguration +metadata: + name: default +spec: + userGroups: + - capsule.clastix.io diff --git a/resource_customizations/capsule.clastix.io/CustomQuota/health.lua b/resource_customizations/capsule.clastix.io/CustomQuota/health.lua new file mode 100644 index 0000000000000..d501f33563e19 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CustomQuota/health.lua @@ -0,0 +1,40 @@ +-- CustomQuota is a namespace-scoped resource that defines a custom resource +-- quota for the namespaces belonging to a Capsule Tenant. +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/CustomQuota/health_test.yaml b/resource_customizations/capsule.clastix.io/CustomQuota/health_test.yaml new file mode 100644 index 0000000000000..84875dec9e237 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CustomQuota/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "Usage has been successfully calculated for the namespace" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "CustomQuota failed to reconcile" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/capsule.clastix.io/CustomQuota/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/CustomQuota/testdata/degraded.yaml new file mode 100644 index 0000000000000..58b1f7311450a --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CustomQuota/testdata/degraded.yaml @@ -0,0 +1,15 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: CustomQuota +metadata: + name: solar-custom-quota + namespace: solar-production + generation: 1 +spec: {} +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: CustomQuota failed to reconcile + reason: ReconcileError diff --git a/resource_customizations/capsule.clastix.io/CustomQuota/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/CustomQuota/testdata/healthy.yaml new file mode 100644 index 0000000000000..361fe54b94920 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CustomQuota/testdata/healthy.yaml @@ -0,0 +1,15 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: CustomQuota +metadata: + name: solar-custom-quota + namespace: solar-production + generation: 1 +spec: {} +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Usage has been successfully calculated for the namespace + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/CustomQuota/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/CustomQuota/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..133b5e9f30ff4 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CustomQuota/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,15 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: CustomQuota +metadata: + name: solar-custom-quota + namespace: solar-production + generation: 2 +spec: {} +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Usage has been successfully calculated for the namespace + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/CustomQuota/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/CustomQuota/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..2fd63dda978e1 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/CustomQuota/testdata/progressing_no_status.yaml @@ -0,0 +1,6 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: CustomQuota +metadata: + name: solar-custom-quota + namespace: solar-production +spec: {} diff --git a/resource_customizations/capsule.clastix.io/GlobalCustomQuota/health.lua b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/health.lua new file mode 100644 index 0000000000000..8db99b5fe2bec --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/health.lua @@ -0,0 +1,40 @@ +-- GlobalCustomQuota is a cluster-scoped resource that defines a custom resource +-- quota across all namespaces belonging to a Capsule Tenant. +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/GlobalCustomQuota/health_test.yaml b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/health_test.yaml new file mode 100644 index 0000000000000..1b64f646432d0 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "Usage has been successfully calculated across all selected namespaces" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "GlobalCustomQuota failed to reconcile" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/degraded.yaml new file mode 100644 index 0000000000000..1ccdbee142dc6 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/degraded.yaml @@ -0,0 +1,14 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: GlobalCustomQuota +metadata: + name: global-custom-quota + generation: 1 +spec: {} +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: GlobalCustomQuota failed to reconcile + reason: ReconcileError diff --git a/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/healthy.yaml new file mode 100644 index 0000000000000..aeac2013322df --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/healthy.yaml @@ -0,0 +1,14 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: GlobalCustomQuota +metadata: + name: global-custom-quota + generation: 1 +spec: {} +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Usage has been successfully calculated across all selected namespaces + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..490a825ff5722 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,14 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: GlobalCustomQuota +metadata: + name: global-custom-quota + generation: 2 +spec: {} +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Usage has been successfully calculated across all selected namespaces + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..685f6ad4e65df --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalCustomQuota/testdata/progressing_no_status.yaml @@ -0,0 +1,5 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: GlobalCustomQuota +metadata: + name: global-custom-quota +spec: {} diff --git a/resource_customizations/capsule.clastix.io/GlobalProxySettings/health.lua b/resource_customizations/capsule.clastix.io/GlobalProxySettings/health.lua new file mode 100644 index 0000000000000..ddbb8c07ebce8 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalProxySettings/health.lua @@ -0,0 +1,40 @@ +-- GlobalProxySettings is a cluster-scoped resource that configures cluster-wide +-- proxy rules for Capsule Proxy. +-- +-- Documentation: +-- https://github.com/projectcapsule/capsule-proxy +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/GlobalProxySettings/health_test.yaml b/resource_customizations/capsule.clastix.io/GlobalProxySettings/health_test.yaml new file mode 100644 index 0000000000000..327cbc37142ef --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalProxySettings/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "Global proxy rules have been successfully applied" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "GlobalProxySettings failed to reconcile" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/degraded.yaml new file mode 100644 index 0000000000000..93ab44e46e479 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/degraded.yaml @@ -0,0 +1,22 @@ +apiVersion: capsule.clastix.io/v1beta1 +kind: GlobalProxySettings +metadata: + name: global-proxy + generation: 1 +spec: + subjects: + - name: cluster-admin-group + kind: Group + proxySettings: + - kind: Nodes + operations: + - List + - Get +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: GlobalProxySettings failed to reconcile + reason: ReconcileError diff --git a/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/healthy.yaml new file mode 100644 index 0000000000000..43f7624cee4ad --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/healthy.yaml @@ -0,0 +1,22 @@ +apiVersion: capsule.clastix.io/v1beta1 +kind: GlobalProxySettings +metadata: + name: global-proxy + generation: 1 +spec: + subjects: + - name: cluster-admin-group + kind: Group + proxySettings: + - kind: Nodes + operations: + - List + - Get +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Global proxy rules have been successfully applied + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..ca5b140e2510e --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,22 @@ +apiVersion: capsule.clastix.io/v1beta1 +kind: GlobalProxySettings +metadata: + name: global-proxy + generation: 2 +spec: + subjects: + - name: cluster-admin-group + kind: Group + proxySettings: + - kind: Nodes + operations: + - List + - Get +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Global proxy rules have been successfully applied + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..a7de405697065 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalProxySettings/testdata/progressing_no_status.yaml @@ -0,0 +1,13 @@ +apiVersion: capsule.clastix.io/v1beta1 +kind: GlobalProxySettings +metadata: + name: global-proxy +spec: + subjects: + - name: cluster-admin-group + kind: Group + proxySettings: + - kind: Nodes + operations: + - List + - Get diff --git a/resource_customizations/capsule.clastix.io/GlobalTenantResource/health.lua b/resource_customizations/capsule.clastix.io/GlobalTenantResource/health.lua new file mode 100644 index 0000000000000..6f649b9d57085 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalTenantResource/health.lua @@ -0,0 +1,40 @@ +-- GlobalTenantResource is a cluster-scoped resource that allows cluster admins +-- to replicate resources across all namespaces of all Capsule Tenants. +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/GlobalTenantResource/health_test.yaml b/resource_customizations/capsule.clastix.io/GlobalTenantResource/health_test.yaml new file mode 100644 index 0000000000000..1cff9dbe26fb2 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalTenantResource/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "Resources have been successfully replicated across all tenant namespaces" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "GlobalTenantResource failed to replicate resources" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/degraded.yaml new file mode 100644 index 0000000000000..59d4633a1aa3d --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/degraded.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: GlobalTenantResource +metadata: + name: global-shared-config + generation: 1 +spec: + tenantSelector: {} + resyncPeriod: 60s + resources: [] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: GlobalTenantResource failed to replicate resources + reason: ReplicationError diff --git a/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/healthy.yaml new file mode 100644 index 0000000000000..be4efa7d10565 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/healthy.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: GlobalTenantResource +metadata: + name: global-shared-config + generation: 1 +spec: + tenantSelector: {} + resyncPeriod: 60s + resources: [] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Resources have been successfully replicated across all tenant namespaces + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..f6a7b15d1966b --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: GlobalTenantResource +metadata: + name: global-shared-config + generation: 2 +spec: + tenantSelector: {} + resyncPeriod: 60s + resources: [] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Resources have been successfully replicated across all tenant namespaces + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..f82b0e8c4e13d --- /dev/null +++ b/resource_customizations/capsule.clastix.io/GlobalTenantResource/testdata/progressing_no_status.yaml @@ -0,0 +1,16 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: GlobalTenantResource +metadata: + name: global-shared-config +spec: + tenantSelector: {} + resyncPeriod: 60s + resources: + - namespaceSelector: {} + rawItems: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: global-config + data: + key: value diff --git a/resource_customizations/capsule.clastix.io/ProxySetting/health.lua b/resource_customizations/capsule.clastix.io/ProxySetting/health.lua new file mode 100644 index 0000000000000..143abae6f9544 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ProxySetting/health.lua @@ -0,0 +1,40 @@ +-- ProxySetting is a namespace-scoped resource that configures per-user or +-- per-group proxy rules for Capsule Proxy. +-- +-- Documentation: +-- https://github.com/projectcapsule/capsule-proxy +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/ProxySetting/health_test.yaml b/resource_customizations/capsule.clastix.io/ProxySetting/health_test.yaml new file mode 100644 index 0000000000000..7f8488f24617a --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ProxySetting/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "Proxy rules have been successfully applied" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "ProxySetting failed to reconcile" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/capsule.clastix.io/ProxySetting/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/ProxySetting/testdata/degraded.yaml new file mode 100644 index 0000000000000..52fec917827b5 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ProxySetting/testdata/degraded.yaml @@ -0,0 +1,22 @@ +apiVersion: capsule.clastix.io/v1beta1 +kind: ProxySetting +metadata: + name: alice-proxy + namespace: solar-production + generation: 1 +spec: + subjects: + - name: alice + kind: User + proxySettings: + - kind: IngressClasses + operations: + - List +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ProxySetting failed to reconcile + reason: ReconcileError diff --git a/resource_customizations/capsule.clastix.io/ProxySetting/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/ProxySetting/testdata/healthy.yaml new file mode 100644 index 0000000000000..faffdbea31b00 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ProxySetting/testdata/healthy.yaml @@ -0,0 +1,22 @@ +apiVersion: capsule.clastix.io/v1beta1 +kind: ProxySetting +metadata: + name: alice-proxy + namespace: solar-production + generation: 1 +spec: + subjects: + - name: alice + kind: User + proxySettings: + - kind: IngressClasses + operations: + - List +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Proxy rules have been successfully applied + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/ProxySetting/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/ProxySetting/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..1f3493f731684 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ProxySetting/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,22 @@ +apiVersion: capsule.clastix.io/v1beta1 +kind: ProxySetting +metadata: + name: alice-proxy + namespace: solar-production + generation: 2 +spec: + subjects: + - name: alice + kind: User + proxySettings: + - kind: IngressClasses + operations: + - List +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Proxy rules have been successfully applied + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/ProxySetting/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/ProxySetting/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..59ec9416085f7 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ProxySetting/testdata/progressing_no_status.yaml @@ -0,0 +1,13 @@ +apiVersion: capsule.clastix.io/v1beta1 +kind: ProxySetting +metadata: + name: alice-proxy + namespace: solar-production +spec: + subjects: + - name: alice + kind: User + proxySettings: + - kind: IngressClasses + operations: + - List diff --git a/resource_customizations/capsule.clastix.io/ResourcePool/health.lua b/resource_customizations/capsule.clastix.io/ResourcePool/health.lua new file mode 100644 index 0000000000000..6a54b3c05fdcc --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePool/health.lua @@ -0,0 +1,54 @@ +-- ResourcePool is a cluster-scoped resource that defines a shared pool of +-- resource quotas that can be claimed by tenant namespaces. +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- +-- ArgoCD health mapping: +-- exhaustions set => Degraded (one or more resources are exhausted) +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +if obj.status.exhaustions ~= nil then + local exhausted = {} + for resource, _ in pairs(obj.status.exhaustions) do + table.insert(exhausted, resource) + end + table.sort(exhausted) + if #exhausted > 0 then + hs.status = "Degraded" + hs.message = "Pool exhausted for: " .. table.concat(exhausted, ", ") + return hs + end +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/ResourcePool/health_test.yaml b/resource_customizations/capsule.clastix.io/ResourcePool/health_test.yaml new file mode 100644 index 0000000000000..eb6c12ee10959 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePool/health_test.yaml @@ -0,0 +1,21 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "ResourcePool is active and within limits" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "ResourcePool failed to reconcile" + inputPath: testdata/degraded_ready_false.yaml +- healthStatus: + status: Degraded + message: "Pool exhausted for: cpu, memory" + inputPath: testdata/degraded_exhausted.yaml diff --git a/resource_customizations/capsule.clastix.io/ResourcePool/testdata/degraded_exhausted.yaml b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/degraded_exhausted.yaml new file mode 100644 index 0000000000000..d828c8a1b9e16 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/degraded_exhausted.yaml @@ -0,0 +1,24 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePool +metadata: + name: solar-pool + generation: 1 +spec: + selectors: + - matchLabels: + capsule.clastix.io/tenant: solar + quota: + hard: + cpu: "10" + memory: 20Gi +status: + observedGeneration: 1 + exhaustions: + cpu: "10" + memory: 20Gi + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePool is active and within limits + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/ResourcePool/testdata/degraded_ready_false.yaml b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/degraded_ready_false.yaml new file mode 100644 index 0000000000000..e7a308912286d --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/degraded_ready_false.yaml @@ -0,0 +1,21 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePool +metadata: + name: solar-pool + generation: 1 +spec: + selectors: + - matchLabels: + capsule.clastix.io/tenant: solar + quota: + hard: + cpu: "10" + memory: 20Gi +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePool failed to reconcile + reason: ReconcileError diff --git a/resource_customizations/capsule.clastix.io/ResourcePool/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/healthy.yaml new file mode 100644 index 0000000000000..e6c237c0e66bb --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/healthy.yaml @@ -0,0 +1,21 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePool +metadata: + name: solar-pool + generation: 1 +spec: + selectors: + - matchLabels: + capsule.clastix.io/tenant: solar + quota: + hard: + cpu: "10" + memory: 20Gi +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePool is active and within limits + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/ResourcePool/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..6eaff6705f6a3 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,21 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePool +metadata: + name: solar-pool + generation: 2 +spec: + selectors: + - matchLabels: + capsule.clastix.io/tenant: solar + quota: + hard: + cpu: "10" + memory: 20Gi +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePool is active and within limits + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/ResourcePool/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..b5e9de1e3e325 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePool/testdata/progressing_no_status.yaml @@ -0,0 +1,12 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePool +metadata: + name: solar-pool +spec: + selectors: + - matchLabels: + capsule.clastix.io/tenant: solar + quota: + hard: + cpu: "10" + memory: 20Gi diff --git a/resource_customizations/capsule.clastix.io/ResourcePoolClaim/health.lua b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/health.lua new file mode 100644 index 0000000000000..993d9425d8e38 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/health.lua @@ -0,0 +1,49 @@ +-- ResourcePoolClaim is a namespace-scoped resource that allows a tenant namespace +-- to claim a portion of the quota from a ResourcePool. +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- +-- ArgoCD health mapping: +-- Bound=False => Suspended (waiting for a pool to accept the claim) +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Bound" and condition.status == "False" then + hs.status = "Suspended" + hs.message = condition.message + return hs + end +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/ResourcePoolClaim/health_test.yaml b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/health_test.yaml new file mode 100644 index 0000000000000..632a55fb52081 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/health_test.yaml @@ -0,0 +1,21 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "ResourcePoolClaim is bound and ready" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "ResourcePoolClaim failed to bind" + inputPath: testdata/degraded.yaml +- healthStatus: + status: Suspended + message: "Waiting for a pool to accept the claim" + inputPath: testdata/suspended_unbound.yaml diff --git a/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/degraded.yaml new file mode 100644 index 0000000000000..c9dfbbc2d9619 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/degraded.yaml @@ -0,0 +1,25 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePoolClaim +metadata: + name: solar-claim + namespace: solar-production + generation: 1 +spec: + poolRef: + name: solar-pool + resources: + cpu: "2" + memory: 4Gi +status: + observedGeneration: 1 + conditions: + - type: Bound + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePoolClaim is bound + reason: Bound + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePoolClaim failed to bind + reason: BindError diff --git a/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/healthy.yaml new file mode 100644 index 0000000000000..c9cb8242a2e57 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/healthy.yaml @@ -0,0 +1,25 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePoolClaim +metadata: + name: solar-claim + namespace: solar-production + generation: 1 +spec: + poolRef: + name: solar-pool + resources: + cpu: "2" + memory: 4Gi +status: + observedGeneration: 1 + conditions: + - type: Bound + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePoolClaim is bound and ready + reason: Bound + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePoolClaim is bound and ready + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..8eb97b64cb713 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,25 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePoolClaim +metadata: + name: solar-claim + namespace: solar-production + generation: 2 +spec: + poolRef: + name: solar-pool + resources: + cpu: "2" + memory: 4Gi +status: + observedGeneration: 1 + conditions: + - type: Bound + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePoolClaim is bound and ready + reason: Bound + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: ResourcePoolClaim is bound and ready + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..1ca87275598f7 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/progressing_no_status.yaml @@ -0,0 +1,11 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePoolClaim +metadata: + name: solar-claim + namespace: solar-production +spec: + poolRef: + name: solar-pool + resources: + cpu: "2" + memory: 4Gi diff --git a/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/suspended_unbound.yaml b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/suspended_unbound.yaml new file mode 100644 index 0000000000000..021fbc97359f2 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/ResourcePoolClaim/testdata/suspended_unbound.yaml @@ -0,0 +1,20 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: ResourcePoolClaim +metadata: + name: solar-claim + namespace: solar-production + generation: 1 +spec: + poolRef: + name: solar-pool + resources: + cpu: "2" + memory: 4Gi +status: + observedGeneration: 1 + conditions: + - type: Bound + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Waiting for a pool to accept the claim + reason: Unbound diff --git a/resource_customizations/capsule.clastix.io/Tenant/actions/action_test.yaml b/resource_customizations/capsule.clastix.io/Tenant/actions/action_test.yaml new file mode 100644 index 0000000000000..31dcbc0e8666f --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/actions/action_test.yaml @@ -0,0 +1,28 @@ +discoveryTests: +- inputPath: testdata/normal.yaml + result: + - name: cordon + disabled: false + iconClass: "fa fa-fw fa-pause" + displayName: "" + - name: uncordon + disabled: true + iconClass: "fa fa-fw fa-play" + displayName: "" +- inputPath: testdata/cordoned.yaml + result: + - name: cordon + disabled: true + iconClass: "fa fa-fw fa-pause" + displayName: "" + - name: uncordon + disabled: false + iconClass: "fa fa-fw fa-play" + displayName: "" +actionTests: +- action: cordon + inputPath: testdata/normal.yaml + expectedOutputPath: testdata/after_cordon.yaml +- action: uncordon + inputPath: testdata/cordoned.yaml + expectedOutputPath: testdata/after_uncordon.yaml diff --git a/resource_customizations/capsule.clastix.io/Tenant/actions/cordon/action.lua b/resource_customizations/capsule.clastix.io/Tenant/actions/cordon/action.lua new file mode 100644 index 0000000000000..d3143f332eee8 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/actions/cordon/action.lua @@ -0,0 +1,5 @@ +if obj.spec == nil then + obj.spec = {} +end +obj.spec.cordoned = true +return obj diff --git a/resource_customizations/capsule.clastix.io/Tenant/actions/discovery.lua b/resource_customizations/capsule.clastix.io/Tenant/actions/discovery.lua new file mode 100644 index 0000000000000..df47c15a6be2d --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/actions/discovery.lua @@ -0,0 +1,23 @@ +local actions = {} + +actions["cordon"] = { + ["iconClass"] = "fa fa-fw fa-pause", + ["disabled"] = true, +} +actions["uncordon"] = { + ["iconClass"] = "fa fa-fw fa-play", + ["disabled"] = true, +} + +local cordoned = false +if obj.spec ~= nil and obj.spec.cordoned ~= nil then + cordoned = obj.spec.cordoned +end + +if cordoned then + actions["uncordon"]["disabled"] = false +else + actions["cordon"]["disabled"] = false +end + +return actions diff --git a/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/after_cordon.yaml b/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/after_cordon.yaml new file mode 100644 index 0000000000000..b2c2845244ed0 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/after_cordon.yaml @@ -0,0 +1,18 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar + generation: 1 +spec: + owners: + - kind: User + name: alice + cordoned: true +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/after_uncordon.yaml b/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/after_uncordon.yaml new file mode 100644 index 0000000000000..604defc326e23 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/after_uncordon.yaml @@ -0,0 +1,23 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar + generation: 1 +spec: + owners: + - kind: User + name: alice + cordoned: false +status: + observedGeneration: 1 + conditions: + - type: Cordoned + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant is cordoned + reason: Cordoned + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/cordoned.yaml b/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/cordoned.yaml new file mode 100644 index 0000000000000..aae3480d998bd --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/cordoned.yaml @@ -0,0 +1,23 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar + generation: 1 +spec: + owners: + - kind: User + name: alice + cordoned: true +status: + observedGeneration: 1 + conditions: + - type: Cordoned + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant is cordoned + reason: Cordoned + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/normal.yaml b/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/normal.yaml new file mode 100644 index 0000000000000..7dd16f1e654cd --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/actions/testdata/normal.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar + generation: 1 +spec: + owners: + - kind: User + name: alice +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/Tenant/actions/uncordon/action.lua b/resource_customizations/capsule.clastix.io/Tenant/actions/uncordon/action.lua new file mode 100644 index 0000000000000..2497421b1c874 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/actions/uncordon/action.lua @@ -0,0 +1,4 @@ +if obj.spec ~= nil and obj.spec.cordoned ~= nil and obj.spec.cordoned then + obj.spec.cordoned = false +end +return obj diff --git a/resource_customizations/capsule.clastix.io/Tenant/health.lua b/resource_customizations/capsule.clastix.io/Tenant/health.lua new file mode 100644 index 0000000000000..58230ac356490 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/health.lua @@ -0,0 +1,55 @@ +-- Tenant is a cluster-scoped resource managed by Capsule that represents a group +-- of namespaces owned by one or more owners (users, service accounts, groups). +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- https://github.com/projectcapsule/capsule +-- +-- Condition types: +-- Ready (True) - Tenant has been successfully reconciled +-- Ready (False) - Tenant reconciliation failed +-- Cordoned (True) - Tenant is cordoned; no new workloads can be scheduled +-- +-- ArgoCD health mapping: +-- Cordoned=True => Suspended (tenant intentionally paused) +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status yet => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Cordoned" and condition.status == "True" then + hs.status = "Suspended" + hs.message = condition.message + return hs + end +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/Tenant/health_test.yaml b/resource_customizations/capsule.clastix.io/Tenant/health_test.yaml new file mode 100644 index 0000000000000..51a06195fa2e9 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/health_test.yaml @@ -0,0 +1,21 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "Tenant has been successfully reconciled" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "Tenant failed to reconcile" + inputPath: testdata/degraded.yaml +- healthStatus: + status: Suspended + message: "Tenant is cordoned" + inputPath: testdata/suspended_cordoned.yaml diff --git a/resource_customizations/capsule.clastix.io/Tenant/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/Tenant/testdata/degraded.yaml new file mode 100644 index 0000000000000..97b15132a56a6 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/testdata/degraded.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar + generation: 1 +spec: + owners: + - kind: User + name: alice +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant failed to reconcile + reason: ReconcileError diff --git a/resource_customizations/capsule.clastix.io/Tenant/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/Tenant/testdata/healthy.yaml new file mode 100644 index 0000000000000..7dd16f1e654cd --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/testdata/healthy.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar + generation: 1 +spec: + owners: + - kind: User + name: alice +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/Tenant/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/Tenant/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..1d303644bb290 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar + generation: 2 +spec: + owners: + - kind: User + name: alice +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/Tenant/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/Tenant/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..8d37bbe2bbe95 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/testdata/progressing_no_status.yaml @@ -0,0 +1,8 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar +spec: + owners: + - kind: User + name: alice diff --git a/resource_customizations/capsule.clastix.io/Tenant/testdata/suspended_cordoned.yaml b/resource_customizations/capsule.clastix.io/Tenant/testdata/suspended_cordoned.yaml new file mode 100644 index 0000000000000..aae3480d998bd --- /dev/null +++ b/resource_customizations/capsule.clastix.io/Tenant/testdata/suspended_cordoned.yaml @@ -0,0 +1,23 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: Tenant +metadata: + name: solar + generation: 1 +spec: + owners: + - kind: User + name: alice + cordoned: true +status: + observedGeneration: 1 + conditions: + - type: Cordoned + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant is cordoned + reason: Cordoned + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Tenant has been successfully reconciled + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/TenantOwner/health.lua b/resource_customizations/capsule.clastix.io/TenantOwner/health.lua new file mode 100644 index 0000000000000..754752a71d21b --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantOwner/health.lua @@ -0,0 +1,40 @@ +-- TenantOwner is a namespace-scoped resource that binds a user, service account, +-- or group to a Capsule Tenant as an owner. +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/TenantOwner/health_test.yaml b/resource_customizations/capsule.clastix.io/TenantOwner/health_test.yaml new file mode 100644 index 0000000000000..2e8d4067c840a --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantOwner/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "Owner has been successfully bound to its tenant" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "TenantOwner failed to reconcile" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/capsule.clastix.io/TenantOwner/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/TenantOwner/testdata/degraded.yaml new file mode 100644 index 0000000000000..2c6f37c2a40a1 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantOwner/testdata/degraded.yaml @@ -0,0 +1,20 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: TenantOwner +metadata: + name: alice + namespace: solar + generation: 1 +spec: + tenantRef: + name: solar + ownerRef: + kind: User + name: alice +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: TenantOwner failed to reconcile + reason: ReconcileError diff --git a/resource_customizations/capsule.clastix.io/TenantOwner/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/TenantOwner/testdata/healthy.yaml new file mode 100644 index 0000000000000..df9bb9cf39d97 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantOwner/testdata/healthy.yaml @@ -0,0 +1,20 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: TenantOwner +metadata: + name: alice + namespace: solar + generation: 1 +spec: + tenantRef: + name: solar + ownerRef: + kind: User + name: alice +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Owner has been successfully bound to its tenant + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/TenantOwner/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/TenantOwner/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..1f50cd49451f8 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantOwner/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,20 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: TenantOwner +metadata: + name: alice + namespace: solar + generation: 2 +spec: + tenantRef: + name: solar + ownerRef: + kind: User + name: alice +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Owner has been successfully bound to its tenant + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/TenantOwner/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/TenantOwner/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..84d875cccfdfd --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantOwner/testdata/progressing_no_status.yaml @@ -0,0 +1,11 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: TenantOwner +metadata: + name: alice + namespace: solar +spec: + tenantRef: + name: solar + ownerRef: + kind: User + name: alice diff --git a/resource_customizations/capsule.clastix.io/TenantResource/health.lua b/resource_customizations/capsule.clastix.io/TenantResource/health.lua new file mode 100644 index 0000000000000..bf47ff67de59f --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantResource/health.lua @@ -0,0 +1,40 @@ +-- TenantResource is a namespace-scoped resource that allows tenants to replicate +-- Kubernetes resources into their own namespaces. +-- +-- Documentation: +-- https://projectcapsule.dev/ +-- +-- ArgoCD health mapping: +-- Ready=True => Healthy +-- Ready=False => Degraded +-- No status => Progressing +local hs = {} +if obj.status == nil or obj.status.conditions == nil then + hs.status = "Progressing" + hs.message = "Waiting for status" + return hs +end + +if obj.metadata ~= nil and obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil + and obj.status.observedGeneration ~= obj.metadata.generation then + hs.status = "Progressing" + hs.message = "Waiting for reconciliation (generation mismatch)" + return hs +end + +for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Ready condition" +return hs diff --git a/resource_customizations/capsule.clastix.io/TenantResource/health_test.yaml b/resource_customizations/capsule.clastix.io/TenantResource/health_test.yaml new file mode 100644 index 0000000000000..b5de85a5a1bf6 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantResource/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/progressing_no_status.yaml +- healthStatus: + status: Progressing + message: "Waiting for reconciliation (generation mismatch)" + inputPath: testdata/progressing_generation_mismatch.yaml +- healthStatus: + status: Healthy + message: "Resources have been successfully replicated into the target namespaces" + inputPath: testdata/healthy.yaml +- healthStatus: + status: Degraded + message: "TenantResource failed to replicate resources" + inputPath: testdata/degraded.yaml diff --git a/resource_customizations/capsule.clastix.io/TenantResource/testdata/degraded.yaml b/resource_customizations/capsule.clastix.io/TenantResource/testdata/degraded.yaml new file mode 100644 index 0000000000000..a820ac15d4b8b --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantResource/testdata/degraded.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: TenantResource +metadata: + name: solar-tenantresource + namespace: solar-production + generation: 1 +spec: + resyncPeriod: 60s + resources: [] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "False" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: TenantResource failed to replicate resources + reason: ReplicationError diff --git a/resource_customizations/capsule.clastix.io/TenantResource/testdata/healthy.yaml b/resource_customizations/capsule.clastix.io/TenantResource/testdata/healthy.yaml new file mode 100644 index 0000000000000..3ffbc348e8243 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantResource/testdata/healthy.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: TenantResource +metadata: + name: solar-tenantresource + namespace: solar-production + generation: 1 +spec: + resyncPeriod: 60s + resources: [] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Resources have been successfully replicated into the target namespaces + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/TenantResource/testdata/progressing_generation_mismatch.yaml b/resource_customizations/capsule.clastix.io/TenantResource/testdata/progressing_generation_mismatch.yaml new file mode 100644 index 0000000000000..170277ac2fb0d --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantResource/testdata/progressing_generation_mismatch.yaml @@ -0,0 +1,17 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: TenantResource +metadata: + name: solar-tenantresource + namespace: solar-production + generation: 2 +spec: + resyncPeriod: 60s + resources: [] +status: + observedGeneration: 1 + conditions: + - type: Ready + status: "True" + lastTransitionTime: "2024-01-01T00:00:00Z" + message: Resources have been successfully replicated into the target namespaces + reason: Ready diff --git a/resource_customizations/capsule.clastix.io/TenantResource/testdata/progressing_no_status.yaml b/resource_customizations/capsule.clastix.io/TenantResource/testdata/progressing_no_status.yaml new file mode 100644 index 0000000000000..05b195c2c4446 --- /dev/null +++ b/resource_customizations/capsule.clastix.io/TenantResource/testdata/progressing_no_status.yaml @@ -0,0 +1,18 @@ +apiVersion: capsule.clastix.io/v1beta2 +kind: TenantResource +metadata: + name: solar-tenantresource + namespace: solar-production +spec: + resyncPeriod: 60s + resources: + - namespaceSelector: + matchLabels: + capsule.clastix.io/tenant: solar + rawItems: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-config + data: + key: value