From 0b6364a5de0a8247fb420f11ec995ef06931d067 Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Tue, 11 Aug 2026 21:25:58 +0200 Subject: [PATCH 01/10] fix WPB-27900: added cd infrastructre resources from master to current branch --- .gitignore | 6 +- changelog.d/0-release-notes/.title | 1 + changelog.d/1-debian-builds/.title | 1 + changelog.d/2-wire-builds/.title | 1 + changelog.d/3-deploy-builds/.title | 1 + changelog.d/3-deploy-builds/sync-with-master | 1 + changelog.d/4-docs/.title | 1 + changelog.d/5-bug-fixes/.title | 1 + changelog.d/mk-changelog.sh | 54 ++++ changelog.d/mk-cleanup.sh | 9 + terraform/examples/wiab-demo-hetzner/.envrc | 5 + .../examples/wiab-demo-hetzner/README.md | 4 + terraform/examples/wiab-demo-hetzner/main.tf | 89 ++++++ .../examples/wiab-demo-hetzner/outputs.tf | 33 ++ .../examples/wiab-demo-hetzner/versions.tf | 8 + .../examples/wiab-staging-hetzner/.envrc | 5 + .../examples/wiab-staging-hetzner/README.md | 5 + .../examples/wiab-staging-hetzner/main.tf | 215 +++++++++++++ .../examples/wiab-staging-hetzner/outputs.tf | 114 +++++++ .../retry-selection.auto.tfvars.json | 5 + .../wiab-staging-hetzner/setup_nodes.yml | 235 ++++++++++++++ .../examples/wiab-staging-hetzner/versions.tf | 8 + .../main.tf | 299 +++++++++++++----- .../outputs.tf | 151 +++++++-- .../setup_nodes.yml | 205 ++++++++++++ 25 files changed, 1348 insertions(+), 109 deletions(-) create mode 100644 changelog.d/0-release-notes/.title create mode 100644 changelog.d/1-debian-builds/.title create mode 100644 changelog.d/2-wire-builds/.title create mode 100644 changelog.d/3-deploy-builds/.title create mode 100644 changelog.d/3-deploy-builds/sync-with-master create mode 100644 changelog.d/4-docs/.title create mode 100644 changelog.d/5-bug-fixes/.title create mode 100755 changelog.d/mk-changelog.sh create mode 100755 changelog.d/mk-cleanup.sh create mode 100644 terraform/examples/wiab-demo-hetzner/.envrc create mode 100644 terraform/examples/wiab-demo-hetzner/README.md create mode 100644 terraform/examples/wiab-demo-hetzner/main.tf create mode 100644 terraform/examples/wiab-demo-hetzner/outputs.tf create mode 100644 terraform/examples/wiab-demo-hetzner/versions.tf create mode 100644 terraform/examples/wiab-staging-hetzner/.envrc create mode 100644 terraform/examples/wiab-staging-hetzner/README.md create mode 100644 terraform/examples/wiab-staging-hetzner/main.tf create mode 100644 terraform/examples/wiab-staging-hetzner/outputs.tf create mode 100644 terraform/examples/wiab-staging-hetzner/retry-selection.auto.tfvars.json create mode 100644 terraform/examples/wiab-staging-hetzner/setup_nodes.yml create mode 100644 terraform/examples/wiab-staging-hetzner/versions.tf create mode 100644 terraform/examples/wire-server-deploy-offline-hetzner/setup_nodes.yml diff --git a/.gitignore b/.gitignore index 641b57d70..d4afd0427 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ values-init-done # Envrc local overrides .envrc.local - +.vscode # Nix-created result symlinks result result-* @@ -30,4 +30,8 @@ secrets_cache/ terraform.tfstate terraform.tfstate.backup +*.auto.tfvars.json kubeconfig.new +.vscode/* + +.terraform.lock.hcl diff --git a/changelog.d/0-release-notes/.title b/changelog.d/0-release-notes/.title new file mode 100644 index 000000000..d754b17ca --- /dev/null +++ b/changelog.d/0-release-notes/.title @@ -0,0 +1 @@ +Release notes \ No newline at end of file diff --git a/changelog.d/1-debian-builds/.title b/changelog.d/1-debian-builds/.title new file mode 100644 index 000000000..d1563daab --- /dev/null +++ b/changelog.d/1-debian-builds/.title @@ -0,0 +1 @@ +Base OS dependencies \ No newline at end of file diff --git a/changelog.d/2-wire-builds/.title b/changelog.d/2-wire-builds/.title new file mode 100644 index 000000000..7fa4b7bd0 --- /dev/null +++ b/changelog.d/2-wire-builds/.title @@ -0,0 +1 @@ +Internal Dependencies \ No newline at end of file diff --git a/changelog.d/3-deploy-builds/.title b/changelog.d/3-deploy-builds/.title new file mode 100644 index 000000000..fe92586cd --- /dev/null +++ b/changelog.d/3-deploy-builds/.title @@ -0,0 +1 @@ +External dependencies \ No newline at end of file diff --git a/changelog.d/3-deploy-builds/sync-with-master b/changelog.d/3-deploy-builds/sync-with-master new file mode 100644 index 000000000..4a132ec87 --- /dev/null +++ b/changelog.d/3-deploy-builds/sync-with-master @@ -0,0 +1 @@ +Added: new CD infrastructure definition from master here diff --git a/changelog.d/4-docs/.title b/changelog.d/4-docs/.title new file mode 100644 index 000000000..7d1420123 --- /dev/null +++ b/changelog.d/4-docs/.title @@ -0,0 +1 @@ +Offline Documentation \ No newline at end of file diff --git a/changelog.d/5-bug-fixes/.title b/changelog.d/5-bug-fixes/.title new file mode 100644 index 000000000..23bcb1b64 --- /dev/null +++ b/changelog.d/5-bug-fixes/.title @@ -0,0 +1 @@ +Bug fixes and other updates \ No newline at end of file diff --git a/changelog.d/mk-changelog.sh b/changelog.d/mk-changelog.sh new file mode 100755 index 000000000..c9616788d --- /dev/null +++ b/changelog.d/mk-changelog.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +set -euo pipefail +shopt -s nullglob + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +getPRNumber() { + git log --reverse --format=%s -- "$1" | sed -rn '1 { /\((#.*)\)$/ s|^.*\((#.*)\)$|\1|p; }' | grep "" || + echo "#PR_NOT_FOUND" +} + +for d in "$DIR"/*; do + if [[ ! -d "$d" ]]; then continue; fi + + entries=("$d"/*[^~]) + + if [[ ${#entries[@]} -eq 0 ]]; then continue; fi + + echo -n "## " + # shellcheck disable=SC1003 + sed '$ a\' "$d/.title" + echo "" + # shellcheck disable=SC2094 + for f in "${entries[@]}"; do + pr=$(getPRNumber "$f") + # shellcheck disable=SC1003 + < "$f" sed -r ' + # create a bullet point on the first line + 1 { s/^/\* /; } + + # indent subsequent lines + 1 !{ s/^/ /; } + + # replace ## with PR number throughout + s/##/'"$pr"'/g' | + ( + if grep -q -r '\(#[^)]\)' "$f"; then + cat + else + sed -r ' + # add PR number at the end (unless already present) + $ { /^.*\((#.*)\)$/ ! { s/$/ ('"$pr"')/; } } + ' + fi + ) | sed -r ' + # remove trailing whitespace + s/\s+$// + + # make sure there is a trailing newline + $ a\' + done + echo "" +done diff --git a/changelog.d/mk-cleanup.sh b/changelog.d/mk-cleanup.sh new file mode 100755 index 000000000..b483a6e60 --- /dev/null +++ b/changelog.d/mk-cleanup.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -euo pipefail +shopt -s nullglob + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +rm -f "$DIR"/*/* +git add "$DIR" diff --git a/terraform/examples/wiab-demo-hetzner/.envrc b/terraform/examples/wiab-demo-hetzner/.envrc new file mode 100644 index 000000000..8a29ee6d6 --- /dev/null +++ b/terraform/examples/wiab-demo-hetzner/.envrc @@ -0,0 +1,5 @@ +[[ -f .envrc.local ]] && source_env .envrc.local +# You can set this in .envrc.local to keep it out of VCS +export HCLOUD_TOKEN +source_up + diff --git a/terraform/examples/wiab-demo-hetzner/README.md b/terraform/examples/wiab-demo-hetzner/README.md new file mode 100644 index 000000000..40d77a5e5 --- /dev/null +++ b/terraform/examples/wiab-demo-hetzner/README.md @@ -0,0 +1,4 @@ +# WiaB-demo-hetzner + +This environment is set up and destroyed on demand to test our demo-wiab solution. It will be used to provide an Ubuntu system for the demo. +https://docs.wire.com/latest/how-to/install/demo-wiab.html#installation-guide diff --git a/terraform/examples/wiab-demo-hetzner/main.tf b/terraform/examples/wiab-demo-hetzner/main.tf new file mode 100644 index 000000000..e667fecd1 --- /dev/null +++ b/terraform/examples/wiab-demo-hetzner/main.tf @@ -0,0 +1,89 @@ +locals { +} + +variable "location" { + description = "Hetzner location selected by the deployment script" + type = string + default = "hel1" +} + +variable "server_type" { + description = "Server type selected by the deployment script" + type = string + default = "cx53" +} + +# Get available server types and locations +data "hcloud_server_types" "available" {} +data "hcloud_datacenters" "available" {} + +locals { + available_server_type_names = [for st in data.hcloud_server_types.available.server_types : st.name] + available_location_names = [for dc in data.hcloud_datacenters.available.datacenters : dc.location.name] +} + +resource "null_resource" "location_validation" { + count = contains(local.available_location_names, var.location) ? 0 : 1 + + provisioner "local-exec" { + command = <<-EOT + echo "DEPLOYMENT FAILED: Requested location is unavailable" + echo "Requested location: ${var.location}" + echo "Available locations: ${join(", ", local.available_location_names)}" + echo "Please check Hetzner Cloud region availability" + exit 1 + EOT + } +} + +resource "null_resource" "server_type_validation" { + count = contains(local.available_server_type_names, var.server_type) ? 0 : 1 + + provisioner "local-exec" { + command = <<-EOT + echo "DEPLOYMENT FAILED: Requested server type is currently unavailable" + echo "Requested server type: ${var.server_type}" + echo "Available types: ${join(", ", local.available_server_type_names)}" + echo "Please check server type availability" + exit 1 + EOT + } +} + +resource "null_resource" "deployment_info" { + depends_on = [ + null_resource.location_validation, + null_resource.server_type_validation + ] + + provisioner "local-exec" { + command = <<-EOT + echo "VALIDATION PASSED: Deploying WIAB dev infrastructure" + echo "Location: ${var.location}" + echo "Server type: ${var.server_type}" + EOT + } +} + +resource "random_pet" "host" { + depends_on = [null_resource.deployment_info] +} + +resource "tls_private_key" "host" { + algorithm = "ECDSA" + ecdsa_curve = "P256" +} + +resource "hcloud_ssh_key" "host" { + name = "host-${random_pet.host.id}" + public_key = tls_private_key.host.public_key_openssh +} + +resource "hcloud_server" "host" { + location = var.location + name = "host-${random_pet.host.id}" + image = "ubuntu-24.04" + ssh_keys = [hcloud_ssh_key.host.name] + server_type = var.server_type + +} diff --git a/terraform/examples/wiab-demo-hetzner/outputs.tf b/terraform/examples/wiab-demo-hetzner/outputs.tf new file mode 100644 index 000000000..de46ae866 --- /dev/null +++ b/terraform/examples/wiab-demo-hetzner/outputs.tf @@ -0,0 +1,33 @@ +output "ssh_private_key" { + sensitive = true + value = tls_private_key.host.private_key_pem +} + +output "selected_server_types" { + description = "Server types selected for the current deployment attempt" + value = { + server_type = var.server_type + } +} + +output "selected_location" { + description = "Location selected for the current deployment attempt" + value = var.location +} + +output "resource_fallback_info" { + description = "Information about the requested deployment combination and its availability" + value = { + requested_location = var.location + selected_location = var.location + requested_server_type = var.server_type + selected_server_type = var.server_type + available_locations = local.available_location_names + available_server_types = local.available_server_type_names + } +} + +output "host" { + sensitive = true + value = hcloud_server.host.ipv4_address +} diff --git a/terraform/examples/wiab-demo-hetzner/versions.tf b/terraform/examples/wiab-demo-hetzner/versions.tf new file mode 100644 index 000000000..b047ba54e --- /dev/null +++ b/terraform/examples/wiab-demo-hetzner/versions.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + } + } + required_version = "~> 1.1" +} diff --git a/terraform/examples/wiab-staging-hetzner/.envrc b/terraform/examples/wiab-staging-hetzner/.envrc new file mode 100644 index 000000000..8a29ee6d6 --- /dev/null +++ b/terraform/examples/wiab-staging-hetzner/.envrc @@ -0,0 +1,5 @@ +[[ -f .envrc.local ]] && source_env .envrc.local +# You can set this in .envrc.local to keep it out of VCS +export HCLOUD_TOKEN +source_up + diff --git a/terraform/examples/wiab-staging-hetzner/README.md b/terraform/examples/wiab-staging-hetzner/README.md new file mode 100644 index 000000000..6539dbf65 --- /dev/null +++ b/terraform/examples/wiab-staging-hetzner/README.md @@ -0,0 +1,5 @@ +# Wire-in-a-box-staging-hetzner + +This environment is dynamically provisioned to validate the wiab-staging solution, developed as a follow-up to our HA architecture in which datastore and Kubernetes VMs are physically failure-resilient. + +For wiab-staging, all components are deliberately colocated on a single physical node, resulting in zero physical redundancy and a single point of failure. This design is intentional and suitable only for staging and testing, not production deployments. diff --git a/terraform/examples/wiab-staging-hetzner/main.tf b/terraform/examples/wiab-staging-hetzner/main.tf new file mode 100644 index 000000000..5fb916526 --- /dev/null +++ b/terraform/examples/wiab-staging-hetzner/main.tf @@ -0,0 +1,215 @@ +locals { + rfc1918_cidr = "10.0.0.0/8" + kubenode_count = 3 + datanode_count = 3 + ssh_keys = [hcloud_ssh_key.adminhost.name] +} + +variable "location" { + description = "Hetzner location selected by the deployment script" + type = string + default = "hel1" +} + +variable "small_server_type" { + description = "Server type for assethost and adminhost selected by the deployment script" + type = string + default = "cx33" +} + +variable "medium_server_type" { + description = "Server type for datanodes and Kubernetes nodes selected by the deployment script" + type = string + default = "cx43" +} + +# Get available server types and locations +data "hcloud_server_types" "available" {} +data "hcloud_datacenters" "available" {} + +# Validate the exact combination requested by the deployment script. +locals { + available_server_type_names = [for st in data.hcloud_server_types.available.server_types : st.name] + available_location_names = [for dc in data.hcloud_datacenters.available.datacenters : dc.location.name] +} + +resource "null_resource" "location_validation" { + count = contains(local.available_location_names, var.location) ? 0 : 1 + + provisioner "local-exec" { + command = <<-EOT + echo "DEPLOYMENT FAILED: Requested location is unavailable" + echo "Requested location: ${var.location}" + echo "Available locations: ${join(", ", local.available_location_names)}" + echo "Please check Hetzner Cloud region availability" + exit 1 + EOT + } +} + +resource "null_resource" "small_server_type_validation" { + count = contains(local.available_server_type_names, var.small_server_type) ? 0 : 1 + + provisioner "local-exec" { + command = <<-EOT + echo "DEPLOYMENT FAILED: Requested small server type is currently unavailable" + echo "Requested small server type: ${var.small_server_type}" + echo "Available types: ${join(", ", local.available_server_type_names)}" + echo "Please check server type availability" + exit 1 + EOT + } +} + +resource "null_resource" "medium_server_type_validation" { + count = contains(local.available_server_type_names, var.medium_server_type) ? 0 : 1 + + provisioner "local-exec" { + command = <<-EOT + echo "DEPLOYMENT FAILED: Requested medium server type is currently unavailable" + echo "Requested medium server type: ${var.medium_server_type}" + echo "Available types: ${join(", ", local.available_server_type_names)}" + echo "Please check server type availability" + exit 1 + EOT + } +} + +resource "null_resource" "deployment_info" { + depends_on = [ + null_resource.location_validation, + null_resource.small_server_type_validation, + null_resource.medium_server_type_validation + ] + + provisioner "local-exec" { + command = <<-EOT + echo "VALIDATION PASSED: Deploying WIAB staging infrastructure" + echo "Location: ${var.location}" + echo "Small server type: ${var.small_server_type}" + echo "Medium server type: ${var.medium_server_type}" + echo "Total instances: ${local.datanode_count + local.kubenode_count + 2}" + EOT + } +} + +resource "random_pet" "main" { + depends_on = [null_resource.deployment_info] +} + +resource "hcloud_network" "main" { + name = "main-${random_pet.main.id}" + ip_range = cidrsubnet(local.rfc1918_cidr, 8, 1) +} + +resource "hcloud_network_subnet" "main" { + network_id = hcloud_network.main.id + type = "cloud" + network_zone = "eu-central" + ip_range = cidrsubnet(hcloud_network.main.ip_range, 8, 1) +} + + +resource "random_pet" "adminhost" { +} + +resource "tls_private_key" "admin" { + algorithm = "ECDSA" + ecdsa_curve = "P256" +} + +resource "hcloud_ssh_key" "adminhost" { + name = "adminhost-${random_pet.adminhost.id}" + public_key = tls_private_key.admin.public_key_openssh +} + +# Connected to all other servers. Simulates the admin's "laptop" +resource "hcloud_server" "adminhost" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] + location = var.location + name = "adminhost-${random_pet.adminhost.id}" + image = "ubuntu-22.04" + ssh_keys = local.ssh_keys + server_type = var.small_server_type + network { + network_id = hcloud_network.main.id + ip = "" + } +} + +# The server hosting all the bootstrap assets +resource "random_pet" "assethost" { +} + +resource "hcloud_server" "assethost" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] + location = var.location + name = "assethost-${random_pet.assethost.id}" + image = "ubuntu-22.04" + ssh_keys = local.ssh_keys + server_type = var.small_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } +} + +resource "random_pet" "kubenode" { + count = local.kubenode_count +} + +resource "hcloud_server" "kubenode" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] + count = local.kubenode_count + location = var.location + name = "kubenode-${random_pet.kubenode[count.index].id}" + image = "ubuntu-22.04" + ssh_keys = local.ssh_keys + server_type = var.medium_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } +} + +resource "random_pet" "datanode" { + count = local.datanode_count +} + +resource "hcloud_server" "datanode" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] + count = local.datanode_count + location = var.location + name = "datanode-${random_pet.datanode[count.index].id}" + image = "ubuntu-22.04" + ssh_keys = local.ssh_keys + server_type = var.medium_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } +} diff --git a/terraform/examples/wiab-staging-hetzner/outputs.tf b/terraform/examples/wiab-staging-hetzner/outputs.tf new file mode 100644 index 000000000..c3fa5037b --- /dev/null +++ b/terraform/examples/wiab-staging-hetzner/outputs.tf @@ -0,0 +1,114 @@ +output "ssh_private_key" { + sensitive = true + value = tls_private_key.admin.private_key_pem +} + +output "selected_server_types" { + description = "Server types selected for the current deployment attempt" + value = { + small_server_type = var.small_server_type + medium_server_type = var.medium_server_type + } +} + +output "selected_location" { + description = "Location selected for the current deployment attempt" + value = var.location +} + +output "resource_fallback_info" { + description = "Information about the requested deployment combination and its availability" + value = { + requested_location = var.location + available_locations = local.available_location_names + selected_location = var.location + + requested_small_type = var.small_server_type + selected_small_type = var.small_server_type + + requested_medium_type = var.medium_server_type + selected_medium_type = var.medium_server_type + + available_server_types = local.available_server_type_names + } +} + +output "adminhost" { + sensitive = true + value = hcloud_server.adminhost.ipv4_address +} +# output format that a static inventory file expects +output "static-inventory" { + sensitive = true + value = { + all = { + vars = { + adminhost_ip = tolist(hcloud_server.adminhost.network)[0].ip + ansible_user = "root" + private_interface = "enp7s0" + } + } + adminhost = { + hosts = { + "adminhost" = { + ansible_host = hcloud_server.adminhost.ipv4_address + } + } + vars = { + ansible_ssh_common_args = "-o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s -o BatchMode=yes -o ConnectionAttempts=10 -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o ConnectTimeout=10" + } + } + private = { + children = { + assethost = {} + datanode = {} + "kube-node" = {} + adminhost_local = {} + } + vars = { + ansible_ssh_common_args = "-o ProxyCommand=\"ssh -i ssh_private_key -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -W %h:%p -q root@${hcloud_server.adminhost.ipv4_address}\" -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s -o BatchMode=yes -o ConnectionAttempts=10 -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o ConnectTimeout=10" + } + } + adminhost_local = { + hosts = { + "adminhost_local" = { + ansible_host = tolist(hcloud_server.adminhost.network)[0].ip + } + } + } + assethost = { + hosts = { + "assethost" = { + ansible_host = tolist(hcloud_server.assethost.network)[0].ip + } + } + } + kube-node = { + hosts = { + for index, server in hcloud_server.kubenode : server.name => { + ansible_host = tolist(hcloud_server.kubenode[index].network)[0].ip + ip = tolist(hcloud_server.kubenode[index].network)[0].ip + } + } + # NOTE: Necessary for the Hetzner Cloud until Calico v3.17 arrives in Kubespray + # Hetzner private networks have an MTU of 1450 instead of 1500 + vars = { + calico_mtu = 1450 + calico_veth_mtu = 1430 + # NOTE: relax handling a list with more than 3 items; required on Hetzner + docker_dns_servers_strict = false + upstream_dns_servers = [tolist(hcloud_server.adminhost.network)[0].ip] + } + } + datanode = { + hosts = { + for index, server in hcloud_server.datanode : server.name => { + ansible_host = tolist(hcloud_server.datanode[index].network)[0].ip + } + } + vars = { + datanode_network_interface = "enp7s0" + } + } + } +} diff --git a/terraform/examples/wiab-staging-hetzner/retry-selection.auto.tfvars.json b/terraform/examples/wiab-staging-hetzner/retry-selection.auto.tfvars.json new file mode 100644 index 000000000..0d6a504b2 --- /dev/null +++ b/terraform/examples/wiab-staging-hetzner/retry-selection.auto.tfvars.json @@ -0,0 +1,5 @@ +{ + "location": "hel1", + "small_server_type": "cpx22", + "medium_server_type": "cpx42" +} diff --git a/terraform/examples/wiab-staging-hetzner/setup_nodes.yml b/terraform/examples/wiab-staging-hetzner/setup_nodes.yml new file mode 100644 index 000000000..b77b9ec3d --- /dev/null +++ b/terraform/examples/wiab-staging-hetzner/setup_nodes.yml @@ -0,0 +1,235 @@ +--- +- name: Wait for adminhost private SSH + hosts: adminhost + gather_facts: no + tasks: + - name: Wait for SSH on public adminhost + wait_for_connection: + timeout: 300 + delay: 5 + + - name: Wait until adminhost private IP is reachable from public adminhost + wait_for: + host: "{{ hostvars['adminhost_local'].ansible_host }}" + port: 22 + timeout: 300 + delay: 5 + +- name: Setup adminhost with dnsmasq and Docker + hosts: adminhost_local + become: yes + tasks: + + - name: Check if private interface exists + shell: ip addr show {{ private_interface }} + register: interface_check + retries: 60 + delay: 2 + until: interface_check.rc == 0 + + - name: Get private IP address + shell: ip -o -4 addr show {{ private_interface }} | awk '{print $4}' | cut -d'/' -f1 + register: private_ip_result + + - name: Set private IP fact + set_fact: + private_ip: "{{ private_ip_result.stdout }}" + + - name: Disable systemd-resolved + systemd: + name: systemd-resolved + enabled: no + state: stopped + + - name: Remove existing resolv.conf symlink + file: + path: /etc/resolv.conf + state: absent + + - name: Create new resolv.conf with Google DNS + copy: + content: "nameserver 8.8.8.8\n" + dest: /etc/resolv.conf + mode: '0644' + + - name: Update package cache + apt: + update_cache: yes + + - name: Install dnsmasq + apt: + name: dnsmasq + state: present + + - name: Configure dnsmasq + copy: + content: | + port=53 + domain-needed + bogus-priv + bind-interfaces + listen-address={{ private_ip }} + listen-address=127.0.0.1 + dest: /etc/dnsmasq.conf + mode: '0644' + + - name: Restart dnsmasq + systemd: + name: dnsmasq + state: restarted + + - name: Add Docker GPG key + apt_key: + url: https://download.docker.com/linux/ubuntu/gpg + keyring: /usr/share/keyrings/docker-archive-keyring.gpg + state: present + + - name: Add Docker repository + apt_repository: + repo: "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" + state: present + + - name: Install Docker packages + apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + state: present + update_cache: yes + + - name: Configure iptables for DNS + iptables: + chain: OUTPUT + protocol: udp + destination_port: "53" + jump: ACCEPT + state: present + + - name: Configure iptables for DNS input from private network + iptables: + chain: INPUT + protocol: udp + source: 10.0.0.0/8 + destination_port: "53" + jump: ACCEPT + state: present + action: insert + rule_num: 1 + + - name: Start and enable Docker + systemd: + name: docker + state: started + enabled: yes + +- name: Setup default routing for all k8s and datastore nodes + hosts: datanode:kube-node + become: yes + gather_facts: false + + pre_tasks: + - name: Wait for SSH to become reachable + ansible.builtin.wait_for_connection: + timeout: 20 + register: _wait + retries: 6 + delay: 10 + until: _wait is succeeded + + - name: Gather facts after connection is stable + ansible.builtin.setup: + + tasks: + - name: Check if private interface exists + shell: ip addr show {{ private_interface }} + register: interface_check + retries: 60 + delay: 2 + until: interface_check.rc == 0 + + - name: Get existing gateway for private interface + shell: ip route show | grep "{{ private_interface }}" | grep "via" | head -1 | awk '{print $3}' + register: gateway_check + failed_when: false + changed_when: false + + - name: Check if default route already exists + shell: ip route show default + register: default_route_check + failed_when: false + changed_when: false + + - name: Add default route via existing gateway + shell: ip route add default via {{ gateway_check.stdout }} + register: route_result + failed_when: > + route_result.rc != 0 and + "File exists" not in route_result.stderr and + "RTNETLINK answers: File exists" not in route_result.stderr + changed_when: route_result.rc == 0 + when: + - default_route_check.stdout == "" + - gateway_check.stdout != "" + +- name: Configure DNS for all k8s and datastore nodes + hosts: datanode:kube-node + become: yes + + tasks: + - name: Disable systemd-resolved + systemd: + name: systemd-resolved + enabled: no + state: stopped + + - name: Remove existing resolv.conf symlink + file: + path: /etc/resolv.conf + state: absent + + - name: Create new resolv.conf with Adminhost dnsmasq service + copy: + content: "nameserver {{ adminhost_ip }}\n" + dest: /etc/resolv.conf + mode: '0644' + +- name: Add demo user on all nodes + hosts: all + tasks: + - name: Create demo user + ansible.builtin.user: + name: demo + shell: /bin/bash + create_home: yes + state: present + + - name: Add demo user to sudo group + ansible.builtin.user: + name: demo + groups: sudo + append: yes + + - name: Ensure .ssh directory exists for demo user + ansible.builtin.file: + path: /home/demo/.ssh + state: directory + owner: demo + group: demo + mode: '0700' + + - name: Copy root's authorized_keys to demo user + ansible.builtin.copy: + remote_src: yes + src: /root/.ssh/authorized_keys + dest: /home/demo/.ssh/authorized_keys + owner: demo + group: demo + mode: '0600' + + - name: Allow demo user to run sudo without password + ansible.builtin.lineinfile: + path: /etc/sudoers.d/demo + line: 'demo ALL=(ALL) NOPASSWD:ALL' + create: yes + validate: 'visudo -cf %s' diff --git a/terraform/examples/wiab-staging-hetzner/versions.tf b/terraform/examples/wiab-staging-hetzner/versions.tf new file mode 100644 index 000000000..b047ba54e --- /dev/null +++ b/terraform/examples/wiab-staging-hetzner/versions.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + } + } + required_version = "~> 1.1" +} diff --git a/terraform/examples/wire-server-deploy-offline-hetzner/main.tf b/terraform/examples/wire-server-deploy-offline-hetzner/main.tf index 854a24309..657f6ed0c 100644 --- a/terraform/examples/wire-server-deploy-offline-hetzner/main.tf +++ b/terraform/examples/wire-server-deploy-offline-hetzner/main.tf @@ -4,30 +4,101 @@ locals { minio_count = 2 elasticsearch_count = 2 cassandra_count = 3 + postgresql_count = 3 + rabbitmq_count = 3 ssh_keys = [hcloud_ssh_key.adminhost.name] +} - # TODO: IPv6 - disable_network_cfg = <<-EOF - #cloud-config - runcmd: +variable "location" { + description = "Hetzner location selected by the deployment script" + type = string + default = "hel1" +} - # Allow DNS - - iptables -A OUTPUT -o eth0 -p udp --dport 53 -j ACCEPT - - ip6tables -A OUTPUT -o eth0 -p udp --dport 53 -j ACCEPT +variable "small_server_type" { + description = "Server type for cassandra, elasticsearch, minio, postgresql, and rabbitmq selected by the deployment script" + type = string + default = "cx23" +} + +variable "medium_server_type" { + description = "Server type for adminhost, assethost, and kubenode selected by the deployment script" + type = string + default = "cx33" +} + +# Get available server types and locations +data "hcloud_server_types" "available" {} +data "hcloud_datacenters" "available" {} + +# Validate the exact combination requested by the deployment script. +locals { + available_server_type_names = [for st in data.hcloud_server_types.available.server_types : st.name] + available_location_names = [for dc in data.hcloud_datacenters.available.datacenters : dc.location.name] +} - # Allow NTP - - iptables -A OUTPUT -o eth0 -p udp --dport 123 -j ACCEPT - - ip6tables -A OUTPUT -o eth0 -p udp --dport 123 -j ACCEPT +resource "null_resource" "location_validation" { + count = contains(local.available_location_names, var.location) ? 0 : 1 + + provisioner "local-exec" { + command = <<-EOT + echo "DEPLOYMENT FAILED: Requested location is unavailable" + echo "Requested location: ${var.location}" + echo "Available locations: ${join(", ", local.available_location_names)}" + echo "Please check Hetzner Cloud region availability" + exit 1 + EOT + } +} - # Drop all other traffic - - iptables -A OUTPUT -o eth0 -j DROP - - ip6tables -A OUTPUT -o eth0 -j DROP +resource "null_resource" "small_server_type_validation" { + count = contains(local.available_server_type_names, var.small_server_type) ? 0 : 1 + + provisioner "local-exec" { + command = <<-EOT + echo "DEPLOYMENT FAILED: No suitable database server types available" + echo "Requested small server type: ${var.small_server_type}" + echo "Available types: ${join(", ", local.available_server_type_names)}" + echo "Please check server type availability" + exit 1 + EOT + } +} - EOF +resource "null_resource" "medium_server_type_validation" { + count = contains(local.available_server_type_names, var.medium_server_type) ? 0 : 1 + + provisioner "local-exec" { + command = <<-EOT + echo "DEPLOYMENT FAILED: No suitable Kubernetes server types available" + echo "Requested medium server type: ${var.medium_server_type}" + echo "Available types: ${join(", ", local.available_server_type_names)}" + echo "Please check server type availability" + exit 1 + EOT + } } +resource "null_resource" "deployment_info" { + depends_on = [ + null_resource.location_validation, + null_resource.small_server_type_validation, + null_resource.medium_server_type_validation + ] + + provisioner "local-exec" { + command = <<-EOT + echo "VALIDATION PASSED: Deploying WSD default infrastructure" + echo "Location: ${var.location}" + echo "Database server type: ${var.small_server_type}" + echo "Kubernetes server type: ${var.medium_server_type}" + echo "Total instances: ${local.cassandra_count + local.postgresql_count + local.elasticsearch_count + local.minio_count + local.kubenode_count + 2}" + EOT + } +} resource "random_pet" "main" { + depends_on = [null_resource.deployment_info] } resource "hcloud_network" "main" { @@ -58,52 +129,43 @@ resource "hcloud_ssh_key" "adminhost" { # Connected to all other servers. Simulates the admin's "laptop" resource "hcloud_server" "adminhost" { - location = "nbg1" + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] + location = var.location name = "adminhost-${random_pet.adminhost.id}" image = "ubuntu-22.04" ssh_keys = local.ssh_keys - server_type = "cpx41" - user_data = <<-EOF - #cloud-config - apt: - sources: - docker.list: - source: deb [arch=amd64] https://download.docker.com/linux/ubuntu $RELEASE stable - keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 - packages: - - docker-ce - - docker-ce-cli - users: - - name: admin - groups: - - sudo - shell: /bin/bash - ssh_authorized_keys: - - "${tls_private_key.admin.public_key_openssh}" - EOF -} - -resource "hcloud_server_network" "adminhost" { - server_id = hcloud_server.adminhost.id - subnet_id = hcloud_network_subnet.main.id + server_type = var.medium_server_type + network { + network_id = hcloud_network.main.id + ip = "" + } } +# The server hosting all the bootstrap assets resource "random_pet" "assethost" { } -# The server hosting all the bootstrap assets resource "hcloud_server" "assethost" { - location = "nbg1" + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] + location = var.location name = "assethost-${random_pet.assethost.id}" image = "ubuntu-22.04" ssh_keys = local.ssh_keys - server_type = "cpx41" - user_data = local.disable_network_cfg -} - -resource "hcloud_server_network" "assethost" { - server_id = hcloud_server.assethost.id - subnet_id = hcloud_network_subnet.main.id + server_type = var.medium_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } } resource "random_pet" "kubenode" { @@ -111,80 +173,147 @@ resource "random_pet" "kubenode" { } resource "hcloud_server" "kubenode" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] count = local.kubenode_count - location = "nbg1" + location = var.location name = "kubenode-${random_pet.kubenode[count.index].id}" image = "ubuntu-22.04" ssh_keys = local.ssh_keys - server_type = "cpx41" - user_data = local.disable_network_cfg -} - -resource "hcloud_server_network" "kubenode" { - count = local.kubenode_count - server_id = hcloud_server.kubenode[count.index].id - subnet_id = hcloud_network_subnet.main.id + server_type = var.medium_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } } - resource "random_pet" "cassandra" { count = local.cassandra_count } resource "hcloud_server" "cassandra" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] count = local.cassandra_count - location = "nbg1" + location = var.location name = "cassandra-${random_pet.cassandra[count.index].id}" image = "ubuntu-22.04" ssh_keys = local.ssh_keys - server_type = "cx22" - # user_data = local.disable_network_cfg -} - -resource "hcloud_server_network" "cassandra" { - count = local.cassandra_count - server_id = hcloud_server.cassandra[count.index].id - subnet_id = hcloud_network_subnet.main.id + server_type = var.small_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } } - resource "random_pet" "elasticsearch" { count = local.elasticsearch_count } resource "hcloud_server" "elasticsearch" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] count = local.elasticsearch_count - location = "nbg1" + location = var.location name = "elasticsearch-${random_pet.elasticsearch[count.index].id}" image = "ubuntu-22.04" ssh_keys = local.ssh_keys - server_type = "cx22" - # user_data = local.disable_network_cfg + server_type = var.small_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } } -resource "hcloud_server_network" "elasticsearch" { - count = local.elasticsearch_count - server_id = hcloud_server.elasticsearch[count.index].id - subnet_id = hcloud_network_subnet.main.id -} - - resource "random_pet" "minio" { count = local.minio_count } resource "hcloud_server" "minio" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] count = local.minio_count - location = "nbg1" + location = var.location name = "minio-${random_pet.minio[count.index].id}" image = "ubuntu-22.04" ssh_keys = local.ssh_keys - server_type = "cx22" - # user_data = local.disable_network_cfg + server_type = var.small_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } +} + +resource "random_pet" "postgresql" { + count = local.postgresql_count } -resource "hcloud_server_network" "minio" { - count = local.minio_count - server_id = hcloud_server.minio[count.index].id - subnet_id = hcloud_network_subnet.main.id +resource "hcloud_server" "postgresql" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] + count = local.postgresql_count + location = var.location + name = "postgresql-${random_pet.postgresql[count.index].id}" + image = "ubuntu-22.04" + ssh_keys = local.ssh_keys + server_type = var.small_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } +} + +resource "random_pet" "rabbitmq" { + count = local.rabbitmq_count +} + +resource "hcloud_server" "rabbitmq" { + depends_on = [ + null_resource.deployment_info, + hcloud_network_subnet.main + ] + count = local.rabbitmq_count + location = var.location + name = "rabbitmq-${random_pet.rabbitmq[count.index].id}" + image = "ubuntu-22.04" + ssh_keys = local.ssh_keys + server_type = var.small_server_type + public_net { + ipv4_enabled = false + ipv6_enabled = false + } + network { + network_id = hcloud_network.main.id + ip = "" + } } diff --git a/terraform/examples/wire-server-deploy-offline-hetzner/outputs.tf b/terraform/examples/wire-server-deploy-offline-hetzner/outputs.tf index f951b28e4..e7cc5fd21 100644 --- a/terraform/examples/wire-server-deploy-offline-hetzner/outputs.tf +++ b/terraform/examples/wire-server-deploy-offline-hetzner/outputs.tf @@ -1,29 +1,89 @@ output "ssh_private_key" { sensitive = true - value = tls_private_key.admin.private_key_pem + value = tls_private_key.admin.private_key_pem } + +output "selected_server_types" { + description = "Server types selected for the current deployment attempt" + value = { + small_server_type = var.small_server_type + medium_server_type = var.medium_server_type + } +} + +output "selected_location" { + description = "Location selected for the current deployment attempt" + value = var.location +} + +output "resource_fallback_info" { + description = "Information about the requested deployment combination and its availability" + value = { + requested_location = var.location + available_locations = local.available_location_names + selected_location = var.location + + requested_small_type = var.small_server_type + selected_small_type = var.small_server_type + + requested_medium_type = var.medium_server_type + selected_medium_type = var.medium_server_type + + available_server_types = local.available_server_type_names + } +} + output "adminhost" { sensitive = true - value = hcloud_server.adminhost.ipv4_address + value = hcloud_server.adminhost.ipv4_address } # output format that a static inventory file expects output "static-inventory" { sensitive = true value = { - - assethost = { - hosts = { - "assethost" = { - ansible_host = hcloud_server_network.assethost.ip - ansible_user = "root" - } + all = { + vars = { + ansible_user = "root" + private_interface = "enp7s0" + adminhost_ip = tolist(hcloud_server.adminhost.network)[0].ip } } adminhost = { hosts = { "adminhost" = { ansible_host = hcloud_server.adminhost.ipv4_address - ansible_user = "root" + } + } + vars = { + ansible_ssh_common_args = "-o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s -o BatchMode=yes -o ConnectionAttempts=10 -o ServerAliveInterval=60 -o ServerAliveCountMax=3" + } + } + private = { + children = { + adminhost_local = {} + assethost = {} + "kube-node" = {} + cassandra = {} + elasticsearch = {} + minio = {} + postgresql = {} + rmq-cluster = {} + } + vars = { + ansible_ssh_common_args = "-o ProxyCommand=\"ssh -i ssh_private_key -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -W %h:%p -q root@${hcloud_server.adminhost.ipv4_address}\" -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s -o BatchMode=yes -o ConnectionAttempts=10 -o ServerAliveInterval=60 -o ServerAliveCountMax=3" + } + } + adminhost_local = { + hosts = { + "adminhost_local" = { + ansible_host = tolist(hcloud_server.adminhost.network)[0].ip + } + } + } + assethost = { + hosts = { + "assethost" = { + ansible_host = tolist(hcloud_server.assethost.network)[0].ip } } } @@ -36,9 +96,8 @@ output "static-inventory" { kube-node = { hosts = { for index, server in hcloud_server.kubenode : server.name => { - ansible_host = hcloud_server_network.kubenode[index].ip - ip = hcloud_server_network.kubenode[index].ip - ansible_user = "root" + ansible_host = tolist(hcloud_server.kubenode[index].network)[0].ip + ip = tolist(hcloud_server.kubenode[index].network)[0].ip etcd_member_name = server.name } } @@ -54,18 +113,18 @@ output "static-inventory" { calico_mtu = 1450 calico_veth_mtu = 1430 # NOTE: relax handling a list with more than 3 items; required on Hetzner - docker_dns_servers_strict: false + docker_dns_servers_strict = false + upstream_dns_servers = [tolist(hcloud_server.adminhost.network)[0].ip] } } cassandra = { hosts = { for index, server in hcloud_server.cassandra : server.name => { - ansible_host = hcloud_server_network.cassandra[index].ip - ansible_user = "root" + ansible_host = tolist(hcloud_server.cassandra[index].network)[0].ip } } vars = { - cassandra_network_interface = "eth0" + cassandra_network_interface = "enp7s0" } } cassandra_seed = { @@ -74,12 +133,11 @@ output "static-inventory" { elasticsearch = { hosts = { for index, server in hcloud_server.elasticsearch : server.name => { - ansible_host = hcloud_server_network.elasticsearch[index].ip - ansible_user = "root" + ansible_host = tolist(hcloud_server.elasticsearch[index].network)[0].ip } } vars = { - elasticsearch_network_interface = "eth0" + elasticsearch_network_interface = "enp7s0" } } elasticsearch_master = { @@ -88,12 +146,59 @@ output "static-inventory" { minio = { hosts = { for index, server in hcloud_server.minio : server.name => { - ansible_host = hcloud_server_network.minio[index].ip - ansible_user = "root" + ansible_host = tolist(hcloud_server.minio[index].network)[0].ip + } + } + vars = { + minio_network_interface = "enp7s0" + } + } + postgresql = { + hosts = { + for index, server in hcloud_server.postgresql : "postgresql${index + 1}" => { + ansible_host = tolist(hcloud_server.postgresql[index].network)[0].ip + } + } + vars = { + wire_dbname = "wire-server" + postgresql_network_interface = "enp7s0" + repmgr_node_config = { + postgresql1 = { + node_id = 1 + priority = 150 + role = "primary" + } + postgresql2 = { + node_id = 2 + priority = 100 + role = "standby" + } + postgresql3 = { + node_id = 3 + priority = 50 + role = "standby" + } + } + } + } + postgresql_rw = { + hosts = { "postgresql1" = {} } + } + postgresql_ro = { + hosts = { "postgresql2" = {}, + "postgresql3" = {} } + } + rmq-cluster = { + hosts = { + # host names here must match each node's actual hostname + for index, server in hcloud_server.rabbitmq : server.name => { + ansible_host = tolist(hcloud_server.rabbitmq[index].network)[0].ip } } vars = { - minio_network_interface = "eth0" + # host name here must match each node's actual hostname + rabbitmq_cluster_master = hcloud_server.rabbitmq[0].name + rabbitmq_network_interface = "enp7s0" } } } diff --git a/terraform/examples/wire-server-deploy-offline-hetzner/setup_nodes.yml b/terraform/examples/wire-server-deploy-offline-hetzner/setup_nodes.yml new file mode 100644 index 000000000..316e2d97d --- /dev/null +++ b/terraform/examples/wire-server-deploy-offline-hetzner/setup_nodes.yml @@ -0,0 +1,205 @@ +--- +- name: Setup adminhost with dnsmasq and Docker + hosts: adminhost_local + become: yes + tasks: + - name: Check if private interface exists + shell: ip addr show {{ private_interface }} + register: interface_check + retries: 60 + delay: 2 + until: interface_check.rc == 0 + + - name: Get private IP address + shell: ip -o -4 addr show {{ private_interface }} | awk '{print $4}' | cut -d'/' -f1 + register: private_ip_result + + - name: Set private IP fact + set_fact: + private_ip: "{{ private_ip_result.stdout }}" + + - name: Disable systemd-resolved + systemd: + name: systemd-resolved + enabled: no + state: stopped + + - name: Remove existing resolv.conf symlink + file: + path: /etc/resolv.conf + state: absent + + - name: Create new resolv.conf with Google DNS + copy: + content: "nameserver 8.8.8.8\n" + dest: /etc/resolv.conf + mode: '0644' + + - name: Update package cache + apt: + update_cache: yes + + - name: Install dnsmasq + apt: + name: dnsmasq + state: present + + - name: Configure dnsmasq + copy: + content: | + port=53 + domain-needed + bogus-priv + bind-interfaces + listen-address={{ private_ip }} + listen-address=127.0.0.1 + dest: /etc/dnsmasq.conf + mode: '0644' + + - name: Restart dnsmasq + systemd: + name: dnsmasq + state: restarted + + - name: Add Docker GPG key + apt_key: + url: https://download.docker.com/linux/ubuntu/gpg + keyring: /usr/share/keyrings/docker-archive-keyring.gpg + state: present + + - name: Add Docker repository + apt_repository: + repo: "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" + state: present + + - name: Install Docker packages + apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + state: present + update_cache: yes + + - name: Configure iptables for DNS + iptables: + chain: OUTPUT + protocol: udp + destination_port: "53" + jump: ACCEPT + state: present + + - name: Configure iptables for DNS input from private network + iptables: + chain: INPUT + protocol: udp + source: 10.0.0.0/8 + destination_port: "53" + jump: ACCEPT + state: present + action: insert + rule_num: 1 + + - name: Start and enable Docker + systemd: + name: docker + state: started + enabled: yes + +- name: Setup default routing for all k8s and datastore nodes + hosts: cassandra:elasticsearch:minio:postgresql:kube-node:rmq-cluster + become: yes + + tasks: + - name: Check if private interface exists + shell: ip addr show {{ private_interface }} + register: interface_check + retries: 60 + delay: 2 + until: interface_check.rc == 0 + + - name: Get existing gateway for private interface + shell: ip route show | grep "{{ private_interface }}" | grep "via" | head -1 | awk '{print $3}' + register: gateway_check + failed_when: false + changed_when: false + + - name: Check if default route already exists + shell: ip route show default + register: default_route_check + failed_when: false + changed_when: false + + - name: Add default route via existing gateway + shell: ip route add default via {{ gateway_check.stdout }} + register: route_result + failed_when: > + route_result.rc != 0 and + "File exists" not in route_result.stderr and + "RTNETLINK answers: File exists" not in route_result.stderr + changed_when: route_result.rc == 0 + when: + - default_route_check.stdout == "" + - gateway_check.stdout != "" + +- name: Configure DNS for all k8s and datastore nodes + hosts: cassandra:elasticsearch:minio:postgresql:kube-node:rmq-cluster + become: yes + + tasks: + - name: Disable systemd-resolved + systemd: + name: systemd-resolved + enabled: no + state: stopped + + - name: Remove existing resolv.conf symlink + file: + path: /etc/resolv.conf + state: absent + + - name: Create new resolv.conf with Adminhost dnsmasq service + copy: + content: "nameserver {{ adminhost_ip }}\n" + dest: /etc/resolv.conf + mode: '0644' + +- name: Add demo user on all nodes + hosts: all + tasks: + - name: Create demo user + ansible.builtin.user: + name: demo + shell: /bin/bash + create_home: yes + state: present + + - name: Add demo user to sudo group + ansible.builtin.user: + name: demo + groups: sudo + append: yes + + - name: Ensure .ssh directory exists for demo user + ansible.builtin.file: + path: /home/demo/.ssh + state: directory + owner: demo + group: demo + mode: '0700' + + - name: Copy root's authorized_keys to demo user + ansible.builtin.copy: + remote_src: yes + src: /root/.ssh/authorized_keys + dest: /home/demo/.ssh/authorized_keys + owner: demo + group: demo + mode: '0600' + + - name: Allow demo user to run sudo without password + ansible.builtin.lineinfile: + path: /etc/sudoers.d/demo + line: 'demo ALL=(ALL) NOPASSWD:ALL' + create: yes + validate: 'visudo -cf %s' From 2f5c5a06ad17409e2bd2412f3593d92c2ab955f7 Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Tue, 11 Aug 2026 21:58:29 +0200 Subject: [PATCH 02/10] fix WPB-27900: Changed: synced the workflows from master branch --- .github/workflows/changelog-verify.yml | 105 ++++++++ .github/workflows/custom-artifact.yml | 72 ------ .github/workflows/deploy-wiab.yml | 37 --- .github/workflows/offline-min.yml | 51 ---- .github/workflows/offline.yml | 251 +++++++++++++++++-- changelog.d/3-deploy-builds/sync-with-master | 1 + 6 files changed, 334 insertions(+), 183 deletions(-) create mode 100644 .github/workflows/changelog-verify.yml delete mode 100644 .github/workflows/custom-artifact.yml delete mode 100644 .github/workflows/deploy-wiab.yml delete mode 100644 .github/workflows/offline-min.yml diff --git a/.github/workflows/changelog-verify.yml b/.github/workflows/changelog-verify.yml new file mode 100644 index 000000000..a3e8ce3a6 --- /dev/null +++ b/.github/workflows/changelog-verify.yml @@ -0,0 +1,105 @@ +name: Changelog verification +on: + pull_request: + branches: ["**"] + push: + branches: ["master"] + +permissions: + contents: read + +jobs: + commitlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + + - name: Check for entry in changelog.d changes in the PR + run: | + set -x + + # Set BASE_SHA and HEAD_SHA based on event type + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + BASE_SHA=${{ github.event.pull_request.base.sha }} + HEAD_SHA=${{ github.event.pull_request.head.sha }} + + # Use 3-dot syntax so we only see changes introduced by this PR + CHANGED_FILES=$(git diff --name-status "$BASE_SHA...$HEAD_SHA" -- changelog.d/ | awk '$1 ~ /^[AM]/ {print $2}' || true) + DELETED_FILES=$(git diff --name-status "$BASE_SHA...$HEAD_SHA" -- changelog.d/ | awk '$1 ~ /^D/ {print $2}' || true) + CHANGELOG_MODIFIED=$(git diff --name-only "$BASE_SHA...$HEAD_SHA" -- CHANGELOG.md | grep -vE "^$" || true) + else + # For push events, compare with the previous commit + HEAD_SHA=${{ github.sha }} + BASE_SHA=$(git rev-parse HEAD~1) + CHANGED_FILES=$(git diff --name-status "$BASE_SHA" "$HEAD_SHA" -- changelog.d/ | awk '$1 ~ /^[AM]/ {print $2}' || true) + DELETED_FILES=$(git diff --name-status "$BASE_SHA" "$HEAD_SHA" -- changelog.d/ | awk '$1 ~ /^D/ {print $2}' || true) + CHANGELOG_MODIFIED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- CHANGELOG.md | grep -vE "^$" || true) + fi + + echo "BASE_SHA: $BASE_SHA" + echo "HEAD_SHA: $HEAD_SHA" + echo "CHANGELOG_MODIFIED: ${CHANGELOG_MODIFIED:-false}" + echo "CHANGED_FILES:" + echo "$CHANGED_FILES" + if [ -n "$DELETED_FILES" ]; then + echo "DELETED_CHANGELOG_FILES:" + echo "$DELETED_FILES" + fi + + # Check if commit is by zebot with wire-build update message + COMMIT_AUTHOR=$(git log --format="%an" -1 $HEAD_SHA) + COMMIT_MESSAGE=$(git log --format="%s" -1 $HEAD_SHA) + + if [[ "$COMMIT_AUTHOR" == "Zebot" && "$COMMIT_MESSAGE" == "Update wire-build to version"* ]]; then + echo "Skipping changelog check for zebot wire-build update commit" + echo "Author: $COMMIT_AUTHOR" + echo "Message: $COMMIT_MESSAGE" + exit 0 + fi + + PATTERN='^(Added|Changed|Deprecated|Removed|Fixed|Security): .+' + LINE_FOUND=false + ALLOW_RELEASE_CLEANUP=false + + if [ -z "$CHANGED_FILES" ]; then + if [ -n "$CHANGELOG_MODIFIED" ]; then + if [ -n "$DELETED_FILES" ]; then + echo "Release detected via CHANGELOG.md update; deleted changelog.d/ entries are allowed." + else + echo "Release detected via CHANGELOG.md update; no changelog.d/ deletions found in this compare range." + fi + ALLOW_RELEASE_CLEANUP=true + else + echo "No files changed in changelog.d/ for this ${GITHUB_EVENT_NAME:-event}." + echo "Every PR must add or modify at least one changelog.d/ entry." + exit 1 + fi + fi + + for file in $CHANGED_FILES; do + if [ ! -f "$file" ]; then + echo "Skipping missing changelog file: $file" + continue + fi + while IFS= read -r line; do + if [[ -z "$line" ]]; then + continue + fi + if echo "$line" | grep -qE "$PATTERN"; then + LINE_FOUND=true + echo "Valid pattern found in file $file: '$line'" + else + echo "Invalid format in file $file: '$line'" + exit 1 + fi + done < "$file" + done + + if [ "$LINE_FOUND" = false ] && [ "$ALLOW_RELEASE_CLEANUP" = false ]; then + echo "No valid lines found in changelog.d/ files. Ensure there is a newline at the end of files." + echo "Please read more policies about changelog at https://keepachangelog.com/en/1.1.0" + exit 1 + fi diff --git a/.github/workflows/custom-artifact.yml b/.github/workflows/custom-artifact.yml deleted file mode 100644 index 293033cab..000000000 --- a/.github/workflows/custom-artifact.yml +++ /dev/null @@ -1,72 +0,0 @@ -on: - push: - branches: [master, develop] - tags: [ v* ] - workflow_dispatch: -jobs: - offline: - name: Prepare custom offline package # Do not change this name, it is used to trigger deploy-wiab workflow - # Useful to skip expensive CI when writing docs - if: "!contains(github.event.head_commit.message, 'skip ci')" - runs-on: - group: wire-server-deploy - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: cachix/install-nix-action@v27 - - uses: cachix/cachix-action@v15 - with: - name: wire-server - signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}" - - - name: Install nix environment - run: nix-env -f default.nix -iA env - - - name: Run offline build - run: ./offline/ci.sh HELM_CHART_EXCLUDE_LIST=elasticsearch-curator,fluent-bit,kibana,redis-cluster,inbucket,aws-ingress,backoffice,calling-test,nginx-ingress-controller - env: - GPG_PRIVATE_KEY: '${{ secrets.GPG_PRIVATE_KEY }}' - DOCKER_LOGIN: '${{ secrets.DOCKER_LOGIN }}' - - - name: Get upload name - id: upload_name - run: | - # FIXME: Tag with a nice release name using the github tag... - # SOURCE_TAG=${GITHUB_REF#refs/tags/} - echo ::set-output name=UPLOAD_NAME::$GITHUB_SHA-custom - # echo ::set-output name=UPLOAD_NAME::${SOURCE_TAG:-$GITHUB_SHA} - - name: Copy assets tarball to S3 - run: | - aws s3 cp assets.tgz s3://public.wire.com/artifacts/wire-server-deploy-static-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz - echo "Uploaded to: https://s3-$AWS_REGION.amazonaws.com/public.wire.com/artifacts/wire-server-deploy-static-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz" - env: - AWS_ACCESS_KEY_ID: '${{ secrets.AWS_ACCESS_KEY_ID }}' - AWS_SECRET_ACCESS_KEY: '${{ secrets.AWS_SECRET_ACCESS_KEY }}' - AWS_REGION: "eu-west-1" - - - name: Build and upload wire-server-deploy container - run: | - container_image=$(nix-build --no-out-link -A container) - skopeo copy --retry-times 10 --dest-creds "$DOCKER_LOGIN" \ - docker-archive:"$container_image" \ - "docker://quay.io/wire/wire-server-deploy:${{ steps.upload_name.outputs.UPLOAD_NAME }}" - env: - DOCKER_LOGIN: '${{ secrets.DOCKER_LOGIN }}' - - # Set output for deploy-wiab workflow to start - - name: Set output to trigger dependent workflow - if: success() - run: echo "::set-output name=trigger_next_workflow::true" - - - name: Deploy offline environment to hetzner - run: | - ./offline/cd.sh - env: - HCLOUD_TOKEN: '${{ secrets.HCLOUD_TOKEN }}' - - - name: Clean up hetzner environment; just in case - if: always() - run: (cd terraform/examples/wire-server-deploy-offline-hetzner ; terraform init && terraform destroy -auto-approve) - env: - HCLOUD_TOKEN: '${{ secrets.HCLOUD_TOKEN }}' diff --git a/.github/workflows/deploy-wiab.yml b/.github/workflows/deploy-wiab.yml deleted file mode 100644 index a7c74e289..000000000 --- a/.github/workflows/deploy-wiab.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Deploy on Hetzner WIAB setup -on: - workflow_run: - workflows: ["Prepare custom offline package"] - types: - - completed - -jobs: - deploy: - runs-on: ubuntu-latest - concurrency: - group: autodeploy-script - cancel-in-progress: false - - steps: - # Step 1: Checkout the repository code - - name: Checkout code - uses: actions/checkout@v3 - - # Step 2: Set up SSH key for remote access - - name: Set up SSH key - uses: webfactory/ssh-agent@v0.5.3 - with: - ssh-private-key: ${{ secrets.WIAB_PRIVATE_SSH_KEY }} - - # Step 3: Get the latest commit SHA, for the artifact - - name: Get latest commit SHA - id: get_commit_sha - run: | - COMMIT_SHA=$(git rev-parse HEAD) - echo "commit_sha=$COMMIT_SHA" >> $GITHUB_ENV - - # Step 4: Run the autodeploy script - - name: Run Auto Deploy Script - run: | - cd bin - ./autodeploy.sh --artifact-hash ${{ env.COMMIT_SHA }} --target-domain wiab-test-box.wire.link --force-redeploy diff --git a/.github/workflows/offline-min.yml b/.github/workflows/offline-min.yml deleted file mode 100644 index b1d979e4a..000000000 --- a/.github/workflows/offline-min.yml +++ /dev/null @@ -1,51 +0,0 @@ -on: - push: - branches: [5.14*] - paths-ignore: - - '*.md' - - '**/*.md' -jobs: - offline: - name: Prepare min offline package - # Useful to skip expensive CI when writing docs - if: "!contains(github.event.head_commit.message, 'skip ci')" - runs-on: - group: wire-server-deploy - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: cachix/install-nix-action@v27 - - uses: cachix/cachix-action@v15 - with: - name: wire-server - signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}" - - - name: Install nix environment - run: nix-env -f default.nix -iA env - - - name: Get upload name - id: upload_name - run: | - # FIXME: Tag with a nice release name using the github tag... - # SOURCE_TAG=${GITHUB_REF#refs/tags/} - echo ::set-output name=UPLOAD_NAME::$GITHUB_SHA - # echo ::set-output name=UPLOAD_NAME::${SOURCE_TAG:-$GITHUB_SHA} - - - name: Process the min profile build - run: ./offline/min-build/build.sh - env: - GPG_PRIVATE_KEY: '${{ secrets.GPG_PRIVATE_KEY }}' - DOCKER_LOGIN: '${{ secrets.DOCKER_LOGIN }}' - - - name: Copy min build assets tarball to S3 - run: | - # Upload tarball for each profile by specifying their OUTPUT_TAR path - aws s3 cp offline/min-build/output/assets.tgz s3://public.wire.com/artifacts/wire-server-deploy-static-min-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz - echo "Uploaded to: https://s3-$AWS_REGION.amazonaws.com/public.wire.com/artifacts/wire-server-deploy-static-min-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz" - # remove the archives from the build to optimize the space on the server - rm -rf offline/min-build/output/* - env: - AWS_ACCESS_KEY_ID: '${{ secrets.AWS_ACCESS_KEY_ID }}' - AWS_SECRET_ACCESS_KEY: '${{ secrets.AWS_SECRET_ACCESS_KEY }}' - AWS_REGION: "eu-west-1" diff --git a/.github/workflows/offline.yml b/.github/workflows/offline.yml index 25d6a2523..0df59627c 100644 --- a/.github/workflows/offline.yml +++ b/.github/workflows/offline.yml @@ -1,16 +1,47 @@ +# Offline Build Workflow +# +# This workflow builds offline deployment artifacts for different profiles: +# - default: Production deployment (includes external charts, ansible, terraform) +# - build-wiab-staging: Wire-in-a-box (wiab-stag) a production like deployment (includes external charts, ansible, terraform) +# - wiab-dev: Wire-in-a-box dev deployment (includes databases-ephemeral) +# - min: Minimal deployment +# +# Build Optimization via PR Labels: +# - No label: No builds run (must add label to trigger builds) +# - 'build-default': Builds only default profile +# - 'build-dev': Builds only demo profile +# - 'build-wiab-staging' - Builds only wiab-staging profile +# - 'build-min': Builds only min profile +# - 'build-all': Explicitly builds all profiles (useful for workflow changes) +# +# Push to master/develop: Always builds all profiles regardless of labels +# on: push: - branches: [master, "5.14"] - tags: [ v* ] + branches: ["**"] + tags: [v*] + paths-ignore: + - "*.md" + - "**/*.md" pull_request: - branches: [master, "5.14"] + types: [synchronize, reopened, labeled] + branches: ["**"] + paths-ignore: + - "*.md" + - "**/*.md" jobs: - offline: - name: Prepare offline package - # Useful to skip expensive CI when writing docs - if: "!contains(github.event.head_commit.message, 'skip ci')" + # Build default profile and create local assets + build-default: + name: Build default profile + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/master') || + contains(github.event.pull_request.labels.*.name, 'build-all') || + contains(github.event.pull_request.labels.*.name, 'build-default') || + contains(github.event.pull_request.labels.*.name, 'build-wiab-staging') runs-on: group: wire-server-deploy + outputs: + upload_name: ${{ steps.upload_name.outputs.UPLOAD_NAME }} steps: - uses: actions/checkout@v2 with: @@ -26,55 +57,229 @@ jobs: - name: Get upload name id: upload_name - run: | - # FIXME: Tag with a nice release name using the github tag... - # SOURCE_TAG=${GITHUB_REF#refs/tags/} - echo ::set-output name=UPLOAD_NAME::$GITHUB_SHA - # echo ::set-output name=UPLOAD_NAME::${SOURCE_TAG:-$GITHUB_SHA} + run: echo "UPLOAD_NAME=$GITHUB_SHA" >> $GITHUB_OUTPUT - # deafult profile build + # default profile build - name: Process the default profile build run: ./offline/default-build/build.sh env: GPG_PRIVATE_KEY: '${{ secrets.GPG_PRIVATE_KEY }}' DOCKER_LOGIN: '${{ secrets.DOCKER_LOGIN }}' - - name: Copy default build assets tarball to S3 and clean up + - name: Copy default build assets tarball to S3 run: | - # Upload tarball for each profile by specifying their OUTPUT_TAR path aws s3 cp offline/default-build/output/assets.tgz s3://public.wire.com/artifacts/wire-server-deploy-static-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz echo "Uploaded to: https://s3-$AWS_REGION.amazonaws.com/public.wire.com/artifacts/wire-server-deploy-static-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz" - # remove the archives from the build to optimize the space on the server - # rm offline/default-build/output/containers-helm.tar env: AWS_ACCESS_KEY_ID: '${{ secrets.AWS_ACCESS_KEY_ID }}' AWS_SECRET_ACCESS_KEY: '${{ secrets.AWS_SECRET_ACCESS_KEY }}' AWS_REGION: "eu-west-1" + verify-default: + name: Verify default profile + needs: build-default + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/master') || + contains(github.event.pull_request.labels.*.name, 'build-all') || + contains(github.event.pull_request.labels.*.name, 'build-default') + runs-on: + group: wire-server-deploy + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: cachix/install-nix-action@v27 + - uses: cachix/cachix-action@v15 + with: + name: wire-server + signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}" + + - name: Install nix environment + run: nix-env -f default.nix -iA env + + - name: Install terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: "^1.3.7" + terraform_wrapper: false + + - name: Deploy offline environment to hetzner + run: ./offline/cd.sh + env: + HCLOUD_TOKEN: '${{ secrets.HCLOUD_TOKEN }}' + + - name: Clean up hetzner environment; just in case + if: always() + run: (cd terraform/examples/wire-server-deploy-offline-hetzner ; terraform init && terraform destroy -auto-approve) + env: + HCLOUD_TOKEN: '${{ secrets.HCLOUD_TOKEN }}' + + # verify wiab-staging profile + verify-wiab-staging: + name: Verify wiab staging profile + needs: build-default + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/master') || + contains(github.event.pull_request.labels.*.name, 'build-all') || + contains(github.event.pull_request.labels.*.name, 'build-wiab-staging') + runs-on: + group: wire-server-deploy + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: cachix/install-nix-action@v27 + - uses: cachix/cachix-action@v15 + with: + name: wire-server + signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}" + + - name: Install nix environment + run: nix-env -f default.nix -iA env + + - name: Install terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: "^1.3.7" + terraform_wrapper: false + + - name: Deploy offline wiab-staging environment to hetzner + run: ./offline/cd_staging.sh + env: + HCLOUD_TOKEN: '${{ secrets.HCLOUD_TOKEN }}' + + - name: Clean up hetzner wiab-staging environment; just in case + if: always() + run: (cd terraform/examples/wiab-staging-hetzner ; terraform init && terraform destroy -auto-approve) + env: + HCLOUD_TOKEN: '${{ secrets.HCLOUD_TOKEN }}' + + # Build container in parallel + build-container: + name: Build container + needs: build-default + runs-on: + group: wire-server-deploy + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: cachix/install-nix-action@v27 + - uses: cachix/cachix-action@v15 + with: + name: wire-server + signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}" + - name: Build and upload wire-server-deploy container run: | container_image=$(nix-build --no-out-link -A container) skopeo copy --retry-times 10 --dest-creds "$DOCKER_LOGIN" \ docker-archive:"$container_image" \ - "docker://quay.io/wire/wire-server-deploy:${{ steps.upload_name.outputs.UPLOAD_NAME }}" + "docker://quay.io/wire/wire-server-deploy:${{ needs.build-default.outputs.upload_name }}" env: DOCKER_LOGIN: '${{ secrets.DOCKER_LOGIN }}' + # Build dev profile + build-dev: + name: Build dev profile + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/master') || + contains(github.event.pull_request.labels.*.name, 'build-all') || + contains(github.event.pull_request.labels.*.name, 'build-dev') + runs-on: + group: wire-server-deploy + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: cachix/install-nix-action@v27 + - uses: cachix/cachix-action@v15 + with: + name: wire-server + signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}" + + - name: Install nix environment + run: nix-env -f default.nix -iA env + + - name: Get upload name + id: upload_name + run: echo "UPLOAD_NAME=$GITHUB_SHA" >> $GITHUB_OUTPUT + + - name: Process the dev profile build + run: ./offline/demo-build/build.sh + env: + GPG_PRIVATE_KEY: "${{ secrets.GPG_PRIVATE_KEY }}" + DOCKER_LOGIN: "${{ secrets.DOCKER_LOGIN }}" + + - name: Copy dev build assets tarball to S3 + run: | + aws s3 cp offline/demo-build/output/assets.tgz s3://public.wire.com/artifacts/wire-server-deploy-static-demo-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz + echo "Uploaded to: https://s3-$AWS_REGION.amazonaws.com/public.wire.com/artifacts/wire-server-deploy-static-demo-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz" + env: + AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}" + AWS_REGION: "eu-west-1" + - name: Install terraform uses: hashicorp/setup-terraform@v3 with: terraform_version: "^1.3.7" terraform_wrapper: false - - name: Deploy offline environment to hetzner - run: | - ./offline/cd.sh + - name: Deploy offline demo-wiab environment to hetzner + run: ./offline/cd_demo.sh env: HCLOUD_TOKEN: '${{ secrets.HCLOUD_TOKEN }}' - - name: Clean up hetzner environment; just in case + - name: Clean up hetzner wiab environment; just in case if: always() - run: (cd terraform/examples/wire-server-deploy-offline-hetzner ; terraform init && terraform destroy -auto-approve) + run: (cd terraform/examples/wiab-demo-hetzner ; terraform init && terraform destroy -auto-approve) env: HCLOUD_TOKEN: '${{ secrets.HCLOUD_TOKEN }}' + - name: Cleanup dev build assets + run: rm -rf offline/demo-build/output/ + + # Build min profile + build-min: + name: Build min profile + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/master') || + contains(github.event.pull_request.labels.*.name, 'build-all') || + contains(github.event.pull_request.labels.*.name, 'build-min') + runs-on: + group: wire-server-deploy + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: cachix/install-nix-action@v27 + - uses: cachix/cachix-action@v15 + with: + name: wire-server + signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}" + + - name: Install nix environment + run: nix-env -f default.nix -iA env + + - name: Get upload name + id: upload_name + run: echo "UPLOAD_NAME=$GITHUB_SHA" >> $GITHUB_OUTPUT + + - name: Process the min profile build + run: ./offline/min-build/build.sh + env: + GPG_PRIVATE_KEY: "${{ secrets.GPG_PRIVATE_KEY }}" + DOCKER_LOGIN: "${{ secrets.DOCKER_LOGIN }}" + + - name: Copy min build assets tarball to S3 + run: | + aws s3 cp offline/min-build/output/assets.tgz s3://public.wire.com/artifacts/wire-server-deploy-static-min-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz + echo "Uploaded to: https://s3-$AWS_REGION.amazonaws.com/public.wire.com/artifacts/wire-server-deploy-static-min-${{ steps.upload_name.outputs.UPLOAD_NAME }}.tgz" + env: + AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}" + AWS_REGION: "eu-west-1" + + - name: Cleanup min build assets + run: rm -rf offline/min-build/output/ diff --git a/changelog.d/3-deploy-builds/sync-with-master b/changelog.d/3-deploy-builds/sync-with-master index 4a132ec87..a302e15de 100644 --- a/changelog.d/3-deploy-builds/sync-with-master +++ b/changelog.d/3-deploy-builds/sync-with-master @@ -1 +1,2 @@ Added: new CD infrastructure definition from master here +Changed: synced the workflows from master branch From 03241fe81f6ba0d49ebeb8fd88944ff402b7ec66 Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Tue, 11 Aug 2026 22:02:07 +0200 Subject: [PATCH 03/10] fix WPB-27900: Dump version information post creating the artifacts on the stdout --- changelog.d/2-wire-builds/dump-version-info | 1 + nix/scripts/create-container-dump.sh | 88 +++++++++++++++------ offline/default-build/build.sh | 41 +++++++--- offline/demo-build/build.sh | 71 ++++++++--------- offline/min-build/build.sh | 26 ++++-- offline/tasks/proc_pull_charts.sh | 8 +- 6 files changed, 157 insertions(+), 78 deletions(-) create mode 100644 changelog.d/2-wire-builds/dump-version-info diff --git a/changelog.d/2-wire-builds/dump-version-info b/changelog.d/2-wire-builds/dump-version-info new file mode 100644 index 000000000..562586784 --- /dev/null +++ b/changelog.d/2-wire-builds/dump-version-info @@ -0,0 +1 @@ +Added: Dump version information post creating the artifacts on the stdout diff --git a/nix/scripts/create-container-dump.sh b/nix/scripts/create-container-dump.sh index a098d0d79..cecf792eb 100644 --- a/nix/scripts/create-container-dump.sh +++ b/nix/scripts/create-container-dump.sh @@ -8,33 +8,77 @@ if [[ ! $# -eq 1 ]]; then exit 1 fi +export HTTP_TIMEOUT=600 # Timeout in seconds (default is typically 90) +export REGISTRY_TIMEOUT=600 # Registry specific timeout + +output_dir=$1 mkdir -p $1 + # Download all the docker images into $1, and append its name to an index.txt # If this errors out for you, copy default-policy.json from the skopeo repo to # /etc/containers/policy.json while IFS= read -r image; do - # sanitize the image file name, replace slashes with underscores, suffix with .tar - image_filename=$(sed -r "s/[:\/]/_/g" <<< $image) - image_path=$(realpath $1)/${image_filename}.tar - if [[ -e $image_path ]];then - echo "Skipping $image_filename…" + +# sanitize the image file name, replace slashes with underscores, suffix with .tar + image_filename=$(sed -r "s/[:\/]/_/g" <<< "$image") + image_path="$(realpath "$1")/${image_filename}.tar" + + if [[ -s "$image_path" ]]; then + echo "Skipping $image_filename…" + continue + fi + + echo "Fetching $image_filename…" + + # All of these images should be publicly fetchable, especially given we + # ship public tarballs containing these images. + # ci.sh already honors DOCKER_LOGIN, so do the same here, otherwise + # fallback to unauthorized fetching. + + # If an image has both a tag and digest, remove the tag. Return the original if there is no match. + image_trimmed=$(echo "$image" | sed -E 's/(.+)(:.+(@.+))/\1\3/') + + tmp_path="${image_path}.tmp" + rm -f "$tmp_path" + + success=false + + for attempt in {1..5}; do + echo "Attempt $attempt/5 for $image_trimmed" + + if [[ -n "${DOCKER_LOGIN:-}" && "$image" =~ quay.io/wire ]]; then + skopeo copy --insecure-policy \ + --src-creds "$DOCKER_LOGIN" \ + --retry-times 10 \ + "docker://$image_trimmed" \ + "docker-archive:${tmp_path}" \ + --additional-tag "$image" || rc=$? else - echo "Fetching $image_filename…" - - # All of these images should be publicly fetchable, especially given we - # ship public tarballs containing these images. - # ci.sh already honors DOCKER_LOGIN, so do the same here, otherwise - # fallback to unauthorized fetching. - - # If an image has both a tag and digest, remove the tag. Return the original if there is no match. - image_trimmed=$(echo "$image" | sed -E 's/(.+)(:.+(@.+))/\1\3/') - if [[ -n "${DOCKER_LOGIN:-}" && "$image" =~ quay.io/wire ]];then - skopeo copy --insecure-policy --src-creds "$DOCKER_LOGIN" \ - docker://$image_trimmed docker-archive:${image_path} --additional-tag $image - else - skopeo copy --insecure-policy \ - docker://$image_trimmed docker-archive:${image_path} --additional-tag $image - fi - echo "${image_filename}.tar" >> $(realpath "$1")/index.txt + skopeo copy --insecure-policy \ + --retry-times 10 \ + "docker://$image_trimmed" \ + "docker-archive:${tmp_path}" \ + --additional-tag "$image" || rc=$? + fi + + rc=$? + + if [[ $rc -eq 0 && -s "$tmp_path" ]]; then + mv "$tmp_path" "$image_path" + success=true + break fi + + echo "Fetch failed for $image_trimmed with rc=$rc; retrying…" + rm -f "$tmp_path" + sleep $((attempt * 20)) + done + + if [[ "$success" != true ]]; then + echo "ERROR: failed to fetch $image after retries" >&2 + exit 1 + fi + + echo "${image_filename}.tar" >> "$(realpath "$1")/index.txt" + create-build-entry "$image" "$output_dir" done diff --git a/offline/default-build/build.sh b/offline/default-build/build.sh index b6a77acc1..239b5937c 100755 --- a/offline/default-build/build.sh +++ b/offline/default-build/build.sh @@ -8,7 +8,7 @@ OUTPUT_DIR="$SCRIPT_DIR/output" # expected structure to be: /wire-server-deploy/offline/default-build/build.sh ROOT_DIR="${SCRIPT_DIR}/../../" -mkdir -p "${OUTPUT_DIR}"/containers-{helm,other,system,adminhost} "${OUTPUT_DIR}"/binaries "${OUTPUT_DIR}"/versions +mkdir -p "${OUTPUT_DIR}"/containers-{helm,other,system,adminhost} "${OUTPUT_DIR}"/binaries "${OUTPUT_DIR}"/versions # Define the output tar file OUTPUT_TAR="${OUTPUT_DIR}/assets.tgz" @@ -19,32 +19,41 @@ TASKS_DIR="${SCRIPT_DIR}/../tasks" #cp $SCRIPT_DIR/..//output/containers-helm.tar "${OUTPUT_DIR}"/ # one need to comment the tasks below for which one wants to optimize the build -# Any of the tasks can be skipped by commenting them out +# Any of the tasks can be skipped by commenting them out # however, mind the dependencies between them and how they are grouped # Processing helm charts # -------------------------- -# pulling the charts, charts to be skipped are passed as arguments HELM_CHART_EXCLUDE_LIST -"${TASKS_DIR}"/proc_pull_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise,postgresql" +# pulling the charts based on builds.json, charts to be skipped are passed as arguments HELM_CHART_EXCLUDE_LIST +"${TASKS_DIR}"/proc_pull_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise,postgresql,rust-sft,fluent-bit" + +# pulling the charts from helm-charts repo, charts to be included are passed as arguments HELM_CHART_INCLUDE_LIST +# "${TASKS_DIR}"/proc_pull_ext_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" HELM_CHART_INCLUDE_LIST="postgresql-external" # copy local copy of values from root directory to output directory cp -r "${ROOT_DIR}"/values "${OUTPUT_DIR}"/ +# copy local copy of dashboards from root directory to output directory +cp -r "${ROOT_DIR}"/dashboards "${OUTPUT_DIR}"/ + +# removing the values/$chart directories in values directory if not required +"${TASKS_DIR}"/pre_clean_values_0.sh VALUES_DIR="${OUTPUT_DIR}/values" HELM_VALUES_EXCLUDE_LIST="postgresql" VALUES_TYPE="prod" + # all basic chart pre-processing tasks -"${TASKS_DIR}"/pre_chart_process_0.sh "${OUTPUT_DIR}" +"${TASKS_DIR}"/pre_chart_process_0.sh OUTPUT_DIR="${OUTPUT_DIR}" # all extra pre chart processing tasks for this profile should come here -# pre_chart_process_1.sh -# pre_chart_process_2.sh +# pre_chart_process_1.sh +# pre_chart_process_2.sh # processing the charts # here we also filter the images post processing the helm charts # pass the image names to be filtered as arguments as regex #IMAGE_EXCLUDE_LIST='brig|galley' -"${TASKS_DIR}"/process_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" #IMAGE_EXCLUDE_LIST="" +"${TASKS_DIR}"/process_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" VALUES_TYPE="prod" #IMAGE_EXCLUDE_LIST="" # all basic chart pre-processing tasks -"${TASKS_DIR}"/post_chart_process_0.sh "${OUTPUT_DIR}" +"${TASKS_DIR}"/post_chart_process_0.sh OUTPUT_DIR="${OUTPUT_DIR}" # all extra post chart processing tasks for this profile should come here # post_chart_process_1.sh @@ -87,6 +96,7 @@ ITEMS_TO_ARCHIVE=( "../../../ansible" "../../../bin" "versions" + "dashboards" ) # Function to check if an item exists @@ -106,3 +116,16 @@ done # Create the tar archive with relative paths tar czf "$OUTPUT_TAR" "${ITEMS_TO_ARCHIVE[@]}" + +# Dumping details of versions for the build and packed +echo "Dump of versions/helm_image_tree.json" +cat "${OUTPUT_DIR}/versions/helm_image_tree.json" + +echo "Dump of versions/containers_system_images.json" +cat "${OUTPUT_DIR}/versions/containers_system_images.json" + +echo "Dump of versions/wire-binaries.json" +cat "${OUTPUT_DIR}/versions/wire-binaries.json" + +echo "Dump of wire-builds used" +cat "${OUTPUT_DIR}/build.json" diff --git a/offline/demo-build/build.sh b/offline/demo-build/build.sh index 99bbdbbf5..a33349b1d 100755 --- a/offline/demo-build/build.sh +++ b/offline/demo-build/build.sh @@ -13,63 +13,57 @@ mkdir -p "${OUTPUT_DIR}"/containers-{helm,other,system,adminhost} "${OUTPUT_DIR} # Define the output tar file OUTPUT_TAR="${OUTPUT_DIR}/assets.tgz" -# for optmization purposes, if these tarballs are already processed by previous profiles check wire-server-deploy/.github/workflows/offline.yml, one can copy those artifacts from previous profiles to your profile by using -#cp $SCRIPT_DIR/..//output/containers-helm.tar "${OUTPUT_DIR}"/ -# one need to comment the tasks below for which one wants to optimize the build +TASKS_DIR="${SCRIPT_DIR}/../tasks" # Any of the tasks can be skipped by commenting them out # however, mind the dependencies between them and how they are grouped # Processing helm charts # -------------------------- +HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise,k8ssandra-operator,k8ssandra-test-cluster,elasticsearch-curator,keycloakx,openebs,nginx-ingress-controller,kibana,restund,fluent-bit,aws-ingress,redis-cluster,calling-test,demo-smtp,cassandra-external,elasticsearch-external,minio-external,postgresql-external,rabbitmq-external,rust-sft,fluent-bit" -# copying charts from the default build -cp -r "${SCRIPT_DIR}"/../default-build/output/charts "${OUTPUT_DIR}/" +# pulling the charts, charts to be skipped are passed as arguments HELM_CHART_EXCLUDE_LIST +"${TASKS_DIR}"/proc_pull_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" HELM_CHART_EXCLUDE_LIST="${HELM_CHART_EXCLUDE_LIST}" -# copy values from the default build -cp -r "${SCRIPT_DIR}"/../default-build/output/values "${OUTPUT_DIR}/" +# copy local copy of values from root directory to output directory +cp -r "${ROOT_DIR}"/values "${OUTPUT_DIR}"/ -# here removing the federation image from cintainers-helm directory -"${SCRIPT_DIR}"/post_chart_process_1.sh "${OUTPUT_DIR}"/ "${SCRIPT_DIR}/../default-build/output" -# -------------------------- +# copy local copy of dashboards from root directory to output directory +cp -r "${ROOT_DIR}"/dashboards "${OUTPUT_DIR}"/ -# Following tasks are independent from each other -# linking the output from the SOURCE_OUTPUT_DIR to the OUTPUT_DIR to confirm if they exist -# -------------------------- -SOURCE_OUTPUT_DIR="${SCRIPT_DIR}/../default-build/output" +# copy offline-env.sh to bin directory in output +mkdir "${OUTPUT_DIR}/bin" +cp "${ROOT_DIR}/bin/offline-env-wiab.sh" "${OUTPUT_DIR}/bin/" -# linking containers-adminhost directory from the default build -ln -sf "${SOURCE_OUTPUT_DIR}/containers-adminhost" "${OUTPUT_DIR}/containers-adminhost" +# removing the values/$chart directories in values directory if not required +"${TASKS_DIR}"/pre_clean_values_0.sh VALUES_DIR="${OUTPUT_DIR}/values" HELM_VALUES_EXCLUDE_LIST="${HELM_CHART_EXCLUDE_LIST}" VALUES_TYPE="demo" -# link debs-jammy.tar from the default build -ln -sf "${SOURCE_OUTPUT_DIR}/debs-jammy.tar" "${OUTPUT_DIR}/debs-jammy.tar" +# all basic chart pre-processing tasks +"${TASKS_DIR}"/pre_chart_process_0.sh OUTPUT_DIR="${OUTPUT_DIR}" VALUES_TYPE="demo" -# link containers-system.tar from the default build -ln -sf "${SOURCE_OUTPUT_DIR}/containers-system.tar" "${OUTPUT_DIR}/containers-system.tar" +# processing the charts +# here we also filter the images post processing the helm charts +# pass the image names to be filtered as arguments as regex #IMAGE_EXCLUDE_LIST='brig|galley' +"${TASKS_DIR}"/process_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" IMAGE_EXCLUDE_LIST="quay.io/wire/federator" VALUES_TYPE="demo" -# copy binaries.tar from the default build -ln -sf "${SOURCE_OUTPUT_DIR}/binaries.tar" "${OUTPUT_DIR}/binaries.tar" +# all basic chart pre-processing tasks +"${TASKS_DIR}"/post_chart_process_0.sh OUTPUT_DIR="${OUTPUT_DIR}" VALUES_TYPE="demo" -cp "${SOURCE_OUTPUT_DIR}/versions/wire-binaries.json" "${OUTPUT_DIR}/versions/" -cp "${SOURCE_OUTPUT_DIR}/versions/containers-system.txt" "${OUTPUT_DIR}/versions/" -cp "${SOURCE_OUTPUT_DIR}/versions/debian-builds.json" "${OUTPUT_DIR}/versions/" -cp "${SOURCE_OUTPUT_DIR}/versions/containers-adminhost.txt" "${OUTPUT_DIR}/versions/" -cp "${SOURCE_OUTPUT_DIR}/versions/containers-system.txt" "${OUTPUT_DIR}/versions/" +# -------------------------- +# building admin host containers, has dependency on the helm charts +"${TASKS_DIR}"/build_adminhost_containers.sh "${OUTPUT_DIR}" --adminhost # -------------------------- # List of directories and files to include in the tar archive ITEMS_TO_ARCHIVE=( - "debs-jammy.tar" - "binaries.tar" "containers-adminhost" "containers-helm.tar" - "containers-system.tar" "charts" "values" - "../../../ansible" - "../../../bin" + "bin" "versions" + "dashboards" ) # Function to check if an item exists @@ -88,8 +82,11 @@ for item in "${ITEMS_TO_ARCHIVE[@]}"; do done # Create the tar archive with relative paths -# for the outputs from other other profiles, their paths should be mentioned here -tar czf "$OUTPUT_TAR" \ - -C "${SOURCE_OUTPUT_DIR}" debs-jammy.tar binaries.tar containers-adminhost containers-system.tar \ - -C "${ROOT_DIR}" ansible bin \ - -C "${OUTPUT_DIR}" charts values versions containers-helm.tar +tar czf "$OUTPUT_TAR" "${ITEMS_TO_ARCHIVE[@]}" + +# Dumping details of versions for the build and packed +echo "Dump of versions/helm_image_tree.json" +cat "${OUTPUT_DIR}/versions/helm_image_tree.json" + +echo "Dump of wire-builds used" +cat "${OUTPUT_DIR}/build.json" diff --git a/offline/min-build/build.sh b/offline/min-build/build.sh index 6418ed685..94567feee 100755 --- a/offline/min-build/build.sh +++ b/offline/min-build/build.sh @@ -19,35 +19,40 @@ TASKS_DIR="${SCRIPT_DIR}/../tasks" #cp $SCRIPT_DIR/..//output/containers-helm.tar "${OUTPUT_DIR}"/ # one need to comment the tasks below for which one wants to optimize the build -# Any of the tasks can be skipped by commenting them out +# Any of the tasks can be skipped by commenting them out # however, mind the dependencies between them and how they are grouped # Processing helm charts # -------------------------- # pulling the charts, charts to be skipped are passed as arguments HELM_CHART_EXCLUDE_LIST -"${TASKS_DIR}"/proc_pull_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise,k8ssandra-operator,k8ssandra-test-cluster,elasticsearch-ephemeral,elasticsearch-curator,rabbitmq,demo-smtp,fake-aws,fake-aws-s3,postgresql,keycloakx,openebs,nginx-ingress-controller,kibana,restund,fluent-bit,aws-ingress,databases-ephemeral,redis-cluster,calling-test" +HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise,k8ssandra-operator,k8ssandra-test-cluster,elasticsearch-ephemeral,elasticsearch-curator,rabbitmq,smtp,fake-aws,fake-aws-s3,postgresql,keycloakx,openebs,nginx-ingress-controller,kibana,restund,fluent-bit,aws-ingress,databases-ephemeral,redis-cluster,calling-test,cert-manager,kube-prometheus-stack,demo-smtp,wire-utility,rust-sft,fluent-bit" + +"${TASKS_DIR}"/proc_pull_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" HELM_CHART_EXCLUDE_LIST="${HELM_CHART_EXCLUDE_LIST}" + +# pulling the charts from helm-charts repo, charts to be included are passed as arguments HELM_CHART_INCLUDE_LIST +# "${TASKS_DIR}"/proc_pull_ext_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" HELM_CHART_INCLUDE_LIST="postgresql-external" # copy local copy of values from root directory to output directory cp -r "${ROOT_DIR}"/values "${OUTPUT_DIR}"/ # removing the values/$chart directories in values directory if not required -"${SCRIPT_DIR}"/pre_clean_values_1.sh VALUES_DIR="${OUTPUT_DIR}/values" HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise,k8ssandra-operator,k8ssandra-test-cluster,elasticsearch-ephemeral,elasticsearch-curator,rabbitmq,demo-smtp,fake-aws,fake-aws-s3,postgresql,keycloakx,openebs,nginx-ingress-controller,kibana,restund,fluent-bit,aws-ingress,databases-ephemeral,redis-cluster,calling-test" +"${TASKS_DIR}"/pre_clean_values_0.sh VALUES_DIR="${OUTPUT_DIR}/values" HELM_VALUES_EXCLUDE_LIST="${HELM_CHART_EXCLUDE_LIST}" VALUES_TYPE="prod" # all basic chart pre-processing tasks -"${TASKS_DIR}"/pre_chart_process_0.sh "${OUTPUT_DIR}" +"${TASKS_DIR}"/pre_chart_process_0.sh OUTPUT_DIR="${OUTPUT_DIR}" # all extra pre chart processing tasks for this profile should come here # pre_chart_process_1.sh -# pre_chart_process_2.sh +# pre_chart_process_2.sh # processing the charts # here we also filter the images post processing the helm charts # pass the image names to be filtered as arguments as regex #IMAGE_EXCLUDE_LIST='brig|galley' -"${TASKS_DIR}"/process_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" #IMAGE_EXCLUDE_LIST="" +"${TASKS_DIR}"/process_charts.sh OUTPUT_DIR="${OUTPUT_DIR}" VALUES_TYPE="prod" #IMAGE_EXCLUDE_LIST="" # all basic chart pre-processing tasks -"${TASKS_DIR}"/post_chart_process_0.sh "${OUTPUT_DIR}" +"${TASKS_DIR}"/post_chart_process_0.sh OUTPUT_DIR="${OUTPUT_DIR}" # all extra post chart processing tasks for this profile should come here # post_chart_process_1.sh @@ -89,3 +94,10 @@ done # Create the tar archive with relative paths tar czf "$OUTPUT_TAR" "${ITEMS_TO_ARCHIVE[@]}" + +# Dumping details of versions for the build and packed +echo "Dump of versions/helm_image_tree.json" +cat "${OUTPUT_DIR}/versions/helm_image_tree.json" + +echo "Dump of wire-builds used" +cat "${OUTPUT_DIR}/build.json" diff --git a/offline/tasks/proc_pull_charts.sh b/offline/tasks/proc_pull_charts.sh index 55195e2f8..b39b3ddaa 100755 --- a/offline/tasks/proc_pull_charts.sh +++ b/offline/tasks/proc_pull_charts.sh @@ -3,7 +3,7 @@ set -euo pipefail OUTPUT_DIR="" # Default exclude lists -HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise" +HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise,demo-smtp" # Parse the arguments for arg in "$@" @@ -36,13 +36,14 @@ echo "Excluding following charts from the release: $HELM_CHART_EXCLUDE_LIST" wire_build_chart_release () { wire_build="$1" - curl "$wire_build" | jq -r --argjson HELM_CHART_EXCLUDE_LIST "$HELM_CHART_EXCLUDE_LIST" ' + curl "$wire_build" -o "${OUTPUT_DIR}/build.json" + jq -r --argjson HELM_CHART_EXCLUDE_LIST "$HELM_CHART_EXCLUDE_LIST" ' .helmCharts | with_entries(select(.key as $k | $HELM_CHART_EXCLUDE_LIST | index($k) | not)) | to_entries | map("\(.key) \(.value.repo) \(.value.version)") | join("\n") - ' + ' "${OUTPUT_DIR}/build.json" } # pull_charts() accepts charts in format @@ -81,6 +82,7 @@ pull_charts() { (cd "${OUTPUT_DIR}"/charts; helm pull --version "$version" --untar "$repo_short_name/$name") done echo "Pulling charts done." + } wire_build="https://raw.githubusercontent.com/wireapp/wire-builds/ab2f729b10065d42fa2bf5adc9f97d545610c1e9/build.json" From 0636d3f3b61d0fc8da1d0a021ef2c4d2ac24800a Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Tue, 11 Aug 2026 22:22:44 +0200 Subject: [PATCH 04/10] fix WPB-27900: use new q2-2025 wire-builds commit --- offline/tasks/proc_pull_charts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/offline/tasks/proc_pull_charts.sh b/offline/tasks/proc_pull_charts.sh index b39b3ddaa..cc75f58e3 100755 --- a/offline/tasks/proc_pull_charts.sh +++ b/offline/tasks/proc_pull_charts.sh @@ -85,5 +85,5 @@ pull_charts() { } -wire_build="https://raw.githubusercontent.com/wireapp/wire-builds/ab2f729b10065d42fa2bf5adc9f97d545610c1e9/build.json" +wire_build="https://raw.githubusercontent.com/wireapp/wire-builds/ff8075a57069d03855eec809ea1b64c69382f5eb/build.json" wire_build_chart_release "$wire_build" | pull_charts From eb395b62092bb528701f710911777cfa8809801e Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Tue, 11 Aug 2026 23:00:13 +0200 Subject: [PATCH 05/10] fix WPB-27900: add pre_clean_values_0.sh and remove addition of dashboards --- offline/default-build/build.sh | 1 - offline/demo-build/build.sh | 1 - offline/tasks/pre_clean_values_0.sh | 58 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100755 offline/tasks/pre_clean_values_0.sh diff --git a/offline/default-build/build.sh b/offline/default-build/build.sh index 239b5937c..e6a14c927 100755 --- a/offline/default-build/build.sh +++ b/offline/default-build/build.sh @@ -96,7 +96,6 @@ ITEMS_TO_ARCHIVE=( "../../../ansible" "../../../bin" "versions" - "dashboards" ) # Function to check if an item exists diff --git a/offline/demo-build/build.sh b/offline/demo-build/build.sh index a33349b1d..96c03c7f0 100755 --- a/offline/demo-build/build.sh +++ b/offline/demo-build/build.sh @@ -63,7 +63,6 @@ ITEMS_TO_ARCHIVE=( "values" "bin" "versions" - "dashboards" ) # Function to check if an item exists diff --git a/offline/tasks/pre_clean_values_0.sh b/offline/tasks/pre_clean_values_0.sh new file mode 100755 index 000000000..5702302a5 --- /dev/null +++ b/offline/tasks/pre_clean_values_0.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -x -euo pipefail + +# Default exclude list +VALUES_DIR="" +HELM_VALUES_EXCLUDE_LIST="" +# Default values type will expect to use prod values +VALUES_TYPE="prod" + +# Parse the arguments +for arg in "$@" +do + case $arg in + VALUES_DIR=*) + VALUES_DIR="${arg#*=}" + ;; + HELM_VALUES_EXCLUDE_LIST=*) + HELM_VALUES_EXCLUDE_LIST="${arg#*=}" + ;; + VALUES_TYPE=*) + VALUES_TYPE="${arg#*=}" + ;; + *) + echo "Unknown argument: $arg" >&2 + exit 1 + ;; + esac +done + +# Check if OUTPUT_DIR is set +if [[ -z "$VALUES_DIR" ]]; then + echo "usage: $0 VALUES_DIR=\"values-dir\" [HELM_VALUES_EXCLUDE_LIST=\"chart1,chart2,...\"] [VALUES_TYPE=\"prod|demo\"]" >&2 + exit 1 +fi + +echo "Running pre-clean values process script 1 in dir $VALUES_DIR ..." + +# Split the HELM_VALUES_EXCLUDE_LIST into an array +IFS=',' read -r -a EXCLUDE_ARRAY <<< "$HELM_VALUES_EXCLUDE_LIST" + +# Iterate over each chart in the exclude list +for CHART in "${EXCLUDE_ARRAY[@]}"; do + CHART_DIR="$VALUES_DIR/$CHART" + if [[ -d "$CHART_DIR" ]]; then + echo "Removing values directory: $CHART_DIR" + rm -rf "$CHART_DIR" + else + echo "Directory does not exist: $CHART_DIR" + fi +done + +# Delete files in all directories under $VALUES_DIR that do not match the $VALUES_TYPE* pattern +for DIR in "$VALUES_DIR"/*; do + if [[ -d "$DIR" ]]; then + echo "Processing directory: $DIR" + find "$DIR" -type f ! -name "${VALUES_TYPE}*" -exec rm -v {} \; + fi +done From 57ae6876014fb81c16fe5fbe032886b877059f46 Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Wed, 12 Aug 2026 10:18:49 +0200 Subject: [PATCH 06/10] fix WPB-27900: synced the tasks and sub scripts --- nix/scripts/list-helm-containers.sh | 107 +++++++++++++++++++++++---- offline/default-build/build.sh | 3 - offline/demo-build/build.sh | 3 - offline/tasks/pre_chart_process_0.sh | 34 +++++++-- offline/tasks/process_charts.sh | 17 +++-- 5 files changed, 131 insertions(+), 33 deletions(-) diff --git a/nix/scripts/list-helm-containers.sh b/nix/scripts/list-helm-containers.sh index c96f5526e..1271149f2 100644 --- a/nix/scripts/list-helm-containers.sh +++ b/nix/scripts/list-helm-containers.sh @@ -5,10 +5,17 @@ # those. # In cases where no container image tag has been specified, it'll use `latest`. # The list is sorted and deduplicated, then printed to stdout. -set -eou pipefail +set -euo pipefail VALUES_DIR="" HELM_IMAGE_TREE_FILE="" +VALUES_TYPE="" + +# Extract images using yq-go (v4+) syntax +# Note: This requires yq-go to be in PATH (see default.nix) +extract_images() { + yq eval '.. | select(has("image")) | .image' "$1" 2>/dev/null || true +} # Parse the arguments for arg in "$@" @@ -20,6 +27,9 @@ do HELM_IMAGE_TREE_FILE=*) HELM_IMAGE_TREE_FILE="${arg#*=}" ;; + VALUES_TYPE=*) + VALUES_TYPE="${arg#*=}" + ;; *) echo "Unknown argument: $arg" >&2 exit 1 @@ -27,12 +37,27 @@ do esac done -if [[ -z "$VALUES_DIR" || -z "$HELM_IMAGE_TREE_FILE" ]]; then - echo "Error: Both VALUES_DIR and HELM_IMAGE_TREE_FILE must be provided." >&2 - echo "Usage: $0 VALUES_DIR= HELM_IMAGE_TREE_FILE=" >&2 +if [[ -z "$VALUES_DIR" || -z "$HELM_IMAGE_TREE_FILE" || -z "$VALUES_TYPE" ]]; then + echo "Error: VALUES_DIR, HELM_IMAGE_TREE_FILE and VALUES_TYPE must be provided." >&2 + echo "Usage: $0 VALUES_DIR= HELM_IMAGE_TREE_FILE= [VALUES_TYPE=]" >&2 exit 1 fi +# create a dependency tree between helm chart and images +append_chart_entry() { + local chart=$1 + local images=$2 + local json_file=$3 + + if [ ! -f "$json_file" ]; then + echo '[]' > "$json_file" + fi + + existing_content=$(jq '.' "$json_file") + new_entry=$(jq -n --arg chart "$chart" --argjson images "$images" '{"chart": $chart, "images": $images}') + updated_content=$(echo "$existing_content" | jq --argjson new_entry "$new_entry" '. += [$new_entry]') + echo "$updated_content" | jq '.' > "$json_file" +} # Some of these images don't contain a "latest" tag. We don't to download /ALL/ # of them, but only :latest in that case - it's bad enough there's no proper @@ -55,20 +80,72 @@ function optionally_complain() { images="" # For each helm chart passed in from stdin, use the example values to # render the charts, and assemble the list of images this would fetch. +chart_count=0 while IFS= read -r chart; do - echo "Running helm template on chart ${chart}…" >&2 - current_images=$(helm template --debug "${chart}" \ - --set federate.dtls.tls.key=emptyString \ - --set federate.dtls.tls.crt=emptyString \ - $( [[ -f "${VALUES_DIR}"/$(basename "${chart}")/prod-values.example.yaml ]] && echo "-f ${VALUES_DIR}/$(basename "${chart}")/prod-values.example.yaml" ) \ - $( [[ -f "${VALUES_DIR}"/$(basename "${chart}")/prod-secrets.example.yaml ]] && echo "-f ${VALUES_DIR}/$(basename "${chart}")/prod-secrets.example.yaml" ) \ - | yq -r '..|.image? | select(.)' | optionally_complain | sort -u) + chart_count=$((chart_count + 1)) + echo "[$chart_count] Running helm template on chart ${chart}…" >&2 + set +e # Temporarily disable exit on error + # Determine values file to use (prod first, then demo as fallback) + values_file="" + if [[ -f "${VALUES_DIR}"/$(basename "${chart}")/"${VALUES_TYPE}"-values.example.yaml ]]; then + values_file="${VALUES_DIR}/$(basename "${chart}")/${VALUES_TYPE}-values.example.yaml" + elif [[ -f "${VALUES_DIR}"/$(basename "${chart}")/demo-values.example.yaml ]]; then + values_file="${VALUES_DIR}/$(basename "${chart}")/demo-values.example.yaml" + echo "Using demo values for $(basename $chart) (no ${VALUES_TYPE} values found)" >&2 + fi + + # Determine secrets file to use + secrets_file="" + if [[ -f "${VALUES_DIR}"/$(basename "${chart}")/"${VALUES_TYPE}"-secrets.example.yaml ]]; then + secrets_file="${VALUES_DIR}/$(basename "${chart}")/${VALUES_TYPE}-secrets.example.yaml" + elif [[ -f "${VALUES_DIR}"/$(basename "${chart}")/demo-secrets.example.yaml ]]; then + secrets_file="${VALUES_DIR}/$(basename "${chart}")/demo-secrets.example.yaml" + fi + + # Save helm output to temp file to check exit code before parsing + # This prevents yq from attempting to parse helm error messages + temp_helm_output=$(mktemp) + helm template "${chart}" \ + $( [[ -n "$values_file" ]] && echo "-f $values_file" ) \ + $( [[ -n "$secrets_file" ]] && echo "-f $secrets_file" ) \ + > "$temp_helm_output" 2>&1 + + helm_exit_code=$? + + # Extract images using version-appropriate yq syntax + if [[ $helm_exit_code -eq 0 ]]; then + raw_images=$(extract_images "$temp_helm_output" | grep -v "^null$" | grep -v "^---$" | grep -v "^$" || true) + else + raw_images="" + fi + + set -e # Re-enable exit on error + + if [[ $helm_exit_code -ne 0 ]]; then + echo "ERROR: Failed to process chart $(basename $chart)" >&2 + echo "Chart path: $chart" >&2 + echo "Values file: ${values_file:-none}" >&2 + echo "Secrets file: ${secrets_file:-none}" >&2 + echo "Helm error output:" >&2 + cat "$temp_helm_output" >&2 + echo "Try running: helm template $chart $([ -n "$values_file" ] && echo "-f $values_file") $([ -n "$secrets_file" ] && echo "-f $secrets_file")" >&2 + raw_images="" + fi + + rm -f "$temp_helm_output" + + # Process extracted images + if [[ -n "$raw_images" ]]; then + current_images=$(echo "$raw_images" | grep -v "^$" | optionally_complain | sort -u) + else + current_images="" + fi images+="$current_images\n" if [[ -n "$current_images" ]]; then - basename "${chart}" >> "${HELM_IMAGE_TREE_FILE}" - echo -e "$current_images\n" >> "${HELM_IMAGE_TREE_FILE}" - #echo -e "\n" >> "${HELM_IMAGE_TREE_FILE}" + current_images=$(echo "$current_images" | awk NF) + image_array=$(jq -Rn --arg images "$current_images" '$images | split("\n")') + append_chart_entry "$(basename $chart)" "$image_array" "${HELM_IMAGE_TREE_FILE}" fi done -echo -e "$images" | grep . | sort -u +echo -e "$images" | grep . | sort -u || true diff --git a/offline/default-build/build.sh b/offline/default-build/build.sh index e6a14c927..80a02b31c 100755 --- a/offline/default-build/build.sh +++ b/offline/default-build/build.sh @@ -34,9 +34,6 @@ TASKS_DIR="${SCRIPT_DIR}/../tasks" # copy local copy of values from root directory to output directory cp -r "${ROOT_DIR}"/values "${OUTPUT_DIR}"/ -# copy local copy of dashboards from root directory to output directory -cp -r "${ROOT_DIR}"/dashboards "${OUTPUT_DIR}"/ - # removing the values/$chart directories in values directory if not required "${TASKS_DIR}"/pre_clean_values_0.sh VALUES_DIR="${OUTPUT_DIR}/values" HELM_VALUES_EXCLUDE_LIST="postgresql" VALUES_TYPE="prod" diff --git a/offline/demo-build/build.sh b/offline/demo-build/build.sh index 96c03c7f0..5db1aa1d7 100755 --- a/offline/demo-build/build.sh +++ b/offline/demo-build/build.sh @@ -28,9 +28,6 @@ HELM_CHART_EXCLUDE_LIST="inbucket,wire-server-enterprise,k8ssandra-operator,k8ss # copy local copy of values from root directory to output directory cp -r "${ROOT_DIR}"/values "${OUTPUT_DIR}"/ -# copy local copy of dashboards from root directory to output directory -cp -r "${ROOT_DIR}"/dashboards "${OUTPUT_DIR}"/ - # copy offline-env.sh to bin directory in output mkdir "${OUTPUT_DIR}/bin" cp "${ROOT_DIR}/bin/offline-env-wiab.sh" "${OUTPUT_DIR}/bin/" diff --git a/offline/tasks/pre_chart_process_0.sh b/offline/tasks/pre_chart_process_0.sh index f633c62ab..80161ad7a 100755 --- a/offline/tasks/pre_chart_process_0.sh +++ b/offline/tasks/pre_chart_process_0.sh @@ -1,18 +1,40 @@ #!/usr/bin/env bash set -euo pipefail -if [[ ! $# -eq 1 ]]; then - echo "usage: $0 OUTPUT-DIR" >&2 +# Default output dir +OUTPUT_DIR="" +# Default values type will expect to use prod values +VALUES_TYPE="prod" + +# Parse the arguments +for arg in "$@" +do + case $arg in + OUTPUT_DIR=*) + OUTPUT_DIR="${arg#*=}" + ;; + VALUES_TYPE=*) + VALUES_TYPE="${arg#*=}" + ;; + *) + echo "Unknown argument: $arg" >&2 + exit 1 + ;; + esac +done + + +# Check if OUTPUT_DIR is set +if [[ -z "$OUTPUT_DIR" ]]; then + echo "usage: $0 OUTPUT_DIR=\"values-dir\" [VALUES_TYPE=\"prod|demo\"]" >&2 exit 1 fi -OUTPUT_DIR="$1" - -echo "Running pre-chart process script 0 in dir $OUTPUT_DIR ..." +echo "Running pre-chart process script 0 in dir $OUTPUT_DIR with values type $VALUES_TYPE" # Patch wire-server values.yaml to include federator # This is needed to bundle it's image. -sed -i -Ee 's/federation: false/federation: true/' "${OUTPUT_DIR}"/values/wire-server/prod-values.example.yaml +sed -i -Ee 's/federation: false/federation: true/' "${OUTPUT_DIR}/values/wire-server/${VALUES_TYPE}-values.example.yaml" sed -i -Ee 's/useSharedFederatorSecret: false/useSharedFederatorSecret: true/' "${OUTPUT_DIR}"/charts/wire-server/charts/federator/values.yaml # drop step-certificates/.../test-connection.yaml because it lacks an image tag diff --git a/offline/tasks/process_charts.sh b/offline/tasks/process_charts.sh index f7ef6d700..f1c6f997d 100755 --- a/offline/tasks/process_charts.sh +++ b/offline/tasks/process_charts.sh @@ -5,6 +5,9 @@ OUTPUT_DIR="" # Default exclude list IMAGE_EXCLUDE_LIST="" +# Default values type will expect to use prod values +VALUES_TYPE="prod" + # Parse the arguments for arg in "$@" do @@ -12,6 +15,9 @@ do OUTPUT_DIR=*) OUTPUT_DIR="${arg#*=}" ;; + VALUES_TYPE=*) + VALUES_TYPE="${arg#*=}" + ;; IMAGE_EXCLUDE_LIST=*) IMAGE_EXCLUDE_LIST="${arg#*=}" ;; @@ -24,14 +30,13 @@ done # Check if OUTPUT_DIR is set if [[ -z "$OUTPUT_DIR" ]]; then - echo "usage: $0 OUTPUT_DIR=\"output-dir\" [IMAGE_EXCLUDE_LIST=\"image1\|image2...\"]" >&2 + echo "usage: $0 OUTPUT_DIR=\"output-dir\" [IMAGE_EXCLUDE_LIST=\"image1\|image2...\"] [VALUES_TYPE=\"prod\"]" >&2 exit 1 fi -echo "Processing Helm charts in ${OUTPUT_DIR}" +echo "Processing Helm charts in ${OUTPUT_DIR} with VALUES_TYPE=${VALUES_TYPE}" -HELM_IMAGE_TREE_FILE="${OUTPUT_DIR}/versions/helm_image_tree.txt" -touch "${HELM_IMAGE_TREE_FILE}" +HELM_IMAGE_TREE_FILE="${OUTPUT_DIR}/versions/helm_image_tree.json" # Check if IMAGE_EXCLUDE_LIST is set, otherwise use a default pattern that matches nothing EXCLUDE_PATTERN=${IMAGE_EXCLUDE_LIST:-".^"} @@ -42,7 +47,7 @@ echo "Excluding images matching the pattern: $EXCLUDE_PATTERN" # containers (e.g. `quay.io_wire_galley-integration_4.22.0`.) for chartPath in "${OUTPUT_DIR}"/charts/*; do echo "$chartPath" -done | list-helm-containers VALUES_DIR="${OUTPUT_DIR}"/values HELM_IMAGE_TREE_FILE="$HELM_IMAGE_TREE_FILE" | grep -v "\-integration:" > "${OUTPUT_DIR}"/images +done | list-helm-containers VALUES_DIR="${OUTPUT_DIR}"/values HELM_IMAGE_TREE_FILE="$HELM_IMAGE_TREE_FILE" VALUES_TYPE="$VALUES_TYPE" | grep -v "\-integration:" > "${OUTPUT_DIR}"/images # Omit integration test # containers (e.g. `quay.io_wire_galley-integration_4.22.0`.) @@ -51,4 +56,4 @@ sed -i '/-integration/d' "${HELM_IMAGE_TREE_FILE}" grep -vE "$EXCLUDE_PATTERN" "${OUTPUT_DIR}"/images | create-container-dump "${OUTPUT_DIR}"/containers-helm tar cf "${OUTPUT_DIR}"/containers-helm.tar -C "${OUTPUT_DIR}" containers-helm -cp "${OUTPUT_DIR}"/containers-helm/index.txt "${OUTPUT_DIR}"/versions/containers-helm.txt +mv "${OUTPUT_DIR}/containers-helm/images.json" "${OUTPUT_DIR}"/versions/containers_helm_images.json From 9e9b60db9774d3326dc796dc70b23981a25c155d Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Wed, 12 Aug 2026 13:50:00 +0200 Subject: [PATCH 07/10] fix WPB-27900: synced the tasks and sub scripts --- bin/offline-env-wiab.sh | 56 +++++++++++++++++++++++++++++ default.nix | 4 ++- nix/overlay.nix | 10 ++++++ nix/scripts/create-build-entry.sh | 38 ++++++++++++++++++++ nix/scripts/list-helm-containers.sh | 2 +- 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 bin/offline-env-wiab.sh create mode 100755 nix/scripts/create-build-entry.sh diff --git a/bin/offline-env-wiab.sh b/bin/offline-env-wiab.sh new file mode 100644 index 000000000..59ec26ec7 --- /dev/null +++ b/bin/offline-env-wiab.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Function to check if image exists and get its name, or load from tar +check_or_load_image() { + local tar_file="$1" + local image_pattern="$2" + + # Check if any image matching pattern exists in docker + local existing_image + existing_image=$(sudo docker images --format "{{.Repository}}:{{.Tag}}" | grep -F "$image_pattern" | head -1) + if [[ -n "$existing_image" ]]; then + echo "$existing_image" + return 0 + fi + + # If no existing image, load from tar + if [[ -z "$tar_file" || ! -f "$tar_file" ]]; then + echo "Tar file does not exist: $tar_file, skipping" >&2 + else + sudo docker load -i "$tar_file" | awk '{print $3}' + fi +} + +ZAUTH_TAR=$(find "$SCRIPT_DIR/../containers-adminhost" -maxdepth 1 -name "quay.io_wire_zauth_*.tar" -type f | sort | tail -1) + +# Get container image names efficiently +ZAUTH_CONTAINER=$(check_or_load_image "$ZAUTH_TAR" "quay.io/wire/zauth") || true +WSD_CONTAINER=$(check_or_load_image "$SCRIPT_DIR/../containers-adminhost/container-wire-server-deploy.tgz" "quay.io/wire/wire-server-deploy") || true + +# Export only if ZAUTH_CONTAINER is not empty +[[ -n "$ZAUTH_CONTAINER" ]] && export ZAUTH_CONTAINER + +# detect if d should run interactively +d() { + local docker_flags="" + # Check if both stdin and stdout are terminals + # If either of them is not a terminal (piped/redirected), run in detached mode + if [[ -t 0 ]] && [[ -t 1 ]]; then + docker_flags="-it" + fi + + # Run docker with appropriate flags + sudo docker run --network=host $docker_flags \ + -v "${SSH_AUTH_SOCK:-nonexistent}:/ssh-agent" \ + -e SSH_AUTH_SOCK=/ssh-agent \ + -v "$HOME/.ssh:/root/.ssh" \ + -v "$PWD:/wire-server-deploy" \ + -v "$HOME/.kube:/root/.kube" \ + -v "$HOME/.minikube:$HOME/.minikube" \ + -e KUBECONFIG=/root/.kube/config \ + "$WSD_CONTAINER" "$@" + + return 0 +} diff --git a/default.nix b/default.nix index 8df06855f..f53733d9d 100644 --- a/default.nix +++ b/default.nix @@ -45,9 +45,10 @@ rec { skopeo sops opentofu - yq + yq-go # Use yq-go (v4+) explicitly instead of python-yq for consistent YAML processing create-container-dump list-helm-containers + create-build-entry mirror-apt-jammy generate-gpg1-key # Linting @@ -57,6 +58,7 @@ rec { jq gnused curl + gawk niv nix-prefetch-docker diff --git a/nix/overlay.nix b/nix/overlay.nix index 8d53f080e..ba6012385 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -67,4 +67,14 @@ super: { install -Dm755 ${./scripts/list-helm-containers.sh} $out/bin/list-helm-containers wrapProgram $out/bin/list-helm-containers --prefix PATH : '${super.lib.makeBinPath [ self.kubernetes-helm ]}' ''; + + create-build-entry = super.runCommandNoCC "create-build-entry" + { + nativeBuildInputs = [ super.makeWrapper ]; + } + '' + install -Dm755 ${./scripts/create-build-entry.sh} $out/bin/create-build-entry + wrapProgram $out/bin/create-build-entry --prefix PATH : '${super.lib.makeBinPath (with self; [ bash jq ])}' + ''; + } diff --git a/nix/scripts/create-build-entry.sh b/nix/scripts/create-build-entry.sh new file mode 100755 index 000000000..e24cba1ae --- /dev/null +++ b/nix/scripts/create-build-entry.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -eou pipefail + +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +IMAGE_WITH_TAG=$1 +DIRECTORY=$2 + +IMAGE=$(echo "$IMAGE_WITH_TAG" | cut -d':' -f1) +TAG=$(echo "$IMAGE_WITH_TAG" | cut -d':' -f2) + +JSON_FILE="$DIRECTORY/images.json" + +if [ ! -d "$DIRECTORY" ]; then + mkdir -p "$DIRECTORY" +fi + +append_image_entry() { + local image=$1 + local tag=$2 + local json_file=$3 + + if [ -f "$json_file" ]; then + existing_content=$(jq '.' "$json_file") + + new_entry=$(jq -n --arg image "$image" --arg tag "$tag" '{$image: $tag}') + updated_content=$(echo "$existing_content" | jq --argjson new_entry "$new_entry" '. += [$new_entry]') + else + updated_content=$(jq -n --arg image "$image" --arg tag "$tag" '[{$image: $tag}]') + fi + + echo "$updated_content" | jq '.' > "$json_file" +} + +append_image_entry "$IMAGE" "$TAG" "$JSON_FILE" diff --git a/nix/scripts/list-helm-containers.sh b/nix/scripts/list-helm-containers.sh index 1271149f2..a94890d0e 100644 --- a/nix/scripts/list-helm-containers.sh +++ b/nix/scripts/list-helm-containers.sh @@ -5,7 +5,7 @@ # those. # In cases where no container image tag has been specified, it'll use `latest`. # The list is sorted and deduplicated, then printed to stdout. -set -euo pipefail +set -x -euo pipefail VALUES_DIR="" HELM_IMAGE_TREE_FILE="" From 63127b97fb650f66e0bbe1a8b20570116ac8b228 Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Wed, 12 Aug 2026 14:37:56 +0200 Subject: [PATCH 08/10] fix WPB-27900: enable patching bitnami to bitnamilegacy images --- nix/scripts/list-helm-containers.sh | 2 +- offline/tasks/process_charts.sh | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/nix/scripts/list-helm-containers.sh b/nix/scripts/list-helm-containers.sh index a94890d0e..1271149f2 100644 --- a/nix/scripts/list-helm-containers.sh +++ b/nix/scripts/list-helm-containers.sh @@ -5,7 +5,7 @@ # those. # In cases where no container image tag has been specified, it'll use `latest`. # The list is sorted and deduplicated, then printed to stdout. -set -x -euo pipefail +set -euo pipefail VALUES_DIR="" HELM_IMAGE_TREE_FILE="" diff --git a/offline/tasks/process_charts.sh b/offline/tasks/process_charts.sh index f1c6f997d..6bb9823c8 100755 --- a/offline/tasks/process_charts.sh +++ b/offline/tasks/process_charts.sh @@ -53,6 +53,14 @@ done | list-helm-containers VALUES_DIR="${OUTPUT_DIR}"/values HELM_IMAGE_TREE_FI # containers (e.g. `quay.io_wire_galley-integration_4.22.0`.) sed -i '/-integration/d' "${HELM_IMAGE_TREE_FILE}" +# Replace docker.io/bitnami with docker.io/bitnamilegacy and log updated images +# https://github.com/bitnami/charts/issues/35164 +echo "Replacing bitnami with bitnamilegacy..." +sed -i 's|bitnami/|bitnamilegacy/|g' "${OUTPUT_DIR}"/images +sed -i 's|bitnami/|bitnamilegacy/|g' "${HELM_IMAGE_TREE_FILE}" +echo "Updated images:" +grep "bitnamilegacy" "${OUTPUT_DIR}"/images || echo "No bitnami images found" + grep -vE "$EXCLUDE_PATTERN" "${OUTPUT_DIR}"/images | create-container-dump "${OUTPUT_DIR}"/containers-helm tar cf "${OUTPUT_DIR}"/containers-helm.tar -C "${OUTPUT_DIR}" containers-helm From 93934c8628ab4994cb6d15b30beeca1eb29cec2c Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Wed, 12 Aug 2026 15:11:12 +0200 Subject: [PATCH 09/10] fix WPB-27900: syncing tasks from master --- offline/tasks/build_adminhost_containers.sh | 7 ++-- offline/tasks/build_linux_pkgs.sh | 2 ++ offline/tasks/post_chart_process_0.sh | 36 +++++++++++++++++---- offline/tasks/pre_chart_process_0.sh | 2 +- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/offline/tasks/build_adminhost_containers.sh b/offline/tasks/build_adminhost_containers.sh index 4c08a1b1d..27823ac22 100755 --- a/offline/tasks/build_adminhost_containers.sh +++ b/offline/tasks/build_adminhost_containers.sh @@ -40,8 +40,9 @@ INDEX_FILE="${OUTPUT_DIR}/containers-adminhost/index.txt" if [ "$ZAUTH" = true ]; then echo "Building zauth container image in ${OUTPUT_DIR} ..." - wire_version=$(helm show chart "${OUTPUT_DIR}"/charts/wire-server | yq -r .version) + wire_version=$(helm show chart "${OUTPUT_DIR}"/charts/wire-server | yq eval '.version' -) echo "quay.io/wire/zauth:$wire_version" | create-container-dump "${OUTPUT_DIR}"/containers-adminhost + mv "${OUTPUT_DIR}/containers-adminhost/images.json" "${OUTPUT_DIR}"/versions/containers_adminhost_images.json fi if [ "$ADMINHOST" = true ]; then @@ -50,7 +51,3 @@ if [ "$ADMINHOST" = true ]; then install -m755 "$container_image" "${OUTPUT_DIR}"/containers-adminhost/container-wire-server-deploy.tgz echo "container-wire-server-deploy.tgz" >> "${INDEX_FILE}" fi - -if [[ -e "$INDEX_FILE" ]];then - cp "$INDEX_FILE" "${OUTPUT_DIR}/versions/containers-adminhost.txt" -fi diff --git a/offline/tasks/build_linux_pkgs.sh b/offline/tasks/build_linux_pkgs.sh index 3d83e620e..1733dc163 100755 --- a/offline/tasks/build_linux_pkgs.sh +++ b/offline/tasks/build_linux_pkgs.sh @@ -38,3 +38,5 @@ fingerprint=$(echo "$GPG_PRIVATE_KEY" | gpg --with-colons --import-options show- echo "$fingerprint" echo "docker_ubuntu_repo_repokey: '${fingerprint}'" > "${ROOT_DIR}"/ansible/inventory/offline/group_vars/all/key.yml +echo "docker_ubuntu_repo_repokey: '${fingerprint}'" > "${ROOT_DIR}"/ansible/inventory/dmz-k8s/group_vars/all/key.yml + diff --git a/offline/tasks/post_chart_process_0.sh b/offline/tasks/post_chart_process_0.sh index 9276809ce..c73553c16 100755 --- a/offline/tasks/post_chart_process_0.sh +++ b/offline/tasks/post_chart_process_0.sh @@ -1,15 +1,37 @@ #!/usr/bin/env bash set -euo pipefail -if [[ ! $# -eq 1 ]]; then - echo "usage: $0 OUTPUT-DIR" >&2 +# Default output dir +OUTPUT_DIR="" +# Default values type will expect to use prod values +VALUES_TYPE="prod" + +# Parse the arguments +for arg in "$@" +do + case $arg in + OUTPUT_DIR=*) + OUTPUT_DIR="${arg#*=}" + ;; + VALUES_TYPE=*) + VALUES_TYPE="${arg#*=}" + ;; + *) + echo "Unknown argument: $arg" >&2 + exit 1 + ;; + esac +done + + +# Check if OUTPUT_DIR is set +if [[ -z "$OUTPUT_DIR" ]]; then + echo "usage: $0 OUTPUT_DIR=\"values-dir\" [VALUES_TYPE=\"prod|demo\"]" >&2 exit 1 fi -OUTPUT_DIR="$1" - -echo "Running post-chart process script 0 in dir ${OUTPUT_DIR} ..." +echo "Running post-chart process script 0 in dir $OUTPUT_DIR with values type $VALUES_TYPE" # Undo changes on wire-server values.yaml -sed -i -Ee 's/useSharedFederatorSecret: true/useSharedFederatorSecret: false/' "${OUTPUT_DIR}"/charts/wire-server/charts/federator/values.yaml -sed -i -Ee 's/federation: true/federation: false/' "${OUTPUT_DIR}"/values/wire-server/prod-values.example.yaml +sed -i -Ee 's/useSharedFederatorSecret: true/useSharedFederatorSecret: false/' "${OUTPUT_DIR}/charts/wire-server/charts/federator/values.yaml" +sed -i -Ee 's/federation: true/federation: false/' "${OUTPUT_DIR}/values/wire-server/${VALUES_TYPE}-values.example.yaml" diff --git a/offline/tasks/pre_chart_process_0.sh b/offline/tasks/pre_chart_process_0.sh index 80161ad7a..fbfdb2c85 100755 --- a/offline/tasks/pre_chart_process_0.sh +++ b/offline/tasks/pre_chart_process_0.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail +set -exuo pipefail # Default output dir OUTPUT_DIR="" From df844ef5ec96807265c8468afd75f08523bf30a6 Mon Sep 17 00:00:00 2001 From: mohitrajain Date: Wed, 12 Aug 2026 15:33:42 +0200 Subject: [PATCH 10/10] fix WPB-27900: add cd scripts for cd_demo and cd_staging.sh --- offline/cd_demo.sh | 140 ++++++++++++++++++ offline/cd_staging.sh | 238 ++++++++++++++++++++++++++++++ offline/tasks/build_linux_pkgs.sh | 3 +- 3 files changed, 379 insertions(+), 2 deletions(-) create mode 100755 offline/cd_demo.sh create mode 100755 offline/cd_staging.sh diff --git a/offline/cd_demo.sh b/offline/cd_demo.sh new file mode 100755 index 000000000..d9b0ed932 --- /dev/null +++ b/offline/cd_demo.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash + +set -euo pipefail + +CD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TF_DIR="${CD_DIR}/../terraform/examples/wiab-demo-hetzner" +# shellcheck disable=SC2034 # May be used in future versions +BIN_DIR="${CD_DIR}/../bin" +# shellcheck disable=SC2034 # May be used in future versions +ARTIFACTS_DIR="${CD_DIR}/demo-build/output" +ANSIBLE_DIR="${CD_DIR}/../ansible" +INVENTORY_DIR="${ANSIBLE_DIR}/inventory/demo" +INVENTORY_FILE="${INVENTORY_DIR}/host.yml" +TF_VARS_FILE="${TF_DIR}/retry-selection.auto.tfvars.json" +TEST_USER="demo" +COMMIT_HASH="${GITHUB_SHA}" + +# Retry matrix +LOCATIONS=("hel1" "fsn1" "nbg1") +SERVER_TYPES=("cx53" "cpx62") + +# Retry configuration +RETRY_DELAY=30 +APPLY_TIMEOUT_SECONDS=300 + + +function cleanup { + (cd "$TF_DIR" && terraform destroy -auto-approve) + echo "done" +} + +trap cleanup EXIT + +function persist_terraform_vars { + local location="$1" + local server_type="$2" + + printf '{\n "location": "%s",\n "server_type": "%s"\n}\n' \ + "$location" \ + "$server_type" > "$TF_VARS_FILE" +} + +cd "$TF_DIR" +terraform init + +if ! command -v timeout >/dev/null 2>&1; then + echo "The 'timeout' command is required but not installed" + exit 1 +fi + +if [[ ${#LOCATIONS[@]} -eq 0 || ${#SERVER_TYPES[@]} -eq 0 ]]; then + echo "No location or server type preferences configured in the retry matrix" + exit 1 +fi + +location_count=${#LOCATIONS[@]} +server_type_count=${#SERVER_TYPES[@]} +MAX_RETRIES=$((location_count * server_type_count)) + +echo "Retry plan: ${location_count} locations x ${server_type_count} server types = ${MAX_RETRIES} attempts" + +attempt=1 +deployment_succeeded=false + +for server_type_index in $(seq 0 $((server_type_count - 1))); do + for location_index in $(seq 0 $((location_count - 1))); do + attempt_location="${LOCATIONS[$location_index]}" + attempt_server_type="${SERVER_TYPES[$server_type_index]}" + + persist_terraform_vars "$attempt_location" "$attempt_server_type" + + echo "Deployment attempt $attempt of $MAX_RETRIES" + echo " -> location=${attempt_location}, size=${attempt_server_type}" + date + + if timeout "${APPLY_TIMEOUT_SECONDS}s" terraform apply -auto-approve; then + deployment_succeeded=true + break 2 + fi + + apply_exit_code=$? + + if [[ $apply_exit_code -eq 124 ]]; then + echo "Infrastructure deployment timed out after ${APPLY_TIMEOUT_SECONDS}s on attempt $attempt" + else + echo "Infrastructure deployment failed on attempt $attempt" + fi + + if [[ $attempt -lt $MAX_RETRIES ]]; then + echo "Cleaning up partial deployment..." + terraform destroy -auto-approve || true + + echo "Waiting ${RETRY_DELAY}s for resources to become available..." + sleep $RETRY_DELAY + fi + + attempt=$((attempt + 1)) + done +done + +if [[ "$deployment_succeeded" != true ]]; then + echo "All deployment attempts failed after $MAX_RETRIES tries" + exit 1 +fi + +echo "" +echo "Infrastructure ready! Proceeding with application deployment..." + +host=$(terraform output -raw host) +ssh_private_key=$(terraform output ssh_private_key) + +rm -f "${INVENTORY_DIR}/ssh_private_key" || true +echo "$ssh_private_key" > "${INVENTORY_DIR}/ssh_private_key" +chmod 400 "${INVENTORY_DIR}/ssh_private_key" + +# clean old host verification keys to avoid SSH issues +ssh-keygen -R "$host" || true + +# create demo user on the remote host +ssh -v -oStrictHostKeyChecking=accept-new -oConnectionAttempts=10 -i "${INVENTORY_DIR}/ssh_private_key" "root@$host" \ +"useradd -m -s /bin/bash ${TEST_USER} && \ +usermod -aG sudo ${TEST_USER} && \ +mkdir -p /home/${TEST_USER}/.ssh && \ +cp /root/.ssh/authorized_keys /home/${TEST_USER}/.ssh/ && \ +chown -R ${TEST_USER}:${TEST_USER} /home/${TEST_USER}/.ssh && \ +chmod 700 /home/${TEST_USER}/.ssh && \ +chmod 600 /home/${TEST_USER}/.ssh/authorized_keys && \ +echo '${TEST_USER} ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/${TEST_USER}" + +# update inventory file with host details +yq eval -i ".wiab.hosts.deploy_node.ansible_host = \"$host\"" "${INVENTORY_FILE}" +yq eval -i ".wiab.hosts.deploy_node.ansible_ssh_private_key_file = \"${INVENTORY_DIR}/ssh_private_key\"" "${INVENTORY_FILE}" +yq eval -i ".wiab.vars.artifact_hash = \"$COMMIT_HASH\"" "${INVENTORY_FILE}" +yq eval -i ".wiab.hosts.deploy_node.ansible_user = \"$TEST_USER\"" "${INVENTORY_FILE}" + +echo "Running ansible playbook deploy_wiab.yml against node $host" +# deploying demo-wiab +ansible-playbook -i "${INVENTORY_FILE}" "${ANSIBLE_DIR}/wiab-demo/deploy_wiab.yml" --skip-tags verify_dns +# cleaning demo-wiab +ansible-playbook -i "${INVENTORY_FILE}" "${ANSIBLE_DIR}/wiab-demo/clean_cluster.yml" --tags remove_minikube,remove_artifacts,remove_packages,remove_iptables,remove_ssh diff --git a/offline/cd_staging.sh b/offline/cd_staging.sh new file mode 100755 index 000000000..cd4d2ccbc --- /dev/null +++ b/offline/cd_staging.sh @@ -0,0 +1,238 @@ +#!/usr/bin/env bash + +set -euo pipefail + +CD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TF_DIR="${CD_DIR}/../terraform/examples/wiab-staging-hetzner" +VALUES_DIR="${CD_DIR}/../values" +TF_VARS_FILE="${TF_DIR}/retry-selection.auto.tfvars.json" +COMMIT_HASH="${GITHUB_SHA}" +ARTIFACT="wire-server-deploy-static-${COMMIT_HASH}" + +# Retry matrix +LOCATIONS=("hel1" "nbg1" "fsn1") +SMALL_SERVER_TYPES=("cpx22" "cpx32" "cpx42") +MEDIUM_SERVER_TYPES=("cpx42" "cpx52" "cpx62") + +# Retry configuration +RETRY_DELAY=30 +APPLY_TIMEOUT_SECONDS=300 + +echo "Wire Offline Deployment with Retry Logic" +echo "========================================" + +function cleanup { + (cd "$TF_DIR" && terraform destroy -auto-approve) + echo "Cleanup completed" +} +trap cleanup EXIT + +function persist_terraform_vars { + local location="$1" + local small_server_type="$2" + local medium_server_type="$3" + + printf '{\n "location": "%s",\n "small_server_type": "%s",\n "medium_server_type": "%s"\n}\n' \ + "$location" \ + "$small_server_type" \ + "$medium_server_type" > "$TF_VARS_FILE" +} + +cd "$TF_DIR" +terraform init + +if ! command -v timeout >/dev/null 2>&1; then + echo "The 'timeout' command is required but not installed" + exit 1 +fi + +if [[ ${#SMALL_SERVER_TYPES[@]} -ne ${#MEDIUM_SERVER_TYPES[@]} ]]; then + echo "Small and medium server type retry lists must have the same length" + exit 1 +fi + +if [[ ${#LOCATIONS[@]} -eq 0 || ${#SMALL_SERVER_TYPES[@]} -eq 0 ]]; then + echo "No location or server type preferences configured in the retry matrix" + exit 1 +fi + +location_count=${#LOCATIONS[@]} +server_type_count=${#SMALL_SERVER_TYPES[@]} +MAX_RETRIES=$((location_count * server_type_count)) + +echo "Retry plan: ${location_count} locations x ${server_type_count} server type pairs = ${MAX_RETRIES} attempts" + +# Retry loop for terraform apply +echo "Starting deployment with automatic retry on resource unavailability..." +attempt=1 +deployment_succeeded=false + +for server_type_index in $(seq 0 $((server_type_count - 1))); do + for location_index in $(seq 0 $((location_count - 1))); do + attempt_location="${LOCATIONS[$location_index]}" + attempt_small_server_type="${SMALL_SERVER_TYPES[$server_type_index]}" + attempt_medium_server_type="${MEDIUM_SERVER_TYPES[$server_type_index]}" + + persist_terraform_vars "$attempt_location" "$attempt_small_server_type" "$attempt_medium_server_type" + + + echo "" + echo "Deployment attempt $attempt of $MAX_RETRIES" + echo " -> location=${attempt_location}, small=${attempt_small_server_type}, medium=${attempt_medium_server_type}" + date + + if timeout "${APPLY_TIMEOUT_SECONDS}s" terraform apply -auto-approve; then + echo "Infrastructure deployment successful on attempt $attempt!" + deployment_succeeded=true + break 2 + fi + + apply_exit_code=$? + + if [[ $apply_exit_code -eq 124 ]]; then + echo "Infrastructure deployment timed out after ${APPLY_TIMEOUT_SECONDS}s on attempt $attempt" + else + echo "Infrastructure deployment failed on attempt $attempt" + fi + + if [[ $attempt -lt $MAX_RETRIES ]]; then + echo "Will retry with the next location and server type combination..." + + echo "Cleaning up partial deployment..." + terraform destroy -auto-approve || true + + echo "Waiting ${RETRY_DELAY}s for resources to become available..." + sleep $RETRY_DELAY + fi + + attempt=$((attempt + 1)) + done +done + +if [[ "$deployment_succeeded" != true ]]; then + echo "All deployment attempts failed after $MAX_RETRIES tries" + echo "" + echo "This usually means:" + echo " 1. High demand for Hetzner Cloud resources in EU regions" + echo " 2. Your account may have resource limits" + echo " 3. Try again later when resources become available" + echo "" + echo "Manual solutions:" + echo " 1. Check Hetzner Console for resource limits" + echo " 2. Try different server types manually" + echo " 3. Contact Hetzner support for resource availability" + exit 1 +fi + +echo "" +echo "Infrastructure ready! Proceeding with application deployment..." + +# Common SSH options for all ssh and scp commands +SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=10 -o ConnectTimeout=15 -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o TCPKeepAlive=yes" + +# Continue with the rest of the original cd.sh logic +adminhost=$(terraform output -raw adminhost) +ssh_private_key=$(terraform output ssh_private_key) + +eval "$(ssh-agent)" +ssh-add - <<< "$ssh_private_key" +rm -f ssh_private_key || true +echo "$ssh_private_key" > ssh_private_key +chmod 400 ssh_private_key + +terraform output -json static-inventory > inventory.json +yq eval -o=yaml '.' inventory.json > inventory.yml + +echo "Running ansible playbook setup_nodes.yml via adminhost ($adminhost)..." +ansible-playbook -i inventory.yml setup_nodes.yml --private-key "ssh_private_key" + +# user demo needs to exist +ssh $SSH_OPTS "demo@$adminhost" wget -q "https://s3-eu-west-1.amazonaws.com/public.wire.com/artifacts/${ARTIFACT}.tgz" + +ssh $SSH_OPTS "demo@$adminhost" tar xzf "${ARTIFACT}.tgz" + +# Source and target files +SOURCE="inventory.yml" +cp "${CD_DIR}/../ansible/inventory/offline/staging.yml" "inventory-secondary.yml" +TARGET="inventory-secondary.yml" + +# Read assethost IP +ASSETHOST_IP=$(yq eval '.assethost.hosts.assethost.ansible_host' "$SOURCE") +yq eval -i ".assethost.hosts.assethost.ansible_host = \"$ASSETHOST_IP\"" "$TARGET" + +# Read kube-node IPs using to_entries +KUBENODE1_IP=$(yq eval '.["kube-node"].hosts | to_entries | .[0].value.ansible_host' "$SOURCE") +KUBENODE2_IP=$(yq eval '.["kube-node"].hosts | to_entries | .[1].value.ansible_host' "$SOURCE") +KUBENODE3_IP=$(yq eval '.["kube-node"].hosts | to_entries | .[2].value.ansible_host' "$SOURCE") + +yq eval -i ".kube-node.hosts.kubenode1.ansible_host = \"$KUBENODE1_IP\"" "$TARGET" +yq eval -i ".kube-node.hosts.kubenode2.ansible_host = \"$KUBENODE2_IP\"" "$TARGET" +yq eval -i ".kube-node.hosts.kubenode3.ansible_host = \"$KUBENODE3_IP\"" "$TARGET" + +# Read datanodes IPs using to_entries +DATANODE1_IP=$(yq eval '.datanode.hosts | to_entries | .[0].value.ansible_host' "$SOURCE") +DATANODE2_IP=$(yq eval '.datanode.hosts | to_entries | .[1].value.ansible_host' "$SOURCE") +DATANODE3_IP=$(yq eval '.datanode.hosts | to_entries | .[2].value.ansible_host' "$SOURCE") + +# Read datanodes names using to_entries +DATANODE1_NAME=$(yq eval '.datanode.hosts | keys | .[0]' "$SOURCE") +DATANODE2_NAME=$(yq eval '.datanode.hosts | keys | .[1]' "$SOURCE") +DATANODE3_NAME=$(yq eval '.datanode.hosts | keys | .[2]' "$SOURCE") + +# clean old hosts for datanodes +yq eval -i '.datanodes.hosts = {}' "$TARGET" + +# re-create the datanodes group with actual names from SOURCE +yq eval -i ".datanodes.hosts[\"${DATANODE1_NAME}\"].ansible_host = \"${DATANODE1_IP}\"" "$TARGET" +yq eval -i ".datanodes.hosts[\"${DATANODE2_NAME}\"].ansible_host = \"${DATANODE2_IP}\"" "$TARGET" +yq eval -i ".datanodes.hosts[\"${DATANODE3_NAME}\"].ansible_host = \"${DATANODE3_IP}\"" "$TARGET" + +# Override network_interface from SOURCE to TARGET for all service groups +NETWORK_INTERFACE=$(yq eval '.datanode.vars.datanode_network_interface' "$SOURCE") +yq eval -i ".cassandra.vars.cassandra_network_interface = \"$NETWORK_INTERFACE\"" "$TARGET" +yq eval -i ".elasticsearch.vars.elasticsearch_network_interface = \"$NETWORK_INTERFACE\"" "$TARGET" +yq eval -i ".minio.vars.minio_network_interface = \"$NETWORK_INTERFACE\"" "$TARGET" +yq eval -i ".postgresql.vars.postgresql_network_interface = \"$NETWORK_INTERFACE\"" "$TARGET" +yq eval -i ".rmq-cluster.vars.rabbitmq_network_interface = \"$NETWORK_INTERFACE\"" "$TARGET" + +# re-writing sub-groups for rabbitmq_cluster_master, cassandra_seed, postgresql_rw and postgresql_ro +yq eval -i ".rmq-cluster.vars.rabbitmq_cluster_master = \"${DATANODE1_NAME}\"" "$TARGET" + +yq eval -i '.cassandra_seed.hosts = {}' "$TARGET" +yq eval -i ".cassandra_seed.hosts.[\"${DATANODE1_NAME}\"] = \"\"" "$TARGET" + +yq eval -i '.postgresql_rw.hosts = {}' "$TARGET" +yq eval -i '.postgresql_ro.hosts = {}' "$TARGET" +yq eval -i ".postgresql_rw.hosts.[\"${DATANODE1_NAME}\"] = \"\"" "$TARGET" +yq eval -i ".postgresql_ro.hosts.[\"${DATANODE2_NAME}\"] = \"\"" "$TARGET" +yq eval -i ".postgresql_ro.hosts.[\"${DATANODE3_NAME}\"] = \"\"" "$TARGET" + +# re-populate the postgresql.vars.repmgr_node_config group with actual names from SOURCE +i=1 +while IFS= read -r actual_name; do + yq eval -i " + .postgresql.vars.repmgr_node_config[\"${actual_name}\"] = + .postgresql.vars.repmgr_node_config.datanode${i} + | del(.postgresql.vars.repmgr_node_config.datanode${i}) + " "$TARGET" + i=$((i+1)) +done < <(yq eval -r '.datanode.hosts | keys | .[]' "$SOURCE") + +# Extract all kube-node vars from SOURCE and merge into TARGET +KUBE_NODE_VARS_FILE=$(mktemp) +yq eval '.["kube-node"].vars' "$SOURCE" > "$KUBE_NODE_VARS_FILE" +yq eval -i '.kube-node.vars |= load("'"$KUBE_NODE_VARS_FILE"'")' "$TARGET" + +rm -f "$KUBE_NODE_VARS_FILE" + +echo "created secondary inventory file $TARGET successfully" + +scp $SSH_OPTS "$TARGET" "demo@$adminhost":./ansible/inventory/offline/inventory.yml + +ssh $SSH_OPTS "demo@$adminhost" cat ./ansible/inventory/offline/inventory.yml || true + +# NOTE: Agent is forwarded; so that the adminhost can provision the other boxes +ssh $SSH_OPTS -A "demo@$adminhost" ./bin/offline-deploy.sh + +echo "" +echo "Wire offline deployment completed successfully!" diff --git a/offline/tasks/build_linux_pkgs.sh b/offline/tasks/build_linux_pkgs.sh index 1733dc163..e73a269e0 100755 --- a/offline/tasks/build_linux_pkgs.sh +++ b/offline/tasks/build_linux_pkgs.sh @@ -38,5 +38,4 @@ fingerprint=$(echo "$GPG_PRIVATE_KEY" | gpg --with-colons --import-options show- echo "$fingerprint" echo "docker_ubuntu_repo_repokey: '${fingerprint}'" > "${ROOT_DIR}"/ansible/inventory/offline/group_vars/all/key.yml -echo "docker_ubuntu_repo_repokey: '${fingerprint}'" > "${ROOT_DIR}"/ansible/inventory/dmz-k8s/group_vars/all/key.yml - +#echo "docker_ubuntu_repo_repokey: '${fingerprint}'" > "${ROOT_DIR}"/ansible/inventory/dmz-k8s/group_vars/all/key.yml