├── .gitignore ├── versions.tf ├── outputs.tf ├── cluster.tf ├── variables.tf └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform* 2 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0" 3 | 4 | required_providers { 5 | ovh = { 6 | source = "ovh/ovh" 7 | version = "~> 2.5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "The ID of the cluster" 3 | value = ovh_cloud_project_kube.cluster.id 4 | } 5 | 6 | output "name" { 7 | description = "Name of the cluster" 8 | value = ovh_cloud_project_kube.cluster.name 9 | } 10 | 11 | output "nodes_url" { 12 | description = "URLs of the nodes attached to the cluster" 13 | value = ovh_cloud_project_kube.cluster.nodes_url 14 | } 15 | 16 | output "kubeconfig" { 17 | description = "kubeconfig to use when connecting to the cluster" 18 | value = ovh_cloud_project_kube.cluster.kubeconfig 19 | } 20 | -------------------------------------------------------------------------------- /cluster.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | savings_plan = (var.savings_plan_count > 0 && var.savings_plan_instance != "") ? true : false 3 | savings_plan_name = var.savings_plan_name != "" ? var.savings_plan_name : "savings-plan-${var.cluster_name}" 4 | } 5 | 6 | resource "ovh_cloud_project_kube" "cluster" { 7 | service_name = var.project_id 8 | name = var.cluster_name 9 | region = var.region 10 | version = var.cluster_version 11 | update_policy = var.update_policy 12 | 13 | private_network_id = var.network_openstack_id 14 | nodes_subnet_id = var.subnet_id 15 | } 16 | 17 | resource "ovh_cloud_project_kube_nodepool" "node_pool" { 18 | count = length(var.node_pools) 19 | 20 | service_name = var.project_id 21 | kube_id = ovh_cloud_project_kube.cluster.id 22 | name = var.node_pools[count.index].name != "" ? var.node_pools[count.index].name : "pool${count.index}" 23 | flavor_name = var.node_pools[count.index].flavor_name 24 | desired_nodes = var.node_pools[count.index].nodes 25 | max_nodes = var.node_pools[count.index].max_nodes < 1 ? var.node_pools[count.index].nodes : var.node_pools[count.index].max_nodes 26 | min_nodes = var.node_pools[count.index].min_nodes < 1 ? var.node_pools[count.index].nodes : var.node_pools[count.index].min_nodes 27 | monthly_billed = var.node_pools[count.index].monthly_billed 28 | availability_zones = var.node_pools[count.index].availability_zones 29 | 30 | autoscale = var.node_pools[count.index].autoscale 31 | autoscaling_scale_down_unneeded_time_seconds = var.node_pools[count.index].autoscaling_scale_down_unneeded_time_seconds 32 | autoscaling_scale_down_unready_time_seconds = var.node_pools[count.index].autoscaling_scale_down_unready_time_seconds 33 | autoscaling_scale_down_utilization_threshold = var.node_pools[count.index].autoscaling_scale_down_utilization_threshold 34 | dynamic "template" { 35 | for_each = var.node_pools[count.index].template != null ? [1] : [] 36 | 37 | content { 38 | metadata { 39 | annotations = var.node_pools[count.index].template["annotations"] 40 | finalizers = var.node_pools[count.index].template["finalizers"] 41 | labels = var.node_pools[count.index].template["labels"] 42 | } 43 | spec { 44 | unschedulable = var.node_pools[count.index].template["unschedulable"] 45 | taints = var.node_pools[count.index].template["taints"] 46 | } 47 | } 48 | } 49 | } 50 | 51 | resource "ovh_cloud_project_kube_oidc" "oidc" { 52 | count = var.oidc_provider_url != "" ? 1 : 0 53 | 54 | service_name = var.project_id 55 | kube_id = ovh_cloud_project_kube.cluster.id 56 | 57 | client_id = var.oidc_client_id 58 | issuer_url = var.oidc_provider_url 59 | 60 | oidc_username_claim = var.oidc_username_claim 61 | oidc_username_prefix = var.oidc_username_prefix 62 | oidc_groups_claim = var.oidc_groups_claim 63 | oidc_groups_prefix = var.oidc_groups_prefix 64 | } 65 | 66 | resource "ovh_savings_plan" "plan" { 67 | count = local.savings_plan ? 1 : 0 68 | 69 | service_name = var.project_id 70 | display_name = local.savings_plan_name 71 | 72 | flavor = var.savings_plan_instance 73 | period = var.savings_plan_period 74 | size = var.savings_plan_count 75 | auto_renewal = var.savings_plan_auto_renewal 76 | } 77 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "project_id" { 2 | description = "The ID of the Public Cloud project the resources will be created in." 3 | type = string 4 | } 5 | 6 | variable "region" { 7 | description = "Region in which to create the various resources." 8 | type = string 9 | default = "DE1" 10 | } 11 | 12 | # Cluster settings 13 | 14 | variable "cluster_name" { 15 | description = "Name of the cluster to create." 16 | type = string 17 | } 18 | 19 | variable "cluster_version" { 20 | description = "Kubelet version to use when creating the cluster." 21 | type = string 22 | } 23 | 24 | variable "update_policy" { 25 | description = "How the cluster should perform updates." 26 | type = string 27 | default = "MINIMAL_DOWNTIME" 28 | 29 | validation { 30 | condition = contains(["ALWAYS_UPDATE", "MINIMAL_DOWNTIME", "NEVER_UPDATE"], var.update_policy) 31 | error_message = "Must be one of: [ALWAYS_UPDATE, MINIMAL_DOWNTIME, NEVER_UPDATE]" 32 | } 33 | } 34 | variable "node_pools" { 35 | description = "Node pools to create for the cluster." 36 | type = list(object({ 37 | flavor_name = string 38 | nodes = number 39 | min_nodes = optional(number, 0) 40 | max_nodes = optional(number, 0) 41 | name = optional(string, "") 42 | availability_zones = optional(list(string), []) 43 | monthly_billed = optional(bool, false) 44 | autoscale = optional(bool, false) 45 | autoscaling_scale_down_unneeded_time_seconds = optional(number, null) 46 | autoscaling_scale_down_unready_time_seconds = optional(number, null) 47 | autoscaling_scale_down_utilization_threshold = optional(number, null) 48 | template = optional( 49 | object({ 50 | annotations = optional(map(any), {}) 51 | labels = optional(map(any), {}) 52 | finalizers = optional(list(string), []) 53 | unschedulable = optional(bool, false) 54 | taints = optional(list(object({ 55 | effect = string 56 | key = string 57 | value = string 58 | })), []) 59 | }), 60 | null 61 | ) 62 | })) 63 | default = [] 64 | } 65 | 66 | # Network settings 67 | variable "network_openstack_id" { 68 | description = "Openstack ID of the private network in which the cluster should be placed." 69 | type = string 70 | } 71 | 72 | variable "subnet_id" { 73 | description = "ID of the subnet in the private network in which nodes should be created." 74 | type = string 75 | } 76 | 77 | # OIDC settings 78 | 79 | variable "oidc_provider_url" { 80 | description = "URL to use for OIDC authentication. Enables OIDC if specified." 81 | type = string 82 | default = "" 83 | } 84 | 85 | variable "oidc_client_id" { 86 | description = "OIDC client ID to give the cluster for login." 87 | type = string 88 | default = "" 89 | } 90 | 91 | variable "oidc_username_claim" { 92 | description = "OIDC Property to use for username." 93 | type = string 94 | default = "" 95 | } 96 | 97 | variable "oidc_username_prefix" { 98 | description = "Prefix to add to all usernames connecting to the cluster." 99 | type = string 100 | default = "" 101 | } 102 | 103 | variable "oidc_groups_claim" { 104 | description = "Groups to include in the OIDC claim." 105 | type = list(string) 106 | default = [] 107 | } 108 | 109 | variable "oidc_groups_prefix" { 110 | description = "Prefix to add to all groups connecting to the cluster." 111 | type = string 112 | default = "" 113 | } 114 | 115 | # Savings Plan 116 | 117 | variable "savings_plan_instance" { 118 | description = "Type of instance this savings plan applies to." 119 | type = string 120 | default = "" 121 | } 122 | 123 | variable "savings_plan_count" { 124 | description = "Number of instances this savings plan applies to." 125 | type = number 126 | default = 0 127 | } 128 | 129 | variable "savings_plan_name" { 130 | description = "Name of the savings plan." 131 | type = string 132 | default = "" 133 | } 134 | 135 | variable "savings_plan_period" { 136 | description = "The period of time the savings plan applies to." 137 | type = string 138 | default = "P1M" 139 | } 140 | 141 | variable "savings_plan_auto_renewal" { 142 | description = "Whether the savings plan should renew automatically." 143 | type = bool 144 | default = true 145 | } 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mastodon Terraform - OVHCloud Kubernetes Cluster 2 | 3 | Terraform module for creating a managed kubernetes cluster in OVHCloud. 4 | 5 | ## Requirements 6 | 7 | | Name | Version | 8 | |------|---------| 9 | | [terraform](#requirement\_terraform) | >= 1.0.0 | 10 | | [ovh](#requirement\_ovh) | ~> 2.5.0 | 11 | 12 | ## Providers 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [ovh](#provider\_ovh) | 2.5.0 | 17 | 18 | ## Modules 19 | 20 | No modules. 21 | 22 | ## Resources 23 | 24 | | Name | Type | 25 | |------|------| 26 | | [ovh_cloud_project_kube.cluster](https://registry.terraform.io/providers/ovh/ovh/latest/docs/resources/cloud_project_kube) | resource | 27 | | [ovh_cloud_project_kube_nodepool.node_pool](https://registry.terraform.io/providers/ovh/ovh/latest/docs/resources/cloud_project_kube_nodepool) | resource | 28 | | [ovh_cloud_project_kube_oidc.oidc](https://registry.terraform.io/providers/ovh/ovh/latest/docs/resources/cloud_project_kube_oidc) | resource | 29 | | [ovh_savings_plan.plan](https://registry.terraform.io/providers/ovh/ovh/latest/docs/resources/savings_plan) | resource | 30 | 31 | ## Inputs 32 | 33 | | Name | Description | Type | Default | Required | 34 | |------|-------------|------|---------|:--------:| 35 | | [cluster\_name](#input\_cluster\_name) | Name of the cluster to create. | `string` | n/a | yes | 36 | | [cluster\_version](#input\_cluster\_version) | Kubelet version to use when creating the cluster. | `string` | n/a | yes | 37 | | [network\_openstack\_id](#input\_network\_openstack\_id) | Openstack ID of the private network in which the cluster should be placed. | `string` | n/a | yes | 38 | | [node\_pools](#input\_node\_pools) | Node pools to create for the cluster. |
list(object({
flavor_name = string
nodes = number
min_nodes = optional(number, 0)
max_nodes = optional(number, 0)
name = optional(string, "")
availability_zones = optional(list(string), [])
monthly_billed = optional(bool, false)
autoscale = optional(bool, false)
autoscaling_scale_down_unneeded_time_seconds = optional(number, null)
autoscaling_scale_down_unready_time_seconds = optional(number, null)
autoscaling_scale_down_utilization_threshold = optional(number, null)
template = optional(
object({
annotations = optional(map(any), {})
labels = optional(map(any), {})
finalizers = optional(list(string), [])
unschedulable = optional(bool, false)
taints = optional(list(object({
effect = string
key = string
value = string
})), [])
}),
null
)
}))
| `[]` | no | 39 | | [oidc\_client\_id](#input\_oidc\_client\_id) | OIDC client ID to give the cluster for login. | `string` | `""` | no | 40 | | [oidc\_groups\_claim](#input\_oidc\_groups\_claim) | Groups to include in the OIDC claim. | `list(string)` | `[]` | no | 41 | | [oidc\_groups\_prefix](#input\_oidc\_groups\_prefix) | Prefix to add to all groups connecting to the cluster. | `string` | `""` | no | 42 | | [oidc\_provider\_url](#input\_oidc\_provider\_url) | URL to use for OIDC authentication. Enables OIDC if specified. | `string` | `""` | no | 43 | | [oidc\_username\_claim](#input\_oidc\_username\_claim) | OIDC Property to use for username. | `string` | `""` | no | 44 | | [oidc\_username\_prefix](#input\_oidc\_username\_prefix) | Prefix to add to all usernames connecting to the cluster. | `string` | `""` | no | 45 | | [project\_id](#input\_project\_id) | The ID of the Public Cloud project the resources will be created in. | `string` | n/a | yes | 46 | | [region](#input\_region) | Region in which to create the various resources. | `string` | `"DE1"` | no | 47 | | [savings\_plan\_auto\_renewal](#input\_savings\_plan\_auto\_renewal) | Whether the savings plan should renew automatically. | `bool` | `true` | no | 48 | | [savings\_plan\_count](#input\_savings\_plan\_count) | Number of instances this savings plan applies to. | `number` | `0` | no | 49 | | [savings\_plan\_instance](#input\_savings\_plan\_instance) | Type of instance this savings plan applies to. | `string` | `""` | no | 50 | | [savings\_plan\_name](#input\_savings\_plan\_name) | Name of the savings plan. | `string` | `""` | no | 51 | | [savings\_plan\_period](#input\_savings\_plan\_period) | The period of time the savings plan applies to. | `string` | `"P1M"` | no | 52 | | [subnet\_id](#input\_subnet\_id) | ID of the subnet in the private network in which nodes should be created. | `string` | n/a | yes | 53 | | [update\_policy](#input\_update\_policy) | How the cluster should perform updates. | `string` | `"MINIMAL_DOWNTIME"` | no | 54 | 55 | ## Outputs 56 | 57 | | Name | Description | 58 | |------|-------------| 59 | | [id](#output\_id) | The ID of the cluster | 60 | | [kubeconfig](#output\_kubeconfig) | kubeconfig to use when connecting to the cluster | 61 | | [name](#output\_name) | Name of the cluster | 62 | | [nodes\_url](#output\_nodes\_url) | URLs of the nodes attached to the cluster | 63 | --------------------------------------------------------------------------------