diff --git a/Makefile b/Makefile index 480966ba59..4aeb6dfea0 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ TERRAFORM ?= $(shell command -v terraform) # These images either do something with Alpine, # or are somehow incompatible with tfgen (still using tagger etc.) -TFGEN_SKIP ?= busybox,calico,git,graalvm-native,harbor,k3s,keda,kubeflow,kubeflow-katib,maven,powershell,prometheus,static,terraform +TFGEN_SKIP ?= busybox,calico,git,graalvm-native,harbor,k3s,kubeflow,kubeflow-katib,maven,powershell,prometheus,static,terraform # These are the tfgen generators applied to this repo (in order) TFGEN_GENERATORS ?= Image01Outputs,Toplevel01Modules,Toplevel02Outputs diff --git a/generated.tf b/generated.tf index cc1a95e35c..5914ca004d 100644 --- a/generated.tf +++ b/generated.tf @@ -600,6 +600,11 @@ module "karpenter" { target_repository = "${var.target_repository}/karpenter" } +module "keda" { + source = "./images/keda" + target_repository = "${var.target_repository}/keda" +} + module "keycloak" { source = "./images/keycloak" target_repository = "${var.target_repository}/keycloak" @@ -1998,6 +2003,10 @@ output "summary_karpenter" { value = module.karpenter.summary } +output "summary_keda" { + value = module.keda.summary +} + output "summary_keycloak" { value = module.keycloak.summary } diff --git a/images/keda/config/main.tf b/images/keda/config/main.tf new file mode 100644 index 0000000000..81841e45e7 --- /dev/null +++ b/images/keda/config/main.tf @@ -0,0 +1,39 @@ +variable "name" { + description = "Component name (e.g. keda, keda-adapter, keda-admission-webhooks)" +} + +variable "package" { + description = "Package name (e.g. keda-2.14)" +} + +variable "extra_packages" { + description = "Additional packages to install." + type = list(string) + default = [] +} + +locals { + entrypoints = { + "keda" = "/usr/bin/keda --zap-log-level=info --zap-encoder=console", + "keda-adapter" = "/usr/bin/keda-adapter --secure-port=6443 --logtostderr=true --v=0", + "keda-admission-webhooks" = "/usr/bin/keda-admission-webhooks --zap-log-level=info --zap-encoder=console", + } +} + +module "accts" { source = "../../../tflib/accts" } + +output "config" { + value = jsonencode({ + contents = { + packages = concat([ + var.package, // keda, keda-adapter, keda-admission-webhooks + "busybox", + "keda-compat", + ], var.extra_packages) + } + accounts = module.accts.block + entrypoint = { + command = local.entrypoints[var.name] + } + }) +} diff --git a/images/keda/configs/latest.adapter.apko.yaml b/images/keda/configs/latest.adapter.apko.yaml deleted file mode 100644 index 0a63c9a3a0..0000000000 --- a/images/keda/configs/latest.adapter.apko.yaml +++ /dev/null @@ -1,18 +0,0 @@ -contents: - packages: - - busybox - - keda-adapter - - keda-compat - -accounts: - groups: - - groupname: nonroot - gid: 65532 - users: - - username: nonroot - uid: 65532 - gid: 65532 - run-as: 65532 - -entrypoint: - command: /usr/bin/keda-adapter --secure-port=6443 --logtostderr=true --v=0 diff --git a/images/keda/configs/latest.controller.apko.yaml b/images/keda/configs/latest.controller.apko.yaml deleted file mode 100644 index 72d2eb301a..0000000000 --- a/images/keda/configs/latest.controller.apko.yaml +++ /dev/null @@ -1,18 +0,0 @@ -contents: - packages: - - busybox - - keda - - keda-compat - -accounts: - groups: - - groupname: nonroot - gid: 65532 - users: - - username: nonroot - uid: 65532 - gid: 65532 - run-as: 65532 - -entrypoint: - command: /usr/bin/keda --zap-log-level=info --zap-encoder=console diff --git a/images/keda/configs/latest.webhooks.apko.yaml b/images/keda/configs/latest.webhooks.apko.yaml deleted file mode 100644 index 249392c4ab..0000000000 --- a/images/keda/configs/latest.webhooks.apko.yaml +++ /dev/null @@ -1,18 +0,0 @@ -contents: - packages: - - busybox - - keda-admission-webhooks - - keda-compat - -accounts: - groups: - - groupname: nonroot - gid: 65532 - users: - - username: nonroot - uid: 65532 - gid: 65532 - run-as: 65532 - -entrypoint: - command: /usr/bin/keda-admission-webhooks --zap-log-level=info --zap-encoder=console diff --git a/images/keda/generated.tf b/images/keda/generated.tf new file mode 100644 index 0000000000..e7a317ed5d --- /dev/null +++ b/images/keda/generated.tf @@ -0,0 +1,13 @@ +# DO NOT EDIT - this file is autogenerated by tfgen + +output "summary" { + value = merge( + { + for k, v in module.versioned : k => { + "ref" = v.image_ref + "config" = v.config + "tags" = v.tag_list + } + }) +} + diff --git a/images/keda/main.tf b/images/keda/main.tf index 0e7ff2e2d6..282d2aeb4b 100644 --- a/images/keda/main.tf +++ b/images/keda/main.tf @@ -1,55 +1,127 @@ locals { + # List of all components components = toset([ - "controller", - "adapter", - "webhooks", + "keda", + "keda-adapter", + "keda-admission-webhooks", ]) - packages = merge( - { for k in local.components : k => k }, - { - "controller" = "keda" - "adapter" = "keda-adapter" - "webhooks" = "keda-admission-webhooks" - }, - ) + # List of versions. + # + # When version metadata is not available this will look like + # "versions" = [""] + # + # When version metadata is available this will look like + # "versions" = ["2.13", "2.14"] + # + # Order matters here, we want the latest version to be the last element in + # the list so it's tagged with latest. + versions = [ + for _, version_metadata in module.versions.versions : lookup(version_metadata, "version", "") + ] - repositories = merge( - { for k in local.components : k => k }, - { - "controller" = var.target_repository - "adapter" = "${var.target_repository}-adapter" - "webhooks" = "${var.target_repository}-admission-webhooks" - }, - ) + # Cross product of versions and components + # + # When version metadata is not available this will look like + # "component_versions" = { + # "keda" = { + # "component" = "keda" + # "is_latest" = true + # "main" = "keda" + # } + # "keda-adapter" = { + # "component" = "keda-adapter" + # "is_latest" = true + # "main" = "keda-adapter" + # } + # ... + # } + # + # When version metadata is not available this will look like + # "component_versions" = { + # "keda2.14" = { + # "component" = "keda" + # "is_latest" = true + # "main" = "keda-2.14" + # } + # "keda-adapter2.14" = { + # "component" = "keda-adapter" + # "is_latest" = true + # "main" = "keda-2.14-adapter" + # } + # ... + # } + component_versions = merge([ + for component in local.components : merge([ + for key, version_metadata in module.versions.versions : { + + # This sets the key to `$component` when version data does not exist and + # `$component$version` when version data exists. We just smash the two + # together to make it easier to access in subsequent modules. + format("%s%s", component, lookup(version_metadata, "version", "")) : { + is_latest = version_metadata.is_latest + component = component + + # Strips off `keda` or `keda-` from the components and joins with the + # version_metadata key which will be `keda` when version data does not + # exist and `keda-$version` when version data exists + main = join("-", compact([key, trimprefix(replace(component, "keda", ""), "-")])) + } + } + ]...) + ]...) } variable "target_repository" { description = "The docker repo into which the image and attestations should be published." } -module "latest" { - for_each = local.repositories +# Versions module plugs into to the version metadata for the top-level package +module "versions" { + source = "../../tflib/versions" + package = basename(path.module) +} + +# Need a config for every version of every component +module "config" { + for_each = local.component_versions + source = "./config" + name = each.value.component + package = each.value.main +} + +# Need a publisher invocation for every version of every component +module "versioned" { + for_each = local.component_versions source = "../../tflib/publisher" name = basename(path.module) - target_repository = each.value - config = file("${path.module}/configs/latest.${each.key}.apko.yaml") + target_repository = replace(var.target_repository, "/keda", "/${each.value.component}") + config = module.config[each.key].config build-dev = true + + main_package = each.value.main + update-repo = each.value.is_latest } -module "test-latest" { - source = "./tests" - digests = { for k, v in module.latest : k => v.image_ref } +# Tests need to be grouped by version. We want to test all components of +# the same version at the same time. +module "test" { + for_each = toset(local.versions) + source = "./tests" + + digests = { for component in local.components : component => module.versioned["${component}${each.value}"].image_ref } + name = "${basename(path.module)}${each.value}" } +# Tagger should be grouped by components. We want to tag multiple versions of +# the same component so we iterate over the versions list for each component module "tagger" { for_each = local.components source = "../../tflib/tagger" - depends_on = [module.test-latest] + depends_on = [module.test] - tags = { - "latest" = module.latest[each.key].image_ref - "latest-dev" = module.latest[each.key].dev_ref - } + tags = merge( + [for version in local.versions : module.versioned["${each.value}${version}"].latest_tag_map]... + ) } diff --git a/images/keda/tests/main.tf b/images/keda/tests/main.tf index 95f7d0c72d..08fd1b3697 100644 --- a/images/keda/tests/main.tf +++ b/images/keda/tests/main.tf @@ -1,27 +1,21 @@ terraform { required_providers { - oci = { source = "chainguard-dev/oci" } - helm = { source = "hashicorp/helm" } + oci = { source = "chainguard-dev/oci" } + imagetest = { source = "chainguard-dev/imagetest" } } } variable "digests" { description = "The image digests to run tests over." type = object({ - controller = string - adapter = string - webhooks = string + keda = string + keda-adapter = string + keda-admission-webhooks = string }) } -variable "skip_crds" { - description = "Used to deconflict between multiple installations within the same cluster." - default = false -} - -variable "chart-version" { - description = "The version of the Helm chart to install." - default = "latest" +variable "name" { + default = "keda" } data "oci_string" "ref" { @@ -29,45 +23,68 @@ data "oci_string" "ref" { input = each.value } -resource "helm_release" "keda" { - name = "keda" - namespace = "keda" - repository = "https://kedacore.github.io/charts" - chart = "keda" - create_namespace = true - timeout = 600 - - version = var.chart-version == "latest" ? null : var.chart-version +data "imagetest_inventory" "this" {} - values = [ - <