diff --git a/docs/getting_started/examples/automated_operations/scale_up_down_resources.md b/docs/getting_started/examples/automated_operations/scale_up_down_resources.md index 90f5214..f72915f 100644 --- a/docs/getting_started/examples/automated_operations/scale_up_down_resources.md +++ b/docs/getting_started/examples/automated_operations/scale_up_down_resources.md @@ -33,71 +33,71 @@ The example below demonstrates how to automatically scale down Deployments, Daem # - sets their replicas to zero (scale down and pause) # --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: scale-down-deployment-statefulset-daemonset - spec: - schedule: "* 20 * * *" - action: Transform - transform: | - -- Set replicas to 0 - function transform() - hs = {} - - if obj.metadata.annotations == nil then - obj.metadata.annotations = {} - end - -- store in the annotation current replicas value - obj.metadata.annotations["previous-replicas"] = tostring(obj.spec.replicas) - - -- reset replicas to 0 - obj.spec.replicas = 0 - - hs.resource = obj - return hs - end - resourcePolicySet: - resourceSelectors: - - kind: Deployment - group: apps - version: v1 - - kind: StatefulSet - group: "apps" - version: v1 - - kind: DaemonSet - group: "apps" - version: v1 - aggregatedSelection: | - function evaluate() - local hs = {} - - -- returns true if object has annotaiton "pause-resume" - function hasPauseAnnotation(obj) - if obj.metadata.annotations ~= nil then - if obj.metadata.annotations["pause-resume"] then - return true - end - - return false - end - - return - end - - local resourceToPause = {} - - for _, resource in ipairs(resources) do - if hasPauseAnnotation(resource) then - table.insert(resourceToPause, {resource = resource}) - end - end - - if #resourceToPause > 0 then - hs.resources = resourceToPause - end - return hs - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: scale-down-deployment-statefulset-daemonset + spec: + schedule: "* 20 * * *" + action: Transform + transform: | + -- Set replicas to 0 + function transform() + hs = {} + + if obj.metadata.annotations == nil then + obj.metadata.annotations = {} + end + -- store in the annotation current replicas value + obj.metadata.annotations["previous-replicas"] = tostring(obj.spec.replicas) + + -- reset replicas to 0 + obj.spec.replicas = 0 + + hs.resource = obj + return hs + end + resourcePolicySet: + resourceSelectors: + - kind: Deployment + group: apps + version: v1 + - kind: StatefulSet + group: "apps" + version: v1 + - kind: DaemonSet + group: "apps" + version: v1 + aggregatedSelection: | + function evaluate() + local hs = {} + + -- returns true if object has annotaiton "pause-resume" + function hasPauseAnnotation(obj) + if obj.metadata.annotations ~= nil then + if obj.metadata.annotations["pause-resume"] then + return true + end + + return false + end + + return + end + + local resourceToPause = {} + + for _, resource in ipairs(resources) do + if hasPauseAnnotation(resource) then + table.insert(resourceToPause, {resource = resource}) + end + end + + if #resourceToPause > 0 then + hs.resources = resourceToPause + end + return hs + end ``` ### Resume YAML Definition @@ -117,67 +117,67 @@ As we defined the pause action for specific resources, during peak times, we wou # - sets the replicas to such value found above (scale deployment/statefulset/daemonset up) # --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: scale-up-deployment-statefulset-daemonset - spec: - schedule: "* 8 * * *" - action: Transform - transform: | - -- Set replicas to 0 - function transform() - hs = {} - if obj.metadata.annotations == nil then - return - end - if not obj.metadata.annotations["previous-replicas"] then - return - end - -- reset replicas - obj.spec.replicas = tonumber(obj.metadata.annotations["previous-replicas"]) - hs.resource = obj - return hs - end - resourcePolicySet: - resourceSelectors: - - kind: Deployment - group: apps - version: v1 - - kind: StatefulSet - group: "apps" - version: v1 - - kind: DaemonSet - group: "apps" - version: v1 - aggregatedSelection: | - function evaluate() - local hs = {} - - -- returns true if object has annotaiton "pause-resume" - function hasPauseAnnotation(obj) - if obj.metadata.annotations ~= nil then - if obj.metadata.annotations["pause-resume"] then - return true - end - - return false - end - - return false - end - - local resourceToUnPause = {} - - for _, resource in ipairs(resources) do - if hasPauseAnnotation(resource) then - table.insert(resourceToUnPause, {resource = resource}) - end - end - - if #resourceToUnPause > 0 then - hs.resources = resourceToUnPause - end - return hs - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: scale-up-deployment-statefulset-daemonset + spec: + schedule: "* 8 * * *" + action: Transform + transform: | + -- Set replicas to 0 + function transform() + hs = {} + if obj.metadata.annotations == nil then + return + end + if not obj.metadata.annotations["previous-replicas"] then + return + end + -- reset replicas + obj.spec.replicas = tonumber(obj.metadata.annotations["previous-replicas"]) + hs.resource = obj + return hs + end + resourcePolicySet: + resourceSelectors: + - kind: Deployment + group: apps + version: v1 + - kind: StatefulSet + group: "apps" + version: v1 + - kind: DaemonSet + group: "apps" + version: v1 + aggregatedSelection: | + function evaluate() + local hs = {} + + -- returns true if object has annotaiton "pause-resume" + function hasPauseAnnotation(obj) + if obj.metadata.annotations ~= nil then + if obj.metadata.annotations["pause-resume"] then + return true + end + + return false + end + + return false + end + + local resourceToUnPause = {} + + for _, resource in ipairs(resources) do + if hasPauseAnnotation(resource) then + table.insert(resourceToUnPause, {resource = resource}) + end + end + + if #resourceToUnPause > 0 then + hs.resources = resourceToUnPause + end + return hs + end ``` diff --git a/docs/getting_started/examples/unhealthy_resources/deployment.md b/docs/getting_started/examples/unhealthy_resources/deployment.md index aad15e2..bc5520e 100644 --- a/docs/getting_started/examples/unhealthy_resources/deployment.md +++ b/docs/getting_started/examples/unhealthy_resources/deployment.md @@ -28,134 +28,134 @@ Any unhealthy deployment will be transformed by scaling its replicas to zero. ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: scale-down-high-restart-deployment - spec: - schedule: "* 0 * * *" - action: Transform - transform: | - -- Set replicas to 0 - function transform() - hs = {} - obj.spec.replicas = 0 - hs.resource = obj - return hs - end - resourcePolicySet: - resourceSelectors: - - kind: Deployment - group: apps - version: v1 - - kind: Pod - group: "" - version: v1 - aggregatedSelection: | - -- any pod restarted more than this value is considered unhealthy - min_restarts = 50 - - deployment_pods_map = {} - - function getKey(namespace, name) - return namespace .. ":" .. name - end - - -- Given a ReplicaSet name, returns the deployment name - function getDeploymentName(replicaSetName) - local reversed = string.reverse(replicaSetName) - local firstDashIndex, _ = string.find(reversed, "-") - if firstDashIndex then - -- Convert index from reversed string to original string - local lastDashIndex = #replicaSetName - firstDashIndex + 1 - return replicaSetName:sub(1, lastDashIndex - 1) - else - return replicaSetName - end - end - - -- If Pod's OwnerReference is a ReplicaSet - -- returns deployment name. Returns an empty string otherwise - function getDeployment(pod) - if pod.metadata.ownerReferences ~= nil then - for _, owner in ipairs(pod.metadata.ownerReferences) do - if owner.kind == "ReplicaSet" and owner.apiVersion == "apps/v1" then - local ownerName = owner.name - return getDeploymentName(ownerName) - end - end - end - return "" - end - - -- Function to add a pod to a deployment's pod list - function add_pod_to_deployment(depl_key, pod) - -- Get the existing pod list for the deployment (or create an empty list if it doesn't exist) - pods = deployment_pods_map[depl_key] or {} - - -- Append the new pod to the list - table.insert(pods, pod) - -- Update the map with the modified pod list - deployment_pods_map[depl_key] = pods - end - - -- Returns true if - function isPodUnhealthy(pod) - if pod.status ~= nil and pod.status.containerStatuses ~= nil then - for _,container_status in ipairs (pod.status.containerStatuses) do - if container_status.restartCount > min_restarts then - return true - end - end - end - return false - end - - function isDeploymentUnhealthy(deployment) - depl_key = getKey(deployment.metadata.namespace, deployment.metadata.name) - pods = deployment_pods_map[depl_key] or {} - for _,pod in ipairs (pods) do - if not isPodUnhealthy(pod) then - return false - end - end - - return true - end - - function evaluate() - local hs = {} - - local deployments = {} - - -- Separate pods from deployments - for _, resource in ipairs(resources) do - local kind = resource.kind - if kind == "Deployment" then - table.insert(deployments, resource) - else - deplName = getDeployment(resource) - if deplName ~= "" then - depl_key = getKey(resource.metadata.namespace, deplName) - add_pod_to_deployment(depl_key, resource) - end - end - end - - local unhealthy_deployments = {} - - for _, deployment in ipairs(deployments) do - isUnhealthy = isDeploymentUnhealthy(deployment, references) - if isUnhealthy then - table.insert(unhealthy_deployments, {resource = deployment}) - end - end - - if #unhealthy_deployments > 0 then - hs.resources = unhealthy_deployments - end - return hs - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: scale-down-high-restart-deployment + spec: + schedule: "* 0 * * *" + action: Transform + transform: | + -- Set replicas to 0 + function transform() + hs = {} + obj.spec.replicas = 0 + hs.resource = obj + return hs + end + resourcePolicySet: + resourceSelectors: + - kind: Deployment + group: apps + version: v1 + - kind: Pod + group: "" + version: v1 + aggregatedSelection: | + -- any pod restarted more than this value is considered unhealthy + min_restarts = 50 + + deployment_pods_map = {} + + function getKey(namespace, name) + return namespace .. ":" .. name + end + + -- Given a ReplicaSet name, returns the deployment name + function getDeploymentName(replicaSetName) + local reversed = string.reverse(replicaSetName) + local firstDashIndex, _ = string.find(reversed, "-") + if firstDashIndex then + -- Convert index from reversed string to original string + local lastDashIndex = #replicaSetName - firstDashIndex + 1 + return replicaSetName:sub(1, lastDashIndex - 1) + else + return replicaSetName + end + end + + -- If Pod's OwnerReference is a ReplicaSet + -- returns deployment name. Returns an empty string otherwise + function getDeployment(pod) + if pod.metadata.ownerReferences ~= nil then + for _, owner in ipairs(pod.metadata.ownerReferences) do + if owner.kind == "ReplicaSet" and owner.apiVersion == "apps/v1" then + local ownerName = owner.name + return getDeploymentName(ownerName) + end + end + end + return "" + end + + -- Function to add a pod to a deployment's pod list + function add_pod_to_deployment(depl_key, pod) + -- Get the existing pod list for the deployment (or create an empty list if it doesn't exist) + pods = deployment_pods_map[depl_key] or {} + + -- Append the new pod to the list + table.insert(pods, pod) + -- Update the map with the modified pod list + deployment_pods_map[depl_key] = pods + end + + -- Returns true if + function isPodUnhealthy(pod) + if pod.status ~= nil and pod.status.containerStatuses ~= nil then + for _,container_status in ipairs (pod.status.containerStatuses) do + if container_status.restartCount > min_restarts then + return true + end + end + end + return false + end + + function isDeploymentUnhealthy(deployment) + depl_key = getKey(deployment.metadata.namespace, deployment.metadata.name) + pods = deployment_pods_map[depl_key] or {} + for _,pod in ipairs (pods) do + if not isPodUnhealthy(pod) then + return false + end + end + + return true + end + + function evaluate() + local hs = {} + + local deployments = {} + + -- Separate pods from deployments + for _, resource in ipairs(resources) do + local kind = resource.kind + if kind == "Deployment" then + table.insert(deployments, resource) + else + deplName = getDeployment(resource) + if deplName ~= "" then + depl_key = getKey(resource.metadata.namespace, deplName) + add_pod_to_deployment(depl_key, resource) + end + end + end + + local unhealthy_deployments = {} + + for _, deployment in ipairs(deployments) do + isUnhealthy = isDeploymentUnhealthy(deployment, references) + if isUnhealthy then + table.insert(unhealthy_deployments, {resource = deployment}) + end + end + + if #unhealthy_deployments > 0 then + hs.resources = unhealthy_deployments + end + return hs + end ``` ## Example - Pods with High Restarts @@ -170,132 +170,132 @@ When an unhealthy Deployment is found, it will be automatically **scaled down** ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: scale-down-high-restart-deployment - spec: - schedule: "* 0 * * *" - action: Transform - transform: | - -- Set replicas to 0 - function transform() - hs = {} - obj.spec.replicas = 0 - hs.resource = obj - return hs - end - resourcePolicySet: - resourceSelectors: - - kind: Deployment - group: apps - version: v1 - - kind: Pod - group: "" - version: v1 - aggregatedSelection: | - -- any pod restarted more than this value is considered unhealthy - min_restarts = 50 - - deployment_pods_map = {} - - function getKey(namespace, name) - return namespace .. ":" .. name - end - - -- Given a ReplicaSet name, returns the deployment name - function getDeploymentName(replicaSetName) - local reversed = string.reverse(replicaSetName) - local firstDashIndex, _ = string.find(reversed, "-") - if firstDashIndex then - -- Convert index from reversed string to original string - local lastDashIndex = #replicaSetName - firstDashIndex + 1 - return replicaSetName:sub(1, lastDashIndex - 1) - else - return replicaSetName - end - end - - -- If Pod's OwnerReference is a ReplicaSet - -- returns deployment name. Returns an empty string otherwise - function getDeployment(pod) - if pod.metadata.ownerReferences ~= nil then - for _, owner in ipairs(pod.metadata.ownerReferences) do - if owner.kind == "ReplicaSet" and owner.apiVersion == "apps/v1" then - local ownerName = owner.name - return getDeploymentName(ownerName) - end - end - end - return "" - end - - -- Function to add a pod to a deployment's pod list - function add_pod_to_deployment(depl_key, pod) - -- Get the existing pod list for the deployment (or create an empty list if it doesn't exist) - pods = deployment_pods_map[depl_key] or {} - - -- Append the new pod to the list - table.insert(pods, pod) - -- Update the map with the modified pod list - deployment_pods_map[depl_key] = pods - end - - -- Returns true if - function isPodUnhealthy(pod) - if pod.status ~= nil and pod.status.containerStatuses ~= nil then - for _,container_status in ipairs (pod.status.containerStatuses) do - if container_status.restartCount > min_restarts then - return true - end - end - end - return false - end - - function isDeploymentUnhealthy(deployment) - depl_key = getKey(deployment.metadata.namespace, deployment.metadata.name) - pods = deployment_pods_map[depl_key] or {} - for _,pod in ipairs (pods) do - if not isPodUnhealthy(pod) then - return false - end - end - - return true - end - - function evaluate() - local hs = {} - - local deployments = {} - - -- Separate pods from deployments - for _, resource in ipairs(resources) do - local kind = resource.kind - if kind == "Deployment" then - table.insert(deployments, resource) - else - deplName = getDeployment(resource) - if deplName ~= "" then - depl_key = getKey(resource.metadata.namespace, deplName) - add_pod_to_deployment(depl_key, resource) - end - end - end - - local unhealthy_deployments = {} - - for _, deployment in ipairs(deployments) do - isUnhealthy = isDeploymentUnhealthy(deployment, references) - if isUnhealthy then - table.insert(unhealthy_deployments, {resource = deployment}) - end - end - - if #unhealthy_deployments > 0 then - hs.resources = unhealthy_deployments - end - return hs - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: scale-down-high-restart-deployment + spec: + schedule: "* 0 * * *" + action: Transform + transform: | + -- Set replicas to 0 + function transform() + hs = {} + obj.spec.replicas = 0 + hs.resource = obj + return hs + end + resourcePolicySet: + resourceSelectors: + - kind: Deployment + group: apps + version: v1 + - kind: Pod + group: "" + version: v1 + aggregatedSelection: | + -- any pod restarted more than this value is considered unhealthy + min_restarts = 50 + + deployment_pods_map = {} + + function getKey(namespace, name) + return namespace .. ":" .. name + end + + -- Given a ReplicaSet name, returns the deployment name + function getDeploymentName(replicaSetName) + local reversed = string.reverse(replicaSetName) + local firstDashIndex, _ = string.find(reversed, "-") + if firstDashIndex then + -- Convert index from reversed string to original string + local lastDashIndex = #replicaSetName - firstDashIndex + 1 + return replicaSetName:sub(1, lastDashIndex - 1) + else + return replicaSetName + end + end + + -- If Pod's OwnerReference is a ReplicaSet + -- returns deployment name. Returns an empty string otherwise + function getDeployment(pod) + if pod.metadata.ownerReferences ~= nil then + for _, owner in ipairs(pod.metadata.ownerReferences) do + if owner.kind == "ReplicaSet" and owner.apiVersion == "apps/v1" then + local ownerName = owner.name + return getDeploymentName(ownerName) + end + end + end + return "" + end + + -- Function to add a pod to a deployment's pod list + function add_pod_to_deployment(depl_key, pod) + -- Get the existing pod list for the deployment (or create an empty list if it doesn't exist) + pods = deployment_pods_map[depl_key] or {} + + -- Append the new pod to the list + table.insert(pods, pod) + -- Update the map with the modified pod list + deployment_pods_map[depl_key] = pods + end + + -- Returns true if + function isPodUnhealthy(pod) + if pod.status ~= nil and pod.status.containerStatuses ~= nil then + for _,container_status in ipairs (pod.status.containerStatuses) do + if container_status.restartCount > min_restarts then + return true + end + end + end + return false + end + + function isDeploymentUnhealthy(deployment) + depl_key = getKey(deployment.metadata.namespace, deployment.metadata.name) + pods = deployment_pods_map[depl_key] or {} + for _,pod in ipairs (pods) do + if not isPodUnhealthy(pod) then + return false + end + end + + return true + end + + function evaluate() + local hs = {} + + local deployments = {} + + -- Separate pods from deployments + for _, resource in ipairs(resources) do + local kind = resource.kind + if kind == "Deployment" then + table.insert(deployments, resource) + else + deplName = getDeployment(resource) + if deplName ~= "" then + depl_key = getKey(resource.metadata.namespace, deplName) + add_pod_to_deployment(depl_key, resource) + end + end + end + + local unhealthy_deployments = {} + + for _, deployment in ipairs(deployments) do + isUnhealthy = isDeploymentUnhealthy(deployment, references) + if isUnhealthy then + table.insert(unhealthy_deployments, {resource = deployment}) + end + end + + if #unhealthy_deployments > 0 then + hs.resources = unhealthy_deployments + end + return hs + end ``` \ No newline at end of file diff --git a/docs/getting_started/examples/unhealthy_resources/outdated_secret_data.md b/docs/getting_started/examples/unhealthy_resources/outdated_secret_data.md index 62ce267..f445790 100644 --- a/docs/getting_started/examples/unhealthy_resources/outdated_secret_data.md +++ b/docs/getting_started/examples/unhealthy_resources/outdated_secret_data.md @@ -28,188 +28,188 @@ By identifying and reporting pods that are accessing modified secrets, the Clean ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: list-pods-with-outdated-secret-data - spec: - action: Scan - schedule: "0 * * * *" - notifications: - - name: report - type: CleanerReport - resourcePolicySet: - resourceSelectors: - - kind: Pod - group: "" - version: v1 - - kind: Secret - group: "" - version: v1 - aggregatedSelection: | - function getKey(namespace, name) - return namespace .. ":" .. name - end - - -- Convert creationTimestamp "2023-12-12T09:35:56Z" - function convertTimestampString(timestampStr) - local convertedTimestamp = string.gsub( - timestampStr, - '(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z', - function(y, mon, d, h, mi, s) - return os.time({ - year = tonumber(y), - month = tonumber(mon), - day = tonumber(d), - hour = tonumber(h), - min = tonumber(mi), - sec = tonumber(s) - }) - end - ) - return convertedTimestamp - end - - function getLatestTime(times) - local latestTime = nil - for _, time in ipairs(times) do - if latestTime == nil or os.difftime(tonumber(time), tonumber(latestTime)) > 0 then - latestTime = time - end - end - return latestTime - end - - function getSecretUpdateTime(secret) - local times = {} - if secret.metadata.managedFields ~= nil then - for _, mf in ipairs(secret.metadata.managedFields) do - if mf.time ~= nil then - table.insert(times, convertTimestampString(mf.time)) - end - end - end - - return getLatestTime(times) - end - - function isPodOlderThanSecret(podTimestamp, secretTimestamp) - timeDifference = os.difftime(tonumber(podTimestamp), tonumber(secretTimestamp)) - return timeDifference < 0 - end - - function getPodTimestamp(pod) - if pod.status ~= nil and pod.status.conditions ~= nil then - for _,condition in ipairs(pod.status.conditions) do - if condition.type == "PodReadyToStartContainers" and condition.status == "True" then - return convertTimestampString(condition.lastTransitionTime) - end - end - end - return convertTimestampString(pod.metadata.creationTimestamp) - end - - function hasOutdatedSecret(pod, secrets) - podTimestamp = getPodTimestamp(pod) - - if pod.spec.containers ~= nil then - for _, container in ipairs(pod.spec.containers) do - - if container.env ~= nil then - for _, env in ipairs(container.env) do - if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then - key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name) - if isPodOlderThanSecret(podTimestamp, secrets[key]) then - return true, "secret " .. key .. " has been updated after pod creation" - end - end - end - end - - if container.envFrom ~= nil then - for _, envFrom in ipairs(container.envFrom) do - if envFrom.secretRef ~= nil then - key = getKey(pod.metadata.namespace, envFrom.secretRef.name) - if isPodOlderThanSecret(podTimestamp, secrets[key]) then - return true, "secret " .. key .. " has been updated after pod creation" - end - end - end - end - end - end - - if pod.spec.initContainers ~= nil then - for _, initContainer in ipairs(pod.spec.initContainers) do - if initContainer.env ~= nil then - for _, env in ipairs(initContainer.env) do - if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then - key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name) - if isPodOlderThanSecret(podTimestamp, secrets[key]) then - return true, "secret " .. key .. " has been updated after pod creation" - end - end - end - end - end - end - - if pod.spec.volumes ~= nil then - for _, volume in ipairs(pod.spec.volumes) do - if volume.secret ~= nil then - key = getKey(pod.metadata.namespace, volume.secret.secretName) - if isPodOlderThanSecret(podTimestamp, secrets[key]) then - return true, "secret " .. key .. " has been updated after pod creation" - end - end - - if volume.projected ~= nil and volume.projected.sources ~= nil then - for _, projectedResource in ipairs(volume.projected.sources) do - if projectedResource.secret ~= nil then - key = getKey(pod.metadata.namespace, projectedResource.secret.name) - if isPodOlderThanSecret(podTimestamp, secrets[key]) then - return true, "secret " .. key .. " has been updated after pod creation" - end - end - end - end - end - end - - return false - end - - function evaluate() - local hs = {} - hs.message = "" - - local pods = {} - local secrets = {} - - -- Separate secrets and pods - for _, resource in ipairs(resources) do - local kind = resource.kind - if kind == "Secret" then - key = getKey(resource.metadata.namespace, resource.metadata.name) - updateTimestamp = getSecretUpdateTime(resource) - secrets[key] = updateTimestamp - elseif kind == "Pod" then - table.insert(pods, resource) - end - end - - local podsWithOutdatedSecret = {} - - for _, pod in ipairs(pods) do - outdatedData, message = hasOutdatedSecret(pod, secrets) - if outdatedData then - table.insert(podsWithOutdatedSecret, {resource= pod, message = message}) - end - end - - if #podsWithOutdatedSecret > 0 then - hs.resources = podsWithOutdatedSecret - end - return hs - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: list-pods-with-outdated-secret-data + spec: + action: Scan + schedule: "0 * * * *" + notifications: + - name: report + type: CleanerReport + resourcePolicySet: + resourceSelectors: + - kind: Pod + group: "" + version: v1 + - kind: Secret + group: "" + version: v1 + aggregatedSelection: | + function getKey(namespace, name) + return namespace .. ":" .. name + end + + -- Convert creationTimestamp "2023-12-12T09:35:56Z" + function convertTimestampString(timestampStr) + local convertedTimestamp = string.gsub( + timestampStr, + '(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z', + function(y, mon, d, h, mi, s) + return os.time({ + year = tonumber(y), + month = tonumber(mon), + day = tonumber(d), + hour = tonumber(h), + min = tonumber(mi), + sec = tonumber(s) + }) + end + ) + return convertedTimestamp + end + + function getLatestTime(times) + local latestTime = nil + for _, time in ipairs(times) do + if latestTime == nil or os.difftime(tonumber(time), tonumber(latestTime)) > 0 then + latestTime = time + end + end + return latestTime + end + + function getSecretUpdateTime(secret) + local times = {} + if secret.metadata.managedFields ~= nil then + for _, mf in ipairs(secret.metadata.managedFields) do + if mf.time ~= nil then + table.insert(times, convertTimestampString(mf.time)) + end + end + end + + return getLatestTime(times) + end + + function isPodOlderThanSecret(podTimestamp, secretTimestamp) + timeDifference = os.difftime(tonumber(podTimestamp), tonumber(secretTimestamp)) + return timeDifference < 0 + end + + function getPodTimestamp(pod) + if pod.status ~= nil and pod.status.conditions ~= nil then + for _,condition in ipairs(pod.status.conditions) do + if condition.type == "PodReadyToStartContainers" and condition.status == "True" then + return convertTimestampString(condition.lastTransitionTime) + end + end + end + return convertTimestampString(pod.metadata.creationTimestamp) + end + + function hasOutdatedSecret(pod, secrets) + podTimestamp = getPodTimestamp(pod) + + if pod.spec.containers ~= nil then + for _, container in ipairs(pod.spec.containers) do + + if container.env ~= nil then + for _, env in ipairs(container.env) do + if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then + key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name) + if isPodOlderThanSecret(podTimestamp, secrets[key]) then + return true, "secret " .. key .. " has been updated after pod creation" + end + end + end + end + + if container.envFrom ~= nil then + for _, envFrom in ipairs(container.envFrom) do + if envFrom.secretRef ~= nil then + key = getKey(pod.metadata.namespace, envFrom.secretRef.name) + if isPodOlderThanSecret(podTimestamp, secrets[key]) then + return true, "secret " .. key .. " has been updated after pod creation" + end + end + end + end + end + end + + if pod.spec.initContainers ~= nil then + for _, initContainer in ipairs(pod.spec.initContainers) do + if initContainer.env ~= nil then + for _, env in ipairs(initContainer.env) do + if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then + key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name) + if isPodOlderThanSecret(podTimestamp, secrets[key]) then + return true, "secret " .. key .. " has been updated after pod creation" + end + end + end + end + end + end + + if pod.spec.volumes ~= nil then + for _, volume in ipairs(pod.spec.volumes) do + if volume.secret ~= nil then + key = getKey(pod.metadata.namespace, volume.secret.secretName) + if isPodOlderThanSecret(podTimestamp, secrets[key]) then + return true, "secret " .. key .. " has been updated after pod creation" + end + end + + if volume.projected ~= nil and volume.projected.sources ~= nil then + for _, projectedResource in ipairs(volume.projected.sources) do + if projectedResource.secret ~= nil then + key = getKey(pod.metadata.namespace, projectedResource.secret.name) + if isPodOlderThanSecret(podTimestamp, secrets[key]) then + return true, "secret " .. key .. " has been updated after pod creation" + end + end + end + end + end + end + + return false + end + + function evaluate() + local hs = {} + hs.message = "" + + local pods = {} + local secrets = {} + + -- Separate secrets and pods + for _, resource in ipairs(resources) do + local kind = resource.kind + if kind == "Secret" then + key = getKey(resource.metadata.namespace, resource.metadata.name) + updateTimestamp = getSecretUpdateTime(resource) + secrets[key] = updateTimestamp + elseif kind == "Pod" then + table.insert(pods, resource) + end + end + + local podsWithOutdatedSecret = {} + + for _, pod in ipairs(pods) do + outdatedData, message = hasOutdatedSecret(pod, secrets) + if outdatedData then + table.insert(podsWithOutdatedSecret, {resource= pod, message = message}) + end + end + + if #podsWithOutdatedSecret > 0 then + hs.resources = podsWithOutdatedSecret + end + return hs + end ``` diff --git a/docs/getting_started/examples/unhealthy_resources/pod_expired_certs.md b/docs/getting_started/examples/unhealthy_resources/pod_expired_certs.md index 36285f8..8ce40ad 100644 --- a/docs/getting_started/examples/unhealthy_resources/pod_expired_certs.md +++ b/docs/getting_started/examples/unhealthy_resources/pod_expired_certs.md @@ -28,172 +28,172 @@ A Pod is using an expired certificates if the secret with certificate have been ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: list-pods-with-expired-certificates - spec: - action: Scan - schedule: "0 * * * *" - notifications: - - name: report - type: CleanerReport - resourcePolicySet: - resourceSelectors: - - kind: Pod - group: "" - version: v1 - - kind: Secret - group: "" - version: v1 - - kind: Certificate - group: "cert-manager.io" - version: "v1" - aggregatedSelection: | - function getKey(namespace, name) - return namespace .. ":" .. name - end - - -- Convert creationTimestamp "2023-12-12T09:35:56Z" - function convertTimestampString(timestampStr) - local convertedTimestamp = string.gsub( - timestampStr, - '(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z', - function(y, mon, d, h, mi, s) - return os.time({ - year = tonumber(y), - month = tonumber(mon), - day = tonumber(d), - hour = tonumber(h), - min = tonumber(mi), - sec = tonumber(s) - }) - end - ) - return convertedTimestamp - end - - function getLatestTime(times) - local latestTime = nil - for _, time in ipairs(times) do - if latestTime == nil or os.difftime(tonumber(time), tonumber(latestTime)) > 0 then - latestTime = time - end - end - return latestTime - end - - function getSecretUpdateTime(secret) - local times = {} - if secret.metadata.managedFields ~= nil then - for _, mf in ipairs(secret.metadata.managedFields) do - if mf.time ~= nil then - table.insert(times, convertTimestampString(mf.time)) - end - end - end - - return getLatestTime(times) - end - - function isPodOlderThanSecret(podTimestamp, secretTimestamp) - timeDifference = os.difftime(tonumber(podTimestamp), tonumber(secretTimestamp)) - return timeDifference < 0 - end - - function getPodTimestamp(pod) - if pod.status ~= nil and pod.status.conditions ~= nil then - for _,condition in ipairs(pod.status.conditions) do - if condition.type == "PodReadyToStartContainers" and condition.status == "True" then - return convertTimestampString(condition.lastTransitionTime) - end - end - end - return convertTimestampString(pod.metadata.creationTimestamp) - end - - -- secrets contains key:value where key identify a Secret with a Certificate and value - -- if the time of latest update - function hasOutdatedSecret(pod, secrets) - podTimestamp = getPodTimestamp(pod) - - if pod.spec.volumes ~= nil then - for _, volume in ipairs(pod.spec.volumes) do - if volume.secret ~= nil then - key = getKey(pod.metadata.namespace, volume.secret.secretName) - -- if secrets contains a certificate - if secrets[key] ~= nil then - if isPodOlderThanSecret(podTimestamp, secrets[key]) then - return true, "secret " .. key .. " has been updated after pod creation" - end - end - end - - if volume.projected ~= nil and volume.projected.sources ~= nil then - for _, projectedResource in ipairs(volume.projected.sources) do - if projectedResource.secret ~= nil then - key = getKey(pod.metadata.namespace, projectedResource.secret.name) - -- if secrets contains a certificate - if secrets[key] ~= nil then - if isPodOlderThanSecret(podTimestamp, secrets[key]) then - return true, "secret " .. key .. " has been updated after pod creation" - end - end - end - end - end - end - end - - return false - end - - function evaluate() - local hs = {} - hs.message = "" - - local pods = {} - local certificates = {} - local secrets = {} - - -- Contains all Secrets containing a Certificate generated using cert-manager - local certificateSecrets = {} - - -- Contains all Secrets containing a Certificate generated using cert-manager - local degradedSecrets = {} - - -- Separate secrets, pods and certificates - for _, resource in ipairs(resources) do - local kind = resource.kind - if kind == "Certificate" then - table.insert(certificates, resource) - elseif kind == "Secret" then - key = getKey(resource.metadata.namespace, resource.metadata.name) - updateTimestamp = getSecretUpdateTime(resource) - secrets[key] = updateTimestamp - elseif kind == "Pod" then - table.insert(pods, resource) - end - end - - -- Find all secrets with certificate generated by cert-manager - for _, certificate in ipairs(certificates) do - key = getKey(certificate.metadata.namespace, certificate.spec.secretName) - certificateSecrets[key] = secrets[key] - end - - local podsWithOutdatedSecret = {} - - for _, pod in ipairs(pods) do - outdatedData, message = hasOutdatedSecret(pod, certificateSecrets) - if outdatedData then - table.insert(podsWithOutdatedSecret, {resource= pod, message = message}) - end - end - - if #podsWithOutdatedSecret > 0 then - hs.resources = podsWithOutdatedSecret - end - return hs - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: list-pods-with-expired-certificates + spec: + action: Scan + schedule: "0 * * * *" + notifications: + - name: report + type: CleanerReport + resourcePolicySet: + resourceSelectors: + - kind: Pod + group: "" + version: v1 + - kind: Secret + group: "" + version: v1 + - kind: Certificate + group: "cert-manager.io" + version: "v1" + aggregatedSelection: | + function getKey(namespace, name) + return namespace .. ":" .. name + end + + -- Convert creationTimestamp "2023-12-12T09:35:56Z" + function convertTimestampString(timestampStr) + local convertedTimestamp = string.gsub( + timestampStr, + '(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z', + function(y, mon, d, h, mi, s) + return os.time({ + year = tonumber(y), + month = tonumber(mon), + day = tonumber(d), + hour = tonumber(h), + min = tonumber(mi), + sec = tonumber(s) + }) + end + ) + return convertedTimestamp + end + + function getLatestTime(times) + local latestTime = nil + for _, time in ipairs(times) do + if latestTime == nil or os.difftime(tonumber(time), tonumber(latestTime)) > 0 then + latestTime = time + end + end + return latestTime + end + + function getSecretUpdateTime(secret) + local times = {} + if secret.metadata.managedFields ~= nil then + for _, mf in ipairs(secret.metadata.managedFields) do + if mf.time ~= nil then + table.insert(times, convertTimestampString(mf.time)) + end + end + end + + return getLatestTime(times) + end + + function isPodOlderThanSecret(podTimestamp, secretTimestamp) + timeDifference = os.difftime(tonumber(podTimestamp), tonumber(secretTimestamp)) + return timeDifference < 0 + end + + function getPodTimestamp(pod) + if pod.status ~= nil and pod.status.conditions ~= nil then + for _,condition in ipairs(pod.status.conditions) do + if condition.type == "PodReadyToStartContainers" and condition.status == "True" then + return convertTimestampString(condition.lastTransitionTime) + end + end + end + return convertTimestampString(pod.metadata.creationTimestamp) + end + + -- secrets contains key:value where key identify a Secret with a Certificate and value + -- if the time of latest update + function hasOutdatedSecret(pod, secrets) + podTimestamp = getPodTimestamp(pod) + + if pod.spec.volumes ~= nil then + for _, volume in ipairs(pod.spec.volumes) do + if volume.secret ~= nil then + key = getKey(pod.metadata.namespace, volume.secret.secretName) + -- if secrets contains a certificate + if secrets[key] ~= nil then + if isPodOlderThanSecret(podTimestamp, secrets[key]) then + return true, "secret " .. key .. " has been updated after pod creation" + end + end + end + + if volume.projected ~= nil and volume.projected.sources ~= nil then + for _, projectedResource in ipairs(volume.projected.sources) do + if projectedResource.secret ~= nil then + key = getKey(pod.metadata.namespace, projectedResource.secret.name) + -- if secrets contains a certificate + if secrets[key] ~= nil then + if isPodOlderThanSecret(podTimestamp, secrets[key]) then + return true, "secret " .. key .. " has been updated after pod creation" + end + end + end + end + end + end + end + + return false + end + + function evaluate() + local hs = {} + hs.message = "" + + local pods = {} + local certificates = {} + local secrets = {} + + -- Contains all Secrets containing a Certificate generated using cert-manager + local certificateSecrets = {} + + -- Contains all Secrets containing a Certificate generated using cert-manager + local degradedSecrets = {} + + -- Separate secrets, pods and certificates + for _, resource in ipairs(resources) do + local kind = resource.kind + if kind == "Certificate" then + table.insert(certificates, resource) + elseif kind == "Secret" then + key = getKey(resource.metadata.namespace, resource.metadata.name) + updateTimestamp = getSecretUpdateTime(resource) + secrets[key] = updateTimestamp + elseif kind == "Pod" then + table.insert(pods, resource) + end + end + + -- Find all secrets with certificate generated by cert-manager + for _, certificate in ipairs(certificates) do + key = getKey(certificate.metadata.namespace, certificate.spec.secretName) + certificateSecrets[key] = secrets[key] + end + + local podsWithOutdatedSecret = {} + + for _, pod in ipairs(pods) do + outdatedData, message = hasOutdatedSecret(pod, certificateSecrets) + if outdatedData then + table.insert(podsWithOutdatedSecret, {resource= pod, message = message}) + end + end + + if #podsWithOutdatedSecret > 0 then + hs.resources = podsWithOutdatedSecret + end + return hs + end ``` diff --git a/docs/getting_started/examples/unused_resources/configmap.md b/docs/getting_started/examples/unused_resources/configmap.md index e3c1fc3..11f3389 100644 --- a/docs/getting_started/examples/unused_resources/configmap.md +++ b/docs/getting_started/examples/unused_resources/configmap.md @@ -30,129 +30,129 @@ By orphaned we refer to a ConfigMap that is not used by: ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: unused-configmaps - spec: - schedule: "* 0 * * *" - action: Delete - resourcePolicySet: - resourceSelectors: - - kind: Pod - group: "" - version: v1 - - kind: ConfigMap - group: "" - version: v1 - aggregatedSelection: | - function getKey(namespace, name) - return namespace .. ":" .. name - end - - function configMapsUsedByPods(pods) - local podConfigMaps = {} - - for _, pod in ipairs(pods) do - if pod.spec.containers ~= nil then - for _, container in ipairs(pod.spec.containers) do - - if container.env ~= nil then - for _, env in ipairs(container.env) do - if env.valueFrom ~= nil and env.valueFrom.configMapKeyRef ~= nil then - key = getKey(pod.metadata.namespace, env.valueFrom.configMapKeyRef.name) - podConfigMaps[key] = true - end - end - end - - if container.envFrom ~= nil then - for _, envFrom in ipairs(container.envFrom) do - if envFrom.configMapRef ~= nil then - key = getKey(pod.metadata.namespace, envFrom.configMapRef.name) - podConfigMaps[key] = true - end - end - end - end - end - - if pod.spec.initContainers ~= nil then - for _, initContainer in ipairs(pod.spec.initContainers) do - - if initContainer.env ~= nil then - for _, env in ipairs(initContainer.env) do - if env.valueFrom ~= nil and env.valueFrom.configMapKeyRef ~= nil then - key = getKey(pod.metadata.namespace, env.valueFrom.configMapKeyRef.name) - podConfigMaps[key] = true - end - end - end - - if initContainer.envFrom ~= nil then - for _, envFrom in ipairs(initContainer.envFrom) do - if envFrom.configMapRef ~= nil then - key = getKey(pod.metadata.namespace,envFrom.configMapRef.name) - podConfigMaps[key] = true - end - end - end - - end - end - - if pod.spec.volumes ~= nil then - for _, volume in ipairs(pod.spec.volumes) do - if volume.configMap ~= nil then - key = getKey(pod.metadata.namespace,volume.configMap.name) - podConfigMaps[key] = true - end - - if volume.projected ~= nil and volume.projected.sources ~= nil then - for _, projectedResource in ipairs(volume.projected.sources) do - if projectedResource.configMap ~= nil then - key = getKey(pod.metadata.namespace,projectedResource.configMap.name) - podConfigMaps[key] = true - end - end - end - end - end - end - - return podConfigMaps - end - - function evaluate() - local hs = {} - hs.message = "" - - local pods = {} - local configMaps = {} - local unusedConfigMaps = {} - - -- Separate configMaps and podsfrom the resources - for _, resource in ipairs(resources) do - local kind = resource.kind - if kind == "ConfigMap" then - table.insert(configMaps, resource) - elseif kind == "Pod" then - table.insert(pods, resource) - end - end - - podConfigMaps = configMapsUsedByPods(pods) - - for _, configMap in ipairs(configMaps) do - key = getKey(configMap.metadata.namespace,configMap.metadata.name) - if not podConfigMaps[key] then - table.insert(unusedConfigMaps, {resource = configMap}) - end - end - - if #unusedConfigMaps > 0 then - hs.resources = unusedConfigMaps - end - return hs - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: unused-configmaps + spec: + schedule: "* 0 * * *" + action: Delete + resourcePolicySet: + resourceSelectors: + - kind: Pod + group: "" + version: v1 + - kind: ConfigMap + group: "" + version: v1 + aggregatedSelection: | + function getKey(namespace, name) + return namespace .. ":" .. name + end + + function configMapsUsedByPods(pods) + local podConfigMaps = {} + + for _, pod in ipairs(pods) do + if pod.spec.containers ~= nil then + for _, container in ipairs(pod.spec.containers) do + + if container.env ~= nil then + for _, env in ipairs(container.env) do + if env.valueFrom ~= nil and env.valueFrom.configMapKeyRef ~= nil then + key = getKey(pod.metadata.namespace, env.valueFrom.configMapKeyRef.name) + podConfigMaps[key] = true + end + end + end + + if container.envFrom ~= nil then + for _, envFrom in ipairs(container.envFrom) do + if envFrom.configMapRef ~= nil then + key = getKey(pod.metadata.namespace, envFrom.configMapRef.name) + podConfigMaps[key] = true + end + end + end + end + end + + if pod.spec.initContainers ~= nil then + for _, initContainer in ipairs(pod.spec.initContainers) do + + if initContainer.env ~= nil then + for _, env in ipairs(initContainer.env) do + if env.valueFrom ~= nil and env.valueFrom.configMapKeyRef ~= nil then + key = getKey(pod.metadata.namespace, env.valueFrom.configMapKeyRef.name) + podConfigMaps[key] = true + end + end + end + + if initContainer.envFrom ~= nil then + for _, envFrom in ipairs(initContainer.envFrom) do + if envFrom.configMapRef ~= nil then + key = getKey(pod.metadata.namespace,envFrom.configMapRef.name) + podConfigMaps[key] = true + end + end + end + + end + end + + if pod.spec.volumes ~= nil then + for _, volume in ipairs(pod.spec.volumes) do + if volume.configMap ~= nil then + key = getKey(pod.metadata.namespace,volume.configMap.name) + podConfigMaps[key] = true + end + + if volume.projected ~= nil and volume.projected.sources ~= nil then + for _, projectedResource in ipairs(volume.projected.sources) do + if projectedResource.configMap ~= nil then + key = getKey(pod.metadata.namespace,projectedResource.configMap.name) + podConfigMaps[key] = true + end + end + end + end + end + end + + return podConfigMaps + end + + function evaluate() + local hs = {} + hs.message = "" + + local pods = {} + local configMaps = {} + local unusedConfigMaps = {} + + -- Separate configMaps and podsfrom the resources + for _, resource in ipairs(resources) do + local kind = resource.kind + if kind == "ConfigMap" then + table.insert(configMaps, resource) + elseif kind == "Pod" then + table.insert(pods, resource) + end + end + + podConfigMaps = configMapsUsedByPods(pods) + + for _, configMap in ipairs(configMaps) do + key = getKey(configMap.metadata.namespace,configMap.metadata.name) + if not podConfigMaps[key] then + table.insert(unusedConfigMaps, {resource = configMap}) + end + end + + if #unusedConfigMaps > 0 then + hs.resources = unusedConfigMaps + end + return hs + end ``` diff --git a/docs/getting_started/examples/unused_resources/jobs.md b/docs/getting_started/examples/unused_resources/jobs.md index f7bf21f..242e37f 100644 --- a/docs/getting_started/examples/unused_resources/jobs.md +++ b/docs/getting_started/examples/unused_resources/jobs.md @@ -29,29 +29,29 @@ The Cleaner instance definition will find any `Jobs` with the below specificatio ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: completed-jobs - spec: - schedule: "* 0 * * *" - resourcePolicySet: - resourceSelectors: - - kind: Job - group: "batch" - version: v1 - evaluate: | - function evaluate() - hs = {} - hs.matching = false - if obj.status ~= nil then - if obj.status.completionTime ~= nil and obj.status.succeeded > 0 and obj.status.active == 0 then - hs.matching = true - end - end - return hs - end - action: Delete + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: completed-jobs + spec: + schedule: "* 0 * * *" + resourcePolicySet: + resourceSelectors: + - kind: Job + group: "batch" + version: v1 + evaluate: | + function evaluate() + hs = {} + hs.matching = false + if obj.status ~= nil then + if obj.status.completionTime ~= nil and obj.status.succeeded > 0 and obj.status.active == 0 then + hs.matching = true + end + end + return hs + end + action: Delete ``` ## Example - Long Running Pods diff --git a/docs/getting_started/examples/unused_resources/persistent_volume.md b/docs/getting_started/examples/unused_resources/persistent_volume.md index b05016f..3682296 100644 --- a/docs/getting_started/examples/unused_resources/persistent_volume.md +++ b/docs/getting_started/examples/unused_resources/persistent_volume.md @@ -22,25 +22,25 @@ The k8s-cleaner is able to delete unsused `PersistentVolumes`. The below Cleaner ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: unbound-peristent-volumes - spec: - schedule: "* 0 * * *" - resourcePolicySet: - resourceSelectors: - - kind: PersistentVolume - group: "" - version: v1 - evaluate: | - function evaluate() - hs = {} - hs.matching = false - if obj.status ~= nil and obj.status.phase ~= "Bound" then - hs.matching = true - end - return hs - end - action: Delete + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: unbound-peristent-volumes + spec: + schedule: "* 0 * * *" + resourcePolicySet: + resourceSelectors: + - kind: PersistentVolume + group: "" + version: v1 + evaluate: | + function evaluate() + hs = {} + hs.matching = false + if obj.status ~= nil and obj.status.phase ~= "Bound" then + hs.matching = true + end + return hs + end + action: Delete ``` diff --git a/docs/getting_started/examples/unused_resources/persistent_volume_claims.md b/docs/getting_started/examples/unused_resources/persistent_volume_claims.md index 5bff6f0..156dfdc 100644 --- a/docs/getting_started/examples/unused_resources/persistent_volume_claims.md +++ b/docs/getting_started/examples/unused_resources/persistent_volume_claims.md @@ -22,75 +22,75 @@ The k8s-cleaner is able to delete unsused `PersistentVolumeClaim`. The below Cle ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: stale-persistent-volume-claim - spec: - schedule: "* 0 * * *" - action: Delete # Delete matching resources - resourcePolicySet: - resourceSelectors: - - kind: Pod - group: "" - version: v1 - - kind: PersistentVolumeClaim - group: "" - version: v1 - aggregatedSelection: | - function isUsed(pvc, pods) - if pods == nil then - return false - end - for _, pod in ipairs(pods) do - if pod.spec.volumes ~= nil then - for _,volume in ipairs(pod.spec.volumes) do - if volume.persistentVolumeClaim ~= nil and volume.persistentVolumeClaim.claimName == pvc.metadata.name then - return true - end - end - end - end - return false - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: stale-persistent-volume-claim + spec: + schedule: "* 0 * * *" + action: Delete # Delete matching resources + resourcePolicySet: + resourceSelectors: + - kind: Pod + group: "" + version: v1 + - kind: PersistentVolumeClaim + group: "" + version: v1 + aggregatedSelection: | + function isUsed(pvc, pods) + if pods == nil then + return false + end + for _, pod in ipairs(pods) do + if pod.spec.volumes ~= nil then + for _,volume in ipairs(pod.spec.volumes) do + if volume.persistentVolumeClaim ~= nil and volume.persistentVolumeClaim.claimName == pvc.metadata.name then + return true + end + end + end + end + return false + end - function evaluate() - local hs = {} - hs.message = "" + function evaluate() + local hs = {} + hs.message = "" - local pods = {} - local pvcs = {} - local unusedPVCs = {} + local pods = {} + local pvcs = {} + local unusedPVCs = {} - -- Separate pods and pvcs from the resources - -- Group those by namespace - for _, resource in ipairs(resources) do - local kind = resource.kind - if kind == "Pod" then - if not pods[resource.metadata.namespace] then - pods[resource.metadata.namespace] = {} - end - table.insert(pods[resource.metadata.namespace], resource) - elseif kind == "PersistentVolumeClaim" then - if not pvcs[resource.metadata.namespace] then - pvcs[resource.metadata.namespace] = {} - end - table.insert(pvcs[resource.metadata.namespace], resource) - end - end + -- Separate pods and pvcs from the resources + -- Group those by namespace + for _, resource in ipairs(resources) do + local kind = resource.kind + if kind == "Pod" then + if not pods[resource.metadata.namespace] then + pods[resource.metadata.namespace] = {} + end + table.insert(pods[resource.metadata.namespace], resource) + elseif kind == "PersistentVolumeClaim" then + if not pvcs[resource.metadata.namespace] then + pvcs[resource.metadata.namespace] = {} + end + table.insert(pvcs[resource.metadata.namespace], resource) + end + end - -- Iterate through each namespace and identify unused PVCs - for namespace, perNamespacePVCs in pairs(pvcs) do - for _, pvc in ipairs(perNamespacePVCs) do - if not isUsed(pvc, pods[namespace]) then - table.insert(unusedPVCs, {resource = pvc}) - end - end - end - - if #unusedPVCs > 0 then - hs.resources = unusedPVCs - end - return hs - end + -- Iterate through each namespace and identify unused PVCs + for namespace, perNamespacePVCs in pairs(pvcs) do + for _, pvc in ipairs(perNamespacePVCs) do + if not isUsed(pvc, pods[namespace]) then + table.insert(unusedPVCs, {resource = pvc}) + end + end + end + + if #unusedPVCs > 0 then + hs.resources = unusedPVCs + end + return hs + end ``` diff --git a/docs/getting_started/examples/unused_resources/secret.md b/docs/getting_started/examples/unused_resources/secret.md index 45f3692..24f8c3f 100644 --- a/docs/getting_started/examples/unused_resources/secret.md +++ b/docs/getting_started/examples/unused_resources/secret.md @@ -34,148 +34,148 @@ By orphaned we refer to a Secret that is not used by: ```yaml --- - apiVersion: apps.projectsveltos.io/v1alpha1 - kind: Cleaner - metadata: - name: unused-secrets - spec: - schedule: "* 0 * * *" - action: Delete - resourcePolicySet: - resourceSelectors: - - kind: Pod - group: "" - version: v1 - - kind: Secret - group: "" - version: v1 - - kind: Ingress - group: "networking.k8s.io" - version: v1 - aggregatedSelection: | - function getKey(namespace, name) - return namespace .. ":" .. name - end - - function secretsUsedByPods(pods) - local podSecrets = {} - - for _, pod in ipairs(pods) do - if pod.spec.containers ~= nil then - for _, container in ipairs(pod.spec.containers) do - - if container.env ~= nil then - for _, env in ipairs(container.env) do - if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then - key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name) - podSecrets[key] = true - end - end - end - - if container.envFrom ~= nil then - for _, envFrom in ipairs(container.envFrom) do - if envFrom.secretRef ~= nil then - key = getKey(pod.metadata.namespace, envFrom.secretRef.name) - podSecrets[key] = true - end - end - end - end - end - - if pod.spec.initContainers ~= nil then - for _, initContainer in ipairs(pod.spec.initContainers) do - if initContainer.env ~= nil then - for _, env in ipairs(initContainer.env) do - if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then - key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name) - podSecrets[key] = true - end - end - end - end - end - - if pod.spec.imagePullSecrets ~= nil then - for _, secret in ipairs(pod.spec.imagePullSecrets) do - key = getKey(pod.metadata.namespace, secret.name) - podSecrets[key] = true - end - end - - if pod.spec.volumes ~= nil then - for _, volume in ipairs(pod.spec.volumes) do - if volume.secret ~= nil then - key = getKey(pod.metadata.namespace, volume.secret.secretName) - podSecrets[key] = true - end - - if volume.projected ~= nil and volume.projected.sources ~= nil then - for _, projectedResource in ipairs(volume.projected.sources) do - if projectedResource.secret ~= nil then - key = getKey(pod.metadata.namespace, projectedResource.secret.name) - podSecrets[key] = true - end - end - end - end - end - end - - return podSecrets - end - - function secretsUsedByIngresses(ingresses) - local ingressSecrets = {} - for _, ingress in ipairs(ingresses) do - if ingress.spec.tls ~= nil then - for _, tls in ipairs(ingress.spec.tls) do - key = getKey(ingress.metadata.namespace, tls.secretName) - ingressSecrets[key] = true - end - end - end - - return ingressSecrets - end - - function evaluate() - local hs = {} - hs.message = "" - - local pods = {} - local secrets = {} - local ingresses = {} - local unusedSecrets = {} - - -- Separate secrets and pods and ingresses from the resources - for _, resource in ipairs(resources) do - local kind = resource.kind - if kind == "Secret" then - table.insert(secrets, resource) - elseif kind == "Pod" then - table.insert(pods, resource) - elseif kind == "Ingress" then - table.insert(ingresses, resource) - end - end - - podSecrets = secretsUsedByPods(pods) - ingressSecrets = secretsUsedByIngresses(ingresses) - - for _, secret in ipairs(secrets) do - if secret.type ~= "kubernetes.io/service-account-token" then - key = getKey(secret.metadata.namespace, secret.metadata.name) - if not podSecrets[key] and not ingressSecrets[key] then - table.insert(unusedSecrets, {resource = secret}) - end - end - end - - if #unusedSecrets > 0 then - hs.resources = unusedSecrets - end - return hs - end + apiVersion: apps.projectsveltos.io/v1alpha1 + kind: Cleaner + metadata: + name: unused-secrets + spec: + schedule: "* 0 * * *" + action: Delete + resourcePolicySet: + resourceSelectors: + - kind: Pod + group: "" + version: v1 + - kind: Secret + group: "" + version: v1 + - kind: Ingress + group: "networking.k8s.io" + version: v1 + aggregatedSelection: | + function getKey(namespace, name) + return namespace .. ":" .. name + end + + function secretsUsedByPods(pods) + local podSecrets = {} + + for _, pod in ipairs(pods) do + if pod.spec.containers ~= nil then + for _, container in ipairs(pod.spec.containers) do + + if container.env ~= nil then + for _, env in ipairs(container.env) do + if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then + key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name) + podSecrets[key] = true + end + end + end + + if container.envFrom ~= nil then + for _, envFrom in ipairs(container.envFrom) do + if envFrom.secretRef ~= nil then + key = getKey(pod.metadata.namespace, envFrom.secretRef.name) + podSecrets[key] = true + end + end + end + end + end + + if pod.spec.initContainers ~= nil then + for _, initContainer in ipairs(pod.spec.initContainers) do + if initContainer.env ~= nil then + for _, env in ipairs(initContainer.env) do + if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then + key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name) + podSecrets[key] = true + end + end + end + end + end + + if pod.spec.imagePullSecrets ~= nil then + for _, secret in ipairs(pod.spec.imagePullSecrets) do + key = getKey(pod.metadata.namespace, secret.name) + podSecrets[key] = true + end + end + + if pod.spec.volumes ~= nil then + for _, volume in ipairs(pod.spec.volumes) do + if volume.secret ~= nil then + key = getKey(pod.metadata.namespace, volume.secret.secretName) + podSecrets[key] = true + end + + if volume.projected ~= nil and volume.projected.sources ~= nil then + for _, projectedResource in ipairs(volume.projected.sources) do + if projectedResource.secret ~= nil then + key = getKey(pod.metadata.namespace, projectedResource.secret.name) + podSecrets[key] = true + end + end + end + end + end + end + + return podSecrets + end + + function secretsUsedByIngresses(ingresses) + local ingressSecrets = {} + for _, ingress in ipairs(ingresses) do + if ingress.spec.tls ~= nil then + for _, tls in ipairs(ingress.spec.tls) do + key = getKey(ingress.metadata.namespace, tls.secretName) + ingressSecrets[key] = true + end + end + end + + return ingressSecrets + end + + function evaluate() + local hs = {} + hs.message = "" + + local pods = {} + local secrets = {} + local ingresses = {} + local unusedSecrets = {} + + -- Separate secrets and pods and ingresses from the resources + for _, resource in ipairs(resources) do + local kind = resource.kind + if kind == "Secret" then + table.insert(secrets, resource) + elseif kind == "Pod" then + table.insert(pods, resource) + elseif kind == "Ingress" then + table.insert(ingresses, resource) + end + end + + podSecrets = secretsUsedByPods(pods) + ingressSecrets = secretsUsedByIngresses(ingresses) + + for _, secret in ipairs(secrets) do + if secret.type ~= "kubernetes.io/service-account-token" then + key = getKey(secret.metadata.namespace, secret.metadata.name) + if not podSecrets[key] and not ingressSecrets[key] then + table.insert(unusedSecrets, {resource = secret}) + end + end + end + + if #unusedSecrets > 0 then + hs.resources = unusedSecrets + end + return hs + end ```