This is a Terraform module to install a cron job on a Kubernetes cluster that uses the labels of application instances to perform a scale down to zero during predefined periods, thus reducing resource consumption, and then automatically restores the applications by scaling up at the end of the idle period.
In the Makefile.test and in tests folder are present some helpers to create a kind cluster, deploy the Terraform module, deploy some test resources and run some tests.
- Run
make kind-create-clusterto create a kind cluster. - Run
make terraform-cliand execute, inside the container shell the commandterraform init && terraform applyto deploy the module inside the kind cluster. - Run
make deploy-resourcesto deploy some test resources. This command will deploy all the manifests in thetests/manifestsfolder usingkubectl apply -f. - (OPTIONAL) Run
make patch-cronjobto patch the CronJob created by the Terraform module. This command will patch the CronJob using thecontrol-planenode asnodeSelector. - Run
make application-pod-shellto open a shell inside the application pod. Inside the pod you can test your application script which are mounted in the/appfolder.
| Name | Version |
|---|---|
| kubernetes | >= 2.26 |
| Name | Version |
|---|---|
| terraform | >= 1 |
| kubernetes | >= 2.26 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| additional_protected_namespaces | List of additional namespaces where the controller should not manage the scale of deployments. | list(string) |
[] |
no |
| cluster_role_binding_name | Name of the cluster role binding. | string |
"custom:application-sleep-cycles:controller" |
no |
| cluster_role_name_prefix | Name of the cluster role. | string |
"custom:application-sleep-cycles:controller" |
no |
| create_namespace | Create namespace. If false, the namespace must be created before using this module. | bool |
true |
no |
| default_cronjob_timezone | Default timezone to use for the CronJobs. | string |
"Europe/Rome" |
no |
| default_docker_image | Default Docker image parts map (registry, repository, tag) used for all CronJob containers when no feature-specific override is provided. | object({ |
{ |
no |
| default_node_affinity_match_expressions | List of match expressions to use for the node affinity when the node affinity is not specified for the specific feature. | list(object({ |
[] |
no |
| default_tolerations | List of tolerations to use when the tolerations are not specified for the specific feature. | list(object({ |
[] |
no |
| k8s_additional_labels | Set of additional labels to apply to all resources. | map(string) |
{} |
no |
| k8s_labels | Set of labels to apply to all resources. | map(string) |
{ |
no |
| namespace | Namespace to create resources. | string |
"application-sleep-cycles" |
no |
| node_drain_configmap_name_prefix | Name prefix for the node drain ConfigMap. | string |
"application-sleep-cycles-drain-config" |
no |
| node_drain_cronjob_schedule | Cron schedule to drain the nodes. Remember that this is relative to the timezone defined in the node_drain_cronjob_timezone variable. |
string |
"30 20-21 * * *" |
no |
| node_drain_cronjob_timezone | Timezone to use for the node drain CronJob. If not specified, the default_cronjob_timezone variable will be used. |
string |
"" |
no |
| node_drain_docker_image | Override map for the node drain CronJob Docker image parts. Any provided key (registry, repository, tag) will override default_docker_image. |
object({ |
{} |
no |
| node_drain_enabled | Enable node drain feature. | bool |
false |
no |
| node_drain_node_affinity_match_expressions | List of match expressions to use for the node affinity of the node drain CronJobs. If not specified (empty list), the default_node_affinity_match_expressions variable will be used. |
list(object({ |
[] |
no |
| node_drain_nodes_label_selector | List of label selector for the nodes to be drained. | list(map(string)) |
[] |
no |
| node_drain_resource_prefix | Prefix for the node drain resources. | string |
"application-sleep-cycles-drain" |
no |
| node_drain_suspend | Suspend the node drain CronJob. | bool |
false |
no |
| node_drain_tolerations | List of tolerations to use for the node drain CronJobs. If not specified (empty list), the default_tolerations variable will be used. |
list(object({ |
[] |
no |
| protected_namespaces | List of namespaces where the controller should not manage the scale of deployments. Use additional_protected_namespaces to add custom protected namespaces. | list(string) |
[ |
no |
| remove_terminating_pods_configmap_name_prefix | Name prefix for the remove terminating pods ConfigMap. | string |
"application-sleep-cycles-terminating-pods-config" |
no |
| remove_terminating_pods_cronjob_schedule | Cron schedule to remove terminating pods. Remember that this is relative to the timezone defined in the remove_terminating_pods_cronjob_timezone variable. |
string |
"0 * * * *" |
no |
| remove_terminating_pods_cronjob_timezone | Timezone to use for the remove terminating pods CronJob. If not specified, the default_cronjob_timezone variable will be used. |
string |
"" |
no |
| remove_terminating_pods_docker_image | Override map for the remove terminating pods CronJob Docker image parts. Any provided key (registry, repository, tag) will override default_docker_image. |
object({ |
{} |
no |
| remove_terminating_pods_enabled | Enable remove terminating pods feature. | bool |
false |
no |
| remove_terminating_pods_node_affinity_match_expressions | List of match expressions to use for the node affinity of the remove terminating pods CronJobs. If not specified (empty list), the default_node_affinity_match_expressions variable will be used. |
list(object({ |
[] |
no |
| remove_terminating_pods_resource_prefix | Prefix for the remove terminating pods resources. | string |
"application-sleep-cycles-terminating-pods" |
no |
| remove_terminating_pods_suspend | Suspend the remove terminating pods CronJob. | bool |
false |
no |
| remove_terminating_pods_tolerations | List of tolerations to use for the remove terminating pods CronJobs. If not specified (empty list), the default_tolerations variable will be used. |
list(object({ |
[] |
no |
| service_account_name | Name of the service account. | string |
"application-sleep-cycles-sa" |
no |
| working_hours_all_namespaces | Enable working hours for all resources in all namespaces (except the ones defined in the protected_namespaces variable). If set to true, the working_hours_managed_namespaces, working_hours_managed_namespaces_label_selector and working_hours_managed_namespaces_all_label_selector variables will be ignored. The protected_namespaces variable will still be used to protect the namespaces where the controller should not manage the scale of resources. The working_hours_all_namespaces_excluded_resources_label_selector variable will be used to protect resources running in a non-protected namespace which should not be scaled down/up. |
bool |
false |
no |
| working_hours_all_namespaces_excluded_resources_label_selector | Label selector used to exclude resources (deployments and statefulsets) which should not be scaled down/up when working_hours_all_namespaces is set to true. The negation of the label selector will be automatically applied, so using sparkfabrik.com/always-on=true will protect the resources with this label (-l '!sparkfabrik.com/always-on=true'). Use null as label value to consider only the label presence. |
map(string) |
{ |
no |
| working_hours_configmap_name_prefix | Name prefix for the Config Maps. | string |
"application-sleep-cycles-config" |
no |
| working_hours_cronjob_timezone | Timezone to use for the cron jobs. If not specified, the default_cronjob_timezone variable will be used. |
string |
"" |
no |
| working_hours_deployments_label_selector | Label selector for the Deployments to be scaled during working-hours. | map(string) |
{ |
no |
| working_hours_docker_image | Override map for the working hours CronJob Docker image parts. Any provided key (registry, repository, tag) will override default_docker_image. |
object({ |
{} |
no |
| working_hours_enabled | Enable working hours feature. | bool |
true |
no |
| working_hours_managed_namespaces | List of namespaces where the controller should manage the scale of deployments. The namespaces defined here will be merged with the namespaces fetched by the working_hours_managed_namespaces_label_selector variable. |
list(string) |
[] |
no |
| working_hours_managed_namespaces_all_label_selector | Label selector for all resources in the namespaces where the controller should manage the scale of deployments. The namespace must have working_hours_managed_namespaces_label_selector set. |
map(string) |
{ |
no |
| working_hours_managed_namespaces_label_selector | Label selector for the namespaces where the controller should manage the scale of deployments. The namespaces fetched by this selector will be merged with the working_hours_managed_namespaces variable. WARNING: remember that if the labels specified here are added to new namespaces, the module will send the Terraform state into drift, as the list of namespaces is retrieved dynamically. You must then re-apply your Terraform configuration to fix the drift. |
map(string) |
{ |
no |
| working_hours_node_affinity_match_expressions | List of match expressions to use for the node affinity of the working hours CronJobs. If not specified (empty list), the default_node_affinity_match_expressions variable will be used. |
list(object({ |
[] |
no |
| working_hours_resource_prefix | Prefix for the working hours resources. | string |
"application-sleep-cycles-working-hours" |
no |
| working_hours_scale_down_schedule | Cron schedule to scale down the Deployments. Remember that this is relative to the timezone defined in the cronjob_timezone variable. |
string |
"0 20 * * *" |
no |
| working_hours_scale_up_schedule | Cron schedule to scale up the Deployments. Remember that this is relative to the timezone defined in the cronjob_timezone variable. |
string |
"30 7 * * 1-5" |
no |
| working_hours_statefulsets_label_selector | Label selector for the Statefulsets to be scaled during working-hours. | map(string) |
{ |
no |
| working_hours_suspend | Suspend the CronJob. | bool |
false |
no |
| working_hours_tolerations | List of tolerations to use for the working hours CronJobs. If not specified (empty list), the default_tolerations variable will be used. |
list(object({ |
[] |
no |
| Name | Description |
|---|---|
| k8s_full_labels | Full set of labels applied to all resources. |
| namespace | Namespace where the descheduler is installed. |
| Name | Type |
|---|---|
| kubernetes_cluster_role_binding_v1.cluster_scoped | resource |
| kubernetes_cluster_role_v1.cluster_scoped | resource |
| kubernetes_config_map_v1.app | resource |
| kubernetes_config_map_v1.app_env | resource |
| kubernetes_config_map_v1.node_drain_app | resource |
| kubernetes_config_map_v1.node_drain_app_env | resource |
| kubernetes_config_map_v1.remove_terminating_pods_app | resource |
| kubernetes_config_map_v1.remove_terminating_pods_app_env | resource |
| kubernetes_manifest.node_drain_cronjob | resource |
| kubernetes_manifest.remove_terminating_pods_cronjob | resource |
| kubernetes_manifest.scale_down | resource |
| kubernetes_manifest.scale_up | resource |
| kubernetes_namespace_v1.this | resource |
| kubernetes_secret_v1.this | resource |
| kubernetes_service_account_v1.this | resource |
| kubernetes_namespace_v1.this | data source |
| kubernetes_resources.managed_namespaces_by_labels | data source |
No modules.
The module builds final CronJob images by merging the per-feature maps (*_docker_image) with the defaults in default_docker_image. Any missing field (registry/repository/tag) falls back to the default value. Be careful when partially overriding, because inconsistent combinations are easy to create:
- If you change only the repository but not the registry, you may accidentally pull from a registry that does not host that image (e.g.,
registry.k8s.io/custom/kubectl:v1.31.0). - If you change registry + repository but forget the tag, you may still pull the default tag while expecting a newer one.
- If you change only the tag without changing the repository, you could point to a tag that does not exist in the default image.
To avoid surprises, set all three fields together when overriding an image unless you are sure the defaults remain valid.
- The string variables
default_docker_registry,default_docker_repository,default_docker_taghave been removed in favor of a single mapdefault_docker_image = { registry, repository, tag }. Replace the old trio with:
module "sleep_cycles" {
source = "github.com/sparkfabrik/terraform-kubernetes-application-sleep-cycles?ref=1.3.0"
additional_protected_namespaces = var.application_sleep_cycles_additional_protected_namespaces
default_docker_registry = "my-registry.io"
default_docker_repository = "my-repo/kubectl"
default_docker_tag = "v1.31.0"
}to
module "sleep_cycles" {
source = "github.com/sparkfabrik/terraform-kubernetes-application-sleep-cycles?ref=2.0.0"
additional_protected_namespaces = var.application_sleep_cycles_additional_protected_namespaces
default_docker_image = {
registry = "my-registry.io"
repository = "my-repo/kubectl"
tag = "v1.31.0"
}
}- Per-feature string overrides (
working_hours_docker_registry,working_hours_docker_image,node_drain_docker_registry,node_drain_docker_image,remove_terminating_pods_docker_registry,remove_terminating_pods_docker_image) are replaced by the corresponding maps:
# old
working_hours_docker_registry = "my-registry.io"
working_hours_docker_image = "my-repo/kubectl:v1.31.1"
# new
working_hours_docker_image = {
registry = "my-registry.io"
repository = "my-repo/kubectl"
tag = "v1.31.1"
}