feature: simple basic application deployment with service, certificate, dns and ingress exposition available

This commit is contained in:
RouxAntoine 2024-05-29 00:25:56 +02:00
parent 229ab20e55
commit b578babd1b
Signed by: antoine
GPG Key ID: 098FB66FC0475E70
5 changed files with 361 additions and 0 deletions

39
.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
# intellij
*.iml
idea/

138
exposition.tf Normal file
View File

@ -0,0 +1,138 @@
locals {
service_hostname = format("%s.localdomain", var.application_name)
at_least_one_port = length(var.ports) > 0 ? 1 : 0
ports_map = {
for index, port in var.ports : format("port-%s", index) => port
}
exposed_ports_map = {
for index, port in var.ports :
format("port-%s", index) => port if port.expose == true
}
certificate_secret_name = format("%s-certificate", var.application_name)
at_least_one_port_exposed = length(local.exposed_ports_map) > 0 ? 1 : 0
}
resource "kubernetes_service_v1" "service" {
count = local.at_least_one_port
metadata {
name = var.application_name
namespace = var.namespace
labels = {
"app.kubernetes.io/part-of" = var.application_name
"app.kubernetes.io/managed-by" = "terraform"
}
}
spec {
type = "ClusterIP"
dynamic port {
for_each = local.ports_map
content {
name = format("service-%s", port.key)
port = port.value.container_port
target_port = port.key
}
}
selector = {
"app.kubernetes.io/name" = local.label_name
}
}
}
resource "kubernetes_manifest" "certificate" {
count = local.at_least_one_port_exposed
manifest = {
apiVersion = "cert-manager.io/v1"
kind = "Certificate"
metadata = {
name = var.application_name
namespace = var.namespace
labels = {
"app.kubernetes.io/part-of" = var.application_name
"app.kubernetes.io/managed-by" = "terraform"
}
}
spec = {
secretName = local.certificate_secret_name
dnsNames = [
local.service_hostname,
format("*.%s", local.service_hostname)
]
issuerRef = {
kind = "ClusterIssuer"
name = "localdomain-issuer"
group = "cfssl-issuer.wikimedia.org"
}
}
}
}
resource "kubernetes_ingress_v1" "ingress" {
for_each = local.exposed_ports_map
metadata {
name = var.application_name
namespace = var.namespace
labels = {
"app.kubernetes.io/part-of" = var.application_name
"app.kubernetes.io/managed-by" = "terraform"
}
annotations = {
"traefik.ingress.kubernetes.io/router.middlewares" = "kube-ingress-gzip-compress@kubernetescrd"
"traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
rule {
host = local.service_hostname
http {
path {
path = "/"
backend {
service {
name = kubernetes_service_v1.service[0].metadata.0.name
port {
name = format("service-%s", each.key)
}
}
}
}
}
}
tls {
hosts = [local.service_hostname]
secret_name = local.certificate_secret_name
}
}
}
# {{ application_name }}.localdomain IN CNAME internal-lb
resource "kubernetes_manifest" "record" {
count = local.at_least_one_port_exposed
manifest = {
apiVersion = "externaldns.k8s.io/v1alpha1"
kind = "DNSEndpoint"
metadata = {
name = var.application_name
namespace = var.namespace
labels = {
"app.kubernetes.io/part-of" = var.application_name
"app.kubernetes.io/managed-by" = "terraform"
}
}
spec = {
endpoints = [
{
dnsName = local.service_hostname
recordTTL = "180"
recordType = "CNAME"
targets = [
"internal-lb.localdomain"
]
}
]
}
}
}

55
input.tf Normal file
View File

@ -0,0 +1,55 @@
variable "application_name" {
type = string
}
variable "namespace" {
default = "default"
type = string
}
variable "image" {
type = object({
name = string
tag = optional(string, "latest")
pull-policy = optional(string, "Always")
})
}
variable "args" {
type = list(string)
default = null
}
variable "ports" {
type = list(
object({
container_port = number
expose = optional(bool, false)
})
)
default = []
}
variable "env" {
type = list(
object({
name = string
value = string
})
)
default = []
}
variable "config_content" {
type = string
default = null
}
variable "volumes" {
type = map(
object({
path = string
})
)
default = {}
}

121
main.tf Normal file
View File

@ -0,0 +1,121 @@
resource "random_uuid" "identifier" {
}
locals {
label_name = format("%s-%s", var.application_name, random_uuid.identifier.result)
}
resource "kubernetes_persistent_volume_claim_v1" "storage" {
for_each = var.volumes
metadata {
name = format("%s-volumes-%s", var.application_name, each.key)
namespace = var.namespace
}
spec {
access_modes = ["ReadWriteMany"]
resources {
requests = {
storage = "2Gi"
}
}
}
}
resource "kubernetes_config_map_v1" "configuration_file" {
count = var.config_content == null ? 0 : 1
metadata {
name = "configuration-files"
namespace = var.namespace
}
data = {
"config" = var.config_content
}
}
resource "kubernetes_deployment_v1" "deployment" {
metadata {
name = var.application_name
namespace = var.namespace
labels = {
"app.kubernetes.io/part-of" = var.application_name
"app.kubernetes.io/managed-by" = "terraform"
}
}
spec {
selector {
match_labels = {
"app.kubernetes.io/name" = local.label_name
}
}
template {
metadata {
labels = {
"app.kubernetes.io/name" = local.label_name
}
}
spec {
container {
name = var.application_name
image = format("%s:%s", var.image.name, var.image.tag)
image_pull_policy = var.image.pull-policy
args = var.args
dynamic port {
for_each = var.ports
content {
name = format("port-%s", port.key)
container_port = port.value.container_port
}
}
dynamic env {
for_each = var.env
content {
name = env.value.name
value = env.value.value
}
}
dynamic volume_mount {
for_each = var.config_content != null ? [
kubernetes_config_map_v1.configuration_file.0.metadata.0.name
] : []
content {
name = "config-volume"
mount_path = "/application/config.json"
sub_path = "config"
}
}
dynamic volume_mount {
for_each = var.volumes
content {
name = volume_mount.key
mount_path = volume_mount.value.path
}
}
}
dynamic volume {
for_each = var.volumes
content {
name = volume.key
persistent_volume_claim {
claim_name = format("%s-volumes-%s", var.application_name, volume.key)
}
}
}
dynamic volume {
for_each = var.config_content != null ? [kubernetes_config_map_v1.configuration_file.0.metadata.0.name] : []
content {
name = "config-volume"
config_map {
name = volume.value
}
}
}
}
}
}
}

8
output.tf Normal file
View File

@ -0,0 +1,8 @@
output "deployment" {
value = kubernetes_deployment_v1.deployment
}
output "record" {
value = format("Access to the application at https://%s", local.service_hostname)
depends_on = [kubernetes_manifest.record]
}