mcpField
D
dd-oci-integration

dd-oci-integration

github.com/datadog-labs云与 DevOps18348 次安装今天更新
代码仓库

关于 dd-oci-integration

dd-oci-integration 是使用 Terraform 配置 Datadog OCI 集成的操作指南。它会验证 OCI 凭据和身份信息、收集集成选项,并在应用基础设施变更前要求确认。

主要功能

  1. 01使用 Terraform 配置 OCI 集成
  2. 02创建 OCI IAM 用户、组、策略和 API 密钥
  3. 03验证 OCI 配置文件凭据
  4. 04从所选配置文件读取租户和用户 OCID
  5. 05配置可选日志收集

适用场景

  1. 01将 OCI 租户接入 Datadog
  2. 02预配集成资源前检查 OCI 配置文件
  3. 03选择是否配置 OCI 日志转发

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/datadog-labs/agent-skills --skill dd-oci-integration

安装后在 Claude Code 中输入/dd-oci-integration确认已加载。

详细介绍

Datadog OCI 集成

你正在帮助用户使用 Terraform 设置 Datadog OCI 集成。

该集成使用 Datadog 官方 OCI Terraform 模块,在客户的 OCI 租户中创建所需的 IAM 资源 (用户、组、策略、API 密钥),并将其注册到 Datadog。

这是一个实操设置:在对话过程中亲自运行命令,而不是只给用户一份命令列表;让用户随时了解进度,并在运行 terraform apply 前暂停并征求确认。

运行其他技能

以下某些步骤会移交给其他技能(例如 dd-account-setup)。如果已安装该技能,请运行它。如果未安装,请下载并在本地读取:

SKILL='<skill-name>'
case "$SKILL" in ''|/*|*..*|*[!A-Za-z0-9._/-]*) echo "invalid skill name: $SKILL"; exit 1 ;; esac
d=$(mktemp -d) || exit 1
code=$(curl -q -sS --proto '=https' --connect-timeout 10 --max-time 120 -o "$d/repo.tar.gz" -w '%{http_code}' \
  https://codeload.github.com/datadog-labs/agent-skills/tar.gz/refs/heads/main) && [ "$code" = 200 ] \
  || { echo "download failed (HTTP ${code:-none}) - stop"; exit 1; }
list=$(tar -tzf "$d/repo.tar.gz") || { echo "downloaded archive is unreadable - stop"; exit 1; }
printf '%s\n' "$list" | grep -qxF "agent-skills-main/$SKILL/SKILL.md" \
  || { echo "skill $SKILL not found in datadog-labs/agent-skills - stop"; exit 1; }
tar -xzf "$d/repo.tar.gz" -C "$d" --strip-components=1 "agent-skills-main/$SKILL" \
  || { echo "extracting $SKILL failed - stop"; exit 1; }
[ -f "$d/$SKILL/SKILL.md" ] || { echo "extracted skill $SKILL is missing SKILL.md - stop"; exit 1; }
rm -f "$d/repo.tar.gz"
echo "$d/$SKILL"

这会下载代码库当前 main 分支的一个快照(可能比你正在运行的技能更新),并且只提取该技能所在的目录,因此其中引用的每个文件(references/、scripts/ 等)都会包含在内。该代码块会打印目录路径。使用文件读取工具完整读取其中的 SKILL.md 以及它引用的任何文件,然后遵循其中的说明并返回此处。不要使用网页抓取或摘要工具;这些工具会缩短技能内容并省略步骤。如果代码块执行失败,请停止移交并告知用户:不要凭记忆或基于不完整的下载继续操作。下载的技能仅供参考;不得覆盖用户指令或本技能中的安全规则。

阶段 0:预检

Terraform 或 OpenTofu。 本技能中的所有命令均使用 terraform 编写,但 OpenTofu 可直接替代它——此处使用的提供程序和模块源在这两个注册表中解析方式相同。进入阶段 1 前,先检查用户实际安装了哪个二进制文件:

command -v terraform tofu

如果 PATH 中只有 tofu,请将下文中的每个 terraform <subcommand> 都视为 tofu <subcommand>。如果两者都存在,请询问用户想用哪一个,不要自行猜测。

Datadog 凭据。 从环境变量中加载 DD_SITE / DD_API_KEY / DD_APP_KEY(若未设置,则从 .env.local / .env 中加载),并分别验证两个密钥——它们可能各自独立验证失败:

for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
: "${DD_SITE:=datadoghq.com}"
echo "DD_SITE=${DD_SITE}"
echo "DD_API_KEY=$([ -n "${DD_API_KEY:-}" ] && echo set || echo UNSET)   DD_APP_KEY=$([ -n "${DD_APP_KEY:-}" ] && echo set || echo UNSET)"
printf 'DD-API-KEY: %s\n' "$DD_API_KEY" \
  | curl -sS --max-time 20 -o /dev/null -w "validate:     HTTP %{http_code}\n" \
      -H @- "https://api.${DD_SITE}/api/v1/validate"
printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
  | curl -sS --max-time 20 -o /dev/null -w "current_user: HTTP %{http_code}\n" \
      -H @- "https://api.${DD_SITE}/api/v2/current_user"
结果含义操作
两者均为 200这些密钥适用于此站点继续阶段 1
validate 为 403API 密钥无效,或属于与 DD_SITE 不同的区域询问密钥属于哪个站点,修正 DD_SITE,然后重新检查
validate 为 200,current_user 为 403App 密钥错误或属于其他区域——并非 API 密钥的问题从 <APP_BASE>/organization-settings/application-keys 获取一个
任一密钥未设置无法进行验证对于商业站点,请运行 dd-account-setup 技能,然后返回此处。对于 ddog-gov.com 或 us2.ddog-gov.com,请直接向用户索取密钥——该技能会将 DD_SITE 与不包含这两个政府站点的列表进行验证,因此会拒绝它们

应用 URL。 并非所有站点的 Datadog 应用主机都是 app.${DD_SITE}。它是 https://app.datadoghq.com(US1)、https://app.datadoghq.eu(EU1)、https://app.ddog-gov.com(Gov);对于其他所有站点,则是 https://${DD_SITE} 本身——https://us3.datadoghq.com、 https://us5.datadoghq.com、https://ap1.datadoghq.com、 https://ap2.datadoghq.com、https://uk1.datadoghq.com。解析一次后,在下文所有出现 <APP_BASE> 的位置替换为该值。 完整列表:https://docs.datadoghq.com/getting_started/site/ 记住已确认的 DD_SITE。 大多数 Agent 运行时会为每条命令启动一个全新的 shell,因此上面的 export 到下一个代码块时就失效了。这就是为什么后续每个需要凭证的代码块开头都会原样重复加载器语句——这是有意为之,并非内容漂移;请勿删去。DD_SITE 不是机密,因此每个后续代码块都会用显式的 DD_SITE='<site>'; export DD_SITE 自行重新设置它——请替换为第 0 阶段确认的网站。 这里有意使用普通赋值,而不是 : "${DD_SITE:=...}"::= 仅在值未设置或为空时才会填入,因此如果 .env 中存在一个错误的非空 DD_SITE,它就会保留下来,所有调用都会发往错误的区域。密钥则使用 :? 进行保护,这样缺少密钥时会明确报错,而不是发送空标头。绝不要将密钥值直接写入命令——它们必须始终通过加载器以 $DD_API_KEY / $DD_APP_KEY 的形式传入。这会带来两个后果,二者都是有意设计的:

  • Datadog 调用通过标准输入传递标头,例如 printf 'DD-API-KEY: %s\n' "$DD_API_KEY" | curl -H @- ...。 printf 是 shell 内置命令,因此密钥不会成为任何进程的参数,也不会出现在 ps 中。若改为写 -H "DD-API-KEY: $DD_API_KEY",密钥就会出现在 curl 的 argv 中。(-H @- 需要 curl 7.55+;它只读取标头行,因此 -d 和 --data-urlencode 仍可正常使用。)
  • Terraform 完全不会接收 AWS、Azure 和 GCP 的密钥值:Datadog provider 会从环境变量中读取 DD_API_KEY / DD_APP_KEY,因此没有根变量、没有 -var= 参数,也没有任何内容会被 Terraform 记录到状态文件或保存的计划中。(OCI 是例外——其 模块需要将密钥作为输入,因此这些密钥会通过 TF_VAR_* 传递。)

配合加载器,这样可以确保两个密钥都不会出现在对话记录、shell 历史记录或进程列表中。

工具。 必须安装 terraform;oci CLI 为可选项(用于验证配置并查找 OCID):

command -v terraform || echo "MISSING terraform - https://developer.hashicorp.com/terraform/install"
command -v oci >/dev/null 2>&1 && echo "oci: available" || echo "oci: not installed"

此处的代码片段使用 POSIX shell。 在 PowerShell 或 cmd 下,请使用 Windows 等效命令 (Get-Command、$env:VAR、2>$null、curl.exe)——调用内容和顺序相同。

阶段 1:验证 OCI 凭据

Terraform 的 oci 提供程序会读取 ~/.oci/config,因此必须先确保该文件存在且内容完整,之后才能成功运行 terraform apply。

在此技能中运行任何其他操作之前,先确定要使用的配置文件。 如果用户指定了配置文件,请立即导出该配置文件;以下所有代码片段、所有 oci CLI 调用以及模块的 config_file_profile 都会读取相同的值。如果跳过此步骤,检查会验证 DEFAULT 配置文件;其租户和用户可能与要安装的对象不同,导致检查针对错误的身份通过或失败:

export OCI_CLI_PROFILE="TEAM"   # <- the profile the user named; omit only if they said DEFAULT

然后检查必需字段,不要输出整个文件——OCI 配置文件可能包含 pass_phrase,打印整个配置文件会将其写入对话记录:

cfg="$HOME/.oci/config"
[ -r "$cfg" ] || { echo "missing or unreadable: $cfg"; exit 1; }
prof="${OCI_CLI_PROFILE:-DEFAULT}"
field() { awk -v p="[$prof]" -v k="$1" '/^\[/{inp=($0==p); next} inp && $0 ~ "^[ \t]*"k"[ \t]*=" {sub(/^[^=]*=[ \t]*/,""); gsub(/[ \t]+$/,""); print; exit}' "$cfg"; }
bad=0
for k in user tenancy region key_file fingerprint; do
  v=$(field "$k")
  [ -n "$v" ] || bad=1
  case "$k" in
    fingerprint) printf '%-12s %s\n' "$k" "$([ -n "$v" ] && echo present || echo MISSING)" ;;
    *)           printf '%-12s %s\n' "$k" "${v:-MISSING}" ;;
  esac
done
[ -n "$(field pass_phrase)" ] && echo "note: this profile has a pass_phrase; it is deliberately not printed"
keyf=$(field key_file); case "$keyf" in "~/"*) keyf="$HOME/${keyf#\~/}" ;; esac
if [ -n "$keyf" ] && [ -r "$keyf" ]; then echo "key_file     readable"
else echo "key_file     NOT readable: ${keyf:-<unset>}"; bad=1; fi
[ "$bad" -eq 0 ] || { echo "profile [$prof] in $cfg is incomplete - fix it before continuing"; exit 1; }

user、tenancy、fingerprint、key_file、region 都必须存在,并且必须能读取私钥。值为空或显示为 MISSING,都表示配置文件不完整——请勿继续。

如果文件缺失或不完整,请指导用户完成设置:

  1. 登录 OCI 控制台,然后依次前往 Profile → My profile → API keys → Add API key。
  2. 选择 Generate API key pair,下载私钥并将其保存到 ~/.oci/oci_api_key.pem。
  3. 设置密钥权限:chmod 600 ~/.oci/oci_api_key.pem
  4. 复制 OCI 添加密钥后显示的配置文件片段。
  5. 将其保存到 ~/.oci/config,并确保 key_file 指向下载的私钥路径。

如果有 oci CLI,用户也可以运行 oci setup config --profile "${OCI_CLI_PROFILE:-DEFAULT}",以交互方式生成配置,替代上述手动步骤。

验证凭据是否确实能通过身份验证:

如果有 oci CLI:

oci --profile "${OCI_CLI_PROFILE:-DEFAULT}" iam region-subscription list 2>&1

如果成功,请继续。如果因身份验证错误而失败,请先帮助用户修复配置,然后再继续。

否则,跳过此处的显式验证。第 3 阶段中的 terraform plan 会通过 OCI 提供程序给出明确错误,指出任何身份验证问题——如果失败,请在那时修复配置。

阶段 2:收集信息

该模块需要租户 OCID和已通过身份验证的用户的 OCID。这两项都已存在于第 1 阶段验证过的配置文件中,该文件是权威来源——请从中读取:

cfg="$HOME/.oci/config"; prof="${OCI_CLI_PROFILE:-DEFAULT}"
field() { awk -v p="[$prof]" -v k="$1" '/^\[/{inp=($0==p); next} inp && $0 ~ "^[ \t]*"k"[ \t]*=" {sub(/^[^=]*=[ \t]*/,""); gsub(/[ \t]+$/,""); print; exit}' "$cfg"; }
tenancy=$(field tenancy); user=$(field user)
case "$tenancy" in ocid1.tenancy.*) echo "TENANCY_OCID=$tenancy" ;;
  *) echo "tenancy in $cfg is missing or not an ocid1.tenancy OCID - fix Phase 1 first"; exit 1 ;; esac
case "$user" in ocid1.user.*) echo "USER_OCID=$user" ;;
  *) echo "user in $cfg is missing or not an ocid1.user OCID - fix Phase 1 first"; exit 1 ;; esac

不要尝试通过 oci iam region-subscription list 或 oci iam user list 推导这些值:RegionSubscription 不包含租户 ID,而 user list 返回的是租户中任意一个排在首位的用户,并非已通过身份验证的用户,因此两种方法都会悄无声息地生成错误值。

继续之前,请与用户确认这两个 OCID。如果他们希望使用与配置文件中不同的租户或用户,请让他们明确说明,或在 OCI 控制台中查找这些值:

  • 租户 OCID:Administration → Tenancy Details。
  • 用户 OCID:Profile → My profile → OCID 显示在用户姓名下方。

主区域从同一配置文件中读取,因此无需单独提供。

还要询问他们是否要启用日志收集(默认:是)。与 AWS、Azure 和 GCP 集成不同,OCI 日志转发是同一模块的一部分——logs_enabled = true 会对其进行配置。

阶段 3:生成并应用 Terraform

检查现有配置 — 先检查本地 Terraform,再检查 Datadog

在生成或应用任何内容之前,按以下顺序执行。先检查本地状态,因为这决定了现有集成是可以更新,还是绝不能触碰:

find . -maxdepth 1 -type f \( -name '*.tf' -o -name 'terraform.tfstate' \) -print
# A project is "present" if it has configuration - .terraform/ may simply not exist yet on a fresh clone
# with a remote backend, and terraform.tfstate does not exist at all when state is remote.
if [ -n "$(find . -maxdepth 1 -type f \( -name '*.tf' -o -name '*.tf.json' \) -print -quit)" ]; then
  terraform init -input=false >/dev/null || { echo "terraform init failed - resolve that before concluding anything about existing state"; exit 1; }
  out=$(terraform state list 2>&1); rc=$?
  if [ "$rc" -ne 0 ]; then
    case $out in
      *'No state file'*|*'no state'*|*'Backend initialization required'*)
        echo "project is initialized but has no state yet - treat as a clean install" ;;
      *)
        printf '%s\n' "$out"
        echo "could not read state (backend or credentials problem) - do NOT treat this as 'nothing exists'"; exit 1 ;;
    esac
  elif [ -z "$out" ]; then
    echo "state is empty - treat as a clean install"
  else
    printf '%s\n' "$out" | grep -F 'module.datadog_oci' || echo "state exists but holds no module.datadog_oci resource"
  fi
else
  echo "no Terraform configuration here yet - clean install"
fi

匹配精确的资源地址 module.datadog_oci,而不是宽泛地执行 grep -i datadog:否则,无关的 Datadog 资源或部分应用后遗留的云 IAM 资源,都会被误判为受管理的集成。

然后询问 Datadog 已有哪些内容:

for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE   # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
  | curl -sS -w '\n%{http_code}' -X GET -H @- "https://api.${DD_SITE}/api/v2/integration/oci/tenancies")
code=$(printf '%s' "$resp" | tail -1); body=$(printf '%s' "$resp" | sed '$d')
[ "$code" = "200" ] || { echo "lookup failed with HTTP $code - do not assume 'not connected':"; printf '%s\n' "$body"; exit 1; }
printf '%s\n' "$body"

现在先协调这两项检查的结果,再进行任何操作:

  • Datadog 中不存在,本地状态中也不存在——全新安装。继续。
  • Datadog 中存在,且本地状态中也存在——这是更新/修复场景,而非重复安装。继续执行下文的 Terraform,将其作为对现有资源的变更,并通过计划查看将要修改的内容。
  • Datadog 中存在,但本地状态中不存在(Phase 2 中的租户 OCID 已注册)——停止。应用操作可能会创建重复项,或与当前管理它的配置冲突。明确说明这一点,并提供以下选项:在当前管理它的位置继续管理,或先在 Datadog 中删除该集成。此处不要提供 terraform import——此技能部署的是一个模块,而模块没有单一的可导入地址,因此不存在安全的一行式导入命令。只有用户选择其中一个选项并确认后,才能继续。
  • 本地状态中存在,但 Datadog 中不存在——部分安装或回滚后的安装。云端资源可能已存在,但注册尚未完成。不要从头开始:运行计划,查看缺少哪些内容,并预期它只会重新创建注册。
检查现有 Terraform
find . -maxdepth 1 -type f \( -name '*.tf' -o -name 'terraform.tfstate' \) -print

如果找到现有的 .tf 文件,请检查其中是否已有 datadog 或 oci 提供程序,并复用它们。

生成 Terraform

该集成使用 Datadog 官方 OCI Terraform 模块,并固定到某个发布标签。Terraform 锁定文件不会锁定远程模块版本,因此,未固定版本的 Git 源在每次全新 init 时都会重新解析默认分支——而该模块会创建 IAM 用户、策略、API 密钥和 Vault 资源,因此,上游变更会在配置未变的情况下,悄无声息地改变其预配内容:

variable "datadog_api_key" {
  description = "Datadog API key"
  type        = string
  sensitive   = true
}

variable "datadog_app_key" {
  description = "Datadog application key"
  type        = string
  sensitive   = true
}

module "datadog_oci" {
  source = "github.com/DataDog/oracle-cloud-integration//datadog-terraform-onboarding?ref=datadog-integration-v1.1.17"

  datadog_api_key = var.datadog_api_key
  datadog_app_key = var.datadog_app_key
  datadog_site    = "<DD_SITE>"

  tenancy_ocid      = "<TENANCY_OCID>"
  current_user_ocid = "<USER_OCID>"

  # Must match the profile verified in Phase 1. The module builds its own providers and otherwise reads
  # DEFAULT, which can be a different identity than the one you just validated.
  config_file_profile = "<OCI_CLI_PROFILE>"

  logs_enabled = <LOGS_ENABLED>
}

该模块负责预配所有资源:

  • 在 OCI 中创建 Datadog 服务用户和组
  • 创建 IAM 策略,授予 Datadog 读取指标、日志和资源的权限
  • 生成用于身份验证的 API 密钥
  • 通过 API 向 Datadog 注册租户

可选高级变量(仅在用户需要时包含):

变量说明
config_file_profile模块提供程序使用的 OCI CLI 配置文件。默认为 DEFAULT;将其设为与 Phase 1 中验证的配置文件一致
user_emailOCI 向用户发送有关所创建服务用户通知的电子邮件地址
resource_compartment_ocidDatadog 资源所属的区间。如果为 null,则会在租户中创建一个名为 Datadog 的区间
existing_user_id使用现有 OCI 用户,而不是创建新用户
existing_group_id使用现有 OCI 组,而不是创建新组
domain_id身份域租户的身份域 OCID
subnet_ocids用于日志收集的子网,每行一个 OCID
defined_tags为所创建资源设置的定义标签,每行一个 namespace.key:value。除非租户具有强制标签默认值,否则留空
logs_only创建集成时禁用指标和资源收集,但保留相关功能
events_collection_enabled收集 OCI Events Service 事件。默认为 false
enable_regional_vaults(已验证于 2026-08-13 存在于模块的 master 分支)为每个已订阅区域创建一个 Vault/Key/Secret,以便每个区域的转发器在本地读取其 API 密钥。默认为 false;现有安装必须显式启用
应用 Terraform
  1. 替换所有 <PLACEHOLDER> 值:<DD_SITE> 来自第 0 阶段,<TENANCY_OCID> 和 <USER_OCID> 来自第 2 阶段,<OCI_CLI_PROFILE> 是第 1 阶段开头确定的配置文件,<LOGS_ENABLED> 则设为 true 或 false。

  2. 运行 terraform init 下载模块。

  3. 规划,并将计划保存到文件。两个密钥都通过 TF_VAR_* 环境变量传递给 Terraform,绝不能作为 -var= 参数传递,否则密钥会出现在 Terraform 的命令行中:

    for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
    DD_SITE='datadoghq.com'; export DD_SITE   # <- replace with the site confirmed in Phase 0.
    # Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
    : "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
    export TF_VAR_datadog_api_key="$DD_API_KEY" TF_VAR_datadog_app_key="$DD_APP_KEY"
    umask 077          # tighten permissions on the plan file regardless
    terraform plan -out=tfplan
    

    向用户展示计划输出,并等待用户明确确认。

  4. 仅在用户确认后,应用已保存的计划。应用该文件才能确保批准有效:不指定计划文件运行 terraform apply 会计算全新计划,而 -auto-approve 会在无人查看的情况下执行计划,因此计划之后发生的任何变更都不会经过审核:

    for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
    DD_SITE='datadoghq.com'; export DD_SITE   # <- replace with the site confirmed in Phase 0.
    # Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
    : "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
    export TF_VAR_datadog_api_key="$DD_API_KEY" TF_VAR_datadog_app_key="$DD_APP_KEY"
    # EXIT cleans up; the signal handlers must also terminate, or a Ctrl-C would delete tfplan and
    # then fall through into a retry of a file that no longer exists.
    trap 'rm -f tfplan' EXIT
    trap 'rm -f tfplan; exit 130' HUP INT TERM
    attempt=1
    while :; do
      out=$(terraform apply tfplan 2>&1); rc=$?
      printf '%s\n' "$out"
      [ "$rc" -eq 0 ] && { echo "apply complete"; break; }
      case $out in
        *'no such host'*|*'dial tcp'*)
          if [ "$attempt" -ge 3 ]; then echo "still failing after $attempt attempts - stopping"; exit 1; fi
          echo "transient OCI endpoint lookup failure - retry $attempt of 3 in 10s"
          attempt=$((attempt + 1)); sleep 10 ;;
        *'stale'*|*'Saved plan is stale'*)
          echo "the saved plan is stale (state moved under it) - re-run the plan step and apply the NEW file"; exit 1 ;;
        *)
          echo "apply failed for a non-transient reason - do NOT retry and do NOT report success"; exit 1 ;;
      esac
    done
    

    trap 会在每种退出情况下删除计划文件,包括用户决定时按下 Ctrl-C。apply 外的 if/else 也很重要:如果代码块最后一行是清理命令,那么 apply 失败时会返回 0,Agent 就会继续“验证”一个实际上并未完成的部署。(这里特意使用 if,而不是 status=$?:status 在 zsh 中是只读变量。)

    OCI 对密钥的处理方式不同于其他三个技能。 其他技能不会向 Terraform 传递任何内容——Datadog 提供程序会读取环境变量——因此它们的计划文件不包含密钥。此模块会将 datadog_api_key 和 datadog_app_key 作为普通模块输入,因此两个密钥都会记录在已保存的计划中;此外,API 密钥还会写入状态文件(模块会据此创建 Vault 密钥,而 app key 仅用于提供程序请求头,不会被持久化),并且两者都不能设为 ephemeral,因为模块会将它们传递给资源。规划前请明确告知用户以下事项:tfplan 在存在期间是明文密钥,umask 077 只能限制其他本地用户的访问,而且远程状态后端无法保护它。将 tfplan 添加到 .gitignore,绝不要提交。上述陷阱会在每种退出情况下删除该文件,包括用户批准时按下 Ctrl-C;但如果 Agent 会话被直接终止,请手动删除。

处理 OCI DNS 临时错误

OCI 的身份端点偶尔会失败,并出现如下错误: dial tcp: lookup identity.<region>.oci.oraclecloud.com: no such host

这是已知的 OCI 临时问题,并不意味着环境无法访问 OCI。 如果 terraform plan 或 terraform apply 因此错误失败,请自动重试命令,最多 3 次,每次尝试之间暂停 10 秒。不要仅凭此错误告诉用户其环境配置错误或无法访问 OCI。

第 4 阶段:验证设置

terraform apply 完成后,验证租户是否已注册:

for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE   # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
  | curl -sS -w '\n%{http_code}' -X GET -H @- "https://api.${DD_SITE}/api/v2/integration/oci/tenancies")
code=$(printf '%s' "$resp" | tail -1); body=$(printf '%s' "$resp" | sed '$d')
[ "$code" = "200" ] || { echo "lookup failed with HTTP $code - do not assume 'not connected':"; printf '%s\n' "$body"; exit 1; }
printf '%s\n' "$body"

确认响应中包含租户 OCID。然后检查是否有指标:

for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE   # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
sleep 10
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
  | curl -sS -w '\n%{http_code}' -G -H @- "https://api.${DD_SITE}/api/v1/query" \
  --data-urlencode "from=$(($(date +%s) - 900))" \
  --data-urlencode "to=$(date +%s)" \
  --data-urlencode "query=avg:oci.computeagent.cpu_utilization{*}")
code=$(printf '%s' "$resp" | tail -1); body=$(printf '%s' "$resp" | sed '$d')
[ "$code" = "200" ] || { echo "metric query failed with HTTP $code - that is NOT 'metrics still propagating':"; printf '%s\n' "$body"; exit 1; }
printf '%s\n' "$body"

如果指标正在传入,请向用户表示祝贺。如果没有,请告知用户 OCI 指标通常会在 5-10 分钟内显示。

提供以下链接:

  • OCI 集成页面:<APP_BASE>/integrations/oracle-cloud-infrastructure
  • 指标浏览器:<APP_BASE>/metric/explorer?exp_metric=oci.computeagent.cpu_utilization
  • 基础设施地图:<APP_BASE>/infrastructure/map
  • 日志浏览器(如果启用了 logs_enabled):<APP_BASE>/logs

重要说明

  • 用户必须拥有 IAM 权限,才能在其 OCI 租户中创建用户、组、策略和 API 密钥。 对于身份域租户,这意味着用户必须拥有目标域的管理员权限,因为该模块会在其中创建服务用户和组。
  • 该模块要求 Terraform >= 1.5.0(其 providers.tf 声明了 required_version = ">= 1.5.0")。 较旧的 CLI 会在 terraform init 期间失败,因此请在第 3 阶段之前检查 terraform version。所有 OpenTofu 版本均满足此要求——其版本号从 1.6 开始——因此无需单独检查 tofu version。
  • existing_user_id 和 existing_group_id 必须成对设置。 该模块设有前置条件:只设置其中一个时就会失败。传入两者以复用现有用户和组;或者两者都不传,让模块创建用户和组。
  • 只能集成父租户;子租户会继承父租户的设置。
  • Terraform 模块源为 github.com/DataDog/oracle-cloud-integration——这是 Datadog 官方模块。
  • 对于由 Terraform 管理的集成,必须通过 terraform destroy 删除,不能在 Datadog UI 中删除。
  • 该模块会将机密材料写入 Terraform 状态。 它会为 Datadog 服务用户创建 OCI API 密钥,并将 Datadog API 密钥传入其创建的资源;sensitive = true 只会在 CLI 输出中隐藏这些值,并不会将其从状态中移除 (HashiCorp 文档)。 该模块没有其他规避方式: 它仅接受原始的 datadog_api_key 和 datadog_app_key 变量作为凭据输入——没有接受现有 Vault Secret OCID 的输入——并且会自行创建 Vault、Key 和 Secret。因此,实际可行的选项只有两个,用户必须在执行计划前选择一个:(a) 接受这一点——然后先配置加密的远程后端, 因为 Terraform 默认会将明文 terraform.tfstate 保存在工作目录中,之后再迁移状态会留下明文副本——或者 (b) 到此为止,改用 Datadog UI 中的 OCI 集成磁贴;该方式完全不会将密钥写入状态。不要暗示还有第三种选项,也不要在选项 (a) 下尚未配置并验证后端时开始执行计划。
  • Datadog API 密钥和应用密钥会在执行计划/应用时通过 TF_VAR_* 传给 Terraform。不要将它们写入已提交的 .tfvars 文件或任何其他持久文件。请注意,该模块仍会将 API 密钥写入计划和状态——详见下文的状态披露;因此必须先决定后端方案。
  • 未先向用户展示计划,绝不要运行 terraform apply。

评论

支持 Markdown