#!/usr/bin/env bash

set -Eeuo pipefail
export LC_ALL=C

EXPECTED_HOSTNAME="sqjcnginx01"
EXPECTED_IPV4="10.2.203.102"
EXPECTED_OS="openEuler release 24.03 (LTS-SP1)"
EXPECTED_OLD_COUNT=29
EXPECTED_OLD_MANIFEST_SHA256="1afa990482067def7817332403121428d42209a5e6e30e0b58a83be5e36f53df"
EXPECTED_INSTALL_COUNT=18
EXPECTED_UPGRADE_CANDIDATE_COUNT=11
EXPECTED_PERCONA_COUNT=3
PACKAGE_ROOT="/opt/zhct-deploy/packages"
RPM_ROOT="${PACKAGE_ROOT}/rpms"
RPM_MANIFEST="${PACKAGE_ROOT}/rpms.sha256"
STAGE="${PACKAGE_ROOT}/rpms.q040-install-only-stage"
EVIDENCE_DIR="/var/backups/zhct-q040/20260722T203557+0800"
REJECTED_ROOT="${EVIDENCE_DIR}/rejected-assets/rpms-29-upgrades"
REJECTED_EVIDENCE="${EVIDENCE_DIR}/rejected-assets/rpms-29-upgrades-evidence"
CLASSIFICATION="${EVIDENCE_DIR}/install-only-rpm-classification.tsv"

EXPECTED_INSTALL_NAMES=(
  autoconf
  automake
  gcc-c++
  jq
  libmnl-devel
  libnfnetlink-devel
  libnftnl-devel
  libnl3-devel
  libstdc++-devel
  libtool
  oniguruma
  pcre2-devel
  percona-orchestrator
  percona-orchestrator-cli
  percona-orchestrator-client
  systemd-devel
  systemd-pam
  zlib-devel
)

EXPECTED_UPGRADE_NAMES=(
  cpp
  gcc
  libgcc
  libgomp
  libstdc++
  shadow
  systemd
  systemd-cryptsetup
  systemd-libs
  systemd-udev
  zlib
)

die() {
  printf 'ERROR: %s\n' "$*" >&2
  exit 1
}

[[ "${EUID}" -eq 0 ]] || die "must run as root"
[[ "$(hostname -s)" == "${EXPECTED_HOSTNAME}" ]] || die "hostname mismatch"
[[ "$(tr -d '\r' </etc/openEuler-release)" == "${EXPECTED_OS}" ]] || die "OS mismatch"
ip -o -4 addr show scope global | awk '{print $4}' | cut -d/ -f1 | \
  grep -Fxq "${EXPECTED_IPV4}" || die "expected IPv4 is not configured"
[[ -d "${RPM_ROOT}" ]] || die "29-RPM closure is missing"
[[ -s "${RPM_MANIFEST}" ]] || die "29-RPM manifest is missing"
[[ "$(sha256sum "${RPM_MANIFEST}" | awk '{print $1}')" == "${EXPECTED_OLD_MANIFEST_SHA256}" ]] || \
  die "29-RPM manifest SHA256 mismatch"
[[ $(find "${RPM_ROOT}" -maxdepth 1 -type f -name '*.rpm' | wc -l) -eq "${EXPECTED_OLD_COUNT}" ]] || \
  die "old closure does not contain 29 RPMs"
[[ ! -e "${STAGE}" ]] || die "install-only stage already exists"
[[ ! -e "${REJECTED_ROOT}" ]] || die "rejected 29-RPM archive already exists"
[[ ! -e "${REJECTED_EVIDENCE}" ]] || die "rejected 29-RPM evidence archive already exists"

umask 077
install -d -m 0700 "${STAGE}" "${REJECTED_EVIDENCE}"
printf 'decision\tfile\tnevra\tinstalled_nevra\n' >"${CLASSIFICATION}"

install_count=0
upgrade_candidate_count=0
install_names=()
upgrade_candidate_names=()
while IFS= read -r -d '' rpm_file; do
  name=$(rpm -qp --qf '%{NAME}' "${rpm_file}" 2>/dev/null)
  nevra=$(rpm -qp --qf '%{NAME}-%{EPOCHNUM}:%{VERSION}-%{RELEASE}.%{ARCH}' "${rpm_file}" 2>/dev/null)
  if installed_nevra=$(rpm -q --qf '%{NAME}-%{EPOCHNUM}:%{VERSION}-%{RELEASE}.%{ARCH}' "${name}" 2>/dev/null); then
    printf 'REJECT_UPGRADE_CANDIDATE\t%s\t%s\t%s\n' \
      "$(basename "${rpm_file}")" "${nevra}" "${installed_nevra}" >>"${CLASSIFICATION}"
    upgrade_candidate_names+=("${name}")
    upgrade_candidate_count=$((upgrade_candidate_count + 1))
  else
    printf 'KEEP_INSTALL\t%s\t%s\tNOT_INSTALLED\n' \
      "$(basename "${rpm_file}")" "${nevra}" >>"${CLASSIFICATION}"
    cp -p "${rpm_file}" "${STAGE}/"
    install_names+=("${name}")
    install_count=$((install_count + 1))
  fi
done < <(find "${RPM_ROOT}" -maxdepth 1 -type f -name '*.rpm' -print0 | sort -z)

[[ "${install_count}" -eq "${EXPECTED_INSTALL_COUNT}" ]] || \
  die "expected 18 install RPMs, found ${install_count}"
[[ "${upgrade_candidate_count}" -eq "${EXPECTED_UPGRADE_CANDIDATE_COUNT}" ]] || \
  die "expected 11 upgrade candidates, found ${upgrade_candidate_count}"
printf '%s\n' "${EXPECTED_INSTALL_NAMES[@]}" | sort -u \
  >"${EVIDENCE_DIR}/install-only-expected-package-names.txt"
printf '%s\n' "${install_names[@]}" | sort -u \
  >"${EVIDENCE_DIR}/install-only-actual-package-names.txt"
cmp -s "${EVIDENCE_DIR}/install-only-expected-package-names.txt" \
  "${EVIDENCE_DIR}/install-only-actual-package-names.txt" || \
  die "install-only package-name set is not the expected set"
printf '%s\n' "${EXPECTED_UPGRADE_NAMES[@]}" | sort -u \
  >"${EVIDENCE_DIR}/install-only-expected-upgrade-candidate-names.txt"
printf '%s\n' "${upgrade_candidate_names[@]}" | sort -u \
  >"${EVIDENCE_DIR}/install-only-actual-upgrade-candidate-names.txt"
cmp -s "${EVIDENCE_DIR}/install-only-expected-upgrade-candidate-names.txt" \
  "${EVIDENCE_DIR}/install-only-actual-upgrade-candidate-names.txt" || \
  die "upgrade-candidate package-name set is not the expected set"
[[ $(find "${STAGE}" -maxdepth 1 -type f -name 'percona-orchestrator*.rpm' | wc -l) -eq "${EXPECTED_PERCONA_COUNT}" ]] || \
  die "install-only closure does not contain three Percona RPMs"

open_euler_signature_log="${EVIDENCE_DIR}/install-only-openeuler-rpm-signatures.txt"
: >"${open_euler_signature_log}"
open_euler_count=0
while IFS= read -r -d '' rpm_file; do
  rpm --define '_pkgverify_level signature' -K "${rpm_file}" >>"${open_euler_signature_log}" 2>&1
  open_euler_count=$((open_euler_count + 1))
done < <(find "${STAGE}" -maxdepth 1 -type f -name '*.rpm' \
  ! -name 'percona-orchestrator*.rpm' -print0 | sort -z)
[[ "${open_euler_count}" -eq 15 ]] || die "expected 15 openEuler install RPMs"
[[ $(grep -Ec 'signatures OK$' "${open_euler_signature_log}") -eq "${open_euler_count}" ]] || \
  die "not every openEuler install RPM has a trusted signature"
if grep -Eiq 'BAD|NOT OK|NOKEY|NOTTRUSTED' "${open_euler_signature_log}"; then
  die "an openEuler install RPM signature is not trusted"
fi

percona_signature_evidence="${EVIDENCE_DIR}/orchestrator-rpm-signature-verified.txt"
[[ $(grep -Eci '8507efa5:[[:space:]]+OK' "${percona_signature_evidence}") -eq 3 ]] || \
  die "expected three verified Percona signatures"
if grep -Eiq 'BAD|NOT OK|NOKEY|NOTTRUSTED' "${percona_signature_evidence}"; then
  die "Percona signature evidence contains a failure"
fi

mapfile -d '' install_rpms < <(find "${STAGE}" -maxdepth 1 -type f -name '*.rpm' -print0 | sort -z)
set +e
dnf --disablerepo='*' \
  --setopt=strict=True \
  --setopt=install_weak_deps=False \
  --setopt=localpkg_gpgcheck=False \
  --assumeno install "${install_rpms[@]}" \
  >"${EVIDENCE_DIR}/dnf-install-only-assumeno.log" 2>&1
dnf_rc=$?
set -e
[[ "${dnf_rc}" -eq 1 ]] || die "unexpected DNF assumeno exit code: ${dnf_rc}"
grep -Fq 'Transaction Summary' "${EVIDENCE_DIR}/dnf-install-only-assumeno.log" || \
  die "install-only closure did not reach a transaction summary"
grep -Eq '^[[:space:]]*Install[[:space:]]+18[[:space:]]+Packages' \
  "${EVIDENCE_DIR}/dnf-install-only-assumeno.log" || \
  die "install-only closure transaction is not exactly 18 installs"
if grep -Eiq '(^|[[:space:]])(Upgrading|Downgrading|Removing|Reinstalling|Obsoleting)(:|[[:space:]])|^[[:space:]]*(Upgrade|Downgrade|Remove|Reinstall|Obsolete)[[:space:]]+[0-9]+' \
  "${EVIDENCE_DIR}/dnf-install-only-assumeno.log"; then
  die "install-only closure still contains a non-install action"
fi
if grep -Eiq 'nothing provides|conflicting requests|transaction test error|protected packages|受保护的软件包|冲突的请求' \
  "${EVIDENCE_DIR}/dnf-install-only-assumeno.log"; then
  die "install-only closure has an unresolved dependency or conflict"
fi

shopt -s nullglob
old_evidence=(
  "${EVIDENCE_DIR}"/minimal-*
  "${EVIDENCE_DIR}"/dnf-minimal-closure-assumeno.log
  "${EVIDENCE_DIR}"/v8-*
  "${EVIDENCE_DIR}"/q040-v8-evidence-index.sha256
  "${EVIDENCE_DIR}"/nginx-role-install-dry-run.log
)
for evidence_file in "${old_evidence[@]}"; do
  cp -p "${evidence_file}" "${REJECTED_EVIDENCE}/"
done
shopt -u nullglob

mv "${RPM_ROOT}" "${REJECTED_ROOT}"
mv "${RPM_MANIFEST}" "${EVIDENCE_DIR}/rejected-assets/rpms-29-upgrades.sha256"
mv "${STAGE}" "${RPM_ROOT}"

manifest_temp=$(mktemp "${PACKAGE_ROOT}/.rpms.sha256.XXXXXX")
trap 'rm -f "${manifest_temp}"' EXIT
(
  cd "${PACKAGE_ROOT}"
  find rpms -maxdepth 1 -type f -name '*.rpm' -print0 | sort -z | xargs -0 sha256sum
) >"${manifest_temp}"
[[ $(wc -l <"${manifest_temp}") -eq "${EXPECTED_INSTALL_COUNT}" ]] || \
  die "generated manifest does not contain 18 RPMs"
mv "${manifest_temp}" "${RPM_MANIFEST}"
chmod 0640 "${RPM_MANIFEST}"
chmod -R go-rwx "${RPM_ROOT}" "${REJECTED_ROOT}" "${REJECTED_EVIDENCE}"

(
  cd "${PACKAGE_ROOT}"
  sha256sum -c rpms.sha256
) >"${EVIDENCE_DIR}/install-only-rpm-sha256-check.txt"
find "${RPM_ROOT}" -maxdepth 1 -type f -name '*.rpm' -print0 | sort -z | \
  xargs -0 -r rpm -qp --qf '%{NAME}\t%{EPOCHNUM}:%{VERSION}-%{RELEASE}\t%{ARCH}\t%{SIZE}\t%{VENDOR}\n' \
  2>/dev/null | sort -u >"${EVIDENCE_DIR}/install-only-rpm-nevra.tsv"
find "${RPM_ROOT}" -maxdepth 1 -type f -name '*.rpm' -print0 | sort -z | \
  xargs -0 sha256sum >"${EVIDENCE_DIR}/install-only-rpm-sha256-absolute.txt"

cp "${EVIDENCE_DIR}/install-only-rpm-nevra.tsv" "${EVIDENCE_DIR}/minimal-rpm-nevra.tsv"
cp "${EVIDENCE_DIR}/install-only-rpm-sha256-absolute.txt" "${EVIDENCE_DIR}/minimal-rpm-sha256-absolute.txt"
cp "${EVIDENCE_DIR}/install-only-openeuler-rpm-signatures.txt" \
  "${EVIDENCE_DIR}/minimal-openeuler-rpm-signatures.txt"
cp "${EVIDENCE_DIR}/dnf-install-only-assumeno.log" \
  "${EVIDENCE_DIR}/dnf-minimal-closure-assumeno.log"

manifest_sha256=$(sha256sum "${RPM_MANIFEST}" | awk '{print $1}')
rpm_bytes=$(du -sb "${RPM_ROOT}" | awk '{print $1}')
{
  printf 'captured_at=%s\n' "$(date --iso-8601=seconds)"
  printf 'rejected_rpm_count=29\n'
  printf 'rejected_upgrade_candidate_count=%s\n' "${upgrade_candidate_count}"
  printf 'minimal_rpm_count=%s\n' "${install_count}"
  printf 'openeuler_rpm_count=%s\n' "${open_euler_count}"
  printf 'percona_rpm_count=%s\n' "${EXPECTED_PERCONA_COUNT}"
  printf 'minimal_rpm_bytes=%s\n' "${rpm_bytes}"
  printf 'rpm_manifest_sha256=%s\n' "${manifest_sha256}"
  printf 'rpm_sha256_check=PASS\n'
  printf 'openeuler_signature_check=PASS\n'
  printf 'percona_signature_evidence=PASS_EXISTING\n'
  printf 'dnf_transaction_actions=INSTALL_ONLY\n'
  printf 'dnf_offline_dependency_resolution=PASS_ASSUMENO\n'
  printf 'install_execution=NOT_PERFORMED\n'
} >"${EVIDENCE_DIR}/minimal-rpm-closure-result.env"
cp "${EVIDENCE_DIR}/minimal-rpm-closure-result.env" \
  "${EVIDENCE_DIR}/install-only-rpm-closure-result.env"

chmod 0600 "${CLASSIFICATION}" \
  "${EVIDENCE_DIR}"/install-only-* \
  "${EVIDENCE_DIR}"/dnf-install-only-assumeno.log \
  "${EVIDENCE_DIR}"/minimal-* \
  "${EVIDENCE_DIR}"/dnf-minimal-closure-assumeno.log

printf 'Q040_INSTALL_ONLY_RPM_CLOSURE_OK\n'
cat "${EVIDENCE_DIR}/install-only-rpm-closure-result.env"
