From 9db93bc348376185f95a0b895503ed36ccda640a Mon Sep 17 00:00:00 2001 From: RaviAnand Mohabir Date: Fri, 17 Feb 2023 10:38:36 +0100 Subject: [PATCH] feat: :sparkles: add Homepage deployment module --- main.tf | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++ variables.tf | 114 +++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 main.tf create mode 100644 variables.tf diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..9345c60 --- /dev/null +++ b/main.tf @@ -0,0 +1,259 @@ +terraform { + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.13.1" + } + } +} + +locals { + match_labels = { + "app.kubernetes.io/instance" = "homepage" + "app.kubernetes.io/name" = "homepage" + } + labels = merge({ + "app.kubernetes.io/version" = "v0.6.7" + }, local.match_labels) +} + +resource "kubernetes_service_account" "homepage" { + metadata { + name = "homepage" + namespace = var.namespace + labels = local.labels + } + secret { + name = "homepage-sa-token" + } +} + +resource "kubernetes_secret" "homepage" { + type = "kuberneetes.io/service-account-token" + metadata { + name = "homepage" + namespace = var.namespace + labels = local.labels + annotations = { + "kubernetes.io/service-account.name" = kubernetes_service_account.homepage.metadata.0.name + } + } +} + +resource "kubernetes_secret" "homepage_sa_token" { + type = "kuberneetes.io/service-account-token" + metadata { + name = "homepage-sa-token" + namespace = var.namespace + labels = local.labels + annotations = { + "kubernetes.io/service-account.name" = kubernetes_service_account.homepage.metadata.0.name + } + } +} + +resource "kubernetes_cluster_role" "homepage" { + metadata { + name = "homepage" + labels = local.labels + } + rule { + api_groups = [""] + resources = ["namespaces", "pods", "nodes"] + verbs = ["get", "list"] + } + rule { + api_groups = ["extensions", "networking.k8s.io"] + resources = ["ingresses"] + verbs = ["get", "list"] + } + rule { + api_groups = ["metrics.k8s.io"] + resources = ["nodes", "pods"] + verbs = ["get", "list"] + } +} + +resource "kubernetes_cluster_role_binding" "homepage" { + metadata { + name = "homepage" + labels = local.labels + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = kubernetes_cluster_role.homepage.metadata.0.name + } + subject { + kind = "ServiceAccount" + name = kubernetes_service_account.homepage.metadata.0.name + namespace = var.namespace + } +} + +resource "kubernetes_deployment" "homepage" { + metadata { + name = "homepage" + namespace = var.namespace + labels = local.labels + } + + spec { + replicas = 1 + + selector { + match_labels = local.match_labels + } + + template { + metadata { + labels = local.labels + annotations = { + "ravianand.me/config-hash" = sha1(jsonencode(merge( + kubernetes_config_map.homepage_config.data, + ))) + } + } + + spec { + service_account_name = kubernetes_service_account.homepage.metadata.0.name + automount_service_account_token = true + container { + image = "ghcr.io/benphelps/homepage:latest" + name = "homepage" + port { + container_port = 3000 + } + volume_mount { + name = "config" + mount_path = "/app/config" + } + volume_mount { + name = "logs" + mount_path = "/app/config/logs" + } + dynamic "volume_mount" { + for_each = toset(var.volumes) + content { + name = volume_mount.value.name + mount_path = volume_mount.value.mount_path + read_only = volume_mount.value.read_only + } + } + liveness_probe { + failure_threshold = 3 + initial_delay_seconds = 0 + period_seconds = 10 + tcp_socket { + port = 3000 + } + timeout_seconds = 1 + } + readiness_probe { + failure_threshold = 3 + initial_delay_seconds = 0 + period_seconds = 10 + tcp_socket { + port = 3000 + } + timeout_seconds = 1 + } + startup_probe { + failure_threshold = 30 + initial_delay_seconds = 0 + period_seconds = 5 + tcp_socket { + port = 3000 + } + timeout_seconds = 1 + } + } + volume { + name = "config" + config_map { + name = kubernetes_config_map.homepage_config.metadata.0.name + } + } + dynamic "volume" { + for_each = toset(var.volumes) + content { + name = volume.value.name + dynamic "persistent_volume_claim" { + for_each = toset(volume.value.persistent_volume_claim != "" ? [volume.value.persistent_volume_claim] : []) + content { + claim_name = persistent_volume_claim.value + } + } + dynamic "host_path" { + for_each = toset(volume.value.host_path.path != "" ? [volume.value.host_path] : []) + content { + path = host_path.value.path + type = host_path.value.type + } + } + } + } + volume { + name = "logs" + empty_dir {} + } + } + } + } +} + +resource "kubernetes_service" "homepage" { + metadata { + name = "homepage" + namespace = var.namespace + } + spec { + type = "ClusterIP" + selector = local.match_labels + port { + port = 3000 + } + } +} + +resource "kubernetes_ingress_v1" "homepage" { + metadata { + name = "homepage" + namespace = var.namespace + annotations = var.ingress_annotations + } + spec { + rule { + host = var.host + http { + path { + path = "/" + path_type = "Prefix" + backend { + service { + name = kubernetes_service.homepage.metadata.0.name + port { + number = kubernetes_service.homepage.spec.0.port.0.port + } + } + } + } + } + } + } +} + +resource "kubernetes_config_map" "homepage_config" { + metadata { + name = "homepage-config" + namespace = var.namespace + } + data = { + "services.yaml" = yamlencode(var.services_config) + "widgets.yaml" = yamlencode(var.widgets_config) + "settings.yaml" = yamlencode(var.settings) + "bookmarks.yaml" = yamlencode(var.bookmarks) + "docker.yaml" = yamlencode(var.docker_config) + "kubernetes.yaml" = yamlencode(var.kubernetes_config) + } +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..533fdfa --- /dev/null +++ b/variables.tf @@ -0,0 +1,114 @@ +variable "host" { + description = "Hostname for Hompeage" + type = string +} + +variable "namespace" { + description = "Namespace to deploy Homepage to" + type = string + default = "default" +} + +variable "volumes" { + description = "Additional volumes to mount to the pod, useful to display storage" + type = list(object({ + name = string + persistent_volume_claim = optional(string, "") + host_path = optional(object({ + path = string + type = optional(string, "Directory") + }), { path = "", type = "Directory" }) + mount_path = string + read_only = optional(bool, true) + })) + default = [] +} + +variable "ingress_annotations" { + description = "Annotations to add to the Ingress" + type = map(string) + default = {} +} + +variable "services_config" { + description = "Configuration file for services" + type = list( + map( + list( + map( + object({ + icon = string + href = optional(string) + namespace = optional(string) + app = optional(string) + podSelector = optional(string, "") + widget = optional(object({ + type = optional(string) + url = optional(string) + key = optional(string) + username = optional(string) + password = optional(string) + })) + }) + ) + ) + ) + ) + default = [] +} + +variable "widgets_config" { + description = "Configuration for widgets" + type = list(map(object({ + // resources + label = optional(string) + cpu = optional(bool, false) + memory = optional(bool, false) + disk = optional(string) + // datetime + text_size = optional(string) + format = optional(object({ + timeStyle = optional(string) + hour12 = optional(bool, false) + })) + }))) + default = [] +} + +variable "settings" { + description = "General Homepage settings" + type = object({ + title = optional(string) + base = optional(string) + background = optional(string) + backgroundOpacity = optional(string) + theme = optional(string) + layout = optional(map( + object({ + icon = optional(string) + style = optional(string) + columns = optional(number) + }) + )) + }) +} + +variable "bookmarks" { + description = "Bookmarks to show in Homepage" + type = list(map(any)) + default = [] +} + +variable "docker_config" { + description = "Homepage Docker config (for reusability of configuration)" + type = map(any) + default = {} +} + +variable "kubernetes_config" { + description = "Kubernetes service config" + type = object({ + mode = string + }) + default = { mode = "cluster" } +}