diff --git a/apps/repositories/runtime.yaml b/apps/repositories/runtime.yaml index 3c8838f..d6dd249 100644 --- a/apps/repositories/runtime.yaml +++ b/apps/repositories/runtime.yaml @@ -5,4 +5,22 @@ metadata: namespace: infra spec: interval: 5m0s - url: https://pluralsh.github.io/bootstrap \ No newline at end of file + url: https://pluralsh.github.io/bootstrap +--- +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: HelmRepository +metadata: + name: cert-manager + namespace: infra +spec: + interval: 5m0s + url: https://charts.jetstack.io +--- +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: HelmRepository +metadata: + name: flux + namespace: infra +spec: + interval: 5m0s + url: https://fluxcd-community.github.io/helm-charts \ No newline at end of file diff --git a/apps/services/pr-automation/cluster-creator.yaml b/apps/services/pr-automation/cluster-creator.yaml new file mode 100644 index 0000000..a1fb72d --- /dev/null +++ b/apps/services/pr-automation/cluster-creator.yaml @@ -0,0 +1,44 @@ +apiVersion: deployments.plural.sh/v1alpha1 +kind: PrAutomation +metadata: + name: cluster-creator +spec: + name: cluster-creator + documentation: | + Sets up a PR to provision a cluster for a fleet + stage + creates: + templates: + - source: templates/clusters/stack.yaml + destination: "apps/clusters/{{ context.cloud }}/stacks/{{ context.name }}.yaml" + external: false + - source: templates/clusters/cluster.yaml + destination: "apps/clusters/{{ context.cloud }}/clusters/{{ context.name }}.yaml" + external: false + - source: templates/clusters/clusters.yaml + destination: "apps/services/clusters.yaml" + external: false + scmConnectionRef: + name: github # you'll need to add this ScmConnection manually before this is functional + title: "Adding {{ context.cloud }} cluster: {{ context.name }}" + message: "Adding {{ context.cloud }} cluster {{ context.name }} and registering it with Plural" + identifier: [[ .Identifier ]] # REPLACEME with your own repo slug + configuration: + - name: name + type: STRING + documentation: name for this cluster + - name: cloud + type: ENUM + documentation: the cloud you'll host on + values: + - aws + - gcp + - azure + - name: fleet + type: STRING + documentation: a name for the fleet you want this cluster to belong to + - name: tier + type: ENUM + documentation: what tier to place this cluster in + values: + - dev + - prd diff --git a/apps/services/pr-automation/scm.yaml b/apps/services/pr-automation/scm.yaml new file mode 100644 index 0000000..ed8d5ed --- /dev/null +++ b/apps/services/pr-automation/scm.yaml @@ -0,0 +1,8 @@ +# You will need to manually create the github scm connection this refers to +# apiVersion: deployments.plural.sh/v1alpha1 +# kind: ScmConnection +# metadata: +# name: github +# spec: +# name: github +# type: GITHUB \ No newline at end of file diff --git a/apps/services/runtime.yaml b/apps/services/runtime.yaml index eeda527..49179a3 100644 --- a/apps/services/runtime.yaml +++ b/apps/services/runtime.yaml @@ -5,6 +5,7 @@ metadata: name: cert-manager namespace: infra spec: + version: 0.0.1 namespace: cert-manager git: folder: helm-values @@ -19,7 +20,7 @@ spec: valuesFiles: - certmanager.yaml repository: - namespace: plural-runtime + namespace: infra name: cert-manager clusterRef: kind: Cluster @@ -32,6 +33,7 @@ metadata: name: flux namespace: infra spec: + version: 0.0.1 namespace: flux git: folder: helm-values @@ -46,7 +48,7 @@ spec: valuesFiles: - flux.yaml repository: - namespace: plural-runtime + namespace: infra name: flux clusterRef: kind: Cluster @@ -59,6 +61,7 @@ metadata: name: runtime namespace: infra spec: + version: 0.0.1 namespace: plural-runtime git: folder: helm-values diff --git a/apps/services/settings.yaml b/apps/services/settings.yaml new file mode 100644 index 0000000..0427af7 --- /dev/null +++ b/apps/services/settings.yaml @@ -0,0 +1,10 @@ +apiVersion: deployments.plural.sh/v1alpha1 +kind: DeploymentSettings +metadata: + name: global + namespace: plrl-deploy-operator +spec: + stacks: + jobSpec: + namespace: plrl-deploy-operator + serviceAccount: stacks diff --git a/apps/services/setup.yaml b/apps/services/setup.yaml index 87c0071..f446658 100644 --- a/apps/services/setup.yaml +++ b/apps/services/setup.yaml @@ -4,7 +4,7 @@ metadata: name: infra namespace: infra spec: - url: {{ configuration.repoUrl }} + url: [[ .RepoUrl ]] --- apiVersion: deployments.plural.sh/v1alpha1 kind: Cluster diff --git a/templates/clusters/cluster.yaml b/templates/clusters/cluster.yaml new file mode 100644 index 0000000..2dc9578 --- /dev/null +++ b/templates/clusters/cluster.yaml @@ -0,0 +1,7 @@ +apiVersion: deployments.plural.sh/v1alpha1 +kind: Cluster +metadata: + name: {{ context.name }} + namespace: infra +spec: + handle: {{ context.name }} \ No newline at end of file diff --git a/templates/clusters/clusters.yaml b/templates/clusters/clusters.yaml new file mode 100644 index 0000000..bd7d58a --- /dev/null +++ b/templates/clusters/clusters.yaml @@ -0,0 +1,17 @@ +apiVersion: deployments.plural.sh/v1alpha1 +kind: ServiceDeployment +metadata: + name: clusters + namespace: infra +spec: + namespace: infra + git: + folder: apps/clusters + ref: main + repositoryRef: + kind: GitRepository + name: infra + namespace: infra + clusterRef: + name: mgmt + namespace: infra \ No newline at end of file diff --git a/templates/clusters/eks/create.tf.liquid b/templates/clusters/eks/create.tf.liquid deleted file mode 100644 index c105853..0000000 --- a/templates/clusters/eks/create.tf.liquid +++ /dev/null @@ -1,26 +0,0 @@ -module "prod" { - source = "../bootstrap/terraform/clouds/aws" // replace aws with gcp/azure/etc for other clouds - cluster_name = "{{ context.name }}" - vpc_name = "{{ context.vpc_name }}" - create_db = false - providers = { - helm = helm.{{ context.name }} - } -} - - -// setting up the helm provider is necessary for AWS as it'll install a few core resources via helm by default, ignore for AKS/GKE -data "aws_eks_cluster_auth" "prod" { - name = module.prod.cluster.cluster_name - - depends_on = [ module.prod.cluster ] -} - -provider "helm" { - kubernetes { - host = module.prod.cluster.cluster_endpoint - cluster_ca_certificate = base64decode(module.prod.cluster.cluster_certificate_authority_data) - token = data.aws_eks_cluster_auth.prod.token - } - alias = "{{ context.name }}" -} \ No newline at end of file diff --git a/templates/clusters/eks/register.tf.liquid b/templates/clusters/eks/register.tf.liquid deleted file mode 100644 index be3f7fa..0000000 --- a/templates/clusters/eks/register.tf.liquid +++ /dev/null @@ -1,5 +0,0 @@ -module "{{ context.name }}" { - source = "../../bootstrap/terraform/modules/eks-byok" - cluster_name = "{{ context.name }}" - cluster_handle = "{{ context.name }}" -} \ No newline at end of file diff --git a/templates/clusters/stack.yaml b/templates/clusters/stack.yaml new file mode 100644 index 0000000..fa5a894 --- /dev/null +++ b/templates/clusters/stack.yaml @@ -0,0 +1,29 @@ +apiVersion: deployments.plural.sh/v1alpha1 +kind: InfrastructureStack +metadata: + name: cluster-{{ context.name }} +spec: + name: cluster-{{ context.name }} + detach: false + type: TERRAFORM + approval: true + manageState: true + actor: console@plural.sh + configuration: + version: '1.8' + repositoryRef: + name: infra + namespace: infra + clusterRef: + name: mgmt + namespace: infra + git: + ref: main + folder: terraform/modules/clusters/{{ context.cloud }} + environment: + - name: TF_VAR_cluster + value: {{ context.name }} + - name: TF_VAR_fleet + value: {{ context.fleet }} + - name: TF_VAR_tier + value: {{ context.tier }} \ No newline at end of file diff --git a/templates/setup/console.tf b/templates/setup/console.tf index d89620a..6c970a8 100644 --- a/templates/setup/console.tf +++ b/templates/setup/console.tf @@ -76,7 +76,7 @@ resource "helm_release" "console" { namespace = "plrl-console" chart = "console" repository = "https://pluralsh.github.io/console" - version = "0.3.18" + version = "0.3.25" create_namespace = true timeout = 600 wait = true @@ -86,3 +86,7 @@ resource "helm_release" "console" { depends_on = [ module.mgmt.cluster, helm_release.runtime, module.mgmt.db_url ] } + +output "identity" { + value = module.mgmt.identity +} \ No newline at end of file diff --git a/templates/setup/providers/aws.tf b/templates/setup/providers/aws.tf index 7e3df8c..765058b 100644 --- a/templates/setup/providers/aws.tf +++ b/templates/setup/providers/aws.tf @@ -1,4 +1,4 @@ module "mgmt" { - source = "../bootstrap/terraform/clouds/aws" + source = "../terraform/modules/mgmt" cluster_name = "{{ .Cluster }}" } \ No newline at end of file diff --git a/templates/setup/providers/azure.tf b/templates/setup/providers/azure.tf index 1719927..90677de 100644 --- a/templates/setup/providers/azure.tf +++ b/templates/setup/providers/azure.tf @@ -1,5 +1,5 @@ module "mgmt" { - source = "../bootstrap/terraform/clouds/azure" + source = "../terraform/modules/mgmt" resource_group_name = "{{ .Project }}" cluster_name = "{{ .Cluster }}" location = "{{ .Region }}" diff --git a/templates/setup/providers/gcp.tf b/templates/setup/providers/gcp.tf index addf135..79f19b4 100644 --- a/templates/setup/providers/gcp.tf +++ b/templates/setup/providers/gcp.tf @@ -1,5 +1,5 @@ module "mgmt" { - source = "../bootstrap/terraform/clouds/gcp" + source = "../terraform/modules/mgmt" project_id = "{{ .Project }}" cluster_name = "{{ .Cluster }}" region = "{{ .Region }}" diff --git a/templates/setup/providers/linode.tf b/templates/setup/providers/linode.tf index 4d67256..d5a754d 100644 --- a/templates/setup/providers/linode.tf +++ b/templates/setup/providers/linode.tf @@ -1,5 +1,5 @@ module "mgmt" { - source = "../bootstrap/terraform/clouds/linode" + source = "../terraform/modules/mgmt" cluster_name = "{{ .Cluster }}" region = "{{ .Region }}" } \ No newline at end of file diff --git a/templates/setup/stacks/aws.yaml b/templates/setup/stacks/aws.yaml new file mode 100644 index 0000000..66571f4 --- /dev/null +++ b/templates/setup/stacks/aws.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: stacks + namespace: plrl-deploy-operator + annotations: + eks.amazonaws.com/role-arn: {{ .StacksIdentity }} \ No newline at end of file diff --git a/templates/setup/stacks/azure.yaml b/templates/setup/stacks/azure.yaml new file mode 100644 index 0000000..1e72b1c --- /dev/null +++ b/templates/setup/stacks/azure.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: stacks + namespace: plrl-deploy-operator + annotations: + azure.workload.identity/client-id: {{ .StacksIdentity }} \ No newline at end of file diff --git a/templates/setup/stacks/gcp.yaml b/templates/setup/stacks/gcp.yaml new file mode 100644 index 0000000..72779b2 --- /dev/null +++ b/templates/setup/stacks/gcp.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: stacks + namespace: plrl-deploy-operator + annotations: + iam.gke.io/gcp-service-account: {{ .StacksIdentity }} \ No newline at end of file diff --git a/terraform/clouds/aws/eks.tf b/terraform/clouds/aws/eks.tf index 321da24..6d6ba9d 100644 --- a/terraform/clouds/aws/eks.tf +++ b/terraform/clouds/aws/eks.tf @@ -11,6 +11,8 @@ module "eks" { subnet_ids = module.vpc.private_subnets control_plane_subnet_ids = module.vpc.public_subnets + create_kms_key = false + # EKS Managed Node Group(s) eks_managed_node_group_defaults = merge(var.node_group_defaults, {ami_release_version = data.aws_ssm_parameter.eks_ami_release_version.value}) diff --git a/terraform/clouds/aws/iam.tf b/terraform/clouds/aws/iam.tf new file mode 100644 index 0000000..c4e0f88 --- /dev/null +++ b/terraform/clouds/aws/iam.tf @@ -0,0 +1,28 @@ +module "assumable_role_stacks" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" + version = "5.39.1" + create_role = true + role_name = "${var.cluster_name}-plrl-stacks" + provider_url = replace(module.eks.cluster_oidc_issuer_url, "https://", "") + role_policy_arns = [aws_iam_policy.stacks.arn] + oidc_fully_qualified_subjects = [ + "system:serviceaccount:plrl-deploy-operator:stacks", + ] +} + +resource "aws_iam_policy" "stacks" { + name_prefix = "stacks" + description = "stacks permissions for ${var.cluster_name}" + policy = <<-POLICY + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "*", + "Resource": "*" + } + ] + } + POLICY +} \ No newline at end of file diff --git a/terraform/clouds/aws/locals.tf b/terraform/clouds/aws/locals.tf index 97aac46..03219fe 100644 --- a/terraform/clouds/aws/locals.tf +++ b/terraform/clouds/aws/locals.tf @@ -5,5 +5,6 @@ locals { cluster = module.eks addons = module.eks_blueprints_addons } + vpc_name = var.vpc_name == "" ? "${var.cluster_name}-vpc" : var.vpc_name monitoring_role_name = var.monitoring_role == "" ? "${var.cluster_name}-PluralRDSMonitoringRole" : var.monitoring_role } \ No newline at end of file diff --git a/terraform/clouds/aws/outputs.tf b/terraform/clouds/aws/outputs.tf index b66c52b..399965b 100644 --- a/terraform/clouds/aws/outputs.tf +++ b/terraform/clouds/aws/outputs.tf @@ -232,4 +232,8 @@ output "db_url" { output "ready" { value = local.cluster_ready +} + +output "identity" { + value = module.assumable_role_stacks.iam_role_arn } \ No newline at end of file diff --git a/terraform/clouds/aws/variables.tf b/terraform/clouds/aws/variables.tf index fad5a60..d11a6f7 100644 --- a/terraform/clouds/aws/variables.tf +++ b/terraform/clouds/aws/variables.tf @@ -35,7 +35,7 @@ variable "public" { variable "vpc_name" { type = string - default = "plural" + default = "" } variable "vpc_cidr" { diff --git a/terraform/clouds/azure/iam.tf b/terraform/clouds/azure/iam.tf index da8b31b..4cabcae 100644 --- a/terraform/clouds/azure/iam.tf +++ b/terraform/clouds/azure/iam.tf @@ -12,4 +12,28 @@ resource "azurerm_role_assignment" "aks-network-identity-ssi" { principal_id = module.aks.cluster_identity.principal_id depends_on = [module.aks, azurerm_virtual_network.network] +} + + + +resource "azurerm_user_assigned_identity" "stacks" { + resource_group_name = local.resource_group.name + location = local.resource_group.location + + name = "${var.cluster_name}-plrl-stacks" +} + +resource "azurerm_role_assignment" "stacks-ssi" { + scope = local.rg.id + role_definition_name = "Owner" + principal_id = azurerm_user_assigned_identity.stacks.principal_id +} + +resource "azurerm_federated_identity_credential" "stacks" { + name = "${var.cluster_name}-stacks" + resource_group_name = local.resource_group.name + audience = ["api://AzureADTokenExchange"] + issuer = module.aks.oidc_issuer_url + parent_id = azurerm_user_assigned_identity.stacks.id + subject = "system:serviceaccount:plrl-deploy-operator:stacks" } \ No newline at end of file diff --git a/terraform/clouds/azure/locals.tf b/terraform/clouds/azure/locals.tf index 3b27c4d..281f122 100644 --- a/terraform/clouds/azure/locals.tf +++ b/terraform/clouds/azure/locals.tf @@ -5,5 +5,6 @@ locals { name = var.create_resource_group ? azurerm_resource_group.main[0].name : var.resource_group_name location = var.location } + rg = var.create_resource_group ? azurerm_resource_group.main[0] : data.azurerm_resource_group.main[0] db_url = format("postgresql://console:%s@%s:5432/console", random_password.password.result, try(azurerm_postgresql_flexible_server.postgres[0].fqdn, "")) } \ No newline at end of file diff --git a/terraform/clouds/azure/outputs.tf b/terraform/clouds/azure/outputs.tf index a3737c9..a53ff17 100644 --- a/terraform/clouds/azure/outputs.tf +++ b/terraform/clouds/azure/outputs.tf @@ -17,4 +17,8 @@ output "db_url" { output "ready" { value = module.aks +} + +output "identity" { + value = azurerm_user_assigned_identity.stacks.client_id } \ No newline at end of file diff --git a/terraform/clouds/azure/resource_group.tf b/terraform/clouds/azure/resource_group.tf index 28032f6..3f8bbae 100644 --- a/terraform/clouds/azure/resource_group.tf +++ b/terraform/clouds/azure/resource_group.tf @@ -7,4 +7,9 @@ resource "azurerm_resource_group" "main" { location = var.location name = coalesce(var.resource_group_name, "${random_id.prefix.hex}-rg") +} + +data "azurerm_resource_group" "main" { + count = var.create_resource_group ? 0 : 1 + name = var.resource_group_name } \ No newline at end of file diff --git a/terraform/clouds/gcp/iam.tf b/terraform/clouds/gcp/iam.tf new file mode 100644 index 0000000..a82c50c --- /dev/null +++ b/terraform/clouds/gcp/iam.tf @@ -0,0 +1,10 @@ +module "console-workload-identity" { + source = "terraform-google-modules/kubernetes-engine/google//modules/workload-identity" + name = "${var.cluster_name}-console" + namespace = "plrl-deploy-operator" + project_id = var.project_id + use_existing_k8s_sa = true + annotate_k8s_sa = false + k8s_sa_name = "stacks" + roles = ["roles/owner", "roles/storage.admin"] +} \ No newline at end of file diff --git a/terraform/clouds/gcp/outputs.tf b/terraform/clouds/gcp/outputs.tf index eae6a5c..66b9ba2 100644 --- a/terraform/clouds/gcp/outputs.tf +++ b/terraform/clouds/gcp/outputs.tf @@ -17,4 +17,8 @@ output "db_url" { output "ready" { value = module.gke +} + +output "identity" { + value = module.console-workload-identity.gcp_service_account_email } \ No newline at end of file diff --git a/terraform/modules/aks-byok/cluster.tf b/terraform/modules/aks-byok/cluster.tf deleted file mode 100644 index 81ec79c..0000000 --- a/terraform/modules/aks-byok/cluster.tf +++ /dev/null @@ -1,19 +0,0 @@ - -data "azurerm_kubernetes_cluster" "cluster" { - name = var.cluster_name - resource_group_name = var.resource_group_name -} - -resource "plural_cluster" "this" { - handle = var.cluster_handle - name = var.cluster_name - tags = var.tags - protect = var.protect - # bindings = var.bindings - kubeconfig = { - host = data.azurerm_kubernetes_cluster.cluster.kube_config[0].host - client_certificate = base64decode(data.azurerm_kubernetes_cluster.cluster.kube_config[0].client_certificate) - client_key = base64decode(data.azurerm_kubernetes_cluster.cluster.kube_config[0].client_key) - cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.cluster.kube_config[0].cluster_ca_certificate) - } -} \ No newline at end of file diff --git a/terraform/modules/aks-byok/variable.tf b/terraform/modules/aks-byok/variable.tf deleted file mode 100644 index b9b5c96..0000000 --- a/terraform/modules/aks-byok/variable.tf +++ /dev/null @@ -1,26 +0,0 @@ -variable "cluster_name" { - type = string -} - -variable "resource_group_name" { - type = string -} - -variable "cluster_handle" { - type = string -} - -variable "tags" { - type = map(string) - default = {} -} - -variable "protect" { - type = bool - default = false -} - -variable "bindings" { - type = list(any) - default = [] -} \ No newline at end of file diff --git a/terraform/modules/aks-byok/versions.tf b/terraform/modules/aks-byok/versions.tf deleted file mode 100644 index e4fba4a..0000000 --- a/terraform/modules/aks-byok/versions.tf +++ /dev/null @@ -1,12 +0,0 @@ -terraform { - required_providers { - plural = { - source = "pluralsh/plural" - version = ">= 0.2.0" - } - azurerm = { - source = "hashicorp/azurerm" - version = ">=3.51.0, < 4.0" - } - } -} \ No newline at end of file diff --git a/terraform/modules/clusters/aws/addons.tf b/terraform/modules/clusters/aws/addons.tf new file mode 100644 index 0000000..2daa324 --- /dev/null +++ b/terraform/modules/clusters/aws/addons.tf @@ -0,0 +1,79 @@ +module "addons" { + source = "aws-ia/eks-blueprints-addons/aws" + version = "~> 1.12" #ensure to update this to the latest/desired version + + cluster_name = module.eks.cluster_name + cluster_endpoint = module.eks.cluster_endpoint + cluster_version = module.eks.cluster_version + oidc_provider_arn = module.eks.oidc_provider_arn + + eks_addons = { + aws-ebs-csi-driver = { + most_recent = true + service_account_role_arn = module.ebs_csi_irsa_role.iam_role_arn + } + coredns = { + most_recent = true + } + vpc-cni = { + most_recent = true + service_account_role_arn = module.vpc_cni_irsa_role.iam_role_arn + } + kube-proxy = { + most_recent = true + } + } + + # mostly need this module to install the lb controller here. + enable_aws_load_balancer_controller = true + enable_cluster_autoscaler = true + + create_kubernetes_resources = false +} + +module "vpc_cni_irsa_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" + version = "~> 5.33" + + role_name = "${module.eks.cluster_name}-vpc-cni" + attach_vpc_cni_policy = true + vpc_cni_enable_ipv4 = true + vpc_cni_enable_ipv6 = true + + oidc_providers = { + main = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:aws-node"] + } + } +} + +module "ebs_csi_irsa_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" + version = "~> 5.33" + + role_name = "${module.eks.cluster_name}-ebs-csi" + attach_ebs_csi_policy = true + + oidc_providers = { + main = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] + } + } +} + +module "externaldns_irsa_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" + version = "~> 5.33" + + role_name = "${module.eks.cluster_name}-externaldns" + attach_external_dns_policy = true + + oidc_providers = { + main = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["plural-runtime:external-dns"] + } + } +} \ No newline at end of file diff --git a/terraform/modules/clusters/aws/eks.tf b/terraform/modules/clusters/aws/eks.tf new file mode 100644 index 0000000..2413396 --- /dev/null +++ b/terraform/modules/clusters/aws/eks.tf @@ -0,0 +1,25 @@ +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 19.0" + + cluster_name = var.cluster + cluster_version = var.kubernetes_version + + cluster_endpoint_public_access = var.public + + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.private_subnets + control_plane_subnet_ids = module.vpc.public_subnets + + # EKS Managed Node Group(s) + eks_managed_node_group_defaults = merge(var.node_group_defaults, + {ami_release_version = data.aws_ssm_parameter.eks_ami_release_version.value}) + + eks_managed_node_groups = var.managed_node_groups + + create_cloudwatch_log_group = false +} + +data "aws_ssm_parameter" "eks_ami_release_version" { + name = "/aws/service/eks/optimized-ami/${var.kubernetes_version}/amazon-linux-2/recommended/release_version" +} \ No newline at end of file diff --git a/terraform/modules/clusters/aws/locals.tf b/terraform/modules/clusters/aws/locals.tf new file mode 100644 index 0000000..fc13be3 --- /dev/null +++ b/terraform/modules/clusters/aws/locals.tf @@ -0,0 +1,3 @@ +locals { + vpc_name = "${var.cluster}-network" +} \ No newline at end of file diff --git a/terraform/modules/clusters/aws/network.tf b/terraform/modules/clusters/aws/network.tf new file mode 100644 index 0000000..90a6776 --- /dev/null +++ b/terraform/modules/clusters/aws/network.tf @@ -0,0 +1,29 @@ +data "aws_availability_zones" "available" {} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.4" + + name = local.vpc_name + cidr = var.vpc_cidr + azs = data.aws_availability_zones.available.names + public_subnets = var.public_subnets + private_subnets = var.private_subnets + enable_dns_hostnames = true + enable_ipv6 = false + public_subnet_enable_dns64 = false + private_subnet_enable_dns64 = false + + enable_nat_gateway = true + single_nat_gateway = false + + public_subnet_tags = { + "kubernetes.io/cluster/${var.cluster}" = "shared" + "kubernetes.io/role/elb" = "1" + } + + private_subnet_tags = { + "kubernetes.io/cluster/${var.cluster}" = "shared" + "kubernetes.io/role/internal-elb" = "1" + } +} diff --git a/terraform/modules/clusters/aws/plural.tf b/terraform/modules/clusters/aws/plural.tf new file mode 100644 index 0000000..6e5eb64 --- /dev/null +++ b/terraform/modules/clusters/aws/plural.tf @@ -0,0 +1,30 @@ +data "aws_eks_cluster_auth" "cluster" { + name = module.eks.cluster_name + + depends_on = [ module.eks ] +} + +resource "plural_cluster" "this" { + handle = var.cluster + name = var.cluster + tags = { + fleet = var.fleet + tier = var.tier + } + + metadata = jsonencode({ + iam = { + load_balancer = module.addons.gitops_metadata.aws_load_balancer_controller_iam_role_arn + cluster_autoscaler = module.addons.gitops_metadata.cluster_autoscaler_iam_role_arn + external_dns = module.externaldns_irsa_role.iam_role_arn + } + }) + + kubeconfig = { + host = module.eks.cluster_endpoint + cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) + token = data.aws_eks_cluster_auth.cluster.token + } + + depends_on = [ module.vpc ] +} \ No newline at end of file diff --git a/terraform/modules/clusters/aws/variables.tf b/terraform/modules/clusters/aws/variables.tf new file mode 100644 index 0000000..e8466b4 --- /dev/null +++ b/terraform/modules/clusters/aws/variables.tf @@ -0,0 +1,73 @@ +variable "cluster" { + type = string + default = "plural" +} + +variable "fleet" { + type = string +} + +variable "tier" { + type = string +} + +variable "region" { + type = string + default = "us-east-2" +} + +variable "public" { + type = bool + default = true +} + +variable "kubernetes_version" { + type = string + default = "1.28" +} + +variable "vpc_cidr" { + type = string + default = "10.0.0.0/16" +} + +variable "private_subnets" { + type = list(string) + default = ["10.0.16.0/20", "10.0.32.0/20", "10.0.48.0/20"] +} + +variable "public_subnets" { + type = list(string) + default = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] +} + +variable "node_group_defaults" { + type = any + default = { + instance_types = ["t3.xlarge", "t3a.xlarge"] + block_device_mappings = [ + { + device_name = "/dev/xvda" + ebs = { + volume_size = 50 + volume_type = "gp3" + delete_on_termination = true + encrypted = true + } + } + ] + disk_size = 50 + } +} + +variable "managed_node_groups" { + type = any + default = { + green = { + use_name_prefix = false + min_size = 3 + max_size = 10 + desired_size = 3 + } + } +} \ No newline at end of file diff --git a/terraform/modules/eks-byok/versions.tf b/terraform/modules/clusters/aws/versions.tf similarity index 53% rename from terraform/modules/eks-byok/versions.tf rename to terraform/modules/clusters/aws/versions.tf index 647afbc..327b76d 100644 --- a/terraform/modules/eks-byok/versions.tf +++ b/terraform/modules/clusters/aws/versions.tf @@ -1,12 +1,21 @@ terraform { + required_version = ">= 1.0" + required_providers { - plural = { - source = "pluralsh/plural" - version = ">= 0.2.0" - } aws = { source = "hashicorp/aws" - version = ">= 4.57" + } + + plural = { + source = "pluralsh/plural" + version = ">= 0.2.9" } } -} \ No newline at end of file +} + +provider "aws" { + region = var.region +} + + +provider "plural" { } \ No newline at end of file diff --git a/terraform/modules/clusters/gcp/gke.tf b/terraform/modules/clusters/gcp/gke.tf new file mode 100644 index 0000000..ec4244d --- /dev/null +++ b/terraform/modules/clusters/gcp/gke.tf @@ -0,0 +1,32 @@ +module "gke" { + source = "terraform-google-modules/kubernetes-engine/google" + version = "~> 29.0" + + kubernetes_version = var.kubernetes_version + project_id = var.project_id + name = var.cluster + regional = true + grant_registry_access = true + region = var.region + network = module.gcp-network.network_name + subnetwork = module.gcp-network.subnets_names[0] + ip_range_pods = var.ip_range_pods_name + ip_range_services = var.ip_range_services_name + create_service_account = true + deletion_protection = false + node_pools = var.node_pools + node_pools_taints = var.node_pools_taints + node_pools_labels = var.node_pools_labels + node_pools_tags = var.node_pools_tags + + datapath_provider = "ADVANCED_DATAPATH" + + depends_on = [ + google_project_service.gcr, + google_project_service.container, + google_project_service.iam, + google_project_service.storage, + google_project_service.dns, + # local.db_created, + ] +} \ No newline at end of file diff --git a/terraform/modules/clusters/gcp/locals.tf b/terraform/modules/clusters/gcp/locals.tf new file mode 100644 index 0000000..36e6d79 --- /dev/null +++ b/terraform/modules/clusters/gcp/locals.tf @@ -0,0 +1,7 @@ +locals { + network_name = "${var.fleet}-${var.cluster}-network" + subnet_name = "${var.fleet}-${var.cluster}-subnetwork" + range_name = var.allocated_range_name == "" ? "${var.cluster}-${var.fleet}-managed-services" : var.allocated_range_name + + # db_created = var.create_db ? module.pg.0.google_sql_user.default[0] : {} +} \ No newline at end of file diff --git a/terraform/modules/clusters/gcp/network.tf b/terraform/modules/clusters/gcp/network.tf new file mode 100644 index 0000000..9c1f228 --- /dev/null +++ b/terraform/modules/clusters/gcp/network.tf @@ -0,0 +1,28 @@ +module "gcp-network" { + source = "terraform-google-modules/network/google" + version = ">= 7.5" + + project_id = var.project_id + network_name = local.network_name + + subnets = [ + { + subnet_name = local.subnet_name + subnet_ip = var.subnet_cidr + subnet_region = var.region + }, + ] + + secondary_ranges = { + (local.subnet_name) = [ + { + range_name = var.ip_range_pods_name + ip_cidr_range = var.pods_cidr + }, + { + range_name = var.ip_range_services_name + ip_cidr_range = var.services_cidr + }, + ] + } +} diff --git a/terraform/modules/clusters/gcp/plural.tf b/terraform/modules/clusters/gcp/plural.tf new file mode 100644 index 0000000..ec8be1f --- /dev/null +++ b/terraform/modules/clusters/gcp/plural.tf @@ -0,0 +1,19 @@ +data "google_client_config" "default" {} + +resource "plural_cluster" "this" { + handle = var.cluster + name = var.cluster + + tags = { + tier = var.tier + fleet = var.fleet + } + + kubeconfig = { + host = "https://${module.gke.endpoint}" + cluster_ca_certificate = base64decode(module.gke.ca_certificate) + token = data.google_client_config.default.access_token + } + + depends_on = [ module.gcp-network ] +} \ No newline at end of file diff --git a/terraform/modules/clusters/gcp/services.tf b/terraform/modules/clusters/gcp/services.tf new file mode 100644 index 0000000..7ea2f27 --- /dev/null +++ b/terraform/modules/clusters/gcp/services.tf @@ -0,0 +1,96 @@ +# annoyingly need to ensure these are enabled +resource "google_project_service" "gcr" { + project = var.project_id + service = "artifactregistry.googleapis.com" + + timeouts { + create = "30m" + update = "40m" + } + + disable_on_destroy = false +} + +resource "google_project_service" "container" { + project = var.project_id + service = "container.googleapis.com" + + timeouts { + create = "30m" + update = "40m" + } + + disable_on_destroy = false +} + +resource "google_project_service" "iam" { + project = var.project_id + service = "iam.googleapis.com" + + timeouts { + create = "30m" + update = "40m" + } + + disable_on_destroy = false +} + +resource "google_project_service" "storage" { + project = var.project_id + service = "storage.googleapis.com" + + timeouts { + create = "30m" + update = "40m" + } + + disable_on_destroy = false +} + +resource "google_project_service" "dns" { + project = var.project_id + service = "dns.googleapis.com" + + timeouts { + create = "30m" + update = "40m" + } + + disable_on_destroy = false +} + +resource "google_project_service" "compute" { + project = var.project_id + service = "compute.googleapis.com" + + timeouts { + create = "30m" + update = "40m" + } + + disable_on_destroy = false +} + +resource "google_project_service" "sql" { + project = var.project_id + service = "sqladmin.googleapis.com" + + timeouts { + create = "30m" + update = "40m" + } + + disable_on_destroy = false +} + +resource "google_project_service" "servicenetworking" { + project = var.project_id + service = "servicenetworking.googleapis.com" + + timeouts { + create = "30m" + update = "40m" + } + + disable_on_destroy = false +} \ No newline at end of file diff --git a/terraform/modules/clusters/gcp/variables.tf b/terraform/modules/clusters/gcp/variables.tf new file mode 100644 index 0000000..e1967ed --- /dev/null +++ b/terraform/modules/clusters/gcp/variables.tf @@ -0,0 +1,102 @@ +variable "cluster" { + type = string + default = "plural" +} + +variable "fleet" { + type = string +} + +variable "tier" { + type = string +} + +variable "region" { + type = string + default = "us-east-2" +} + +variable "kubernetes_version" { + type = string + default = "1.28.9-gke.1000000" +} + +variable "node_pools" { + type = list(any) + default = [ {name = "default-node-pool"} ] +} + +variable "node_pools_taints" { + type = map(list(object({ key = string, value = string, effect = string }))) + default = { "all": [], "default-node-pool": [] } +} + +variable "node_pools_labels" { + type = map(map(string)) + default = { "all": {}, "default-node-pool": {} } +} + +variable "node_pools_tags" { + type = map(list(string)) + default = { "all": [], "default-node-pool": [] } +} + +variable "project_id" { + type = string + default = "pluralsh-test-384515" +} + +variable "region" { + type = string + description = "The region to host the cluster in" + default = "us-central1" +} + +variable "network" { + type = string + description = "The VPC network created to host the cluster in" + default = "plural-network" +} + +variable "subnetwork" { + type = string + description = "The subnetwork created to host the cluster in" + default = "plural-subnet" +} + +variable "subnet_cidr" { + type = string + default = "10.0.16.0/20" +} + +variable "pods_cidr" { + type = string + default = "10.16.0.0/12" +} + +variable "allocated_range_name" { + type = string + default = "" +} + +variable "services_cidr" { + type = string + default = "10.1.0.0/20" +} + +variable "ip_range_pods_name" { + type = string + description = "The secondary ip range to use for pods" + default = "ip-range-pods" +} + +variable "ip_range_services_name" { + type = string + description = "The secondary ip range to use for services" + default = "ip-range-svc" +} + +variable "tags" { + type = map(string) + default = {} +} \ No newline at end of file diff --git a/terraform/modules/clusters/gcp/versions.tf b/terraform/modules/clusters/gcp/versions.tf new file mode 100644 index 0000000..b519a70 --- /dev/null +++ b/terraform/modules/clusters/gcp/versions.tf @@ -0,0 +1,28 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + google = { + source = "hashicorp/google" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.10" + } + plural = { + source = "pluralsh/plural" + version = ">= 0.2.9" + } + local = { + source = "hashicorp/local" + } + } +} + +provider "google" { + region = var.region +} + +data "google_client_config" "default" {} + +provider "plural" { } \ No newline at end of file diff --git a/terraform/modules/clusters/linode/lke.tf b/terraform/modules/clusters/linode/lke.tf new file mode 100644 index 0000000..cae2991 --- /dev/null +++ b/terraform/modules/clusters/linode/lke.tf @@ -0,0 +1,28 @@ +resource "linode_lke_cluster" "cluster" { + label = var.cluster + k8s_version = var.kubernetes_vsn + region = var.region + + control_plane { + high_availability = false + # acl { + # enabled = true + # addresses { + # ipv4 = ["0.0.0.0/0"] + # ipv6 = ["2001:db8::/32"] + # } + # } + } + + dynamic "pool" { + for_each = var.node_pools + content { + type = pool.value.type + count = pool.value.count + autoscaler { + min = pool.value.autoscaler.min + max = pool.value.autoscaler.max + } + } + } +} \ No newline at end of file diff --git a/terraform/modules/clusters/linode/plural.tf b/terraform/modules/clusters/linode/plural.tf new file mode 100644 index 0000000..242fe3a --- /dev/null +++ b/terraform/modules/clusters/linode/plural.tf @@ -0,0 +1,21 @@ +locals { + kubeconfig = yamldecode(base64decode(linode_lke_cluster.cluster.kubeconfig)) + cluster = local.kubeconfig.clusters[0].cluster + user = local.kubeconfig.users[0].user +} + +resource "plural_cluster" "this" { + handle = var.cluster + name = var.cluster + + tags = { + tier = var.tier + fleet = var.fleet + } + + kubeconfig = { + host = local.cluster.server + cluster_ca_certificate = base64decode(local.cluster["certificate-authority-data"]) + token = local.user.token + } +} \ No newline at end of file diff --git a/terraform/modules/clusters/linode/variables.tf b/terraform/modules/clusters/linode/variables.tf new file mode 100644 index 0000000..94f9d73 --- /dev/null +++ b/terraform/modules/clusters/linode/variables.tf @@ -0,0 +1,39 @@ + +variable "kubernetes_vsn" { + type = string + default = "1.29" +} + +variable "cluster" { + type = string + default = "test" +} + +variable "fleet" { + type = string + default = "demo" +} + +variable "tier" { + type = string + default = "dev" +} + +variable "region" { + type = string + default = "us-east" +} + +variable "node_pools" { + type = list(any) + default = [ + { + type="g6-standard-2", + count=3 + autoscaler={ + min=3 + max=20 + } + } + ] +} \ No newline at end of file diff --git a/terraform/modules/clusters/linode/versions.tf b/terraform/modules/clusters/linode/versions.tf new file mode 100644 index 0000000..ba80c46 --- /dev/null +++ b/terraform/modules/clusters/linode/versions.tf @@ -0,0 +1,16 @@ +terraform { + required_providers { + linode = { + source = "linode/linode" + version = "2.20.1" + } + plural = { + source = "pluralsh/plural" + version = ">= 0.2.9" + } + } +} + +provider "plural" { } + +provider "linode" { } \ No newline at end of file diff --git a/terraform/modules/eks-byok/cluster.tf b/terraform/modules/eks-byok/cluster.tf deleted file mode 100644 index 2093963..0000000 --- a/terraform/modules/eks-byok/cluster.tf +++ /dev/null @@ -1,20 +0,0 @@ -data "aws_eks_cluster" "cluster" { - name = var.cluster_name -} - -data "aws_eks_cluster_auth" "cluster" { - name = var.cluster_name -} - -resource "plural_cluster" "this" { - handle = var.cluster_handle - name = var.cluster_name - tags = var.tags - protect = var.protect - # bindings = var.bindings - kubeconfig = { - host = data.aws_eks_cluster.cluster.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data) - token = data.aws_eks_cluster_auth.cluster.token - } -} \ No newline at end of file diff --git a/terraform/modules/eks-byok/variable.tf b/terraform/modules/eks-byok/variable.tf deleted file mode 100644 index 314ef58..0000000 --- a/terraform/modules/eks-byok/variable.tf +++ /dev/null @@ -1,22 +0,0 @@ -variable "cluster_name" { - type = string -} - -variable "cluster_handle" { - type = string -} - -variable "tags" { - type = map(string) - default = {} -} - -variable "protect" { - type = bool - default = false -} - -variable "bindings" { - type = list(any) - default = [] -} \ No newline at end of file diff --git a/terraform/modules/gke-byok/cluster.tf b/terraform/modules/gke-byok/cluster.tf deleted file mode 100644 index 2493da2..0000000 --- a/terraform/modules/gke-byok/cluster.tf +++ /dev/null @@ -1,20 +0,0 @@ -data "google_client_config" "default" {} - -data "google_container_cluster" "cluster" { - name = var.cluster_name - location = var.location - project = var.project -} - -resource "plural_cluster" "this" { - handle = var.cluster_handle - name = var.cluster_name - tags = var.tags - protect = var.protect - # bindings = var.bindings - kubeconfig = { - host = "https://${data.google_container_cluster.cluster.endpoint}" - cluster_ca_certificate = base64decode(data.google_container_cluster.cluster.master_auth.0.cluster_ca_certificate) - token = data.google_client_config.default.access_token - } -} \ No newline at end of file diff --git a/terraform/modules/gke-byok/variable.tf b/terraform/modules/gke-byok/variable.tf deleted file mode 100644 index 1dc3e7a..0000000 --- a/terraform/modules/gke-byok/variable.tf +++ /dev/null @@ -1,30 +0,0 @@ -variable "cluster_name" { - type = string -} - -variable "location" { - type = string -} - -variable "project" { - type = string -} - -variable "cluster_handle" { - type = string -} - -variable "tags" { - type = map(string) - default = {} -} - -variable "protect" { - type = bool - default = false -} - -variable "bindings" { - type = list(any) - default = [] -} \ No newline at end of file diff --git a/terraform/modules/gke-byok/versions.tf b/terraform/modules/gke-byok/versions.tf deleted file mode 100644 index fde555a..0000000 --- a/terraform/modules/gke-byok/versions.tf +++ /dev/null @@ -1,11 +0,0 @@ -terraform { - required_providers { - plural = { - source = "pluralsh/plural" - version = ">= 0.2.0" - } - google = { - source = "hashicorp/google" - } - } -} \ No newline at end of file diff --git a/terraform/modules/raw-kubeconfig/parse.tf b/terraform/modules/raw-kubeconfig/parse.tf deleted file mode 100644 index 39f9663..0000000 --- a/terraform/modules/raw-kubeconfig/parse.tf +++ /dev/null @@ -1,11 +0,0 @@ -locals { - kubeconfig = yamldecode(var.kubeconfig) -} - -output "cluster" { - value = local.kubeconfig.clusters[0] -} - -output "user" { - value = local.kubeconfig.users[0].user -} \ No newline at end of file diff --git a/terraform/modules/raw-kubeconfig/variable.tf b/terraform/modules/raw-kubeconfig/variable.tf deleted file mode 100644 index d3a575a..0000000 --- a/terraform/modules/raw-kubeconfig/variable.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "kubeconfig" { - type = string -} \ No newline at end of file