Skip to content
LinkState
Go back

Can AI help with lab-host preflight drift

Introduction to Pre-Launch Checks

Overview of Pre-Launch Requirements

Before launching a large-scale lab environment, operators must verify that each host meets a baseline set of operational constraints. These constraints typically include:

A pre-launch check is a deterministic gate that runs before workload placement. If any check fails, the automation halts, surfaces the deficiency, and requires remediation before proceeding.

Importance of Kernel Settings, Registry Auth, and Host-Package Drift

Fixed Bash Precheck

Overview of Bash Precheck Script

A fixed Bash precheck is a self-contained script that executes a series of primitive tests using standard Unix utilities (sysctl, crictl, rpm, dpkg, apt, curl, grep, etc.). The script returns a non-zero exit code if any test fails and prints a human-readable summary to stdout/stderr.

Example Bash Precheck Code

#!/usr/bin/env bash
# precheck_basic.sh
set -euo pipefail

# ---- Configurable thresholds ----
KERNEL_SETTINGS=(
  "net.core.rmem_max=26214400"
  "net.core.wmem_max=26214400"
  "vm.swappiness=1"
  "fs.inotify.max_user_watches=524288"
)
REGISTRY_URL="registry.example.com"
REGISTRY_USER="lab-ci"
REGISTRY_TOKEN="$(cat /etc/registry/token)"

REQUIRED_PKGS=(
  "docker-ce=5:24.0.5-1~ubuntu.22.04~jammy"
  "kubeadm=1.29.0-00"
  "openvswitch-switch=2.17.0-0ubuntu0.22.04.1"
)

# ---- Helper functions ----
log() { echo "[precheck] $*"; }
fail() { log "ERROR: $*"; exit 1; }

# ---- Kernel settings check ----
log "Verifying kernel sysctl values..."
for entry in "${KERNEL_SETTINGS[@]}"; do
  key="${entry%%=*}"
  expected="${entry#*=}"
  actual="$(sysctl -n "$key" 2>/dev/null || true)"
  if [[ "$actual" != "$expected" ]]; then
    fail "Kernel mismatch: $key expected $expected, got ${actual:-<not set>}"
  fi
done
log "Kernel settings OK."

# ---- Registry auth check ----
log "Testing registry authentication..."
if ! curl -sSf -u "${REGISTRY_USER}:${REGISTRY_TOKEN}" \
  "https://${REGISTRY_URL}/v2/" >/dev/null; then
  fail "Unable to authenticate to ${REGISTRY_URL} as ${REGISTRY_USER}"
fi
log "Registry auth OK."

# ---- Host-package drift check (Debian/Ubuntu example) ----
log "Checking installed package versions..."
for pkg_spec in "${REQUIRED_PKGS[@]}"; do
  pkg_name="${pkg_spec%%=*}"
  expected_ver="${pkg_spec#*=}"
  installed_ver="$(dpkg-query -W -f='${Version}' "$pkg_name" 2>/dev/null || true)"
  if [[ -z "$installed_ver" ]]; then
    fail "Package $pkg_name not installed"
  fi
  if [[ "$installed_ver" != "$expected_ver" ]]; then
    fail "Package drift: $pkg_name expected $expected_ver, got $installed_ver"
  fi
done
log "Host-package versions OK."
log "All prechecks passed."
exit 0

Limitations of Fixed Bash Precheck

LimitationImpact
Static logicOperational overhead; risk of version skew across fleets.
Primitive error handlingMissed diagnostics; harder to produce structured output for downstream systems.
No data enrichmentLimited context for troubleshooting.
Scalability of executionPotential bottleneck in large labs.
Security exposureRequires strict file-mode auditing.

Structured Validation Pipeline

Architecture of Validation Pipeline

A structured validation pipeline decouples the definition of checks from their execution. Checks are expressed as declarative units (e.g., YAML or JSON) that a pipeline engine evaluates.

Components of Validation Pipeline

ComponentTypical TechnologyResponsibility
InventoryAnsible static inventory, Terraform state, Consul catalog, or a simple CSVProvides list of hostnames/IPs and optional metadata (OS, role).
Check LibraryReusable Bash/Python scripts, OPA policies, or Containerized checks (Docker images)Implements the actual test logic; returns structured JSON.
ExecutorAnsible ad-hoc, ssh with parallel, kubectl jobs, or a custom Go worker poolRuns checks against each host, collects stdout/stderr, respects timeout and retries.
AggregatorPipeline stage that merges per-host results (e.g., using jq -s or a Python script)Produces a summary: total passed/failed, list of failures per host.
ReporterJUnit XML generator, GitLab CI artifacts, or a Slack webhookCommunicates outcome to operators and triggers alerts.
GateCI allow_failure: false, or a manual approval jobBlocks promotion to next stage if any failure exists.

Example Validation Pipeline Code

stages:
  - validate
validate:
  stage: validate
  image: python:3.11-slim
  before_install:
    - apt-get update && apt-get install -y ansible sshpass
  script:
    - |
      ANSIBLE_HOST_KEY_CHECKING=False \
      ansible-playbook -i inventory.yml \
      -e "registry_user=${REGISTRY_USER} registry_token=${REGISTRY_TOKEN}" \
      site.yml
  artifacts:
    reports:
      junit: junit.xml
    expire_in: 1 week
  only:
    - main
all:
  hosts:
    lab-node01:
      ansible_host: 10.0.1.10
    lab-node02:
      ansible_host: 10.0.1.11
    # … scale to hundreds …
- hosts: all
  become: true
  vars:
    required_kernel:
      net.core.rmem_max: 26214400
      net.core.wmem_max: 26214400
      vm.swappiness: 1
      fs.inotify.max_user_watches: 524288
    required_packages:
      docker-ce: "5:24.0.5-1~ubuntu.22.04~jammy"
      kubeadm: "1.29.0-00"
      openvswitch-switch: "2.17.0-0ubuntu0.22.04.1"
  roles:
    - { role: check_kernel }
    - { role: check_registry }
    - { role: check_packages }
- name: Gather current sysctl values
  ansible.builtin.command: sysctl -n {{ item.key }}
  register: sysctl_out
  changed_when: false
  loop: "{{ required_kernel | dict2items }}"
  loop_control: label: "{{ item.key }}"
- name: Compare expected vs actual
  ansible.builtin.assert:
    that: - "{{ item.1.stdout.strip() | string }} == {{ item.0.value | string }}"
    fail_msg: "Kernel mismatch: {{ item.0.key }} expected {{ item.0.value }}, got {{ item.1.stdout.strip() }}"
    success_msg: "Kernel OK: {{ item.0.key }}"
  loop: "{{ required_kernel | dict2items | zip(sysctl_out.results) | list }}"
- name: Test registry auth
  ansible.builtin.uri:
    url: "https://{{ lookup('env','REGISTRY_URL') }}/v2/"
    method: GET
    user: "{{ lookup('env','REGISTRY_USER') }}"
    password: "{{ lookup('env','REGISTRY_TOKEN') }}"
    status_code: 200
    validate_certs: yes
    timeout: 10
  register: reg_result
  until: reg_result.status == 200
  retries: 3
  delay: 5
- name: Get installed package version (Debian)
  ansible.builtin.command: dpkg-query -W -f='{{Package}}={{Version}}' {{ item.key }}
  register: pkg_out
  changed_when: false
  ignore_errors: yes
  loop: "{{ required_packages | dict2items }}"
- name: Assert version matches
  ansible.builtin.assert:
    that: - "{{ item.1.stdout }} == '{{ item.0.key }}={{ item.0.value }}'"
    fail_msg: "Package drift: {{ item.0.key }} expected {{ item.0.value }}, got {{ (item.1.stdout | split('='))[1] | default('<not installed>') }}"
  loop: "{{ required_packages | dict2items | zip(pkg_out.results) | list }}"

Share this post on:

Previous Post
Local-pref tiers without permanent hot potato surprises
Next Post
Calico precedence traps during cross-zone canaries