diff --git a/.drone.yml b/.drone.yml index 4b08f885..241039a1 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,6 +20,7 @@ steps: environment: TF_VAR_prod: "true" commands: + - "env" - "terraform init" - "terraform apply -target=module.kubernetes_cluster -auto-approve" diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl index ee12705e..fd286d83 100644 --- a/.terraform.lock.hcl +++ b/.terraform.lock.hcl @@ -1,6 +1,21 @@ # This file is maintained automatically by "terraform init". # Manual edits may be lost in future updates. +provider "registry.terraform.io/gavinbunney/kubectl" { + version = "1.10.0" + constraints = ">= 1.10.0" + hashes = [ + "h1:x5NHOW8DG0cBE1QmJ/Hl4ktMpeIfkEpoOvnf/kISdBU=", + "zh:0786e6cb375e4e6a70220bb67fc3de80c8c30dcb00c0f4f0ec7bb10404a120db", + "zh:577347a8334c8cd13215608780e03b77615d211fac64ad6e4356b7f4bb160022", + "zh:7d3347690a0b68dca54ae5cc90877cf82069f7ef13517668b17fd37f49c91e8c", + "zh:7f4eeae41b22de803ea7bf8977226c2bc0baaf204a4a2a05c421d9358c907808", + "zh:8db7a6550374918109d6f445c6c196f02ea3fa2029b882eca186d6e13bd1e4ce", + "zh:9c93ad71c3039463cf4345acb781c68d7ce82fe8f8495a94a6b588bf87259e51", + "zh:ee94ff2448caee374f3a3e888568d7ff48e6b9438df76f6eb72efa1aadc6391b", + ] +} + provider "registry.terraform.io/hashicorp/helm" { version = "2.0.2" hashes = [ diff --git a/main.tf b/main.tf index 2be57927..379c4bcf 100644 --- a/main.tf +++ b/main.tf @@ -26,12 +26,14 @@ variable "bind_db_viktorbarzin_lan" {} variable "bind_named_conf_options" {} variable "alertmanager_account_password" {} variable "wireguard_wg_0_key" {} +variable "dbaas_root_password" {} variable "drone_github_client_id" {} variable "drone_github_client_secret" {} variable "drone_rpc_secret" {} # variable "dockerhub_password" {} variable "oauth_client_id" {} variable "oauth_client_secret" {} +variable "url_shortener_mysql_password" {} variable "url_shortener_geolite_license_key" {} variable "url_shortener_api_key" {} variable "webhook_handler_fb_verify_token" {} @@ -69,15 +71,6 @@ provider "helm" { config_path = var.prod ? "" : "~/.kube/config" } } -# provider "kubectl" { -# config_path = var.prod ? "" : "~/.kube/config" -# } -# provider "kubectl" { -# host = "kubernetes.viktorbarzin.lan" -# cluster_ca_certificate = base64decode(var.eks_cluster_ca) -# token = data.aws_eks_cluster_auth.main.token -# load_config_file = true -# } # Main module to init infra from module "pxe_server" { @@ -190,6 +183,7 @@ module "k8s_node5" { module "kubernetes_cluster" { source = "./modules/kubernetes" + prod = var.prod tls_secret_name = var.tls_secret_name # dockerhub_password = var.dockerhub_password client_certificate_secret_name = var.client_certificate_secret_name @@ -233,4 +227,10 @@ module "kubernetes_cluster" { url_shortener_geolite_license_key = var.url_shortener_geolite_license_key url_shortener_api_key = var.url_shortener_api_key + url_shortener_mysql_password = var.url_shortener_mysql_password + + # dbaas + dbaas_root_password = var.dbaas_root_password + + } diff --git a/modules/kubernetes/dbaas/main.tf b/modules/kubernetes/dbaas/main.tf index 07ff97b5..1b315075 100644 --- a/modules/kubernetes/dbaas/main.tf +++ b/modules/kubernetes/dbaas/main.tf @@ -1,9 +1,21 @@ # DB as a service. Installs MySQL operator variable "tls_secret_name" {} +variable "dbaas_root_password" {} variable "cluster_master_service" { default = "mysql-cluster-mysql-master" } +variable "prod" { + default = false + type = bool +} +provider "kubectl" { + # config_path = var.prod ? "" : "~/.kube/config" + host = "kubernetes:6443" + client_certificate = var.prod ? "/run/secrets/kubernetes.io/serviceaccount/ca.crt" : "" + token = var.prod ? "/run/secrets/kubernetes.io/serviceaccount/token" : "" + insecure = true +} resource "kubernetes_namespace" "dbaas" { metadata { name = "dbaas" @@ -25,7 +37,9 @@ resource "helm_release" "mysql" { chart = "mysql-operator" values = [templatefile("${path.module}/mysql_chart_values.yaml", { secretName = var.tls_secret_name })] + atomic = true + depends_on = [kubernetes_namespace.dbaas] } resource "kubernetes_persistent_volume" "mysql-operator" { @@ -72,10 +86,13 @@ resource "kubernetes_secret" "cluster-password" { metadata { name = "cluster-secret" namespace = "dbaas" + annotations = { + "reloader.stakater.com/match" = "true" + } } type = "Opaque" data = { - "ROOT_PASSWORD" = "a2VrCg==" + "ROOT_PASSWORD" = var.dbaas_root_password } } @@ -118,6 +135,10 @@ resource "kubernetes_deployment" "phpmyadmin" { namespace = "dbaas" labels = { "app" = "phpmyadmin" + + } + annotations = { + "reloader.stakater.com/search" = "true" } } spec { @@ -212,64 +233,237 @@ resource "kubernetes_ingress" "phpmyadmin" { } -# resource "kubectl_manifest" "mysql-cluster" { -# manifest = { -# apiVersion = "mysql.presslabs.org/v1alpha1" -# kind = "MysqlCluster" -# metadata = { -# name = "mysql-cluster" -# namespace = "dbaas" -# } -# spec = { -# mysqlVersion = "5.7" -# replicas = 1 -# secretName = "cluster-secret" -# mysqlConf = { -# read_only = 0 -# } -# volumeSpec = { -# persistentVolumeClaim = { -# resources = { -# requests = { -# storage = "10Gi" -# } -# } -# } -# } -# } -# } -# } +resource "kubectl_manifest" "mysql-cluster" { + yaml_body = <<-YAML + apiVersion: mysql.presslabs.org/v1alpha1 + kind: MysqlCluster + metadata: + name: mysql-cluster + namespace: dbaas + spec: + mysqlVersion: "5.7" + replicas: 1 + secretName: cluster-secret + mysqlConf: + # read_only: 0 # mysql forms a single transaction for each sql statement, autocommit for each statement + # automatic_sp_privileges: "ON" # automatically grants the EXECUTE and ALTER ROUTINE privileges to the creator of a stored routine + # auto_generate_certs: "ON" # Auto Generation of Certificate + # auto_increment_increment: 1 # Auto Incrementing value from +1 + # auto_increment_offset: 1 # Auto Increment Offset + # binlog-format: "STATEMENT" # contains various options such ROW(SLOW,SAFE) STATEMENT(FAST,UNSAFE), MIXED(combination of both) + # wait_timeout: 31536000 # 28800 number of seconds the server waits for activity on a non-interactive connection before closing it, You might encounter MySQL server has gone away error, you then tweak this value acccordingly + # interactive_timeout: 28800 # The number of seconds the server waits for activity on an interactive connection before closing it. + # max_allowed_packet: "512M" # Maximum size of MYSQL Network protocol packet that the server can create or read 4MB, 8MB, 16MB, 32MB + # max-binlog-size: 1073741824 # binary logs contains the events that describe database changes, this parameter describe size for the bin_log file. + # log_output: "TABLE" # Format in which the logout will be dumped + # master-info-repository: "TABLE" # Format in which the master info will be dumped + # relay_log_info_repository: "TABLE" # Format in which the relay info will be dumped + volumeSpec: + persistentVolumeClaim: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + YAML + depends_on = [helm_release.mysql] + # manifest = { + # apiVersion = "mysql.presslabs.org/v1alpha1" + # kind = "MysqlCluster" + # metadata = { + # name = "mysql-cluster" + # namespace = "dbaas" + # } + # spec = { + # mysqlVersion = "5.7" + # replicas = 1 + # secretName = "cluster-secret" + # mysqlConf = { + # read_only = 0 + # } + # volumeSpec = { + # persistentVolumeClaim = { + # resources = { + # requests = { + # storage = "10Gi" + # } + # } + # } + # } + # } + # } +} -# resource "kubectl_manifest" "mysql-cluster" { -# yaml_body = <<-YAML -# apiVersion: mysql.presslabs.org/v1alpha1 -# kind: MysqlCluster -# metadata: -# name: MyCluster -# spec: -# mysqlVersion: "5.7" -# replicas: 1 -# secretName: MyCluster-Secret -# mysqlConf: -# read_only: 0 # mysql forms a single transaction for each sql statement, autocommit for each statement -# automatic_sp_privileges: "ON" # automatically grants the EXECUTE and ALTER ROUTINE privileges to the creator of a stored routine -# auto_generate_certs: "ON" # Auto Generation of Certificate -# auto_increment_increment: 1 # Auto Incrementing value from +1 -# auto_increment_offset: 1 # Auto Increment Offset -# binlog-format: "STATEMENT" # contains various options such ROW(SLOW,SAFE) STATEMENT(FAST,UNSAFE), MIXED(combination of both) -# wait_timeout: 31536000 # 28800 number of seconds the server waits for activity on a non-interactive connection before closing it, You might encounter MySQL server has gone away error, you then tweak this value acccordingly -# interactive_timeout: 28800 # The number of seconds the server waits for activity on an interactive connection before closing it. -# max_allowed_packet: "512M" # Maximum size of MYSQL Network protocol packet that the server can create or read 4MB, 8MB, 16MB, 32MB -# max-binlog-size: 1073741824 # binary logs contains the events that describe database changes, this parameter describe size for the bin_log file. -# log_output: "TABLE" # Format in which the logout will be dumped -# master-info-repository: "TABLE" # Format in which the master info will be dumped -# relay_log_info_repository: "TABLE" # Format in which the relay info will be dumped -# volumeSpec: -# persistentVolumeClaim: -# accessModes: -# - ReadWriteMany -# resources: -# requests: -# storage: 10Gi -# YAML -# } + +# For some unknwown reason not all CRDs are installed. Add them manually +resource "kubectl_manifest" "mysql-user" { + yaml_body = <<-EOF + apiVersion: apiextensions.k8s.io/v1 + kind: CustomResourceDefinition + metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.5.0 + helm.sh/hook: crd-install + name: mysqlusers.mysql.presslabs.org + labels: + app: mysql-operator + spec: + group: mysql.presslabs.org + names: + kind: MysqlUser + listKind: MysqlUserList + plural: mysqlusers + singular: mysqluser + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The user status + jsonPath: .status.conditions[?(@.type == 'Ready')].status + name: Ready + type: string + - jsonPath: .spec.clusterRef.name + name: Cluster + type: string + - jsonPath: .spec.user + name: UserName + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: MysqlUser is the Schema for the MySQL User API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MysqlUserSpec defines the desired state of MysqlUserSpec + properties: + allowedHosts: + description: AllowedHosts is the allowed host to connect from. + items: + type: string + type: array + clusterRef: + description: ClusterRef represents a reference to the MySQL cluster. This field should be immutable. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + namespace: + description: Namespace the MySQL cluster namespace + type: string + type: object + password: + description: Password is the password for the user. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + permissions: + description: Permissions is the list of roles that user has in the specified database. + items: + description: MysqlPermission defines a MySQL schema permission + properties: + permissions: + description: Permissions represents the permissions granted on the schema/tables + items: + type: string + type: array + schema: + description: Schema represents the schema to which the permission applies + type: string + tables: + description: Tables represents the tables inside the schema to which the permission applies + items: + type: string + type: array + required: + - permissions + - schema + - tables + type: object + type: array + resourceLimits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'ResourceLimits allow settings limit per mysql user as defined here: https://dev.mysql.com/doc/refman/5.7/en/user-resources.html' + type: object + user: + description: User is the name of the user that will be created with will access the specified database. This field should be immutable. + type: string + required: + - allowedHosts + - clusterRef + - password + - user + type: object + status: + description: MysqlUserStatus defines the observed state of MysqlUser + properties: + allowedHosts: + description: AllowedHosts contains the list of hosts that the user is allowed to connect from. + items: + type: string + type: array + conditions: + description: Conditions represents the MysqlUser resource conditions list. + items: + description: MySQLUserCondition defines the condition struct for a MysqlUser resource + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status to another. + format: date-time + type: string + lastUpdateTime: + description: The last time this condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of MysqlUser condition. + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + EOF +} diff --git a/modules/kubernetes/dbaas/mysql_chart_values.yaml b/modules/kubernetes/dbaas/mysql_chart_values.yaml index e0a7934d..5dbeb1e8 100644 --- a/modules/kubernetes/dbaas/mysql_chart_values.yaml +++ b/modules/kubernetes/dbaas/mysql_chart_values.yaml @@ -1,7 +1,7 @@ --- orchestrator: - persistence: - enabled: false + # persistence: + # enabled: false ingress: enable: false hosts: diff --git a/modules/kubernetes/dbaas/versions.tf b/modules/kubernetes/dbaas/versions.tf new file mode 100644 index 00000000..89ee8b53 --- /dev/null +++ b/modules/kubernetes/dbaas/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = ">= 1.10.0" + } + } + required_version = ">= 0.13" +} diff --git a/modules/kubernetes/main.tf b/modules/kubernetes/main.tf index c50e3865..1c583373 100644 --- a/modules/kubernetes/main.tf +++ b/modules/kubernetes/main.tf @@ -1,3 +1,4 @@ +variable "prod" {} variable "tls_secret_name" {} variable "client_certificate_secret_name" {} variable "hackmd_db_password" {} @@ -13,6 +14,7 @@ variable "bind_db_viktorbarzin_me" {} variable "bind_db_viktorbarzin_lan" {} variable "bind_named_conf_options" {} variable "alertmanager_account_password" {} +variable "dbaas_root_password" {} variable "drone_github_client_id" {} variable "drone_github_client_secret" {} variable "drone_rpc_secret" {} @@ -21,6 +23,7 @@ variable "oauth_client_id" {} variable "oauth_client_secret" {} variable "url_shortener_geolite_license_key" {} variable "url_shortener_api_key" {} +variable "url_shortener_mysql_password" {} variable "webhook_handler_fb_verify_token" {} variable "webhook_handler_fb_page_token" {} variable "webhook_handler_fb_app_secret" {} @@ -51,8 +54,10 @@ module "bind" { } module "dbaas" { - source = "./dbaas" - tls_secret_name = var.tls_secret_name + source = "./dbaas" + prod = var.prod + tls_secret_name = var.tls_secret_name + dbaas_root_password = var.dbaas_root_password } module "descheduler" { @@ -188,6 +193,7 @@ module "url" { tls_secret_name = var.tls_secret_name geolite_license_key = var.url_shortener_geolite_license_key api_key = var.url_shortener_api_key + mysql_password = var.url_shortener_mysql_password } module "webhook_handler" { diff --git a/modules/kubernetes/url-shortener/main.tf b/modules/kubernetes/url-shortener/main.tf index 3dcde6cf..4ac261a7 100644 --- a/modules/kubernetes/url-shortener/main.tf +++ b/modules/kubernetes/url-shortener/main.tf @@ -1,6 +1,7 @@ variable "tls_secret_name" {} variable "geolite_license_key" {} variable "api_key" {} +variable "mysql_password" {} variable "domain" { default = "url.viktorbarzin.me" } @@ -17,6 +18,53 @@ module "tls_secret" { tls_secret_name = var.tls_secret_name } +resource "kubernetes_secret" "mysql_config" { + metadata { + name = "mysql-config" + namespace = "url" + annotations = { + "reloader.stakater.com/match" = "true" + } + } + data = { + # TODO user other user... + # "DB_USER" = "shlink" + "DB_USER" = "root" + # "DB_PASSWORD" = var.mysql_password + "DB_PASSWORD" = "cDMyUEFDbGNpQmdjT2RtNXNac2YK" + } +} + +# this depends on the mysql installation +resource "kubectl_manifest" "mysql-user" { + yaml_body = <<-YAML + apiVersion: mysql.presslabs.org/v1alpha1 + kind: MysqlUser + metadata: + name: shlink + namespace: url + spec: + user: shlink + clusterRef: + name: mysql-cluster + namespace: dbaas + password: + name: mysql-config + key: password + allowedHosts: + - '%' + YAML + # permissions: + # - schema: db-name-in-mysql + # tables: ["table1", "table2"] + # permissions: + # - SELECT + # - UPDATE + # - CREATE + # allowedHosts: + # - localhost +} + resource "kubernetes_deployment" "shlink" { metadata { name = "shlink" @@ -54,6 +102,28 @@ resource "kubernetes_deployment" "shlink" { name = "GEOLITE_LICENSE_KEY" value = var.geolite_license_key } + # DB config + env { + name = "DB_DRIVER" + value = "mysql" + } + env { + name = "DB_HOST" + value = "mysql-cluster-mysql-master.dbaas.svc.cluster.local" + } + # env { + # name = "DB_USER" + # value = "shlink" + # } + env_from { + secret_ref { + name = "mysql-config" + } + } + # env { + # name = "DB_PASSWORD" + # value = var.mysql_password + # } resources { limits = { cpu = "0.5" diff --git a/modules/kubernetes/url-shortener/versions.tf b/modules/kubernetes/url-shortener/versions.tf new file mode 100644 index 00000000..89ee8b53 --- /dev/null +++ b/modules/kubernetes/url-shortener/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = ">= 1.10.0" + } + } + required_version = ">= 0.13" +} diff --git a/modules/kubernetes/versions.tf b/modules/kubernetes/versions.tf index d0e41025..5d7c4592 100644 --- a/modules/kubernetes/versions.tf +++ b/modules/kubernetes/versions.tf @@ -3,10 +3,6 @@ terraform { kubernetes = { source = "hashicorp/kubernetes" } - # kubectl = { - # source = "gavinbunney/kubectl" - # version = ">= 1.10.0" - # } } required_version = ">= 0.13" } diff --git a/terraform.tfstate b/terraform.tfstate index cbb4c57f..09651565 100644 Binary files a/terraform.tfstate and b/terraform.tfstate differ diff --git a/terraform.tfvars b/terraform.tfvars index bee0fc20..d2a4cc70 100644 Binary files a/terraform.tfvars and b/terraform.tfvars differ diff --git a/versions.tf b/versions.tf index d0e41025..65b30d16 100644 --- a/versions.tf +++ b/versions.tf @@ -3,10 +3,10 @@ terraform { kubernetes = { source = "hashicorp/kubernetes" } - # kubectl = { - # source = "gavinbunney/kubectl" - # version = ">= 1.10.0" - # } + kubectl = { + source = "gavinbunney/kubectl" + version = ">= 1.10.0" + } } required_version = ">= 0.13" }