diff --git a/.github/workflows/go-sdk.yml b/.github/workflows/go-sdk.yml index 69897e45..a95d62fa 100644 --- a/.github/workflows/go-sdk.yml +++ b/.github/workflows/go-sdk.yml @@ -4,23 +4,11 @@ on: push: branches: - "master" - - "sdk-go" - paths: - - "tng-go/**" - - "tng/**" - - "Cargo.toml" - - "Cargo.lock" - - ".github/workflows/go-sdk.yml" + tags: + - "v*.*.*" pull_request: branches: - "master" - - "sdk-go" - paths: - - "tng-go/**" - - "tng/**" - - "Cargo.toml" - - "Cargo.lock" - - ".github/workflows/go-sdk.yml" jobs: go-sdk-tests: @@ -39,7 +27,15 @@ jobs: set -x sed -i -E 's|https?://mirrors.openanolis.cn/anolis/|https://mirrors.aliyun.com/anolis/|g' /etc/yum.repos.d/*.repo sed -i -E 's|https?://mirrors.cloud.aliyuncs.com/|https://mirrors.aliyun.com/|g' /etc/yum.repos.d/*.repo - yum install -y git make tar clang protobuf-devel jq iptables + # openssl-sys builds OpenSSL from source (vendored) because the + # builtin-as-tdx-rust (dcap-qvl) backend pulls it in; OpenSSL 3.5.x's + # `Configure` requires the IPC::Cmd and Time::Piece Perl modules, + # which on alinux (an8-based) live in their own `perl-IPC-Cmd` and + # `perl-Time-Piece` packages (FindBin / File::Compare / File::Copy + # ship with the base perl-interpreter already present). Without these, + # `perl ./Configure` aborts with "Can't locate IPC/Cmd.pm" / + # "Can't locate Time/Piece.pm". + yum install -y git make tar clang protobuf-devel jq iptables perl-IPC-Cmd perl-Time-Piece - uses: dtolnay/rust-toolchain@1.89.0 diff --git a/.github/workflows/shell-scripts.yml b/.github/workflows/shell-scripts.yml index a905a11e..fc0ea589 100644 --- a/.github/workflows/shell-scripts.yml +++ b/.github/workflows/shell-scripts.yml @@ -30,70 +30,3 @@ jobs: echo "=== Checking $f ===" shellcheck "$f" done - - setup-vendor-config: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Run setup-vendor-config tests - run: | - set -e - SCRIPT="scripts/setup-vendor-config" - chmod +x "$SCRIPT" - - echo "=== Test: --help exits 0 ===" - "$SCRIPT" --help > /dev/null - - echo "=== Test: --list shows aliyun ===" - "$SCRIPT" --list | grep -q "aliyun" - - echo "=== Test: aliyun --dry-run exits 0 ===" - "$SCRIPT" aliyun --dry-run > /dev/null - - echo "=== Test: aliyun --region cn-beijing --dry-run ===" - output=$("$SCRIPT" aliyun --region cn-beijing --dry-run) - echo "$output" | grep -q "cn-beijing" - echo "$output" | grep -q "cn-beijing.aliyuncs.com" - - echo "=== Test: unknown vendor fails ===" - ! "$SCRIPT" unknown 2>/dev/null - - echo "=== Test: no args fails ===" - ! "$SCRIPT" 2>/dev/null - - echo "=== Test: unknown option fails ===" - ! "$SCRIPT" --foobar 2>/dev/null - - echo "=== Test: --region without value fails ===" - ! "$SCRIPT" aliyun --region 2>/dev/null - - echo "=== Test: --internal without --region fails ===" - ! "$SCRIPT" aliyun --internal 2>/dev/null - - echo "=== Test: --internal with --region uses VPC endpoint ===" - output=$("$SCRIPT" aliyun --internal --region cn-hangzhou --dry-run) - echo "$output" | grep -q "sgx-dcap-server-vpc.cn-hangzhou.aliyuncs.com" - echo "$output" | grep -q "internal (VPC)" - - echo "=== Test: public endpoint (default) ===" - output=$("$SCRIPT" aliyun --region cn-beijing --dry-run) - echo "$output" | grep -q "sgx-dcap-server.cn-beijing.aliyuncs.com" - echo "$output" | grep -q "Endpoint: public" - - echo "=== Test: apply and verify config ===" - # Create a temp config file for testing - tmp_conf=$(mktemp) - cat > "$tmp_conf" <<'EOF' - # PCCS server address - PCCS_URL=https://localhost:8081/sgx/certification/v4/ - USE_SECURE_CERT=TRUE - EOF - - # Use sed with a modified CONFIG_FILE via env override approach - # Since the script hardcodes CONFIG_FILE, we test via dry-run output instead - output=$("$SCRIPT" aliyun --region cn-hangzhou --dry-run) - echo "$output" | grep -q "cn-hangzhou.aliyuncs.com" - - echo "All tests passed!" diff --git a/CLAUDE.md b/CLAUDE.md index 41c947c0..b0283681 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,6 +19,19 @@ When creating or amending commits: - **Author and committer** must always be taken from the local git config (`git config user.name` / `git config user.email`). Never use Claude's own identity. - **Never** add `Co-Authored-By:` trailers of any kind. +- **Always** add an `Assisted-by:` trailer to every commit message that Claude authored or co-authored. The trailer is the *only* accepted form of AI attribution. Format: + + ``` + Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2] ... + ``` + + Where `AGENT_NAME` is the AI tool name (e.g. `Claude`), `MODEL_VERSION` is the specific model version used (e.g. `claude-opus-4-8`), and the optional bracketed `[TOOL]` entries are specialized analysis tools employed in producing the change (e.g. `coccinelle`, `sparse`, `smatch`, `clang-tidy`). Basic development tools (`git`, `gcc`, `make`, editors) must **not** be listed. Place the trailer as the last line(s) of the commit message body, separated by a blank line from the rest of the message. Example: + + ``` + Assisted-by: Claude:claude-opus-4-8 clang-tidy + ``` + + Only one `Assisted-by:` trailer per commit. If no specialized tool was used, omit the bracketed list entirely (`Assisted-by: Claude:claude-opus-4-8`). - **Never** include any Claude session URLs, session IDs, or links to claude.ai in commit messages or PR descriptions. Commit messages should only describe the code changes. - **Never** include "🤖 Generated with [Claude Code](https://claude.com/claude-code)" or similar AI attribution footers in PR descriptions or commit messages. - **Always** use `--no-gpg-sign` to avoid GPG signing. @@ -138,6 +151,29 @@ git filter-branch -f --msg-filter 'sed "/Co-Authored-By:/d"' --env-filter ' ' ..HEAD ``` +This rewrites `Co-Authored-By` trailers (forbidden) but **preserves** `Assisted-by:` trailers (expected) — the `sed` only deletes `Co-Authored-By:` lines, so `Assisted-by:` survives untouched. After rewriting, confirm every AI-authored commit still carries its `Assisted-by:` trailer before pushing. + + +## GitHub Actions Workflow Triggers + +When creating or editing `.github/workflows/*.yml`, follow the repo-wide trigger convention: every workflow triggers on `push` to `master` (and tags `v*.*.*`) and on `pull_request` targeting `master` — **without** a `paths:` filter. + +**Never add a `paths:` filter** to gate a workflow on a subset of changed files. A `paths` filter silently skips the job when a change lands outside the listed globs — and because tests transitively depend on files outside their own directory (e.g. the Go SDK tests run `cargo test --package tng-testsuite --features go-sdk`, so they also depend on `tng-testsuite/`, `rats-cert/`, the `tng` binary, etc.), a `paths` filter limited to `tng-go/**` lets a real regression ship undetected. Triggering on every push/PR to `master` costs more CI minutes but closes that gap — the tradeoff is intentional. + +Do not add per-feature legacy branch triggers (e.g. `sdk-go`) either; `master` is the only branch these workflows target. The canonical `on:` block is: + +```yaml +on: + push: + branches: + - "master" + tags: + - "v*.*.*" + pull_request: + branches: + - "master" +``` + ## API Compatibility diff --git a/Dockerfile b/Dockerfile index e2c364a9..b3231f90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,11 +43,6 @@ COPY --from=builder /usr/local/cargo/bin/tng /usr/local/bin/tng RUN mkdir -p /usr/lib/tng/ COPY --from=builder /code/target/release/libtng_hook.so /usr/lib/tng/libtng_hook.so -# Install vendor config setup tool -COPY scripts/setup-vendor-config /usr/local/bin/setup-vendor-config -RUN chmod +x /usr/local/bin/setup-vendor-config - -# install jq for custome shell script RUN yum install -y jq CMD ["tng"] diff --git a/Makefile b/Makefile index 0dda9f86..b0276017 100644 --- a/Makefile +++ b/Makefile @@ -193,6 +193,27 @@ create-tarball: # copy untracked (new) files that are not ignored if [ -n "$$(git ls-files --others --exclude-standard)" ] ; then git ls-files --others --exclude-standard -z | xargs -0 tar -c -f - | tar -x -f - -C /tmp/trusted-network-gateway-tarball/trusted-network-gateway-${VERSION}/src/ ; fi + # attestation-service is a git dep (trustee) that `cargo vendor` packages as just + # its crate dir (vendor/attestation-service-0.1.0/). Its build.rs compiles + # ../protos/reference.proto, but protos/ lives outside the package in the trustee + # repo, so `cargo vendor` omits it and the offline (vendored) build then fails with + # "protoc failed: ../protos/reference.proto: No such file or directory". Extract + # protos/ at the exact rev pinned in Cargo.lock from cargo's trustee git db into + # the vendor dir, so ../protos/ resolves from the vendored crate dir. (Non-vendored + # builds work because the full git checkout keeps protos/ as a sibling.) + @TRUSTEE_REV=$$(sed -n 's|.*trustee/?rev=\([a-f0-9]\{40\}\).*|\1|p' Cargo.lock | head -1); \ + PROTOS_OK=0; \ + for db in $${CARGO_HOME:-$$HOME/.cargo}/git/db/trustee-*; do \ + [ -d "$$db" ] || continue; \ + if git --git-dir="$$db" cat-file -e "$$TRUSTEE_REV" 2>/dev/null; then \ + git --git-dir="$$db" archive "$$TRUSTEE_REV" protos \ + | tar -x -C /tmp/trusted-network-gateway-tarball/trusted-network-gateway-${VERSION}/vendor/ \ + && PROTOS_OK=1 && break; \ + fi; \ + done; \ + [ "$$PROTOS_OK" = 1 ] && [ -f /tmp/trusted-network-gateway-tarball/trusted-network-gateway-${VERSION}/vendor/protos/reference.proto ] \ + || { echo "ERROR: could not extract trustee protos/ for vendored attestation-service (rev $$TRUSTEE_REV)"; exit 1; } + # delete all checksum (this is required due to previous patch work) sed -i 's/checksum = ".*//g' /tmp/trusted-network-gateway-tarball/trusted-network-gateway-${VERSION}/src/Cargo.lock diff --git a/docs/configuration.md b/docs/configuration.md index 05861ac3..fd1751f1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1246,9 +1246,12 @@ The **Verifier** receives and verifies Evidence from the Attester, only recogniz When `as_type` = `"builtin"`, TNG uses the built-in AS to verify Evidence locally without connecting to an external AS. This is suitable for network-isolated, latency-sensitive, or simplified deployment scenarios. > [!NOTE] -> Builtin mode requires compiling with the corresponding TEE feature enabled (`builtin-as-tdx`, `builtin-as-sgx`, or `builtin-as-snp`). GitHub CI-built RPM packages and binary artifacts do not support this mode; only container images support it. +> To verify TDX Evidence, TNG fetches TDX/SGX collateral directly over HTTPS from a PCCS (Provisioning Certificate Caching Service). Set the `PCCS_URL` environment variable to your cloud provider's PCCS; if unset, it defaults to the Alibaba Cloud PCCS (`https://sgx-dcap-server.cn-beijing.aliyuncs.com`). Both bare-host (`https://sgx-dcap-server.cn-hangzhou.aliyuncs.com`) and path-suffixed (`https://sgx-dcap-server.cn-hangzhou.aliyuncs.com/sgx/certification/v4/`) forms are accepted — the backend normalizes the path. Common Alibaba Cloud endpoints: > -> For SGX/TDX builtin AS mode, you also need to configure the PCCS URL in `/etc/sgx_default_qcnl.conf` for your cloud provider. See the [Vendor Configuration Setup](setup-vendor-config.md) guide for details. +> | Endpoint | `PCCS_URL` | +> |---|---| +> | Public (region-specific) | `https://sgx-dcap-server..aliyuncs.com` | +> | VPC internal | `https://sgx-dcap-server-vpc..aliyuncs.com` | **PolicyConfig (OPA Policy):** diff --git a/docs/configuration_zh.md b/docs/configuration_zh.md index 83974aa4..f0adddf7 100644 --- a/docs/configuration_zh.md +++ b/docs/configuration_zh.md @@ -1256,9 +1256,12 @@ Passport 模式适用于网络隔离或性能要求较高的场景,因为它 `as_type` = `"builtin"` 时,TNG 使用内置 AS 在本地直接验证 Evidence,无需连接外部 AS。适用于网络隔离、延迟敏感或简化部署的场景。 > [!NOTE] -> Builtin 模式需在编译时启用对应 TEE 特性(`builtin-as-tdx`、`builtin-as-sgx` 或 `builtin-as-snp`)。GitHub CI 构建的 RPM 包和二进制产物不支持此模式,仅容器镜像支持。 +> 当验证TDX Evidence时,TNG会通过 HTTPS 直接从 PCCS(Provisioning Certificate Caching Service)拉取 TDX/SGX collateral。将 `PCCS_URL` 环境变量设为你的云服务商 PCCS;未设置时默认为阿里云 PCCS(`https://sgx-dcap-server.cn-beijing.aliyuncs.com`)。纯主机形式(`https://sgx-dcap-server.cn-hangzhou.aliyuncs.com`)和带路径后缀形式(`https://sgx-dcap-server.cn-hangzhou.aliyuncs.com/sgx/certification/v4/`)都接受——后端会规范化路径。阿里云常用端点: > -> 对于 SGX/TDX 内置 AS 模式,还需为云服务商配置 `/etc/sgx_default_qcnl.conf` 中的 PCCS URL。详见 [Vendor Configuration Setup](setup-vendor-config.md)。 +> | 端点类型 | `PCCS_URL` | +> |---|---| +> | 公网(按地域) | `https://sgx-dcap-server..aliyuncs.com` | +> | VPC 内网 | `https://sgx-dcap-server-vpc..aliyuncs.com` | **PolicyConfig(OPA 策略):** diff --git a/docs/setup-vendor-config.md b/docs/setup-vendor-config.md deleted file mode 100644 index 5b3f3666..00000000 --- a/docs/setup-vendor-config.md +++ /dev/null @@ -1,105 +0,0 @@ -# Vendor Configuration Setup - -This document describes how to configure cloud-provider-specific PCCS (Provisioning Certificate Caching Service) settings when using TNG's **builtin AS** mode. - -## Background - -When using builtin AS mode with **SGX or TDX** enclaves, the local quote verification process needs to fetch and verify provisioning certificates from a PCCS. The PCCS URL is configured in `/etc/sgx_default_qcnl.conf`. - -If the PCCS URL is not set correctly for your cloud provider, quote verification will fail with: - -``` -tee_verify_quote failed: 0xe019 -``` - -## The `setup-vendor-config` Tool - -TNG ships with a `setup-vendor-config` tool to automatically configure `/etc/sgx_default_qcnl.conf` for supported cloud providers. - -### Usage - -``` -setup-vendor-config [options] -``` - -| Option | Description | -|---|---| -| `` | Cloud provider name (required) | -| `--region REGION` | Cloud region ID (optional for some vendors) | -| `--internal` | Use VPC internal endpoint (requires `--region`) | -| `--dry-run` | Show what would be changed without applying | -| `--no-backup` | Skip creating a backup of the existing config | -| `--list` | List all supported vendors with details | -| `--help` | Show help message | - -### Supported Vendors - -| Vendor | Description | Default Region | -|---|---|---| -| `aliyun` | Alibaba Cloud SGX DCAP PCCS | `cn-hangzhou` | - -### Examples - -```sh -# Configure for Alibaba Cloud (uses default region cn-hangzhou) -setup-vendor-config aliyun - -# Configure for a specific region -setup-vendor-config aliyun --region cn-beijing - -# Use VPC internal endpoint (requires explicit --region) -setup-vendor-config aliyun --internal --region cn-hangzhou - -# Preview changes without applying -setup-vendor-config aliyun --region cn-shanghai --dry-run - -# List all supported vendors -setup-vendor-config --list -``` - -### Docker Usage - -Run the tool inside the container before `tng launch`: - -```sh -docker run -it --rm --privileged --network host --cgroupns=host \ - ghcr.io/inclavare-containers/tng:latest \ - sh -c 'setup-vendor-config aliyun && tng launch --config-content="..."' -``` - -> [!TIP] -> The `--region` flag is optional for Alibaba Cloud — `cn-hangzhou` works for all TDX instances. Use `--region` only if you need a different endpoint. - -### VPC Internal Endpoint - -> [!IMPORTANT] -> When `--internal` is specified, `--region` **must** be provided explicitly — the default region (`cn-hangzhou`) will not be used. - -The `--internal` flag switches the PCCS URL to the VPC internal endpoint (`sgx-dcap-server-vpc..aliyuncs.com`), which uses Alibaba Cloud's internal network instead of the public internet. This provides lower latency and avoids public bandwidth charges when running inside a VPC. - -```sh -# VPC internal endpoint for cn-hangzhou -setup-vendor-config aliyun --internal --region cn-hangzhou -``` - -## Manual Configuration - -If your provider is not yet supported by the tool, manually edit `/etc/sgx_default_qcnl.conf`: - -```sh -sudo sed -i.$(date "+%m%d%y") 's|PCCS_URL=.*|PCCS_URL=https://sgx-dcap-server../sgx/certification/v4/|' /etc/sgx_default_qcnl.conf -``` - -Replace `` and `` with your cloud provider's PCCS endpoint. Consult your provider's confidential computing documentation for the correct URL. - -## Adding a New Vendor - -To add support for a new vendor, edit `scripts/setup-vendor-config` and add three variables following the existing pattern: - -```bash -VENDOR_PCCS_URL_="https:///__REGION__/" -VENDOR_REQUIRES_REGION_="false" # or "true" if region is mandatory -VENDOR_DEFAULT_REGION_="" -``` - -Then add the vendor name to the `SUPPORTED_VENDORS` list. diff --git a/rpm/AnolisOS8/trusted-network-gateway.an8.buildspec.yaml.template b/rpm/AnolisOS8/trusted-network-gateway.an8.buildspec.yaml.template index b5e35818..650b8b4f 100644 --- a/rpm/AnolisOS8/trusted-network-gateway.an8.buildspec.yaml.template +++ b/rpm/AnolisOS8/trusted-network-gateway.an8.buildspec.yaml.template @@ -69,6 +69,15 @@ phases: - rustup default 1.91.1 # Change yum source from Anolis to Alinux - sed -i -E 's|https?://mirrors.openanolis.cn/anolis/|https://mirrors.aliyun.com/anolis/|g' /etc/yum.repos.d/*.repo + # openssl-sys builds OpenSSL from source (vendored); OpenSSL 3.5.x's + # `Configure` requires the IPC::Cmd and Time::Piece Perl modules, which + # on an8 live in their own `perl-IPC-Cmd` and `perl-Time-Piece` packages + # (FindBin/File::Compare/File::Copy ship with the base perl-interpreter + # already present). Without them, `perl ./Configure` aborts with + # "Can't locate IPC/Cmd.pm" / "Can't locate Time/Piece.pm" during + # rpmbuild %build, and rpmbuild's strict BuildRequires check rejects + # perl(IPC::Cmd) / perl(Time::Piece) before %build even runs. + - yum install -y perl-IPC-Cmd perl-Time-Piece build: commands: - rpmbuild --define "_topdir /tmp/rpmbuild" -ba /tmp/rpmbuild/SPECS/trusted-network-gateway.spec --define 'with_rustup 1' diff --git a/scripts/setup-vendor-config b/scripts/setup-vendor-config deleted file mode 100755 index aaec994a..00000000 --- a/scripts/setup-vendor-config +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/env bash -# -# setup-vendor-config — Configure vendor-specific PCCS settings for TNG builtin AS mode. -# -# When using TNG's builtin attestation service (e.g., the Docker image built with -# the builtin-as-tdx-ffi feature), local quote verification requires a correctly -# configured PCCS (Provisioning Certificate Caching Service). If the PCCS_URL in -# /etc/sgx_default_qcnl.conf does not point to your cloud provider's service, -# quote verification will fail with: -# -# tee_verify_quote failed: 0xe019 -# -# This tool updates the configuration for supported vendors automatically. -# -# Usage: -# setup-vendor-config [options] -# -# Examples: -# setup-vendor-config aliyun -# setup-vendor-config aliyun --region cn-beijing -# setup-vendor-config aliyun --dry-run -# setup-vendor-config --list - -set -euo pipefail - -CONFIG_FILE="/etc/sgx_default_qcnl.conf" -# shellcheck disable=SC2034 -DEFAULT_QCNL_CONTENT='# PCCS server address -PCCS_URL=https://localhost:8081/sgx/certification/v4/ -USE_SECURE_CERT=TRUE -RETRY_TIMES=6 -RETRY_DELAY=10 -PCK_CACHE_EXPIRE_HOURS=168 -VERIFY_COLLATERAL_CACHE_EXPIRE_HOURS=168' - -# ============================================================================== -# Vendor definitions -# ============================================================================== -# Add a new vendor by defining: -# VENDOR_PCCS_URL_ — the PCCS URL template (use __REGION__ placeholder) -# VENDOR_REQUIRES_REGION_ — set to "true" if region is mandatory -# VENDOR_DEFAULT_REGION_ — default region if --region not specified - -# These variables are accessed via indirect expansion (${!var}) in main(). -# shellcheck disable=SC2034 -VENDOR_PCCS_URL_aliyun="https://sgx-dcap-server.__REGION__.aliyuncs.com/sgx/certification/v4/" -# shellcheck disable=SC2034 -VENDOR_PCCS_URL_INTERNAL_aliyun="https://sgx-dcap-server-vpc.__REGION__.aliyuncs.com/sgx/certification/v4/" -# shellcheck disable=SC2034 -VENDOR_REQUIRES_REGION_aliyun="false" -# shellcheck disable=SC2034 -VENDOR_DEFAULT_REGION_aliyun="cn-hangzhou" - -# ============================================================================== -# Supported vendors list (kebab-case names) -# ============================================================================== -SUPPORTED_VENDORS="aliyun" - -# ============================================================================== -# Usage / Help -# ============================================================================== -usage() { - cat <<'EOF' -Usage: setup-vendor-config [options] - -Setup vendor-specific PCCS configuration for TNG builtin AS mode. - -Arguments: - Cloud provider name to configure - -Options: - --region REGION Cloud region ID (optional for some vendors, see --list) - --internal Use VPC internal endpoint (requires --region) - --dry-run Show what would be changed without applying - --no-backup Skip creating a backup of the existing config - --list List all supported vendors with details - --help Show this help message - -Supported Vendors: - aliyun Alibaba Cloud SGX DCAP PCCS - Default region: cn-hangzhou (covers all TDX instances) - -Examples: - setup-vendor-config aliyun - setup-vendor-config aliyun --region cn-beijing - setup-vendor-config aliyun --internal --region cn-hangzhou - setup-vendor-config aliyun --region cn-shanghai --dry-run - setup-vendor-config --list - -For more information, see docs/configuration.md. -EOF -} - -list_vendors() { - echo "Supported vendors:" - echo "" - for vendor in $SUPPORTED_VENDORS; do - local url_var="VENDOR_PCCS_URL_${vendor}" - local requires_var="VENDOR_REQUIRES_REGION_${vendor}" - local default_var="VENDOR_DEFAULT_REGION_${vendor}" - local url="${!url_var}" - local requires="${!requires_var}" - local default_region="${!default_var}" - - echo " ${vendor}" - if [[ "$requires" == "true" ]]; then - echo " PCCS URL template: ${url}" - echo " Region: required (use --region)" - else - echo " PCCS URL template: ${url}" - echo " Default region: ${default_region}" - fi - echo "" - done -} - -# ============================================================================== -# Core logic -# ============================================================================== -main() { - local vendor="" - local region="" - local dry_run=false - local do_backup=true - local use_internal=false - - # Parse arguments - while [[ $# -gt 0 ]]; do - case "$1" in - --help|-h) - usage - exit 0 - ;; - --list) - list_vendors - exit 0 - ;; - --dry-run) - dry_run=true - shift - ;; - --no-backup) - do_backup=false - shift - ;; - --internal) - use_internal=true - shift - ;; - --region) - if [[ -z "${2:-}" ]]; then - echo "Error: --region requires a value" >&2 - exit 1 - fi - region="$2" - shift 2 - ;; - -*) - echo "Error: unknown option: $1" >&2 - usage >&2 - exit 1 - ;; - *) - if [[ -n "$vendor" ]]; then - echo "Error: unexpected argument: $1" >&2 - usage >&2 - exit 1 - fi - vendor="$1" - shift - ;; - esac - done - - if [[ -z "$vendor" ]]; then - echo "Error: vendor name is required" >&2 - usage >&2 - exit 1 - fi - - # Validate vendor - local supported=false - for v in $SUPPORTED_VENDORS; do - if [[ "$v" == "$vendor" ]]; then - supported=true - break - fi - done - if [[ "$supported" != "true" ]]; then - echo "Error: unsupported vendor '${vendor}'" >&2 - echo "Run '$0 --list' to see supported vendors." >&2 - exit 1 - fi - - # Resolve region - local requires_var="VENDOR_REQUIRES_REGION_${vendor}" - local default_var="VENDOR_DEFAULT_REGION_${vendor}" - local url_var="VENDOR_PCCS_URL_${vendor}" - local url_var_internal="VENDOR_PCCS_URL_INTERNAL_${vendor}" - local requires_region="${!requires_var}" - local default_region="${!default_var}" - local url_template="${!url_var}" - local url_template_internal="${!url_var_internal:-}" - - # --internal requires explicit region (no default fallback) - if [[ "$use_internal" == "true" && -z "$region" ]]; then - echo "Error: --internal requires --region to be specified explicitly" >&2 - exit 1 - fi - - if [[ -z "$region" ]]; then - if [[ "$requires_region" == "true" ]]; then - echo "Error: --region is required for vendor '${vendor}'" >&2 - exit 1 - fi - region="$default_region" - fi - - # Select the URL template (internal VPC or public) - if [[ "$use_internal" == "true" ]]; then - if [[ -z "$url_template_internal" ]]; then - echo "Error: vendor '${vendor}' does not support --internal" >&2 - exit 1 - fi - url_template="$url_template_internal" - fi - - # Build the final URL - local pccs_url="${url_template//__REGION__/$region}" - - # Show configuration - echo "Vendor: ${vendor}" - echo "Region: ${region}" - if [[ "$use_internal" == "true" ]]; then - echo "Endpoint: internal (VPC)" - else - echo "Endpoint: public" - fi - echo "PCCS URL: ${pccs_url}" - echo "Config file: ${CONFIG_FILE}" - echo "" - - # Dry run - if [[ "$dry_run" == "true" ]]; then - echo "[DRY RUN] The following changes would be made:" - echo "" - if [[ -f "$CONFIG_FILE" ]]; then - local current_url - current_url=$(grep '^PCCS_URL=' "$CONFIG_FILE" | head -1 | cut -d= -f2-) - echo " Current PCCS_URL: ${current_url}" - echo " New PCCS_URL: ${pccs_url}" - echo "" - echo "Backup: ${CONFIG_FILE}.$(date +%m%d%y) (would be created)" - else - echo " Config file does not exist — would be created with default content." - echo " Then PCCS_URL would be set to: ${pccs_url}" - fi - exit 0 - fi - - # Apply configuration - if [[ -f "$CONFIG_FILE" ]]; then - # Backup - if [[ "$do_backup" == "true" ]]; then - local backup_path - backup_path="${CONFIG_FILE}.$(date +%m%d%y)" - cp "$CONFIG_FILE" "$backup_path" - echo "Backup created: ${backup_path}" - fi - - # Update PCCS_URL - sed -i "s|^PCCS_URL=.*|PCCS_URL=${pccs_url}|" "$CONFIG_FILE" - echo "Updated PCCS_URL in ${CONFIG_FILE}" - else - echo "Config file not found — creating with vendor settings." - mkdir -p "$(dirname "$CONFIG_FILE")" - cat > "$CONFIG_FILE" <