diff --git a/icons/snapscheduler.svg b/icons/snapscheduler.svg new file mode 100644 index 00000000..b374eb8a --- /dev/null +++ b/icons/snapscheduler.svg @@ -0,0 +1,222 @@ + + + + + snapscheduler logo + + + + + + + + + + + + + + image/svg+xml + + snapscheduler logo + + + + CC BY-SA + + + + + + + + + + + + + + + + + + + + + + + + + + + + S + + + + + diff --git a/modules/snapscheduler/standard/1.0/README.md b/modules/snapscheduler/standard/1.0/README.md new file mode 100644 index 00000000..69beba15 --- /dev/null +++ b/modules/snapscheduler/standard/1.0/README.md @@ -0,0 +1,151 @@ +# SnapScheduler Operator + +![Version](https://img.shields.io/badge/version-1.0-blue) +![Cloud](https://img.shields.io/badge/cloud-kubernetes-326CE5) + +## Overview + +This module deploys the SnapScheduler operator using Helm charts. SnapScheduler provides automated backup and snapshot scheduling for Kubernetes persistent volumes using the Kubernetes CSI (Container Storage Interface) snapshot API. It enables declarative, schedule-based volume snapshot management through Kubernetes-native Custom Resource Definitions (CRDs). + +The operator automates the creation, retention, and cleanup of persistent volume snapshots, making it easy to implement backup policies and disaster recovery strategies for stateful workloads on Kubernetes. + +## Environment as Dimension + +The module is **environment-aware** through the following mechanisms: + +- **Namespace Management**: Can use environment-specific namespaces or a custom namespace per deployment +- **Resource Allocation**: CPU and memory limits/requests can be adjusted per environment (development vs production) +- **Node Pool Targeting**: Optional node pool assignment allows environment-specific compute resource isolation +- **Helm Release Management**: Supports atomic deployments, wait conditions, and timeout configurations per environment + +The `var.environment` context is used to determine default namespace behavior and can influence deployment topology based on environment requirements. + +## Resources Created + +This module creates the following Kubernetes resources: + +- **Namespace**: Optional namespace creation for SnapScheduler isolation (default: `default`) +- **Helm Release**: SnapScheduler operator deployment via Helm chart (version 3.2.0) +- **Custom Resource Definitions (CRDs)**: Kubernetes CRDs for SnapshotSchedule resources +- **Operator Deployment**: Controller manager pods that watch and reconcile snapshot schedules +- **RBAC Resources**: Service accounts, cluster roles, and cluster role bindings for operator permissions +- **Services**: Internal services for operator management and Prometheus metrics (if enabled) + +## Node Pool Integration + +When a node pool is provided as input, the module configures: + +- **Node Selector**: Ensures operator pods are scheduled on designated nodes matching the node pool's selector +- **Tolerations**: Allows scheduling on tainted nodes (e.g., dedicated node pools for infrastructure workloads) + +This enables dedicated compute resources for the SnapScheduler operator, separating it from application workloads and ensuring consistent scheduling behavior. + +## Resource Configuration + +The module provides granular resource control for the SnapScheduler operator pods: + +- **CPU Request**: Minimum CPU allocation (default: `10m`) +- **CPU Limit**: Maximum CPU allocation (default: `1`) +- **Memory Request**: Minimum memory allocation (default: `100Mi`) +- **Memory Limit**: Maximum memory allocation (default: `1Gi`) + +These settings should be adjusted based on: +- Number of snapshot schedules being managed +- Frequency of snapshot operations +- Size and count of volumes being snapshotted +- Environment-specific resource constraints + +## Namespace Behavior + +The module implements intelligent namespace management: + +- **Custom Namespace Provided**: Uses the specified namespace from `spec.namespace` +- **Environment Namespace**: Falls back to `var.environment.namespace` if available +- **Default Namespace**: Uses `default` namespace if no custom namespace is specified +- **Namespace Creation**: Controlled by `create_namespace` flag (default: `true`) + +This prevents conflicts with existing namespace management and supports both dedicated and shared namespace scenarios. + +## Prometheus Integration + +The module supports optional Prometheus integration for metrics collection: + +- **Automatic Discovery**: When `prometheus_details` input is provided, the operator is configured with the Prometheus release ID +- **Metrics Exposure**: SnapScheduler exposes metrics about snapshot operations, schedules, and status +- **ServiceMonitor**: Prometheus operator can automatically discover and scrape SnapScheduler metrics + +## Helm Release Configuration + +The module supports comprehensive Helm release lifecycle management: + +- **Wait**: Wait for all resources to become ready before marking release as successful (default: `true`) +- **Atomic**: If installation fails, automatically purge the release (default: `false`) +- **Timeout**: Maximum time to wait for Kubernetes operations (default: `600` seconds) +- **Recreate Pods**: Force pod recreation on updates (default: `false`) +- **Cleanup on Fail**: Remove resources on failed installation (always enabled) + +## Security Considerations + +### Operator Permissions + +The SnapScheduler operator requires specific cluster permissions: + +- **VolumeSnapshot API**: Full control over VolumeSnapshot and VolumeSnapshotContent resources +- **PersistentVolumeClaim Access**: Read access to PVCs for snapshot source detection +- **Namespace-scoped**: Operator typically manages snapshots in specific namespaces +- **RBAC Resources**: ClusterRole and ClusterRoleBinding for snapshot management + +### CSI Driver Requirements + +SnapScheduler requires: +- CSI driver with snapshot support (e.g., AWS EBS CSI, GCP PD CSI, Azure Disk CSI) +- VolumeSnapshotClass configured in the cluster +- Snapshot controller running in the cluster (usually installed by default) + +## Advanced Configuration + +The module supports advanced Helm value overrides through the `helm_values` parameter. This allows customization of: + +- Snapshot schedule templates and defaults +- Image registry and tag overrides +- Additional controller flags and arguments +- Logging levels and output formats +- Metrics and monitoring configuration +- RBAC scope and permissions + +Example: +```yaml +helm_values: + logLevel: debug + replicaCount: 2 + snapshotRetention: + defaultPolicy: "7d" +``` + +## Dependencies + +**Required:** +- Kubernetes cluster with Helm provider access +- CSI driver with snapshot support +- VolumeSnapshot CRDs (Kubernetes 1.20+) + +**Optional:** +- Prometheus instance for metrics collection +- Node pool configuration for pod placement + +## Outputs + +The module exposes the following outputs: + +- **release_name**: Helm release name +- **namespace**: Deployment namespace +- **chart**: Chart name used for deployment +- **version**: Chart version deployed +- **status**: Current release status + +## Usage Notes + +- SnapScheduler uses the CSI snapshot API, which requires a CSI driver that supports snapshots +- Snapshot storage location and retention policies are controlled by VolumeSnapshotClass and SnapshotSchedule CRDs +- The operator watches for SnapshotSchedule resources and creates VolumeSnapshots based on the defined schedule +- Snapshot lifecycle (creation, retention, deletion) is fully automated once schedules are defined diff --git a/modules/snapscheduler/standard/1.0/facets.yaml b/modules/snapscheduler/standard/1.0/facets.yaml new file mode 100644 index 00000000..6ebe2756 --- /dev/null +++ b/modules/snapscheduler/standard/1.0/facets.yaml @@ -0,0 +1,129 @@ +intent: snapscheduler +flavor: standard +version: '1.0' +clouds: +- aws +- azure +- gcp +- kubernetes +description: SnapScheduler for automated Kubernetes persistent volume snapshots +intentDetails: + type: Operators + description: SnapScheduler operator for managing automated Kubernetes persistent volume snapshots and backups + displayName: SnapScheduler + iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/snapscheduler.svg +spec: + title: SnapScheduler + description: Installs and manages the SnapScheduler operator using Helm + type: object + properties: + namespace: + type: string + title: Namespace + description: Kubernetes namespace for SnapScheduler installation. If not specified, uses 'default'. + default: default + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + maxLength: 63 + create_namespace: + type: boolean + title: Create Namespace + description: Create the namespace if it does not exist + default: true + wait: + type: boolean + title: Wait + description: Wait for the Helm release to be deployed + default: true + atomic: + type: boolean + title: Atomic + description: If set, installation process purges chart on fail + default: false + timeout: + type: number + title: Timeout + description: Time in seconds to wait for any individual Kubernetes operation + default: 600 + recreate_pods: + type: boolean + title: Recreate Pods + description: Perform pods restart for the resource if applicable + default: false + resources: + type: object + title: Resource Allocation + description: CPU and memory resources for SnapScheduler pods + properties: + cpu_request: + type: string + title: CPU Request + description: Minimum CPU allocation + default: 10m + cpu_limit: + type: string + title: CPU Limit + description: Maximum CPU allocation + default: '1' + memory_request: + type: string + title: Memory Request + description: Minimum memory allocation + default: 100Mi + memory_limit: + type: string + title: Memory Limit + description: Maximum memory allocation + default: 1Gi + helm_values: + type: object + x-ui-yaml-editor: true + title: Helm Values + description: Additional Helm values to override defaults + default: {} +inputs: + kubernetes_cluster: + type: '@facets/kubernetes-details' + optional: false + displayName: Kubernetes Cluster + description: Target Kubernetes cluster for SnapScheduler deployment + providers: + - kubernetes + - helm + node_pool: + type: '@facets/kubernetes_nodepool' + optional: false + displayName: Node Pool + description: Node pool for SnapScheduler pods + prometheus_details: + type: '@facets/prometheus' + optional: true + displayName: Prometheus + description: Prometheus instance for metrics collection +outputs: + default: + type: '@facets/snapscheduler' + title: SnapScheduler +iac: + validated_files: + - main.tf + - variables.tf + - locals.tf + - outputs.tf +sample: + kind: snapscheduler + flavor: standard + version: '1.0' + disabled: false + spec: + namespace: default + create_namespace: true + wait: true + atomic: false + timeout: 600 + recreate_pods: false + resources: + cpu_request: 10m + cpu_limit: '1' + memory_request: 100Mi + memory_limit: 1Gi + helm_values: {} diff --git a/modules/snapscheduler/standard/1.0/locals.tf b/modules/snapscheduler/standard/1.0/locals.tf new file mode 100644 index 00000000..1b110421 --- /dev/null +++ b/modules/snapscheduler/standard/1.0/locals.tf @@ -0,0 +1,59 @@ +locals { + namespace = lookup(var.instance.spec, "namespace", "default") + user_supplied_helm_values = lookup(var.instance.spec, "helm_values", {}) + + # Resource defaults + cpu_request = lookup(lookup(var.instance.spec, "resources", {}), "cpu_request", "10m") + cpu_limit = lookup(lookup(var.instance.spec, "resources", {}), "cpu_limit", "1") + memory_request = lookup(lookup(var.instance.spec, "resources", {}), "memory_request", "100Mi") + memory_limit = lookup(lookup(var.instance.spec, "resources", {}), "memory_limit", "1Gi") + + # Node pool configuration + node_pool_input = lookup(var.inputs, "node_pool", {}) + node_pool_attrs = lookup(local.node_pool_input, "attributes", {}) + node_selector = lookup(local.node_pool_attrs, "node_selector", {}) + node_pool_taints = lookup(local.node_pool_attrs, "taints", []) + + # Convert taints to tolerations format + tolerations = [ + for taint in local.node_pool_taints : { + key = taint.key + operator = "Equal" + value = taint.value + effect = taint.effect + } + ] + + # Prometheus integration + prometheus_release_id = lookup(lookup(lookup(var.inputs, "prometheus_details", {}), "attributes", {}), "helm_release_id", "") + + # Build default helm values + default_values = { + prometheus_id = local.prometheus_release_id + rbacProxy = { + image = { + tag = "v0.19.0" + } + } + image = { + repository = "facetscloud/snapscheduler" + tagOverride = 9 + pullPolicy = "IfNotPresent" + } + resources = { + limits = { + cpu = local.cpu_limit + memory = local.memory_limit + } + requests = { + cpu = local.cpu_request + memory = local.memory_request + } + } + nodeSelector = local.node_selector + tolerations = local.tolerations + } + + # Merge default and user-supplied values + final_values = merge(local.default_values, local.user_supplied_helm_values) +} diff --git a/modules/snapscheduler/standard/1.0/main.tf b/modules/snapscheduler/standard/1.0/main.tf new file mode 100644 index 00000000..78773a66 --- /dev/null +++ b/modules/snapscheduler/standard/1.0/main.tf @@ -0,0 +1,24 @@ +module "name" { + source = "github.com/Facets-cloud/facets-utility-modules//name" + environment = var.environment + limit = 48 + resource_name = var.instance_name + resource_type = "snapscheduler" + globally_unique = true +} + +resource "helm_release" "snapschedule" { + name = "snapscheduler" + chart = "${path.module}/snapscheduler-3.2.0.tgz" + cleanup_on_fail = true + namespace = local.namespace + create_namespace = lookup(var.instance.spec, "create_namespace", true) + wait = lookup(var.instance.spec, "wait", true) + atomic = lookup(var.instance.spec, "atomic", false) + timeout = lookup(var.instance.spec, "timeout", 600) + recreate_pods = lookup(var.instance.spec, "recreate_pods", false) + + values = [ + yamlencode(local.final_values) + ] +} diff --git a/modules/snapscheduler/standard/1.0/outputs.tf b/modules/snapscheduler/standard/1.0/outputs.tf new file mode 100644 index 00000000..56000b54 --- /dev/null +++ b/modules/snapscheduler/standard/1.0/outputs.tf @@ -0,0 +1,12 @@ +locals { + output_attributes = { + namespace = helm_release.snapschedule.namespace + release_name = helm_release.snapschedule.name + chart = helm_release.snapschedule.chart + version = helm_release.snapschedule.version + status = helm_release.snapschedule.status + operator_name = "snapscheduler" + } + + output_interfaces = {} +} diff --git a/modules/snapscheduler/standard/1.0/snapscheduler-3.2.0.tgz b/modules/snapscheduler/standard/1.0/snapscheduler-3.2.0.tgz new file mode 100644 index 00000000..46afb658 Binary files /dev/null and b/modules/snapscheduler/standard/1.0/snapscheduler-3.2.0.tgz differ diff --git a/modules/snapscheduler/standard/1.0/variables.tf b/modules/snapscheduler/standard/1.0/variables.tf new file mode 100644 index 00000000..ffdf5360 --- /dev/null +++ b/modules/snapscheduler/standard/1.0/variables.tf @@ -0,0 +1,78 @@ +variable "instance" { + type = object({ + kind = string + flavor = string + version = string + spec = object({ + namespace = optional(string) + create_namespace = optional(bool) + wait = optional(bool) + atomic = optional(bool) + timeout = optional(number) + recreate_pods = optional(bool) + resources = optional(object({ + cpu_request = optional(string) + cpu_limit = optional(string) + memory_request = optional(string) + memory_limit = optional(string) + })) + helm_values = optional(any) + }) + }) +} + +variable "instance_name" { + description = "Unique architectural name from the blueprint" + type = string +} + +variable "environment" { + type = object({ + name = string + unique_name = string + namespace = optional(string) + cloud_tags = optional(map(string), {}) + }) + description = "Environment context including name and cloud tags" +} + +variable "inputs" { + type = object({ + kubernetes_cluster = object({ + attributes = optional(object({ + cloud_provider = optional(string) + cluster_id = optional(string) + cluster_name = optional(string) + cluster_location = optional(string) + cluster_endpoint = optional(string) + })) + interfaces = optional(object({ + kubernetes = optional(object({ + cluster_ca_certificate = optional(string) + host = optional(string) + })) + })) + }) + node_pool = object({ + attributes = object({ + node_pool_name = optional(string) + node_class_name = optional(string) + node_selector = optional(map(string), {}) + taints = optional(list(object({ + key = string + value = string + effect = string + })), []) + }) + interfaces = optional(any) + }) + prometheus_details = optional(object({ + attributes = optional(object({ + alertmanager_url = optional(string) + helm_release_id = optional(string) + prometheus_url = optional(string) + })) + interfaces = optional(object({})) + })) + }) +} diff --git a/outputs/snapscheduler/outputs.yaml b/outputs/snapscheduler/outputs.yaml new file mode 100644 index 00000000..242a9582 --- /dev/null +++ b/outputs/snapscheduler/outputs.yaml @@ -0,0 +1,29 @@ +name: '@facets/snapscheduler' +properties: + type: object + properties: + attributes: + type: object + properties: + namespace: + description: Kubernetes namespace where SnapScheduler operator is deployed + type: string + release_name: + description: Helm release name + type: string + chart: + description: Helm chart path or name + type: string + version: + description: Helm chart version + type: string + status: + description: Helm release status + type: string + operator_name: + description: Name of the SnapScheduler operator + type: string + interfaces: + type: object + properties: {} +providers: []