Oracle Cloud Infrastructure Skills Use this domain for practical Oracle Cloud Infrastructure guidance. Current content covers OCI Kubernetes Engine (OKE): cluster design, operational troubleshooting, Generic VNIC Attachment (GVA), and Multus multi-interface pod validation. It covers OCI Internet of Things Platform resource discovery, digital twin lifecycle workflows, device publish flows, and optional MCP-assisted operation. It covers OCI Functions local deployment and diagnosis-first troubleshooting. It includes a constrained Terraform Configuration generator for OCI Security Zone recipes an…

\\n'\"${pod_logs}\"\n fi\nfi\n\ncleanup_attempted=\"false\"\ncleanup_message=\"\"\n\ncleanup_lc=\"$(printf \"%s\" \"$cleanup\" | tr '[:upper:]' '[:lower:]')\"\nif [[ \"$cleanup_lc\" == \"true\" && -n \"$pod_name\" ]]; then\n cleanup_attempted=\"true\"\n set +e\n cleanup_out=\"$(kubectl -n \"$namespace\" delete pod \"$pod_name\" --ignore-not-found 2>&1)\"\n cleanup_rc=$?\n set -e\n if [[ $cleanup_rc -eq 0 ]]; then\n cleanup_message=\"$cleanup_out\"\n else\n cleanup_message=\"cleanup failed: $cleanup_out\"\n fi\nfi\n\nRAW_OUT=\"$raw_out\" python3 - \"$node_name\" \"$image_name\" \"$namespace\" \"$cmd_rc\" \"$pod_name\" \"$cleanup_attempted\" \"$cleanup_message\" \u003c\u003c'PY'\nimport json\nimport os\nimport re\nimport sys\n\nnode = sys.argv[1]\nimage = sys.argv[2]\nnamespace = sys.argv[3]\ncmd_rc = int(sys.argv[4])\npod_name = sys.argv[5]\ncleanup_attempted = sys.argv[6].lower() == \"true\"\ncleanup_message = sys.argv[7]\nraw = os.environ.get(\"RAW_OUT\", \"\")\n\n# Strip ANSI escapes so snippets remain readable.\nraw = re.sub(r\"\\x1B\\[[0-9;]*[A-Za-z]\", \"\", raw)\nlines = raw.splitlines()\n\npass_count = sum(1 for l in lines if l.startswith(\"PASS \"))\nfail_count = sum(1 for l in lines if l.startswith(\"FAIL \"))\nwarn_count = sum(1 for l in lines if l.startswith(\"WARN \"))\nskip_count = sum(1 for l in lines if l.startswith(\"SKIP \"))\n\nchecks_line = \"\"\nsignals_line = \"\"\nfor l in lines:\n if \"checks passed\" in l:\n checks_line = l.strip()\n if \"Signal(s) generated\" in l:\n signals_line = l.strip()\n\nhas_check_lines = any(\n l.startswith((\"PASS \", \"FAIL \", \"WARN \", \"SKIP \"))\n for l in lines\n)\nexecuted = (\n \"Running node doctor...\" in raw\n or \"NODE DOCTOR REPORT\" in raw\n or checks_line != \"\"\n or has_check_lines\n)\n\nif not executed:\n result = \"unknown\"\n fallback_reason = \"node doctor checks did not execute or produced no parsable output\"\nelif cmd_rc != 0:\n result = \"unknown\"\n fallback_reason = f\"node doctor command exited with code {cmd_rc}\"\nelif fail_count > 0:\n result = \"fail\"\n fallback_reason = \"\"\nelse:\n result = \"pass\"\n fallback_reason = \"\"\n\nfindings = []\nif checks_line:\n findings.append(checks_line)\nif signals_line:\n findings.append(signals_line)\n\nsignal_pat = re.compile(r\"^Signal \\d+:\\s+(.+)$\")\nfor l in lines:\n m = signal_pat.match(l.strip())\n if m:\n findings.append(f\"Signal: {m.group(1)}\")\n\nsnippet_lines = lines[-120:]\nraw_snippet = \"\\n\".join(snippet_lines)\n\nout = {\n \"node_doctor_attempted\": True,\n \"node_doctor_executed\": executed,\n \"node_doctor_node\": node,\n \"node_doctor_image\": image,\n \"node_doctor_namespace\": namespace,\n \"node_doctor_debug_pod\": pod_name,\n \"node_doctor_result\": result,\n \"node_doctor_command_rc\": cmd_rc,\n \"node_doctor_findings\": findings,\n \"node_doctor_counts\": {\n \"pass\": pass_count,\n \"fail\": fail_count,\n \"warn\": warn_count,\n \"skip\": skip_count\n },\n \"node_doctor_raw_snippet\": raw_snippet,\n \"node_doctor_fallback_reason\": fallback_reason,\n \"cleanup_attempted\": cleanup_attempted,\n \"cleanup_message\": cleanup_message\n}\n\nprint(json.dumps(out, indent=2))\nPY\n\n# Runtime failures are represented in JSON fields above. Exit 0 so callers can\n# always parse structured output and decide fallback behavior.\nexit 0\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":6168,"content_sha256":"d9f6e76b0a8efb5a3c3c409c49bb70e6ec7b87da784fd62af20cfbdab892dfb7"},{"filename":"oke/scripts/oke-addon-health.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Collect OKE add-on health signals from kube-system.\n#\n# Usage:\n# bash scripts/oke-addon-health.sh [--namespace kube-system]\n\nnamespace=\"kube-system\"\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\n\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace)\n require_value \"$1\" \"${2:-}\"\n namespace=\"$2\"\n shift 2\n ;;\n -h|--help)\n echo \"usage: oke-addon-health.sh [--namespace kube-system]\"\n exit 0\n ;;\n *)\n emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\"\n ;;\n esac\ndone\n\nif ! command -v kubectl >/dev/null 2>&1; then\n emit_error 2 \"KUBECTL_NOT_INSTALLED\" \"kubectl is not installed or not on PATH.\" \"Install kubectl and retry.\"\nfi\n\ntmp_dir=\"$(mktemp -d)\"\ntrap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"\n: > \"$records\"\n\nrun_check() {\n local name=\"$1\"\n shift\n local out rc\n set +e\n out=\"$(\"$@\" 2>&1)\"\n rc=$?\n set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json\nimport os\nimport sys\n\nrecord = {\n \"name\": os.environ[\"NAME\"],\n \"cmd\": os.environ[\"CMD\"],\n \"rc\": int(os.environ[\"RC\"]),\n \"output\": os.environ[\"OUT\"][-4000:],\n}\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps(record) + \"\\n\")\nPY\n}\n\nrun_check \"kube-system pods\" kubectl -n \"$namespace\" get pods -o wide\nrun_check \"kube-system deployments\" kubectl -n \"$namespace\" get deploy -o wide\nrun_check \"kube-system daemonsets\" kubectl -n \"$namespace\" get ds -o wide\nrun_check \"CoreDNS deployment\" kubectl -n \"$namespace\" get deploy coredns -o wide\nrun_check \"recent kube-system warnings\" kubectl -n \"$namespace\" get events --field-selector type=Warning --sort-by=.lastTimestamp\n\npython3 - \"$records\" \"$namespace\" \u003c\u003c'PY'\nimport json\nimport re\nimport sys\nfrom pathlib import Path\n\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace = sys.argv[2]\nanomaly_re = re.compile(\n r\"(CrashLoopBackOff|ImagePullBackOff|ErrImagePull|OOMKilled|NotReady|Pending|Error|Failed|0/[1-9])\",\n re.I,\n)\n\nfindings = []\nanomalies = []\nsnippets = []\nfor item in records:\n output = item[\"output\"].strip()\n if item[\"rc\"] == 0:\n findings.append(f\"{item['name']} collected\")\n else:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if output:\n snippets.append(f\"$ {item['cmd']}\\n{output[-800:]}\")\n for line in output.splitlines():\n if anomaly_re.search(line):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\n\nprint(json.dumps({\n \"domain\": \"OKE Add-ons Health\",\n \"namespace\": namespace,\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-8:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3264,"content_sha256":"6e10400f481bc6bd98f1de1af56063ef806bcd483f4065d96b1dd8bd74f6abfc"},{"filename":"oke/scripts/oke-autoscaler-check.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Collect cluster-autoscaler, Pending pod, and optional OKE node-pool evidence.\n#\n# Usage:\n# bash scripts/oke-autoscaler-check.sh --namespace \u003cns> [--deployment \u003cname>] [--cluster-id \u003cocid>] [--compartment-id \u003cocid>] [--region \u003cregion>]\n\nnamespace=\"\"\ndeployment=\"\"\ncluster_id=\"\"\ncompartment_id=\"\"\nregion=\"\"\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\n\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace|-n)\n require_value \"$1\" \"${2:-}\"\n namespace=\"$2\"; shift 2 ;;\n --deployment)\n require_value \"$1\" \"${2:-}\"\n deployment=\"$2\"; shift 2 ;;\n --cluster-id)\n require_value \"$1\" \"${2:-}\"\n cluster_id=\"$2\"; shift 2 ;;\n --compartment-id)\n require_value \"$1\" \"${2:-}\"\n compartment_id=\"$2\"; shift 2 ;;\n --region)\n require_value \"$1\" \"${2:-}\"\n region=\"$2\"; shift 2 ;;\n -h|--help)\n echo \"usage: oke-autoscaler-check.sh --namespace \u003cns> [--deployment \u003cname>] [--cluster-id \u003cocid>] [--compartment-id \u003cocid>] [--region \u003cregion>]\"\n exit 0 ;;\n *)\n emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\" ;;\n esac\ndone\n\nif [[ -z \"$namespace\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing required --namespace.\" \"Provide --namespace \u003cns>.\"\nfi\n\nif ! command -v kubectl >/dev/null 2>&1; then\n emit_error 2 \"KUBECTL_NOT_INSTALLED\" \"kubectl is not installed or not on PATH.\" \"Install kubectl and retry.\"\nfi\n\ntmp_dir=\"$(mktemp -d)\"\ntrap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"\n: > \"$records\"\n\nrun_check() {\n local name=\"$1\"\n shift\n local out rc\n set +e\n out=\"$(\"$@\" 2>&1)\"\n rc=$?\n set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json\nimport os\nimport sys\n\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\n \"name\": os.environ[\"NAME\"],\n \"cmd\": os.environ[\"CMD\"],\n \"rc\": int(os.environ[\"RC\"]),\n \"output\": os.environ[\"OUT\"][-4000:],\n }) + \"\\n\")\nPY\n}\n\nrun_check \"pending pods\" kubectl -n \"$namespace\" get pods --field-selector=status.phase=Pending -o wide\nrun_check \"scheduling warning events\" kubectl -n \"$namespace\" get events --field-selector reason=FailedScheduling --sort-by=.lastTimestamp\nrun_check \"cluster-autoscaler deployment\" kubectl -n kube-system get deploy cluster-autoscaler -o wide\nrun_check \"cluster-autoscaler logs\" kubectl -n kube-system logs deployment/cluster-autoscaler --tail=200\n\nif [[ -n \"$deployment\" ]]; then\n run_check \"target deployment\" kubectl -n \"$namespace\" describe deployment \"$deployment\"\n run_check \"target deployment hpa\" kubectl -n \"$namespace\" get hpa --selector \"app=$deployment\" -o wide\nfi\n\nif [[ -n \"$cluster_id\" && -n \"$compartment_id\" ]] && command -v oci >/dev/null 2>&1; then\n oci_cmd=(oci)\n if [[ -n \"$region\" ]]; then\n oci_cmd+=(--region \"$region\")\n fi\n run_check \"OKE node pools\" \"${oci_cmd[@]}\" ce node-pool list --compartment-id \"$compartment_id\" --cluster-id \"$cluster_id\" --all --output json\nfi\n\npython3 - \"$records\" \"$namespace\" \"$deployment\" \u003c\u003c'PY'\nimport json\nimport re\nimport sys\nfrom pathlib import Path\n\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace, deployment = sys.argv[2], sys.argv[3]\nanomaly_re = re.compile(\n r\"(FailedScheduling|Insufficient|max node group size reached|scale.?up|NotTriggerScaleUp|quota|limit|no node group|didn.t trigger|unremovable|Pending|Error|Failed)\",\n re.I,\n)\n\nfindings = []\nanomalies = []\nsnippets = []\nfor item in records:\n output = item[\"output\"].strip()\n if item[\"rc\"] == 0:\n findings.append(f\"{item['name']} collected\")\n else:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if output:\n snippets.append(f\"$ {item['cmd']}\\n{output[-800:]}\")\n for line in output.splitlines():\n if anomaly_re.search(line):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\n\nprint(json.dumps({\n \"domain\": \"Cluster Autoscaler / Node Pool Scaling\",\n \"namespace\": namespace,\n \"deployment\": deployment,\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-10:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4697,"content_sha256":"3f54eb12c418933902c49ff342f9ef1e283f9359fb3b2ef64f16b9186a4f43c9"},{"filename":"oke/scripts/oke-discover.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Discover OKE cluster context for troubleshooting.\n# Usage:\n# ./scripts/oke-discover.sh --cluster \u003cname-or-ocid> [--region \u003cregion>] [--profile \u003coci-profile>] [--timeout \u003cseconds>] [--kubeconfig \u003cpath>] [--deployment \u003cname>]\n\ncluster_ref=\"\"\nregion_arg=\"\"\nprofile_arg=\"\"\ntimeout_arg=\"\"\nkubeconfig_arg=\"\"\ndeployment_name=\"\"\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n local docs_url=\"${5:-}\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"%s\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" \"$docs_url\" >&2\n exit \"$exit_code\"\n}\n\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \\\n \"Run with --help to view usage.\"\n fi\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --cluster)\n require_value \"$1\" \"${2:-}\"\n cluster_ref=\"$2\"; shift 2 ;;\n --region)\n require_value \"$1\" \"${2:-}\"\n region_arg=\"$2\"; shift 2 ;;\n --profile)\n require_value \"$1\" \"${2:-}\"\n profile_arg=\"$2\"; shift 2 ;;\n --timeout)\n require_value \"$1\" \"${2:-}\"\n timeout_arg=\"$2\"; shift 2 ;;\n --kubeconfig)\n require_value \"$1\" \"${2:-}\"\n kubeconfig_arg=\"$2\"; shift 2 ;;\n --deployment)\n require_value \"$1\" \"${2:-}\"\n deployment_name=\"$2\"; shift 2 ;;\n -h|--help)\n echo \"usage: $0 --cluster \u003cname-or-ocid> [--region \u003cregion>] [--profile \u003coci-profile>] [--timeout \u003cseconds>] [--kubeconfig \u003cpath>] [--deployment \u003cname>]\" >&2\n exit 0 ;;\n *)\n emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \\\n \"Run with --help to view usage.\" ;;\n esac\ndone\n\nif [[ -z \"$cluster_ref\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing required --cluster.\" \\\n \"Provide --cluster \u003cname-or-ocid>.\"\nfi\n\nif ! command -v oci >/dev/null 2>&1; then\n emit_error 2 \"OCI_CLI_NOT_INSTALLED\" \"OCI CLI not found on PATH.\" \\\n \"Install OCI CLI and retry.\" \\\n \"https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm\"\nfi\n\n# Read defaults from OCI config if present\nconfig_file=\"$HOME/.oci/config\"\nconfig_region=\"\"\nif [[ -f \"$config_file\" ]]; then\n config_region=$(awk -F= '/^region=/{print $2; exit}' \"$config_file\" | tr -d ' ')\nfi\n\nhave_timeout=\"no\"\nif command -v timeout >/dev/null 2>&1; then\n have_timeout=\"yes\"\nfi\n\nuse_py_timeout=\"no\"\ndeclare -a timeout_prefix=()\nif [[ -n \"$timeout_arg\" ]]; then\n if [[ \"$have_timeout\" == \"yes\" ]]; then\n timeout_prefix=(timeout \"$timeout_arg\")\n else\n use_py_timeout=\"yes\"\n fi\nfi\n\ndeclare -a profile_args=()\nif [[ -n \"$profile_arg\" ]]; then\n profile_args=(--profile \"$profile_arg\")\nelif [[ -n \"${OCI_CLI_PROFILE:-}\" ]]; then\n profile_args=(--profile \"$OCI_CLI_PROFILE\")\nfi\n\noci_json() {\n local out err rc\n local -a cmd\n err=\"$(mktemp)\"\n cmd=(oci --region \"$region\")\n if [[ ${#profile_args[@]} -gt 0 ]]; then\n cmd+=(\"${profile_args[@]}\")\n fi\n cmd+=(\"$@\")\n if [[ ${#timeout_prefix[@]} -gt 0 ]]; then\n cmd=(\"${timeout_prefix[@]}\" \"${cmd[@]}\")\n fi\n if [[ \"$use_py_timeout\" == \"yes\" ]]; then\n set +e\n out=\"$(python3 - \"$timeout_arg\" \"$err\" \"${cmd[@]}\" \u003c\u003c'PY'\nimport subprocess, sys\ntimeout = float(sys.argv[1])\nerr_path = sys.argv[2]\ncmd = sys.argv[3:]\ntry:\n p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout, text=True)\n if p.stderr:\n with open(err_path, \"w\") as f:\n f.write(p.stderr)\n sys.stdout.write(p.stdout or \"\")\n sys.exit(p.returncode)\nexcept subprocess.TimeoutExpired:\n with open(err_path, \"w\") as f:\n f.write(\"Command timed out after %ss\\n\" % timeout)\n sys.exit(124)\nPY\n)\"\n rc=$?\n set -e\n else\n set +e\n out=\"$(\"${cmd[@]}\" 2>\"$err\")\"\n rc=$?\n set -e\n fi\n if [[ $rc -ne 0 ]]; then\n echo \"error: oci $* failed (exit $rc)\" >&2\n sed -e 's/^/oci stderr: /' \"$err\" >&2\n rm -f \"$err\"\n return \"$rc\"\n fi\n rm -f \"$err\"\n printf \"%s\" \"$out\"\n}\n\njson_get_field() {\n local field=\"$1\"\n python3 -c '\nimport json\nimport sys\n\nfield = sys.argv[1]\ntry:\n data = json.load(sys.stdin)\nexcept Exception:\n data = {}\n\nif isinstance(data, dict):\n value = data.get(field, \"\")\n if value is None:\n value = \"\"\n print(value)\nelse:\n print(\"\")\n' \"$field\"\n}\n\ncluster_ocid=\"\"\ncluster_name=\"\"\ncluster_k8s=\"\"\ncompartment_ocid=\"\"\n\nif [[ \"$cluster_ref\" == ocid1.* ]]; then\n cluster_ocid=\"$cluster_ref\"\nelse\n # Resolve from kubeconfig\n kubeconfig_path=\"${kubeconfig_arg:-$HOME/.kube/config}\"\n if [[ -f \"$kubeconfig_path\" ]]; then\n cluster_ocid=$(python3 - \"$kubeconfig_path\" \"$cluster_ref\" \u003c\u003c'PY'\nimport sys\nfrom pathlib import Path\ntry:\n import yaml\nexcept Exception:\n print(\"\")\n raise SystemExit(0)\npath = Path(sys.argv[1])\nname = sys.argv[2]\ntry:\n data = yaml.safe_load(path.read_text())\nexcept Exception:\n print(\"\")\n raise SystemExit(0)\n\ncontexts = data.get(\"contexts\", []) or []\nusers = {u.get(\"name\"): u.get(\"user\", {}) for u in (data.get(\"users\", []) or [])}\n\ndef pick_user(contexts, target):\n exact = []\n exact_ci = []\n contains = []\n for c in contexts:\n cname = c.get(\"name\", \"\")\n cuser = (c.get(\"context\", {}) or {}).get(\"user\")\n if not cuser:\n continue\n if cname == target:\n exact.append((len(cname), cname, cuser))\n elif cname.lower() == target.lower():\n exact_ci.append((len(cname), cname, cuser))\n elif target and target in cname:\n contains.append((len(cname), cname, cuser))\n for bucket in (exact, exact_ci, contains):\n if bucket:\n bucket.sort()\n return bucket[0][2]\n return None\n\nmatch_user = pick_user(contexts, name)\n\nif not match_user:\n print(\"\")\n raise SystemExit(0)\n\nexec_cfg = users.get(match_user, {}).get(\"exec\", {})\nargs = exec_cfg.get(\"args\", []) if isinstance(exec_cfg, dict) else []\ntry:\n idx = args.index(\"--cluster-id\")\n print(args[idx + 1])\nexcept Exception:\n print(\"\")\nPY\n )\n fi\nfi\n\nif [[ -z \"$cluster_ocid\" ]]; then\n emit_error 1 \"CLUSTER_OCID_NOT_RESOLVED\" \\\n \"Could not resolve cluster OCID from kubeconfig.\" \\\n \"Provide cluster OCID directly with --cluster \u003cocid>.\"\nfi\n\nregion_from_cluster_ocid=\"$(python3 - \"$cluster_ocid\" \u003c\u003c'PY'\nimport re\nimport sys\n\nocid = sys.argv[1].strip()\nmatch = re.match(r\"^ocid1\\.cluster\\.oc1\\.([a-z0-9-]+)\\..+$\", ocid)\nprint(match.group(1) if match else \"\")\nPY\n)\"\n\nregion=\"${region_arg:-${region_from_cluster_ocid:-$config_region}}\"\nif [[ -z \"$region\" ]]; then\n emit_error 2 \"REGION_NOT_PROVIDED\" \\\n \"Region not provided and could not be inferred from cluster OCID or OCI config.\" \\\n \"Provide --region \u003cregion> or set region in ~/.oci/config.\"\nfi\n\n# Fetch cluster details if possible\ncluster_json=$(oci_json ce cluster get --cluster-id \"$cluster_ocid\" --query 'data.{name:\"name\",k8s:\"kubernetes-version\",compartment:\"compartment-id\"}' --output json || true)\nif [[ -n \"$cluster_json\" && \"$cluster_json\" != \"{}\" ]]; then\n cluster_name=$(printf '%s' \"$cluster_json\" | json_get_field name)\n cluster_k8s=$(printf '%s' \"$cluster_json\" | json_get_field k8s)\n compartment_ocid=$(printf '%s' \"$cluster_json\" | json_get_field compartment)\nelse\n echo \"warning: failed to fetch cluster details; returning partial context\" >&2\n cluster_name=\"$cluster_ref\"\nfi\n\n# Optional: try to resolve deployment namespace if kubectl is available\ndeployment_namespace=\"\"\ndeployment_namespaces=\"\"\nif [[ -n \"$deployment_name\" ]]; then\n if command -v kubectl >/dev/null 2>&1; then\n deployment_namespaces=$(kubectl get deploy -A --no-headers 2>/dev/null | awk -v d=\"$deployment_name\" '$2==d {print $1}' | paste -sd \",\" -)\n if [[ -n \"$deployment_namespaces\" ]]; then\n if [[ \"$deployment_namespaces\" == *,* ]]; then\n deployment_namespace=\"\"\n else\n deployment_namespace=\"$deployment_namespaces\"\n fi\n fi\n else\n echo \"warning: kubectl not found; cannot resolve deployment namespace\" >&2\n fi\nfi\n\npython3 - \u003c\u003cPY\nimport json\nprint(json.dumps({\n \"cluster\": {\n \"name\": \"${cluster_name}\",\n \"id\": \"${cluster_ocid}\",\n \"kubernetes_version\": \"${cluster_k8s}\",\n \"compartment_id\": \"${compartment_ocid}\",\n \"region\": \"${region}\"\n },\n \"deployment\": {\n \"name\": \"${deployment_name}\",\n \"namespace\": \"${deployment_namespace}\",\n \"namespaces\": \"${deployment_namespaces}\"\n }\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":8455,"content_sha256":"fcef6b9c1f3f296788a871fcfbdb2b591905d2a13ee94f3ebca0db73ae889184"},{"filename":"oke/scripts/oke-dns-check.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Collect CoreDNS and service discovery signals.\n#\n# Usage:\n# bash scripts/oke-dns-check.sh --namespace \u003cns> [--service \u003csvc>] [--pod \u003cpod>] [--lookup \u003cdns-name>]\n\nnamespace=\"\"\nservice=\"\"\npod=\"\"\nlookup=\"\"\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\n\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace|-n)\n require_value \"$1\" \"${2:-}\"\n namespace=\"$2\"; shift 2 ;;\n --service)\n require_value \"$1\" \"${2:-}\"\n service=\"$2\"; shift 2 ;;\n --pod)\n require_value \"$1\" \"${2:-}\"\n pod=\"$2\"; shift 2 ;;\n --lookup)\n require_value \"$1\" \"${2:-}\"\n lookup=\"$2\"; shift 2 ;;\n -h|--help)\n echo \"usage: oke-dns-check.sh --namespace \u003cns> [--service \u003csvc>] [--pod \u003cpod>] [--lookup \u003cdns-name>]\"\n exit 0 ;;\n *)\n emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\" ;;\n esac\ndone\n\nif [[ -z \"$namespace\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing required --namespace.\" \"Provide --namespace \u003cns>.\"\nfi\n\nif ! command -v kubectl >/dev/null 2>&1; then\n emit_error 2 \"KUBECTL_NOT_INSTALLED\" \"kubectl is not installed or not on PATH.\" \"Install kubectl and retry.\"\nfi\n\ntmp_dir=\"$(mktemp -d)\"\ntrap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"\n: > \"$records\"\n\nrun_check() {\n local name=\"$1\"\n shift\n local out rc\n set +e\n out=\"$(\"$@\" 2>&1)\"\n rc=$?\n set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json\nimport os\nimport sys\n\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\n \"name\": os.environ[\"NAME\"],\n \"cmd\": os.environ[\"CMD\"],\n \"rc\": int(os.environ[\"RC\"]),\n \"output\": os.environ[\"OUT\"][-4000:],\n }) + \"\\n\")\nPY\n}\n\nrun_check \"CoreDNS pods\" kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide\nrun_check \"CoreDNS deployment\" kubectl -n kube-system get deploy coredns -o wide\nrun_check \"CoreDNS config\" kubectl -n kube-system get configmap coredns -o yaml\nrun_check \"CoreDNS logs\" kubectl -n kube-system logs deployment/coredns --tail=200\n\nif [[ -n \"$service\" ]]; then\n run_check \"target service\" kubectl -n \"$namespace\" get svc \"$service\" -o yaml\n run_check \"target endpoints\" kubectl -n \"$namespace\" get endpoints \"$service\" -o yaml\n run_check \"target endpoint slices\" kubectl -n \"$namespace\" get endpointslices -l \"kubernetes.io/service-name=$service\" -o yaml\nfi\n\nif [[ -n \"$pod\" && -n \"$lookup\" ]]; then\n run_check \"pod DNS lookup\" kubectl -n \"$namespace\" exec \"$pod\" -- nslookup \"$lookup\"\nfi\n\npython3 - \"$records\" \"$namespace\" \"$service\" \"$pod\" \"$lookup\" \u003c\u003c'PY'\nimport json\nimport re\nimport sys\nfrom pathlib import Path\n\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace, service, pod, lookup = sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\nanomaly_re = re.compile(\n r\"(SERVFAIL|NXDOMAIN|timeout|no such host|connection refused|no endpoints|CrashLoopBackOff|NotReady|Error|Failed)\",\n re.I,\n)\n\nfindings = []\nanomalies = []\nsnippets = []\nfor item in records:\n output = item[\"output\"].strip()\n if item[\"rc\"] == 0:\n findings.append(f\"{item['name']} collected\")\n else:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if output:\n snippets.append(f\"$ {item['cmd']}\\n{output[-800:]}\")\n for line in output.splitlines():\n if anomaly_re.search(line):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\n\nprint(json.dumps({\n \"domain\": \"DNS / Service Discovery\",\n \"namespace\": namespace,\n \"service\": service,\n \"pod\": pod,\n \"lookup\": lookup,\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-10:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4240,"content_sha256":"cc7c5b7de2c24fe4dad501301ca37024699ce36a97e515e195c331d056cce838"},{"filename":"oke/scripts/oke-incident-timeline.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Build a lightweight incident timeline from Kubernetes events and selected objects.\n#\n# Usage:\n# bash scripts/oke-incident-timeline.sh --namespace \u003cns> [--pod \u003cpod>] [--deployment \u003cname>] [--service \u003csvc>] [--compartment-id \u003cocid>] [--region \u003cregion>]\n\nnamespace=\"\"\npod=\"\"\ndeployment=\"\"\nservice=\"\"\ncompartment_id=\"\"\nregion=\"\"\n\nemit_error() {\n local exit_code=\"$1\"; local error_code=\"$2\"; local message=\"$3\"; local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace|-n) require_value \"$1\" \"${2:-}\"; namespace=\"$2\"; shift 2 ;;\n --pod) require_value \"$1\" \"${2:-}\"; pod=\"$2\"; shift 2 ;;\n --deployment) require_value \"$1\" \"${2:-}\"; deployment=\"$2\"; shift 2 ;;\n --service) require_value \"$1\" \"${2:-}\"; service=\"$2\"; shift 2 ;;\n --compartment-id) require_value \"$1\" \"${2:-}\"; compartment_id=\"$2\"; shift 2 ;;\n --region) require_value \"$1\" \"${2:-}\"; region=\"$2\"; shift 2 ;;\n -h|--help) echo \"usage: oke-incident-timeline.sh --namespace \u003cns> [--pod \u003cpod>] [--deployment \u003cname>] [--service \u003csvc>] [--compartment-id \u003cocid>] [--region \u003cregion>]\"; exit 0 ;;\n *) emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\" ;;\n esac\ndone\nif [[ -z \"$namespace\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing --namespace.\" \"Provide --namespace \u003cns>.\"\nfi\ncommand -v kubectl >/dev/null 2>&1 || emit_error 2 \"KUBECTL_NOT_INSTALLED\" \"kubectl is not installed or not on PATH.\" \"Install kubectl and retry.\"\n\ntmp_dir=\"$(mktemp -d)\"; trap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"; : > \"$records\"\nrun_check() {\n local name=\"$1\"; shift\n local out rc\n set +e; out=\"$(\"$@\" 2>&1)\"; rc=$?; set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json, os, sys\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\"name\": os.environ[\"NAME\"], \"cmd\": os.environ[\"CMD\"], \"rc\": int(os.environ[\"RC\"]), \"output\": os.environ[\"OUT\"][-6000:]}) + \"\\n\")\nPY\n}\n\nrun_check \"namespace events\" kubectl -n \"$namespace\" get events --sort-by=.lastTimestamp\nif [[ -n \"$pod\" ]]; then\n run_check \"pod describe\" kubectl -n \"$namespace\" describe pod \"$pod\"\nfi\nif [[ -n \"$deployment\" ]]; then\n run_check \"deployment rollout history\" kubectl -n \"$namespace\" rollout history deployment/\"$deployment\"\n run_check \"deployment describe\" kubectl -n \"$namespace\" describe deployment \"$deployment\"\nfi\nif [[ -n \"$service\" ]]; then\n run_check \"service describe\" kubectl -n \"$namespace\" describe service \"$service\"\nfi\nif [[ -n \"$compartment_id\" && -n \"$region\" ]] && command -v oci >/dev/null 2>&1; then\n run_check \"firing alarms\" oci --region \"$region\" monitoring alarm-status list-alarms-status --compartment-id \"$compartment_id\" --status FIRING --all --output json\nfi\n\npython3 - \"$records\" \"$namespace\" \"$pod\" \"$deployment\" \"$service\" \u003c\u003c'PY'\nimport json, re, sys\nfrom pathlib import Path\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace, pod, deployment, service = sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\nsignal = re.compile(r\"(Warning|Failed|BackOff|Killing|Pulling|Created|Started|Scaled|Successful|Unhealthy|NotReady|FIRING)\", re.I)\ntimeline = []\nfindings, anomalies, snippets = [], [], []\nfor item in records:\n out = item[\"output\"].strip()\n findings.append(f\"{item['name']} {'collected' if item['rc'] == 0 else 'failed'}\")\n if item[\"rc\"] != 0:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if out:\n snippets.append(f\"$ {item['cmd']}\\n{out[-800:]}\")\n for line in out.splitlines():\n if signal.search(line):\n timeline.append({\"source\": item[\"name\"], \"event\": line.strip()[:300]})\n if re.search(r\"(Warning|Failed|BackOff|Unhealthy|NotReady|FIRING)\", line, re.I):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\nprint(json.dumps({\n \"domain\": \"Incident Timeline\",\n \"namespace\": namespace,\n \"pod\": pod,\n \"deployment\": deployment,\n \"service\": service,\n \"findings\": findings,\n \"timeline\": timeline[-30:],\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-10:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4632,"content_sha256":"01f392006b1c1a5d3bb91881820485675226ab45c2c72caca773b87a3fbc621d"},{"filename":"oke/scripts/oke-ingress-check.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Collect OCI Native Ingress and Kubernetes Ingress evidence.\n#\n# Usage:\n# bash scripts/oke-ingress-check.sh --namespace \u003cns> --ingress \u003cname> [--region \u003cregion>]\n\nnamespace=\"\"\ningress=\"\"\nregion=\"\"\n\nemit_error() {\n local exit_code=\"$1\"; local error_code=\"$2\"; local message=\"$3\"; local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace|-n) require_value \"$1\" \"${2:-}\"; namespace=\"$2\"; shift 2 ;;\n --ingress) require_value \"$1\" \"${2:-}\"; ingress=\"$2\"; shift 2 ;;\n --region) require_value \"$1\" \"${2:-}\"; region=\"$2\"; shift 2 ;;\n -h|--help) echo \"usage: oke-ingress-check.sh --namespace \u003cns> --ingress \u003cname> [--region \u003cregion>]\"; exit 0 ;;\n *) emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\" ;;\n esac\ndone\nif [[ -z \"$namespace\" || -z \"$ingress\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing --namespace or --ingress.\" \"Provide both --namespace and --ingress.\"\nfi\ncommand -v kubectl >/dev/null 2>&1 || emit_error 2 \"KUBECTL_NOT_INSTALLED\" \"kubectl is not installed or not on PATH.\" \"Install kubectl and retry.\"\n\ntmp_dir=\"$(mktemp -d)\"; trap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"; : > \"$records\"\nrun_check() {\n local name=\"$1\"; shift\n local out rc\n set +e; out=\"$(\"$@\" 2>&1)\"; rc=$?; set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json, os, sys\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\"name\": os.environ[\"NAME\"], \"cmd\": os.environ[\"CMD\"], \"rc\": int(os.environ[\"RC\"]), \"output\": os.environ[\"OUT\"][-4000:]}) + \"\\n\")\nPY\n}\n\nrun_check \"ingress classes\" kubectl get ingressclass -o yaml\nrun_check \"target ingress\" kubectl -n \"$namespace\" get ingress \"$ingress\" -o yaml\nrun_check \"target ingress describe\" kubectl -n \"$namespace\" describe ingress \"$ingress\"\nrun_check \"ingress controller pods\" kubectl -n kube-system get pods -l app.kubernetes.io/name=oci-native-ingress-controller -o wide\nrun_check \"ingress controller logs\" kubectl -n kube-system logs -l app.kubernetes.io/name=oci-native-ingress-controller --tail=200\n\npython3 - \"$records\" \"$namespace\" \"$ingress\" \"$region\" \u003c\u003c'PY'\nimport json, re, sys\nfrom pathlib import Path\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace, ingress, region = sys.argv[2], sys.argv[3], sys.argv[4]\npat = re.compile(r\"(Failed|Error|Warning|certificate|TLS|listener|backend|reconcile|NotFound|forbidden|load balancer|ingressclass)\", re.I)\nfindings, anomalies, snippets = [], [], []\nfor item in records:\n out = item[\"output\"].strip()\n findings.append(f\"{item['name']} {'collected' if item['rc'] == 0 else 'failed'}\")\n if item[\"rc\"] != 0:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if out:\n snippets.append(f\"$ {item['cmd']}\\n{out[-800:]}\")\n for line in out.splitlines():\n if pat.search(line):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\nprint(json.dumps({\n \"domain\": \"Ingress / OCI Native Ingress\",\n \"namespace\": namespace,\n \"ingress\": ingress,\n \"region\": region,\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-10:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3690,"content_sha256":"435a4993d2d4e6544ead96af873e930fe9cdd307255ecd13363202352d5bf8c2"},{"filename":"oke/scripts/oke-object-correlator.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Build a read-only Kubernetes-to-OCI object graph for OKE troubleshooting.\n#\n# Usage:\n# bash scripts/oke-object-correlator.sh --namespace \u003cns> --cluster-id \u003cocid> --compartment-id \u003cocid> --region \u003cregion> [--pod \u003cpod>] [--deployment \u003cdeployment>] [--service \u003cservice>] [--ingress \u003cingress>] [--pvc \u003cpvc>] [--node \u003cnode>]\n\nnamespace=\"\"\ncluster_id=\"\"\ncompartment_id=\"\"\nregion=\"\"\npod=\"\"\ndeployment=\"\"\nservice=\"\"\ningress=\"\"\npvc=\"\"\nnode=\"\"\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\n\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace|-n) require_value \"$1\" \"${2:-}\"; namespace=\"$2\"; shift 2 ;;\n --cluster-id) require_value \"$1\" \"${2:-}\"; cluster_id=\"$2\"; shift 2 ;;\n --compartment-id) require_value \"$1\" \"${2:-}\"; compartment_id=\"$2\"; shift 2 ;;\n --region) require_value \"$1\" \"${2:-}\"; region=\"$2\"; shift 2 ;;\n --pod) require_value \"$1\" \"${2:-}\"; pod=\"$2\"; shift 2 ;;\n --deployment) require_value \"$1\" \"${2:-}\"; deployment=\"$2\"; shift 2 ;;\n --service) require_value \"$1\" \"${2:-}\"; service=\"$2\"; shift 2 ;;\n --ingress) require_value \"$1\" \"${2:-}\"; ingress=\"$2\"; shift 2 ;;\n --pvc) require_value \"$1\" \"${2:-}\"; pvc=\"$2\"; shift 2 ;;\n --node) require_value \"$1\" \"${2:-}\"; node=\"$2\"; shift 2 ;;\n -h|--help)\n echo \"usage: oke-object-correlator.sh --namespace \u003cns> --cluster-id \u003cocid> --compartment-id \u003cocid> --region \u003cregion> [--pod \u003cpod>] [--deployment \u003cdeployment>] [--service \u003cservice>] [--ingress \u003cingress>] [--pvc \u003cpvc>] [--node \u003cnode>]\"\n exit 0 ;;\n *) emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\" ;;\n esac\ndone\n\nif [[ -z \"$namespace\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing required --namespace.\" \"Provide --namespace \u003cns>.\"\nfi\n\ntmp_dir=\"$(mktemp -d)\"\ntrap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"\n: > \"$records\"\n\nrecord_check() {\n local name=\"$1\"\n local kind=\"$2\"\n local cmd=\"$3\"\n local rc=\"$4\"\n local out=\"$5\"\n NAME=\"$name\" KIND=\"$kind\" CMD=\"$cmd\" RC=\"$rc\" OUT=\"$out\" python3 - \"$records\" \u003c\u003c'PY'\nimport json\nimport os\nimport sys\n\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\n \"name\": os.environ[\"NAME\"],\n \"kind\": os.environ[\"KIND\"],\n \"cmd\": os.environ[\"CMD\"],\n \"rc\": int(os.environ[\"RC\"]),\n \"output\": os.environ[\"OUT\"][-12000:],\n }) + \"\\n\")\nPY\n}\n\nrun_check() {\n local name=\"$1\"\n local kind=\"$2\"\n shift 2\n local out rc\n set +e\n out=\"$(\"$@\" 2>&1)\"\n rc=$?\n set -e\n record_check \"$name\" \"$kind\" \"$*\" \"$rc\" \"$out\"\n}\n\nkubectl_available=\"yes\"\noci_available=\"yes\"\nif ! command -v kubectl >/dev/null 2>&1; then\n kubectl_available=\"no\"\n record_check \"kubectl availability\" \"tool\" \"kubectl\" 127 \"kubectl is not installed or not on PATH\"\nfi\nif ! command -v oci >/dev/null 2>&1; then\n oci_available=\"no\"\n record_check \"oci availability\" \"tool\" \"oci\" 127 \"oci is not installed or not on PATH\"\nfi\n\nif [[ \"$kubectl_available\" == \"yes\" ]]; then\n if [[ -n \"$pod\" ]]; then\n run_check \"pod\" \"k8s_pod\" kubectl -n \"$namespace\" get pod \"$pod\" -o json\n fi\n if [[ -n \"$deployment\" ]]; then\n run_check \"deployment\" \"k8s_deployment\" kubectl -n \"$namespace\" get deployment \"$deployment\" -o json\n fi\n if [[ -n \"$service\" ]]; then\n run_check \"service\" \"k8s_service\" kubectl -n \"$namespace\" get service \"$service\" -o json\n run_check \"endpoints\" \"k8s_endpoints\" kubectl -n \"$namespace\" get endpoints \"$service\" -o json\n fi\n if [[ -n \"$ingress\" ]]; then\n run_check \"ingress\" \"k8s_ingress\" kubectl -n \"$namespace\" get ingress \"$ingress\" -o json\n fi\n if [[ -n \"$pvc\" ]]; then\n run_check \"pvc\" \"k8s_pvc\" kubectl -n \"$namespace\" get pvc \"$pvc\" -o json\n fi\n if [[ -n \"$node\" ]]; then\n run_check \"node\" \"k8s_node\" kubectl get node \"$node\" -o json\n fi\nfi\n\noci_cmd=(oci)\nif [[ -n \"$region\" ]]; then\n oci_cmd+=(--region \"$region\")\nfi\n\npython3 - \"$records\" \"$namespace\" \"$cluster_id\" \"$compartment_id\" \"$region\" \"$pod\" \"$deployment\" \"$service\" \"$ingress\" \"$pvc\" \"$node\" \u003c\u003c'PY' >\"$tmp_dir/plan.json\"\nimport json\nimport re\nimport sys\nfrom pathlib import Path\n\nrecords_path = Path(sys.argv[1])\nnamespace, cluster_id, compartment_id, region = sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\npod_arg, deployment_arg, service_arg, ingress_arg, pvc_arg, node_arg = sys.argv[6:12]\nrecords = [json.loads(line) for line in records_path.read_text().splitlines() if line.strip()]\n\ndef parse_json(text):\n try:\n return json.loads(text)\n except Exception:\n return None\n\ndef ocid(value):\n return isinstance(value, str) and value.startswith(\"ocid1.\")\n\ndef provider_instance_id(obj):\n provider_id = ((obj.get(\"spec\") or {}).get(\"providerID\") or \"\")\n match = re.search(r\"(ocid1\\.instance\\.[A-Za-z0-9_.-]+)\", provider_id)\n return match.group(1) if match else \"\"\n\ndef lb_addresses(item):\n values = []\n for address in item.get(\"ip-addresses\", []) or []:\n ip = address.get(\"ip-address\") or address.get(\"ip\")\n if ip:\n values.append(ip)\n return values\n\ndef match_lb_by_ip(items, target_ip):\n if not target_ip:\n return None\n for item in items:\n if target_ip in lb_addresses(item):\n return item\n return None\n\ndef ann(obj, key):\n return (((obj or {}).get(\"metadata\") or {}).get(\"annotations\") or {}).get(key, \"\")\n\ndef meta_name(obj):\n return ((obj or {}).get(\"metadata\") or {}).get(\"name\", \"\")\n\ndef first(value):\n if isinstance(value, list) and value:\n return value[0]\n return None\n\nplan = []\nderived = {\"node\": node_arg, \"instance_id\": \"\", \"lb_id\": \"\", \"lb_ip\": \"\", \"pv\": \"\", \"volume_id\": \"\"}\n\nfor rec in records:\n obj = parse_json(rec.get(\"output\", \"\"))\n if rec[\"kind\"] == \"k8s_pod\" and obj:\n node = ((obj.get(\"spec\") or {}).get(\"nodeName\") or \"\")\n if node and not derived[\"node\"]:\n derived[\"node\"] = node\n for vol in (obj.get(\"spec\") or {}).get(\"volumes\", []) or []:\n claim = (vol.get(\"persistentVolumeClaim\") or {}).get(\"claimName\", \"\")\n if claim:\n plan.append({\"name\": f\"pvc {claim}\", \"kind\": \"k8s_pvc\", \"cmd\": [\"kubectl\", \"-n\", namespace, \"get\", \"pvc\", claim, \"-o\", \"json\"]})\n elif rec[\"kind\"] == \"k8s_node\" and obj:\n annotations = ((obj.get(\"metadata\") or {}).get(\"annotations\") or {})\n instance_id = (\n annotations.get(\"node.oci.oraclecloud.com/instance-id\")\n or annotations.get(\"oci.oraclecloud.com/instance-id\")\n or annotations.get(\"oci.oraclecloud.com/instance_id\")\n or provider_instance_id(obj)\n or \"\"\n )\n if instance_id:\n derived[\"instance_id\"] = instance_id\n elif rec[\"kind\"] == \"k8s_service\" and obj:\n lb_id = (\n ann(obj, \"oci.oraclecloud.com/load-balancer-id\")\n or ann(obj, \"service.beta.kubernetes.io/oci-load-balancer-id\")\n or ann(obj, \"oci.oraclecloud.com/network-load-balancer-id\")\n or \"\"\n )\n if lb_id:\n derived[\"lb_id\"] = lb_id\n ingress = (((obj.get(\"status\") or {}).get(\"loadBalancer\") or {}).get(\"ingress\") or [])\n if ingress:\n derived[\"lb_ip\"] = ingress[0].get(\"ip\") or ingress[0].get(\"hostname\") or \"\"\n elif rec[\"kind\"] == \"k8s_ingress\" and obj:\n lb_id = (\n ann(obj, \"oci.oraclecloud.com/load-balancer-id\")\n or ann(obj, \"service.beta.kubernetes.io/oci-load-balancer-id\")\n or \"\"\n )\n if lb_id:\n derived[\"lb_id\"] = lb_id\n status_ing = (((obj.get(\"status\") or {}).get(\"loadBalancer\") or {}).get(\"ingress\") or [])\n if status_ing and not derived[\"lb_ip\"]:\n derived[\"lb_ip\"] = status_ing[0].get(\"ip\") or status_ing[0].get(\"hostname\") or \"\"\n elif rec[\"kind\"] == \"k8s_pvc\" and obj:\n pv = ((obj.get(\"spec\") or {}).get(\"volumeName\") or \"\")\n if pv and not derived[\"pv\"]:\n derived[\"pv\"] = pv\n elif rec[\"kind\"] == \"k8s_pv\" and obj:\n spec = obj.get(\"spec\") or {}\n handle = (\n (spec.get(\"csi\") or {}).get(\"volumeHandle\")\n or spec.get(\"ociVolumeID\")\n or spec.get(\"volumeID\")\n or \"\"\n )\n if handle and not derived[\"volume_id\"]:\n derived[\"volume_id\"] = handle\n\nif derived[\"node\"] and not any(r[\"kind\"] == \"k8s_node\" for r in records):\n plan.append({\"name\": f\"node {derived['node']}\", \"kind\": \"k8s_node\", \"cmd\": [\"kubectl\", \"get\", \"node\", derived[\"node\"], \"-o\", \"json\"]})\nif derived[\"pv\"] and not any(r[\"kind\"] == \"k8s_pv\" for r in records):\n plan.append({\"name\": f\"pv {derived['pv']}\", \"kind\": \"k8s_pv\", \"cmd\": [\"kubectl\", \"get\", \"pv\", derived[\"pv\"], \"-o\", \"json\"]})\nif derived[\"instance_id\"]:\n plan.append({\"name\": \"compute instance\", \"kind\": \"oci_instance\", \"cmd\": [\"oci\", \"compute\", \"instance\", \"get\", \"--instance-id\", derived[\"instance_id\"], \"--output\", \"json\"]})\n if compartment_id:\n plan.append({\"name\": \"vnic attachments\", \"kind\": \"oci_vnic_attachments\", \"cmd\": [\"oci\", \"compute\", \"vnic-attachment\", \"list\", \"--compartment-id\", compartment_id, \"--instance-id\", derived[\"instance_id\"], \"--all\", \"--output\", \"json\"]})\nif derived[\"lb_id\"]:\n if \".networkloadbalancer.\" in derived[\"lb_id\"]:\n plan.append({\"name\": \"network load balancer\", \"kind\": \"oci_nlb\", \"cmd\": [\"oci\", \"nlb\", \"network-load-balancer\", \"get\", \"--network-load-balancer-id\", derived[\"lb_id\"], \"--output\", \"json\"]})\n else:\n plan.append({\"name\": \"load balancer\", \"kind\": \"oci_lb\", \"cmd\": [\"oci\", \"lb\", \"load-balancer\", \"get\", \"--load-balancer-id\", derived[\"lb_id\"], \"--output\", \"json\"]})\n plan.append({\"name\": \"load balancer health\", \"kind\": \"oci_lb_health\", \"cmd\": [\"oci\", \"lb\", \"load-balancer-health\", \"get\", \"--load-balancer-id\", derived[\"lb_id\"], \"--output\", \"json\"]})\nelif derived[\"lb_ip\"] and compartment_id:\n plan.append({\"name\": \"load balancers by IP\", \"kind\": \"oci_lb_list\", \"cmd\": [\"oci\", \"lb\", \"load-balancer\", \"list\", \"--compartment-id\", compartment_id, \"--all\", \"--output\", \"json\"]})\n plan.append({\"name\": \"network load balancers by IP\", \"kind\": \"oci_nlb_list\", \"cmd\": [\"oci\", \"nlb\", \"network-load-balancer\", \"list\", \"--compartment-id\", compartment_id, \"--all\", \"--output\", \"json\"]})\nif derived[\"volume_id\"]:\n plan.append({\"name\": \"block volume\", \"kind\": \"oci_volume\", \"cmd\": [\"oci\", \"bv\", \"volume\", \"get\", \"--volume-id\", derived[\"volume_id\"], \"--output\", \"json\"]})\n if compartment_id:\n plan.append({\"name\": \"volume attachments\", \"kind\": \"oci_volume_attachments\", \"cmd\": [\"oci\", \"compute\", \"volume-attachment\", \"list\", \"--compartment-id\", compartment_id, \"--volume-id\", derived[\"volume_id\"], \"--all\", \"--output\", \"json\"]})\nif service_arg or ingress_arg:\n if compartment_id:\n plan.append({\"name\": \"network security groups\", \"kind\": \"oci_nsgs\", \"cmd\": [\"oci\", \"network\", \"nsg\", \"list\", \"--compartment-id\", compartment_id, \"--all\", \"--output\", \"json\"]})\nif cluster_id:\n plan.append({\"name\": \"cluster node pools\", \"kind\": \"oci_node_pools\", \"cmd\": [\"oci\", \"ce\", \"node-pool\", \"list\", \"--cluster-id\", cluster_id, \"--compartment-id\", compartment_id, \"--all\", \"--output\", \"json\"]})\n\nprint(json.dumps(plan))\nPY\n\nexecute_plan() {\n local plan_file=\"$1\"\n python3 - \"$plan_file\" \"$region\" \u003c\u003c'PY' >\"$tmp_dir/commands.jsonl\"\nimport json\nimport sys\nfrom pathlib import Path\n\nplan = json.loads(Path(sys.argv[1]).read_text())\nregion = sys.argv[2]\nfor item in plan:\n cmd = item[\"cmd\"]\n if cmd and cmd[0] == \"oci\" and region:\n cmd = [\"oci\", \"--region\", region] + cmd[1:]\n print(json.dumps({\"name\": item[\"name\"], \"kind\": item[\"kind\"], \"cmd\": cmd}))\nPY\n while IFS= read -r line; do\n [[ -n \"$line\" ]] || continue\n name=\"$(python3 -c 'import json,sys; print(json.loads(sys.stdin.read())[\"name\"])' \u003c\u003c\u003c\"$line\")\"\n kind=\"$(python3 -c 'import json,sys; print(json.loads(sys.stdin.read())[\"kind\"])' \u003c\u003c\u003c\"$line\")\"\n cmd_parts=()\n while IFS= read -r part; do\n cmd_parts+=(\"$part\")\n done \u003c \u003c(python3 -c 'import json,sys; [print(x) for x in json.loads(sys.stdin.read())[\"cmd\"]]' \u003c\u003c\u003c\"$line\")\n if [[ \"${cmd_parts[0]:-}\" == \"oci\" && \"$oci_available\" != \"yes\" ]]; then\n continue\n fi\n run_check \"$name\" \"$kind\" \"${cmd_parts[@]}\"\n done \u003c\"$tmp_dir/commands.jsonl\"\n}\n\nexecute_plan \"$tmp_dir/plan.json\"\npython3 - \"$records\" \"$namespace\" \"$cluster_id\" \"$compartment_id\" \"$region\" \"$pod\" \"$deployment\" \"$service\" \"$ingress\" \"$pvc\" \"$node\" \u003c\u003c'PY' >\"$tmp_dir/plan2.json\"\nimport json\nimport re\nimport sys\nfrom pathlib import Path\n\nrecords_path = Path(sys.argv[1])\nnamespace, cluster_id, compartment_id, region = sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\nnode_arg = sys.argv[11]\nrecords = [json.loads(line) for line in records_path.read_text().splitlines() if line.strip()]\n\ndef parse_json(text):\n try:\n return json.loads(text)\n except Exception:\n return None\n\ndef ann(obj, key):\n return (((obj or {}).get(\"metadata\") or {}).get(\"annotations\") or {}).get(key, \"\")\n\ndef provider_instance_id(obj):\n provider_id = ((obj.get(\"spec\") or {}).get(\"providerID\") or \"\")\n match = re.search(r\"(ocid1\\.instance\\.[A-Za-z0-9_.-]+)\", provider_id)\n return match.group(1) if match else \"\"\n\nderived = {\"instance_id\": \"\", \"volume_id\": \"\", \"lb_ip\": \"\", \"lb_id\": \"\"}\nexisting = {(r[\"kind\"], r[\"name\"]) for r in records}\nplan = []\n\nfor rec in records:\n obj = parse_json(rec.get(\"output\", \"\"))\n if not obj:\n continue\n data = obj.get(\"data\", obj) if isinstance(obj, dict) else obj\n if rec[\"kind\"] == \"k8s_node\" and isinstance(data, dict):\n annotations = ((data.get(\"metadata\") or {}).get(\"annotations\") or {})\n derived[\"instance_id\"] = (\n annotations.get(\"node.oci.oraclecloud.com/instance-id\")\n or annotations.get(\"oci.oraclecloud.com/instance-id\")\n or annotations.get(\"oci.oraclecloud.com/instance_id\")\n or provider_instance_id(data)\n or derived[\"instance_id\"]\n )\n elif rec[\"kind\"] == \"k8s_pvc\" and isinstance(data, dict):\n pv = ((data.get(\"spec\") or {}).get(\"volumeName\") or \"\")\n if pv and (\"k8s_pv\", f\"pv {pv}\") not in existing:\n plan.append({\"name\": f\"pv {pv}\", \"kind\": \"k8s_pv\", \"cmd\": [\"kubectl\", \"get\", \"pv\", pv, \"-o\", \"json\"]})\n elif rec[\"kind\"] == \"k8s_pv\" and isinstance(data, dict):\n spec = data.get(\"spec\") or {}\n derived[\"volume_id\"] = (\n (spec.get(\"csi\") or {}).get(\"volumeHandle\")\n or spec.get(\"ociVolumeID\")\n or spec.get(\"volumeID\")\n or derived[\"volume_id\"]\n )\n elif rec[\"kind\"] in (\"k8s_service\", \"k8s_ingress\") and isinstance(data, dict):\n annotations = ((data.get(\"metadata\") or {}).get(\"annotations\") or {})\n derived[\"lb_id\"] = (\n annotations.get(\"oci.oraclecloud.com/load-balancer-id\")\n or annotations.get(\"service.beta.kubernetes.io/oci-load-balancer-id\")\n or annotations.get(\"oci.oraclecloud.com/network-load-balancer-id\")\n or derived[\"lb_id\"]\n )\n ingress = (((data.get(\"status\") or {}).get(\"loadBalancer\") or {}).get(\"ingress\") or [])\n if ingress and not derived[\"lb_ip\"]:\n derived[\"lb_ip\"] = ingress[0].get(\"ip\") or ingress[0].get(\"hostname\") or \"\"\n elif rec[\"kind\"] == \"oci_lb_list\" and derived[\"lb_ip\"]:\n items = data.get(\"data\", []) if isinstance(data, dict) else data\n items = items or []\n for item in items:\n for address in item.get(\"ip-addresses\", []) or []:\n if address.get(\"ip-address\") == derived[\"lb_ip\"]:\n derived[\"lb_id\"] = item.get(\"id\", derived[\"lb_id\"])\n break\n elif rec[\"kind\"] == \"oci_nlb_list\" and derived[\"lb_ip\"]:\n items = data.get(\"data\", []) if isinstance(data, dict) else data\n items = items or []\n for item in items:\n for address in item.get(\"ip-addresses\", []) or []:\n if address.get(\"ip-address\") == derived[\"lb_ip\"]:\n derived[\"lb_id\"] = item.get(\"id\", derived[\"lb_id\"])\n break\n\nif derived[\"instance_id\"] and (\"oci_instance\", \"compute instance\") not in existing:\n plan.append({\"name\": \"compute instance\", \"kind\": \"oci_instance\", \"cmd\": [\"oci\", \"compute\", \"instance\", \"get\", \"--instance-id\", derived[\"instance_id\"], \"--output\", \"json\"]})\n if compartment_id and (\"oci_vnic_attachments\", \"vnic attachments\") not in existing:\n plan.append({\"name\": \"vnic attachments\", \"kind\": \"oci_vnic_attachments\", \"cmd\": [\"oci\", \"compute\", \"vnic-attachment\", \"list\", \"--compartment-id\", compartment_id, \"--instance-id\", derived[\"instance_id\"], \"--all\", \"--output\", \"json\"]})\nif derived[\"volume_id\"] and (\"oci_volume\", \"block volume\") not in existing:\n plan.append({\"name\": \"block volume\", \"kind\": \"oci_volume\", \"cmd\": [\"oci\", \"bv\", \"volume\", \"get\", \"--volume-id\", derived[\"volume_id\"], \"--output\", \"json\"]})\n if compartment_id and (\"oci_volume_attachments\", \"volume attachments\") not in existing:\n plan.append({\"name\": \"volume attachments\", \"kind\": \"oci_volume_attachments\", \"cmd\": [\"oci\", \"compute\", \"volume-attachment\", \"list\", \"--compartment-id\", compartment_id, \"--volume-id\", derived[\"volume_id\"], \"--all\", \"--output\", \"json\"]})\nif derived[\"lb_id\"] and (\"oci_lb\", \"load balancer\") not in existing and (\"oci_nlb\", \"network load balancer\") not in existing:\n if \".networkloadbalancer.\" in derived[\"lb_id\"]:\n plan.append({\"name\": \"network load balancer\", \"kind\": \"oci_nlb\", \"cmd\": [\"oci\", \"nlb\", \"network-load-balancer\", \"get\", \"--network-load-balancer-id\", derived[\"lb_id\"], \"--output\", \"json\"]})\n else:\n plan.append({\"name\": \"load balancer\", \"kind\": \"oci_lb\", \"cmd\": [\"oci\", \"lb\", \"load-balancer\", \"get\", \"--load-balancer-id\", derived[\"lb_id\"], \"--output\", \"json\"]})\n plan.append({\"name\": \"load balancer health\", \"kind\": \"oci_lb_health\", \"cmd\": [\"oci\", \"lb\", \"load-balancer-health\", \"get\", \"--load-balancer-id\", derived[\"lb_id\"], \"--output\", \"json\"]})\n\nprint(json.dumps(plan))\nPY\nexecute_plan \"$tmp_dir/plan2.json\"\n\npython3 - \"$records\" \"$compartment_id\" \u003c\u003c'PY' >\"$tmp_dir/plan3.json\"\nimport json\nimport sys\nfrom pathlib import Path\n\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\ncompartment_id = sys.argv[2]\nexisting = {(r[\"kind\"], r[\"name\"]) for r in records}\nplan = []\n\ndef parse_json(text):\n try:\n return json.loads(text)\n except Exception:\n return None\n\nfor rec in records:\n if rec[\"kind\"] != \"k8s_pv\":\n continue\n obj = parse_json(rec.get(\"output\", \"\"))\n if not isinstance(obj, dict):\n continue\n spec = obj.get(\"spec\") or {}\n volume_id = (\n (spec.get(\"csi\") or {}).get(\"volumeHandle\")\n or spec.get(\"ociVolumeID\")\n or spec.get(\"volumeID\")\n or \"\"\n )\n if volume_id and (\"oci_volume\", \"block volume\") not in existing:\n plan.append({\"name\": \"block volume\", \"kind\": \"oci_volume\", \"cmd\": [\"oci\", \"bv\", \"volume\", \"get\", \"--volume-id\", volume_id, \"--output\", \"json\"]})\n if volume_id and compartment_id and (\"oci_volume_attachments\", \"volume attachments\") not in existing:\n plan.append({\"name\": \"volume attachments\", \"kind\": \"oci_volume_attachments\", \"cmd\": [\"oci\", \"compute\", \"volume-attachment\", \"list\", \"--compartment-id\", compartment_id, \"--volume-id\", volume_id, \"--all\", \"--output\", \"json\"]})\n\nprint(json.dumps(plan))\nPY\nexecute_plan \"$tmp_dir/plan3.json\"\n\npython3 - \"$records\" \u003c\u003c'PY' >\"$tmp_dir/plan4.json\"\nimport json\nimport sys\nfrom pathlib import Path\n\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nexisting = {(r[\"kind\"], r[\"name\"]) for r in records}\nrequested = set()\nplan = []\n\ndef parse_json(text):\n try:\n return json.loads(text)\n except Exception:\n return None\n\ndef data(obj):\n if isinstance(obj, dict) and isinstance(obj.get(\"data\"), dict):\n return obj[\"data\"]\n return obj\n\ndef items(obj):\n if isinstance(obj, dict):\n return obj.get(\"data\", [])\n if isinstance(obj, list):\n return obj\n return []\n\ndef add(name, kind, cmd):\n key = (kind, name)\n if key in existing or key in requested:\n return\n requested.add(key)\n plan.append({\"name\": name, \"kind\": kind, \"cmd\": cmd})\n\nfor rec in records:\n obj = parse_json(rec.get(\"output\", \"\"))\n if not obj:\n continue\n obj_data = data(obj)\n if rec[\"kind\"] == \"oci_vnic_attachments\":\n for item in items(obj_data):\n vnic_id = item.get(\"vnic-id\")\n subnet_id = item.get(\"subnet-id\")\n if vnic_id:\n add(f\"vnic {vnic_id}\", \"oci_vnic\", [\"oci\", \"network\", \"vnic\", \"get\", \"--vnic-id\", vnic_id, \"--output\", \"json\"])\n if subnet_id:\n add(f\"subnet {subnet_id}\", \"oci_subnet\", [\"oci\", \"network\", \"subnet\", \"get\", \"--subnet-id\", subnet_id, \"--output\", \"json\"])\n elif rec[\"kind\"] in (\"oci_lb\", \"oci_nlb\"):\n lb = data(obj_data)\n for subnet_id in lb.get(\"subnet-ids\", []) or []:\n add(f\"subnet {subnet_id}\", \"oci_subnet\", [\"oci\", \"network\", \"subnet\", \"get\", \"--subnet-id\", subnet_id, \"--output\", \"json\"])\n\nprint(json.dumps(plan))\nPY\nexecute_plan \"$tmp_dir/plan4.json\"\n\npython3 - \"$records\" \u003c\u003c'PY' >\"$tmp_dir/plan5.json\"\nimport json\nimport sys\nfrom pathlib import Path\n\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nexisting = {(r[\"kind\"], r[\"name\"]) for r in records}\nrequested = set()\nplan = []\n\ndef parse_json(text):\n try:\n return json.loads(text)\n except Exception:\n return None\n\ndef data(obj):\n if isinstance(obj, dict) and isinstance(obj.get(\"data\"), dict):\n return obj[\"data\"]\n return obj\n\ndef add(name, kind, cmd):\n key = (kind, name)\n if key in existing or key in requested:\n return\n requested.add(key)\n plan.append({\"name\": name, \"kind\": kind, \"cmd\": cmd})\n\nfor rec in records:\n obj = parse_json(rec.get(\"output\", \"\"))\n if not obj:\n continue\n obj_data = data(obj)\n if rec[\"kind\"] == \"oci_vnic\":\n vnic = data(obj_data)\n subnet_id = vnic.get(\"subnet-id\")\n if subnet_id:\n add(f\"subnet {subnet_id}\", \"oci_subnet\", [\"oci\", \"network\", \"subnet\", \"get\", \"--subnet-id\", subnet_id, \"--output\", \"json\"])\n for nsg_id in vnic.get(\"nsg-ids\", []) or []:\n add(f\"nsg {nsg_id}\", \"oci_nsg\", [\"oci\", \"network\", \"nsg\", \"get\", \"--nsg-id\", nsg_id, \"--output\", \"json\"])\n elif rec[\"kind\"] == \"oci_subnet\":\n subnet = data(obj_data)\n route_table_id = subnet.get(\"route-table-id\")\n if route_table_id:\n add(f\"route-table {route_table_id}\", \"oci_route_table\", [\"oci\", \"network\", \"route-table\", \"get\", \"--rt-id\", route_table_id, \"--output\", \"json\"])\n for sl_id in subnet.get(\"security-list-ids\", []) or []:\n add(f\"security-list {sl_id}\", \"oci_security_list\", [\"oci\", \"network\", \"security-list\", \"get\", \"--security-list-id\", sl_id, \"--output\", \"json\"])\n\nprint(json.dumps(plan))\nPY\nexecute_plan \"$tmp_dir/plan5.json\"\n\npython3 - \"$records\" \"$namespace\" \"$cluster_id\" \"$compartment_id\" \"$region\" \"$pod\" \"$deployment\" \"$service\" \"$ingress\" \"$pvc\" \"$node\" \u003c\u003c'PY'\nimport json\nimport re\nimport sys\nfrom pathlib import Path\n\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace, cluster_id, compartment_id, region = sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\npod_arg, deployment_arg, service_arg, ingress_arg, pvc_arg, node_arg = sys.argv[6:12]\n\nnodes = {}\nedges = []\nfindings = []\nanomalies = []\nsnippets = []\nseen_edges = set()\n\ndef parse_json(text):\n try:\n return json.loads(text)\n except Exception:\n return None\n\ndef add_node(node_id, node_type, name=\"\", source=\"\", **attrs):\n if not node_id:\n return\n existing = nodes.setdefault(node_id, {\"id\": node_id, \"type\": node_type, \"name\": name or node_id, \"source\": source, \"attributes\": {}})\n existing[\"type\"] = existing.get(\"type\") or node_type\n if name:\n existing[\"name\"] = name\n if source:\n existing[\"source\"] = source\n for key, value in attrs.items():\n if value not in (\"\", None, [], {}):\n existing[\"attributes\"][key] = value\n\ndef add_edge(src, dst, relation, evidence=\"\"):\n if not src or not dst:\n return\n key = (src, dst, relation)\n if key in seen_edges:\n return\n seen_edges.add(key)\n edge = {\"from\": src, \"to\": dst, \"relation\": relation}\n if evidence:\n edge[\"evidence\"] = evidence\n edges.append(edge)\n\ndef data(obj):\n if isinstance(obj, dict) and isinstance(obj.get(\"data\"), dict):\n return obj[\"data\"]\n return obj\n\ndef ann(obj, key):\n return (((obj or {}).get(\"metadata\") or {}).get(\"annotations\") or {}).get(key, \"\")\n\ndef labels(obj):\n return ((obj or {}).get(\"metadata\") or {}).get(\"labels\") or {}\n\ndef meta_name(obj):\n return ((obj or {}).get(\"metadata\") or {}).get(\"name\", \"\")\n\ndef k8s_id(kind, name, ns=None):\n if not name:\n return \"\"\n return f\"k8s:{kind}:{ns + '/' if ns else ''}{name}\"\n\ndef oci_id(kind, ocid):\n if not ocid:\n return \"\"\n return f\"oci:{kind}:{ocid}\"\n\ndef ocid(value):\n return isinstance(value, str) and value.startswith(\"ocid1.\")\n\ndef provider_instance_id(obj):\n provider_id = ((obj.get(\"spec\") or {}).get(\"providerID\") or \"\")\n match = re.search(r\"(ocid1\\.instance\\.[A-Za-z0-9_.-]+)\", provider_id)\n return match.group(1) if match else \"\"\n\ndef lb_addresses(item):\n values = []\n for address in item.get(\"ip-addresses\", []) or []:\n ip = address.get(\"ip-address\") or address.get(\"ip\")\n if ip:\n values.append(ip)\n return values\n\ndef match_lb_by_ip(items, target_ip):\n if not target_ip:\n return None\n for item in items:\n if target_ip in lb_addresses(item):\n return item\n return None\n\ndef network_entity_type(ocid_value):\n if not ocid_value:\n return \"oci.network.entity\"\n mapping = {\n \".internetgateway.\": \"oci.network.internetgateway\",\n \".natgateway.\": \"oci.network.natgateway\",\n \".servicegateway.\": \"oci.network.servicegateway\",\n \".drg.\": \"oci.network.drg\",\n \".localpeeringgateway.\": \"oci.network.localpeeringgateway\",\n \".remotepeeringconnection.\": \"oci.network.remotepeeringconnection\",\n \".privateip.\": \"oci.network.privateip\",\n \".ipsecconnection.\": \"oci.network.ipsecconnection\",\n \".networksecuritygroup.\": \"oci.network.nsg\",\n }\n return next((node_type for marker, node_type in mapping.items() if marker in ocid_value), \"oci.network.entity\")\n\nderived = {\"node\": node_arg, \"instance_id\": \"\", \"lb_id\": \"\", \"lb_ip\": \"\", \"pv\": \"\", \"volume_id\": \"\"}\n\nfor rec in records:\n out = rec.get(\"output\", \"\").strip()\n if rec.get(\"rc\") != 0:\n anomalies.append(f\"{rec['name']} failed with rc={rec['rc']}\")\n if out:\n snippets.append(f\"$ {rec['cmd']}\\n{out[-1000:]}\")\n\nfor rec in records:\n obj = parse_json(rec.get(\"output\", \"\"))\n if not obj:\n continue\n obj_data = data(obj)\n kind = rec[\"kind\"]\n if kind == \"k8s_pod\":\n name = meta_name(obj_data) or pod_arg\n pod_id = k8s_id(\"pod\", name, namespace)\n add_node(pod_id, \"kubernetes.pod\", name, \"kubectl\", namespace=namespace, service_account=(obj_data.get(\"spec\") or {}).get(\"serviceAccountName\"))\n node = (obj_data.get(\"spec\") or {}).get(\"nodeName\", \"\")\n if node:\n derived[\"node\"] = node\n node_id = k8s_id(\"node\", node)\n add_node(node_id, \"kubernetes.node\", node, \"kubectl\")\n add_edge(pod_id, node_id, \"scheduled_on\", \"pod.spec.nodeName\")\n sa = (obj_data.get(\"spec\") or {}).get(\"serviceAccountName\", \"\")\n if sa:\n sa_id = k8s_id(\"serviceaccount\", sa, namespace)\n add_node(sa_id, \"kubernetes.serviceaccount\", sa, \"kubectl\", namespace=namespace)\n add_edge(pod_id, sa_id, \"uses_service_account\", \"pod.spec.serviceAccountName\")\n for vol in (obj_data.get(\"spec\") or {}).get(\"volumes\", []) or []:\n claim = (vol.get(\"persistentVolumeClaim\") or {}).get(\"claimName\", \"\")\n if claim:\n pvc_id = k8s_id(\"pvc\", claim, namespace)\n add_node(pvc_id, \"kubernetes.pvc\", claim, \"kubectl\", namespace=namespace)\n add_edge(pod_id, pvc_id, \"mounts_claim\", f\"pod volume {vol.get('name', '')}\")\n elif kind == \"k8s_node\":\n name = meta_name(obj_data) or derived[\"node\"]\n node_id = k8s_id(\"node\", name)\n annotations = ((obj_data.get(\"metadata\") or {}).get(\"annotations\") or {})\n annotation_instance_id = annotations.get(\"node.oci.oraclecloud.com/instance-id\") or annotations.get(\"oci.oraclecloud.com/instance-id\") or annotations.get(\"oci.oraclecloud.com/instance_id\") or \"\"\n provider_instance = provider_instance_id(obj_data)\n instance_id = annotation_instance_id or provider_instance\n pool = labels(obj_data).get(\"oke.oraclecloud.com/nodepool\") or labels(obj_data).get(\"oci.oraclecloud.com/oke-nodepool\")\n add_node(node_id, \"kubernetes.node\", name, \"kubectl\", nodepool=pool, provider_id=(obj_data.get(\"spec\") or {}).get(\"providerID\"))\n if instance_id:\n derived[\"instance_id\"] = instance_id\n inst_id = oci_id(\"instance\", instance_id)\n source = \"kubectl node annotation\" if annotation_instance_id else \"kubectl node providerID\"\n evidence = \"node instance annotation\" if annotation_instance_id else \"node.spec.providerID\"\n add_node(inst_id, \"oci.compute.instance\", instance_id, source)\n add_edge(node_id, inst_id, \"runs_on_instance\", evidence)\n elif kind == \"k8s_deployment\":\n name = meta_name(obj_data) or deployment_arg\n dep_id = k8s_id(\"deployment\", name, namespace)\n add_node(dep_id, \"kubernetes.deployment\", name, \"kubectl\", namespace=namespace, replicas=(obj_data.get(\"spec\") or {}).get(\"replicas\"))\n elif kind == \"k8s_service\":\n name = meta_name(obj_data) or service_arg\n svc_id = k8s_id(\"service\", name, namespace)\n spec = obj_data.get(\"spec\") or {}\n add_node(svc_id, \"kubernetes.service\", name, \"kubectl\", namespace=namespace, service_type=spec.get(\"type\"), selector=spec.get(\"selector\"))\n lb_id = ann(obj_data, \"oci.oraclecloud.com/load-balancer-id\") or ann(obj_data, \"service.beta.kubernetes.io/oci-load-balancer-id\") or ann(obj_data, \"oci.oraclecloud.com/network-load-balancer-id\")\n status_ing = (((obj_data.get(\"status\") or {}).get(\"loadBalancer\") or {}).get(\"ingress\") or [])\n if status_ing and not lb_id:\n derived[\"lb_ip\"] = status_ing[0].get(\"ip\") or status_ing[0].get(\"hostname\") or \"\"\n if lb_id:\n derived[\"lb_id\"] = lb_id\n lb_kind = \"networkloadbalancer\" if \".networkloadbalancer.\" in lb_id else \"loadbalancer\"\n lb_node = oci_id(lb_kind, lb_id)\n add_node(lb_node, f\"oci.{lb_kind}\", lb_id, \"service annotation\")\n add_edge(svc_id, lb_node, \"provisions\", \"service LB annotation\")\n elif kind == \"k8s_endpoints\":\n name = meta_name(obj_data) or service_arg\n ep_id = k8s_id(\"endpoints\", name, namespace)\n svc_id = k8s_id(\"service\", name, namespace)\n add_node(ep_id, \"kubernetes.endpoints\", name, \"kubectl\", namespace=namespace)\n add_edge(svc_id, ep_id, \"selects_endpoints\", \"endpoints object\")\n addresses = []\n for subset in obj_data.get(\"subsets\", []) or []:\n for addr in subset.get(\"addresses\", []) or []:\n ip = addr.get(\"ip\")\n if ip:\n addresses.append(ip)\n if not addresses:\n anomalies.append(f\"Service {namespace}/{name} has no ready endpoint addresses\")\n else:\n findings.append(f\"Service {namespace}/{name} has {len(addresses)} ready endpoint address(es)\")\n elif kind == \"k8s_ingress\":\n name = meta_name(obj_data) or ingress_arg\n ing_id = k8s_id(\"ingress\", name, namespace)\n add_node(ing_id, \"kubernetes.ingress\", name, \"kubectl\", namespace=namespace, ingress_class=(obj_data.get(\"spec\") or {}).get(\"ingressClassName\"))\n lb_id = ann(obj_data, \"oci.oraclecloud.com/load-balancer-id\") or ann(obj_data, \"service.beta.kubernetes.io/oci-load-balancer-id\")\n if lb_id:\n derived[\"lb_id\"] = lb_id\n lb_node = oci_id(\"loadbalancer\", lb_id)\n add_node(lb_node, \"oci.loadbalancer\", lb_id, \"ingress annotation\")\n add_edge(ing_id, lb_node, \"provisions\", \"ingress LB annotation\")\n status_ing = (((obj_data.get(\"status\") or {}).get(\"loadBalancer\") or {}).get(\"ingress\") or [])\n if status_ing and not derived[\"lb_ip\"]:\n derived[\"lb_ip\"] = status_ing[0].get(\"ip\") or status_ing[0].get(\"hostname\") or \"\"\n for rule in (obj_data.get(\"spec\") or {}).get(\"rules\", []) or []:\n for path in (((rule.get(\"http\") or {}).get(\"paths\")) or []):\n backend_svc = ((((path.get(\"backend\") or {}).get(\"service\") or {}).get(\"name\")) or \"\")\n if backend_svc:\n svc_id = k8s_id(\"service\", backend_svc, namespace)\n add_node(svc_id, \"kubernetes.service\", backend_svc, \"ingress backend\", namespace=namespace)\n add_edge(ing_id, svc_id, \"routes_to\", f\"host {rule.get('host', '')}\")\n elif kind == \"k8s_pvc\":\n name = meta_name(obj_data) or pvc_arg\n pvc_id = k8s_id(\"pvc\", name, namespace)\n pv = (obj_data.get(\"spec\") or {}).get(\"volumeName\", \"\")\n add_node(pvc_id, \"kubernetes.pvc\", name, \"kubectl\", namespace=namespace, phase=(obj_data.get(\"status\") or {}).get(\"phase\"))\n if pv:\n derived[\"pv\"] = pv\n pv_id = k8s_id(\"pv\", pv)\n add_node(pv_id, \"kubernetes.pv\", pv, \"pvc.spec.volumeName\")\n add_edge(pvc_id, pv_id, \"bound_to\", \"pvc.spec.volumeName\")\n elif kind == \"k8s_pv\":\n name = meta_name(obj_data) or derived[\"pv\"]\n pv_id = k8s_id(\"pv\", name)\n spec = obj_data.get(\"spec\") or {}\n handle = (spec.get(\"csi\") or {}).get(\"volumeHandle\") or spec.get(\"ociVolumeID\") or spec.get(\"volumeID\") or \"\"\n add_node(pv_id, \"kubernetes.pv\", name, \"kubectl\", storage_class=spec.get(\"storageClassName\"))\n if handle:\n derived[\"volume_id\"] = handle\n vol_id = oci_id(\"volume\", handle)\n add_node(vol_id, \"oci.blockvolume.volume\", handle, \"pv volume handle\")\n add_edge(pv_id, vol_id, \"backs_onto\", \"PV volume handle\")\n elif kind == \"oci_instance\":\n inst = data(obj_data)\n inst_id_raw = inst.get(\"id\", derived[\"instance_id\"])\n inst_id = oci_id(\"instance\", inst_id_raw)\n add_node(inst_id, \"oci.compute.instance\", inst_id_raw, \"oci\", lifecycle_state=inst.get(\"lifecycle-state\"), shape=inst.get(\"shape\"), availability_domain=inst.get(\"availability-domain\"))\n if inst.get(\"lifecycle-state\") and inst.get(\"lifecycle-state\") != \"RUNNING\":\n anomalies.append(f\"Compute instance {inst_id_raw} lifecycle-state is {inst.get('lifecycle-state')}\")\n elif kind == \"oci_vnic_attachments\":\n items = obj_data.get(\"data\", obj_data if isinstance(obj_data, list) else []) or []\n inst_id = oci_id(\"instance\", derived[\"instance_id\"])\n for item in items:\n vnic_id = item.get(\"vnic-id\")\n subnet_id = item.get(\"subnet-id\")\n if vnic_id:\n vnic_node = oci_id(\"vnic\", vnic_id)\n add_node(vnic_node, \"oci.network.vnic\", vnic_id, \"oci\", lifecycle_state=item.get(\"lifecycle-state\"), display_name=item.get(\"display-name\"))\n add_edge(inst_id, vnic_node, \"has_vnic\", \"vnic attachment\")\n if subnet_id:\n subnet_node = oci_id(\"subnet\", subnet_id)\n add_node(subnet_node, \"oci.network.subnet\", subnet_id, \"oci vnic attachment\")\n add_edge(oci_id(\"vnic\", vnic_id), subnet_node, \"attached_to_subnet\", \"vnic attachment subnet\")\n elif kind == \"oci_vnic\":\n vnic = data(obj_data)\n vnic_id_raw = vnic.get(\"id\", \"\")\n vnic_node = oci_id(\"vnic\", vnic_id_raw)\n add_node(\n vnic_node,\n \"oci.network.vnic\",\n vnic.get(\"display-name\") or vnic_id_raw,\n \"oci\",\n lifecycle_state=vnic.get(\"lifecycle-state\"),\n private_ip=vnic.get(\"private-ip\"),\n public_ip=vnic.get(\"public-ip\"),\n is_primary=vnic.get(\"is-primary\"),\n subnet_id=vnic.get(\"subnet-id\"),\n )\n subnet_id = vnic.get(\"subnet-id\")\n if subnet_id:\n subnet_node = oci_id(\"subnet\", subnet_id)\n add_node(subnet_node, \"oci.network.subnet\", subnet_id, \"oci vnic\")\n add_edge(vnic_node, subnet_node, \"attached_to_subnet\", \"vnic.subnet-id\")\n for nsg_id in vnic.get(\"nsg-ids\", []) or []:\n nsg_node = oci_id(\"nsg\", nsg_id)\n add_node(nsg_node, \"oci.network.nsg\", nsg_id, \"vnic.nsg-ids\")\n add_edge(vnic_node, nsg_node, \"uses_nsg\", \"vnic.nsg-ids\")\n elif kind == \"oci_subnet\":\n subnet = data(obj_data)\n subnet_id_raw = subnet.get(\"id\", \"\")\n subnet_node = oci_id(\"subnet\", subnet_id_raw)\n add_node(\n subnet_node,\n \"oci.network.subnet\",\n subnet.get(\"display-name\") or subnet_id_raw,\n \"oci\",\n cidr_block=subnet.get(\"cidr-block\"),\n ipv6_cidr_block=subnet.get(\"ipv6-cidr-block\"),\n dns_label=subnet.get(\"dns-label\"),\n route_table_id=subnet.get(\"route-table-id\"),\n vcn_id=subnet.get(\"vcn-id\"),\n )\n vcn_id = subnet.get(\"vcn-id\")\n if vcn_id:\n vcn_node = oci_id(\"vcn\", vcn_id)\n add_node(vcn_node, \"oci.network.vcn\", vcn_id, \"subnet.vcn-id\")\n add_edge(subnet_node, vcn_node, \"in_vcn\", \"subnet.vcn-id\")\n route_table_id = subnet.get(\"route-table-id\")\n if route_table_id:\n rt_node = oci_id(\"route-table\", route_table_id)\n add_node(rt_node, \"oci.network.route_table\", route_table_id, \"subnet.route-table-id\")\n add_edge(subnet_node, rt_node, \"uses_route_table\", \"subnet.route-table-id\")\n for sl_id in subnet.get(\"security-list-ids\", []) or []:\n sl_node = oci_id(\"security-list\", sl_id)\n add_node(sl_node, \"oci.network.security_list\", sl_id, \"subnet.security-list-ids\")\n add_edge(subnet_node, sl_node, \"uses_security_list\", \"subnet.security-list-ids\")\n elif kind == \"oci_nsg\":\n nsg = data(obj_data)\n nsg_id_raw = nsg.get(\"id\", \"\")\n add_node(oci_id(\"nsg\", nsg_id_raw), \"oci.network.nsg\", nsg.get(\"display-name\") or nsg_id_raw, \"oci\", lifecycle_state=nsg.get(\"lifecycle-state\"), vcn_id=nsg.get(\"vcn-id\"))\n elif kind == \"oci_security_list\":\n sl = data(obj_data)\n sl_id_raw = sl.get(\"id\", \"\")\n add_node(\n oci_id(\"security-list\", sl_id_raw),\n \"oci.network.security_list\",\n sl.get(\"display-name\") or sl_id_raw,\n \"oci\",\n lifecycle_state=sl.get(\"lifecycle-state\"),\n vcn_id=sl.get(\"vcn-id\"),\n ingress_rules=len(sl.get(\"ingress-security-rules\", []) or []),\n egress_rules=len(sl.get(\"egress-security-rules\", []) or []),\n )\n elif kind == \"oci_route_table\":\n rt = data(obj_data)\n rt_id_raw = rt.get(\"id\", \"\")\n rt_node = oci_id(\"route-table\", rt_id_raw)\n add_node(rt_node, \"oci.network.route_table\", rt.get(\"display-name\") or rt_id_raw, \"oci\", lifecycle_state=rt.get(\"lifecycle-state\"), vcn_id=rt.get(\"vcn-id\"))\n for idx, rule in enumerate(rt.get(\"route-rules\", []) or [], start=1):\n target_id = rule.get(\"network-entity-id\") or rule.get(\"networkEntityId\") or \"\"\n destination = rule.get(\"destination\") or rule.get(\"cidr-block\") or rule.get(\"destination-type\") or f\"rule {idx}\"\n if target_id:\n target_node = oci_id(\"network-entity\", target_id)\n add_node(target_node, network_entity_type(target_id), target_id, \"route rule\", destination=destination)\n add_edge(rt_node, target_node, \"routes_to\", str(destination))\n elif kind in (\"oci_lb\", \"oci_nlb\"):\n lb = data(obj_data)\n lb_id_raw = lb.get(\"id\", derived[\"lb_id\"])\n lb_type = \"oci.networkloadbalancer\" if kind == \"oci_nlb\" else \"oci.loadbalancer\"\n lb_node = oci_id(\"networkloadbalancer\" if kind == \"oci_nlb\" else \"loadbalancer\", lb_id_raw)\n add_node(lb_node, lb_type, lb_id_raw, \"oci\", lifecycle_state=lb.get(\"lifecycle-state\") or lb.get(\"lifecycle-state-details\"), shape=lb.get(\"shape-name\"), ip_addresses=lb_addresses(lb))\n for subnet_id in lb.get(\"subnet-ids\", []) or []:\n subnet_node = oci_id(\"subnet\", subnet_id)\n add_node(subnet_node, \"oci.network.subnet\", subnet_id, \"oci load balancer\")\n add_edge(lb_node, subnet_node, \"uses_subnet\", \"load balancer subnet\")\n if service_arg and (derived[\"lb_id\"] == lb_id_raw or derived[\"lb_ip\"] in lb_addresses(lb)):\n add_edge(k8s_id(\"service\", service_arg, namespace), lb_node, \"provisions\", \"matched service external IP or LB ID\")\n if ingress_arg and (derived[\"lb_id\"] == lb_id_raw or derived[\"lb_ip\"] in lb_addresses(lb)):\n add_edge(k8s_id(\"ingress\", ingress_arg, namespace), lb_node, \"provisions\", \"matched ingress external IP or LB ID\")\n if lb.get(\"lifecycle-state\") and lb.get(\"lifecycle-state\") not in (\"ACTIVE\", \"SUCCEEDED\"):\n anomalies.append(f\"Load balancer {lb_id_raw} lifecycle-state is {lb.get('lifecycle-state')}\")\n elif kind in (\"oci_lb_list\", \"oci_nlb_list\"):\n items = obj_data.get(\"data\", []) if isinstance(obj_data, dict) else obj_data\n items = items or []\n lb = match_lb_by_ip(items, derived[\"lb_ip\"])\n if lb:\n lb_id_raw = lb.get(\"id\", \"\")\n lb_kind = \"networkloadbalancer\" if kind == \"oci_nlb_list\" else \"loadbalancer\"\n lb_type = \"oci.networkloadbalancer\" if kind == \"oci_nlb_list\" else \"oci.loadbalancer\"\n lb_node = oci_id(lb_kind, lb_id_raw)\n derived[\"lb_id\"] = lb_id_raw\n add_node(lb_node, lb_type, lb_id_raw, \"oci ip match\", lifecycle_state=lb.get(\"lifecycle-state\") or lb.get(\"lifecycle-state-details\"), shape=lb.get(\"shape-name\"), ip_addresses=lb_addresses(lb), display_name=lb.get(\"display-name\"))\n if service_arg:\n add_edge(k8s_id(\"service\", service_arg, namespace), lb_node, \"provisions\", f\"matched external IP {derived['lb_ip']}\")\n if ingress_arg:\n add_edge(k8s_id(\"ingress\", ingress_arg, namespace), lb_node, \"provisions\", f\"matched external IP {derived['lb_ip']}\")\n for subnet_id in lb.get(\"subnet-ids\", []) or []:\n subnet_node = oci_id(\"subnet\", subnet_id)\n add_node(subnet_node, \"oci.network.subnet\", subnet_id, \"oci load balancer\")\n add_edge(lb_node, subnet_node, \"uses_subnet\", \"load balancer subnet\")\n findings.append(f\"Matched external IP {derived['lb_ip']} to OCI {'Network ' if kind == 'oci_nlb_list' else ''}Load Balancer {lb_id_raw}\")\n if lb.get(\"lifecycle-state\") and lb.get(\"lifecycle-state\") not in (\"ACTIVE\", \"SUCCEEDED\"):\n anomalies.append(f\"Load balancer {lb_id_raw} lifecycle-state is {lb.get('lifecycle-state')}\")\n elif derived[\"lb_ip\"]:\n anomalies.append(f\"No OCI {'Network ' if kind == 'oci_nlb_list' else ''}Load Balancer matched external IP {derived['lb_ip']}\")\n elif kind == \"oci_lb_health\":\n health = data(obj_data)\n status = health.get(\"status\") or health.get(\"overall-health\")\n lb_node = oci_id(\"loadbalancer\", derived[\"lb_id\"])\n if status:\n add_node(f\"{lb_node}:health\", \"oci.loadbalancer.health\", status, \"oci\", status=status)\n add_edge(lb_node, f\"{lb_node}:health\", \"has_health\", \"LB health API\")\n if str(status).upper() not in (\"OK\", \"SUCCEEDED\", \"HEALTHY\"):\n anomalies.append(f\"Load balancer health status is {status}\")\n elif kind == \"oci_volume\":\n vol = data(obj_data)\n vol_id_raw = vol.get(\"id\", derived[\"volume_id\"])\n add_node(oci_id(\"volume\", vol_id_raw), \"oci.blockvolume.volume\", vol_id_raw, \"oci\", lifecycle_state=vol.get(\"lifecycle-state\"), availability_domain=vol.get(\"availability-domain\"), size_in_gbs=vol.get(\"size-in-gbs\"))\n elif kind == \"oci_volume_attachments\":\n items = obj_data.get(\"data\", obj_data if isinstance(obj_data, list) else []) or []\n for item in items:\n vol_id_raw = item.get(\"volume-id\", derived[\"volume_id\"])\n inst_id_raw = item.get(\"instance-id\")\n if vol_id_raw and inst_id_raw:\n add_node(oci_id(\"volume\", vol_id_raw), \"oci.blockvolume.volume\", vol_id_raw, \"oci volume attachment\")\n add_node(oci_id(\"instance\", inst_id_raw), \"oci.compute.instance\", inst_id_raw, \"oci volume attachment\")\n add_edge(oci_id(\"volume\", vol_id_raw), oci_id(\"instance\", inst_id_raw), \"attached_to_instance\", \"volume attachment\")\n if item.get(\"lifecycle-state\") and item.get(\"lifecycle-state\") != \"ATTACHED\":\n anomalies.append(f\"Volume attachment {item.get('id', '')} lifecycle-state is {item.get('lifecycle-state')}\")\n elif kind == \"oci_node_pools\":\n items = obj_data.get(\"data\", obj_data if isinstance(obj_data, list) else []) or []\n cluster_node = oci_id(\"cluster\", cluster_id)\n if cluster_id:\n add_node(cluster_node, \"oci.oke.cluster\", cluster_id, \"input\", region=region, compartment_id=compartment_id)\n for item in items:\n np_id = item.get(\"id\")\n if np_id:\n np_node = oci_id(\"nodepool\", np_id)\n add_node(np_node, \"oci.oke.nodepool\", item.get(\"name\", np_id), \"oci\", size=item.get(\"size\"), shape=item.get(\"node-shape\"))\n add_edge(cluster_node, np_node, \"has_node_pool\", \"node pool list\")\n\nif cluster_id:\n add_node(oci_id(\"cluster\", cluster_id), \"oci.oke.cluster\", cluster_id, \"input\", region=region, compartment_id=compartment_id)\n\nif not findings:\n findings.append(f\"Built object graph with {len(nodes)} node(s) and {len(edges)} edge(s)\")\nelse:\n findings.insert(0, f\"Built object graph with {len(nodes)} node(s) and {len(edges)} edge(s)\")\n\nif not nodes:\n anomalies.append(\"No Kubernetes or OCI objects could be correlated from the provided selectors\")\n\nprint(json.dumps({\n \"domain\": \"OCI Object Correlation\",\n \"namespace\": namespace,\n \"cluster_id\": cluster_id,\n \"compartment_id\": compartment_id,\n \"region\": region,\n \"selectors\": {\n \"pod\": pod_arg,\n \"deployment\": deployment_arg,\n \"service\": service_arg,\n \"ingress\": ingress_arg,\n \"pvc\": pvc_arg,\n \"node\": node_arg,\n },\n \"graph\": {\n \"kubernetes\": [node for node in nodes.values() if node[\"type\"].startswith(\"kubernetes.\")],\n \"oci\": [node for node in nodes.values() if node[\"type\"].startswith(\"oci.\")],\n \"edges\": edges,\n },\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-12:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":47894,"content_sha256":"f5cbde4e28ac9e1c10e4b009c5a965f847df57b631d869adc87512b80303a669"},{"filename":"oke/scripts/oke-ocir-image-pull-check.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Collect OCIR image pull failure evidence.\n#\n# Usage:\n# bash scripts/oke-ocir-image-pull-check.sh --namespace \u003cns> --pod \u003cpod> [--image \u003cimage>] [--compartment-id \u003cocid>] [--region \u003cregion>]\n\nnamespace=\"\"\npod=\"\"\nimage=\"\"\ncompartment_id=\"\"\nregion=\"\"\n\nemit_error() {\n local exit_code=\"$1\"; local error_code=\"$2\"; local message=\"$3\"; local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace|-n) require_value \"$1\" \"${2:-}\"; namespace=\"$2\"; shift 2 ;;\n --pod) require_value \"$1\" \"${2:-}\"; pod=\"$2\"; shift 2 ;;\n --image) require_value \"$1\" \"${2:-}\"; image=\"$2\"; shift 2 ;;\n --compartment-id) require_value \"$1\" \"${2:-}\"; compartment_id=\"$2\"; shift 2 ;;\n --region) require_value \"$1\" \"${2:-}\"; region=\"$2\"; shift 2 ;;\n -h|--help) echo \"usage: oke-ocir-image-pull-check.sh --namespace \u003cns> --pod \u003cpod> [--image \u003cimage>] [--compartment-id \u003cocid>] [--region \u003cregion>]\"; exit 0 ;;\n *) emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\" ;;\n esac\ndone\nif [[ -z \"$namespace\" || -z \"$pod\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing --namespace or --pod.\" \"Provide both --namespace and --pod.\"\nfi\ncommand -v kubectl >/dev/null 2>&1 || emit_error 2 \"KUBECTL_NOT_INSTALLED\" \"kubectl is not installed or not on PATH.\" \"Install kubectl and retry.\"\n\ntmp_dir=\"$(mktemp -d)\"; trap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"; : > \"$records\"\nrun_check() {\n local name=\"$1\"; shift\n local out rc\n set +e; out=\"$(\"$@\" 2>&1)\"; rc=$?; set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json, os, sys\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\"name\": os.environ[\"NAME\"], \"cmd\": os.environ[\"CMD\"], \"rc\": int(os.environ[\"RC\"]), \"output\": os.environ[\"OUT\"][-4000:]}) + \"\\n\")\nPY\n}\n\nrun_check \"pod describe\" kubectl -n \"$namespace\" describe pod \"$pod\"\nrun_check \"pod yaml\" kubectl -n \"$namespace\" get pod \"$pod\" -o yaml\nrun_check \"pod warning events\" kubectl -n \"$namespace\" get events --field-selector \"involvedObject.name=$pod\" --sort-by=.lastTimestamp\nrun_check \"service accounts\" kubectl -n \"$namespace\" get serviceaccount -o yaml\nrun_check \"image pull secrets\" kubectl -n \"$namespace\" get secret -o yaml\n\nif [[ -n \"$compartment_id\" && -n \"$region\" ]] && command -v oci >/dev/null 2>&1; then\n run_check \"OCIR repositories\" oci --region \"$region\" artifacts container repository list --compartment-id \"$compartment_id\" --all --output json\nfi\n\npython3 - \"$records\" \"$namespace\" \"$pod\" \"$image\" \"$region\" \u003c\u003c'PY'\nimport json, re, sys\nfrom pathlib import Path\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace, pod, image, region = sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\npat = re.compile(r\"(ImagePullBackOff|ErrImagePull|unauthorized|denied|not found|repository|pull access denied|x509|i/o timeout|no basic auth|OCIR)\", re.I)\nfindings, anomalies, snippets = [], [], []\nfor item in records:\n out = item[\"output\"].strip()\n findings.append(f\"{item['name']} {'collected' if item['rc'] == 0 else 'failed'}\")\n if item[\"rc\"] != 0:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if out:\n snippets.append(f\"$ {item['cmd']}\\n{out[-800:]}\")\n for line in out.splitlines():\n if pat.search(line):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\nprint(json.dumps({\n \"domain\": \"OCIR / Image Pull\",\n \"namespace\": namespace,\n \"pod\": pod,\n \"image\": image,\n \"region\": region,\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-10:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4123,"content_sha256":"c75349a798dd0856915ea585aed9eac2f3dfbaaa0354e1f4df19f890447b67f0"},{"filename":"oke/scripts/oke-pod-network-check.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Collect pod networking, OCI CNI/IPAM, and Multus signals.\n#\n# Usage:\n# bash scripts/oke-pod-network-check.sh --namespace \u003cns> [--pod \u003cpod>] [--selector \u003clabel-selector>]\n\nnamespace=\"\"\npod=\"\"\nselector=\"\"\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\n\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace|-n)\n require_value \"$1\" \"${2:-}\"\n namespace=\"$2\"\n shift 2\n ;;\n --pod)\n require_value \"$1\" \"${2:-}\"\n pod=\"$2\"\n shift 2\n ;;\n --selector|-l)\n require_value \"$1\" \"${2:-}\"\n selector=\"$2\"\n shift 2\n ;;\n -h|--help)\n echo \"usage: oke-pod-network-check.sh --namespace \u003cns> [--pod \u003cpod>] [--selector \u003clabel-selector>]\"\n exit 0\n ;;\n *)\n emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\"\n ;;\n esac\ndone\n\nif [[ -z \"$namespace\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing required --namespace.\" \"Provide --namespace \u003cns>.\"\nfi\n\nif ! command -v kubectl >/dev/null 2>&1; then\n emit_error 2 \"KUBECTL_NOT_INSTALLED\" \"kubectl is not installed or not on PATH.\" \"Install kubectl and retry.\"\nfi\n\ntmp_dir=\"$(mktemp -d)\"\ntrap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"\n: > \"$records\"\n\nrun_check() {\n local name=\"$1\"\n shift\n local out rc\n set +e\n out=\"$(\"$@\" 2>&1)\"\n rc=$?\n set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json\nimport os\nimport sys\n\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\n \"name\": os.environ[\"NAME\"],\n \"cmd\": os.environ[\"CMD\"],\n \"rc\": int(os.environ[\"RC\"]),\n \"output\": os.environ[\"OUT\"][-4000:],\n }) + \"\\n\")\nPY\n}\n\nif [[ -n \"$pod\" ]]; then\n run_check \"target pod\" kubectl -n \"$namespace\" get pod \"$pod\" -o wide\n run_check \"target pod describe\" kubectl -n \"$namespace\" describe pod \"$pod\"\n run_check \"target pod network-status\" kubectl -n \"$namespace\" get pod \"$pod\" -o jsonpath='{.metadata.annotations.k8s\\.v1\\.cni\\.cncf\\.io/network-status}'\n run_check \"target pod node\" kubectl -n \"$namespace\" get pod \"$pod\" -o jsonpath='{.spec.nodeName}'\nelse\n pod_args=()\n if [[ -n \"$selector\" ]]; then\n pod_args=(-l \"$selector\")\n fi\n run_check \"namespace pods\" kubectl -n \"$namespace\" get pods \"${pod_args[@]}\" -o wide\nfi\n\nrun_check \"namespace warning events\" kubectl -n \"$namespace\" get events --field-selector type=Warning --sort-by=.lastTimestamp\nrun_check \"OCI CNI pods\" kubectl -n kube-system get pods -l app=oci-cni -o wide\nrun_check \"Multus pods\" kubectl -n kube-system get pods -l name=multus -o wide\nrun_check \"network attachment definitions\" kubectl get network-attachment-definitions -A\n\npython3 - \"$records\" \"$namespace\" \"$pod\" \"$selector\" \u003c\u003c'PY'\nimport json\nimport re\nimport sys\nfrom pathlib import Path\n\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace, pod, selector = sys.argv[2], sys.argv[3], sys.argv[4]\nanomaly_re = re.compile(\n r\"(FailedCreatePodSandBox|failed to find plugin|CNI|multus|ipam|network-status|ImageInspectError|EOF|timeout|NotReady|Pending|Error|Failed)\",\n re.I,\n)\n\nfindings = []\nanomalies = []\nsnippets = []\nfor item in records:\n output = item[\"output\"].strip()\n if item[\"rc\"] == 0:\n findings.append(f\"{item['name']} collected\")\n else:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if output:\n snippets.append(f\"$ {item['cmd']}\\n{output[-800:]}\")\n for line in output.splitlines():\n if anomaly_re.search(line):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\n\nprint(json.dumps({\n \"domain\": \"Pod Networking / OCI CNI / IPAM\",\n \"namespace\": namespace,\n \"pod\": pod,\n \"selector\": selector,\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-10:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4386,"content_sha256":"c8689693e3ba261a1d0a0b369b57ba0ef50e83ddddb8a6255a6bfe16b9d51562"},{"filename":"oke/scripts/oke-private-endpoint-check.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Collect private OKE API endpoint and kubeconfig connectivity signals.\n#\n# Usage:\n# bash scripts/oke-private-endpoint-check.sh --cluster-id \u003cocid> --region \u003cregion> [--compartment-id \u003cocid>]\n\ncluster_id=\"\"\nregion=\"\"\ncompartment_id=\"\"\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\n\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --cluster-id) require_value \"$1\" \"${2:-}\"; cluster_id=\"$2\"; shift 2 ;;\n --region) require_value \"$1\" \"${2:-}\"; region=\"$2\"; shift 2 ;;\n --compartment-id) require_value \"$1\" \"${2:-}\"; compartment_id=\"$2\"; shift 2 ;;\n -h|--help)\n echo \"usage: oke-private-endpoint-check.sh --cluster-id \u003cocid> --region \u003cregion> [--compartment-id \u003cocid>]\"\n exit 0 ;;\n *) emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\" ;;\n esac\ndone\n\nif [[ -z \"$cluster_id\" || -z \"$region\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing --cluster-id or --region.\" \"Provide both --cluster-id and --region.\"\nfi\n\ntmp_dir=\"$(mktemp -d)\"\ntrap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"\n: > \"$records\"\n\nrun_check() {\n local name=\"$1\"; shift\n local out rc\n set +e\n out=\"$(\"$@\" 2>&1)\"\n rc=$?\n set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json, os, sys\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\n \"name\": os.environ[\"NAME\"],\n \"cmd\": os.environ[\"CMD\"],\n \"rc\": int(os.environ[\"RC\"]),\n \"output\": os.environ[\"OUT\"][-4000:],\n }) + \"\\n\")\nPY\n}\n\ncommand -v kubectl >/dev/null 2>&1 && {\n run_check \"current context\" kubectl config current-context\n run_check \"cluster info\" kubectl cluster-info\n run_check \"readyz\" kubectl get --raw=/readyz?verbose\n run_check \"kubectl version\" kubectl version --client\n}\n\nif command -v oci >/dev/null 2>&1; then\n run_check \"OKE cluster\" oci --region \"$region\" ce cluster get --cluster-id \"$cluster_id\" --output json\n if [[ -n \"$compartment_id\" ]]; then\n run_check \"NSGs\" oci --region \"$region\" network nsg list --compartment-id \"$compartment_id\" --all --output json\n fi\nfi\n\npython3 - \"$records\" \"$cluster_id\" \"$region\" \"$compartment_id\" \u003c\u003c'PY'\nimport json, re, sys\nfrom pathlib import Path\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\ncluster_id, region, compartment_id = sys.argv[2], sys.argv[3], sys.argv[4]\npat = re.compile(r\"(Unable to connect|i/o timeout|connection refused|x509|Unauthorized|Forbidden|exec: executable oci failed|NotAuthorized|private|endpoint|NSG|security)\", re.I)\nfindings, anomalies, snippets = [], [], []\nfor item in records:\n out = item[\"output\"].strip()\n findings.append(f\"{item['name']} {'collected' if item['rc'] == 0 else 'failed'}\")\n if item[\"rc\"] != 0:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if out:\n snippets.append(f\"$ {item['cmd']}\\n{out[-800:]}\")\n for line in out.splitlines():\n if pat.search(line):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\nprint(json.dumps({\n \"domain\": \"Private Cluster / API Endpoint Connectivity\",\n \"cluster_id\": cluster_id,\n \"region\": region,\n \"compartment_id\": compartment_id,\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-10:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3837,"content_sha256":"7585e2c651d4f65986a93f25b34bd7745c0d55456be1c1c0cfa0f5dce8a92ea1"},{"filename":"oke/scripts/oke-workload-identity-check.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\n# Collect OKE workload identity and pod OCI API auth evidence.\n#\n# Usage:\n# bash scripts/oke-workload-identity-check.sh --namespace \u003cns> --serviceaccount \u003csa> [--pod \u003cpod>] [--tenancy-id \u003cocid>]\n\nnamespace=\"\"\nserviceaccount=\"\"\npod=\"\"\ntenancy_id=\"\"\n\nemit_error() {\n local exit_code=\"$1\"; local error_code=\"$2\"; local message=\"$3\"; local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\nrequire_value() {\n local flag=\"$1\"\n if [[ $# -lt 2 || -z \"${2:-}\" || \"${2:-}\" == --* ]]; then\n emit_error 2 \"INVALID_ARGUMENT\" \"Missing value for ${flag}.\" \"Run with --help to view usage.\"\n fi\n}\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --namespace|-n) require_value \"$1\" \"${2:-}\"; namespace=\"$2\"; shift 2 ;;\n --serviceaccount|--sa) require_value \"$1\" \"${2:-}\"; serviceaccount=\"$2\"; shift 2 ;;\n --pod) require_value \"$1\" \"${2:-}\"; pod=\"$2\"; shift 2 ;;\n --tenancy-id) require_value \"$1\" \"${2:-}\"; tenancy_id=\"$2\"; shift 2 ;;\n -h|--help) echo \"usage: oke-workload-identity-check.sh --namespace \u003cns> --serviceaccount \u003csa> [--pod \u003cpod>] [--tenancy-id \u003cocid>]\"; exit 0 ;;\n *) emit_error 2 \"UNKNOWN_ARGUMENT\" \"Unknown argument: $1.\" \"Run with --help to view usage.\" ;;\n esac\ndone\nif [[ -z \"$namespace\" || -z \"$serviceaccount\" ]]; then\n emit_error 2 \"MISSING_REQUIRED_ARGUMENT\" \"Missing --namespace or --serviceaccount.\" \"Provide both --namespace and --serviceaccount.\"\nfi\ncommand -v kubectl >/dev/null 2>&1 || emit_error 2 \"KUBECTL_NOT_INSTALLED\" \"kubectl is not installed or not on PATH.\" \"Install kubectl and retry.\"\n\ntmp_dir=\"$(mktemp -d)\"; trap 'rm -rf \"$tmp_dir\"' EXIT\nrecords=\"$tmp_dir/records.jsonl\"; : > \"$records\"\nrun_check() {\n local name=\"$1\"; shift\n local out rc\n set +e; out=\"$(\"$@\" 2>&1)\"; rc=$?; set -e\n NAME=\"$name\" RC=\"$rc\" OUT=\"$out\" CMD=\"$*\" python3 - \"$records\" \u003c\u003c'PY'\nimport json, os, sys\nwith open(sys.argv[1], \"a\") as f:\n f.write(json.dumps({\"name\": os.environ[\"NAME\"], \"cmd\": os.environ[\"CMD\"], \"rc\": int(os.environ[\"RC\"]), \"output\": os.environ[\"OUT\"][-4000:]}) + \"\\n\")\nPY\n}\n\nrun_check \"service account\" kubectl -n \"$namespace\" get serviceaccount \"$serviceaccount\" -o yaml\nif [[ -n \"$pod\" ]]; then\n run_check \"pod describe\" kubectl -n \"$namespace\" describe pod \"$pod\"\n run_check \"pod logs\" sh -c \"kubectl -n '$namespace' logs '$pod' --tail=200 2>&1 | egrep -i 'notauthorized|forbidden|401|403|principal|workload|token|oci' || true\"\nfi\nif [[ -n \"$tenancy_id\" ]] && command -v oci >/dev/null 2>&1; then\n run_check \"IAM policies\" oci iam policy list --compartment-id \"$tenancy_id\" --all --output json\nfi\n\npython3 - \"$records\" \"$namespace\" \"$serviceaccount\" \"$pod\" \"$tenancy_id\" \u003c\u003c'PY'\nimport json, re, sys\nfrom pathlib import Path\nrecords = [json.loads(line) for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]\nnamespace, serviceaccount, pod, tenancy_id = sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\npat = re.compile(r\"(NotAuthorized|Forbidden|401|403|principal|workload|token|policy|serviceaccount|namespace|cluster|oci)\", re.I)\nfindings, anomalies, snippets = [], [], []\nfor item in records:\n out = item[\"output\"].strip()\n findings.append(f\"{item['name']} {'collected' if item['rc'] == 0 else 'failed'}\")\n if item[\"rc\"] != 0:\n anomalies.append(f\"{item['name']} failed with rc={item['rc']}\")\n if out:\n snippets.append(f\"$ {item['cmd']}\\n{out[-800:]}\")\n for line in out.splitlines():\n if pat.search(line):\n anomalies.append(f\"{item['name']}: {line.strip()[:240]}\")\nprint(json.dumps({\n \"domain\": \"Workload Identity / OCI API From Pods\",\n \"namespace\": namespace,\n \"serviceaccount\": serviceaccount,\n \"pod\": pod,\n \"tenancy_id\": tenancy_id,\n \"findings\": findings,\n \"anomalies\": anomalies,\n \"raw_snippets\": snippets[-10:],\n \"fallback_used\": any(item[\"rc\"] != 0 for item in records),\n}, indent=2))\nPY\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3999,"content_sha256":"ac9db8bf619015fb86aa93ce06620856af755fd0d54a6cafdf2bcb146c1935c3"},{"filename":"oke/scripts/preflight-check.sh","content":"#!/usr/bin/env bash\n# preflight-check.sh\n# OCI CLI auth + tenancy + region + compartment validation\n#\n# Usage: bash preflight-check.sh\n# Exit: 0 = success\n# 1 = expected error (CLI not auth'd, no compartments found)\n# 2 = unexpected error (CLI not installed, parse failure)\n#\n# Stderr: JSON {error_code, message, remediation, docs_url}\n# Stdout: JSON {tenancy_ocid, home_region, regions:[], compartments:[]}\n\nset -euo pipefail\n\n# ── helpers ──────────────────────────────────────────────────────────────────\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n local docs_url=\"$5\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"%s\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" \"$docs_url\" >&2\n exit \"$exit_code\"\n}\n\nTMP_SUBSCRIPTIONS_JSON=\"$(mktemp)\"\nTMP_COMPARTMENTS_JSON=\"$(mktemp)\"\nTMP_REGIONS_JSON=\"$(mktemp)\"\nTMP_COMPARTMENTS_OUT_JSON=\"$(mktemp)\"\ncleanup_tmp() {\n rm -f \"$TMP_SUBSCRIPTIONS_JSON\" \"$TMP_COMPARTMENTS_JSON\" \"$TMP_REGIONS_JSON\" \"$TMP_COMPARTMENTS_OUT_JSON\"\n}\ntrap cleanup_tmp EXIT\n\n# ── step 1: verify OCI CLI is installed ──────────────────────────────────────\n\nif ! command -v oci >/dev/null 2>&1; then\n emit_error 2 \\\n \"OCI_CLI_NOT_INSTALLED\" \\\n \"The OCI CLI is not installed or not on PATH.\" \\\n \"Install OCI CLI from https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm\" \\\n \"https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm\"\nfi\n\n# ── step 2: verify OCI CLI is authenticated ───────────────────────────────────\n\nif ! oci iam region-subscription list --output json >/dev/null 2>&1; then\n emit_error 1 \\\n \"OCI_CLI_NOT_AUTHENTICATED\" \\\n \"The OCI CLI is installed but not authenticated. Check ~/.oci/config.\" \\\n \"Run: oci setup config\" \\\n \"https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliconfigure.htm\"\nfi\n\n# ── step 3: extract tenancy OCID and home region ─────────────────────────────\n\nRAW_SUBSCRIPTIONS=$(oci iam region-subscription list --output json 2>/dev/null) || RAW_SUBSCRIPTIONS='{\"data\":[]}'\nprintf '%s' \"$RAW_SUBSCRIPTIONS\" > \"$TMP_SUBSCRIPTIONS_JSON\"\n\nTENANCY_INFO=$(python3 - \"$TMP_SUBSCRIPTIONS_JSON\" \u003c\u003c'PYEOF'\nimport json\nimport sys\nfrom pathlib import Path\nraw = Path(sys.argv[1]).read_text() or '{\"data\":[]}'\ndata = json.loads(raw).get(\"data\", [])\nhome = next((r for r in data if r.get(\"is-home-region\")), None)\nif not home:\n print(\"ERROR\")\n sys.exit(1)\nprint(home.get(\"region-name\", \"\"), home.get(\"tenancy-id\", \"\"))\nPYEOF\n) || true\n\nif [ -z \"$TENANCY_INFO\" ] || [ \"$TENANCY_INFO\" = \"ERROR\" ]; then\n emit_error 1 \\\n \"TENANCY_DISCOVERY_FAILED\" \\\n \"Could not extract tenancy OCID and home region from OCI CLI output.\" \\\n \"Verify your ~/.oci/config has a valid tenancy OCID and region.\" \\\n \"https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliconfigure.htm\"\nfi\n\nHOME_REGION=$(echo \"$TENANCY_INFO\" | awk '{print $1}')\nTENANCY_OCID=$(echo \"$TENANCY_INFO\" | awk '{print $2}')\n\nif [ -z \"$TENANCY_OCID\" ]; then\n emit_error 1 \\\n \"TENANCY_OCID_MISSING\" \\\n \"Tenancy OCID could not be determined from region-subscription output.\" \\\n \"Ensure your OCI CLI profile includes a valid tenancy_ocid in ~/.oci/config.\" \\\n \"https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliconfigure.htm\"\nfi\n\n# ── step 4: list subscribed regions ──────────────────────────────────────────\n\nREGIONS_JSON=$(python3 - \"$TMP_SUBSCRIPTIONS_JSON\" \u003c\u003c'PYEOF'\nimport json\nimport sys\nfrom pathlib import Path\nraw = Path(sys.argv[1]).read_text() or '{\"data\":[]}'\ndata = json.loads(raw).get(\"data\", [])\nregions = [{\"region\": r.get(\"region-name\",\"\"), \"status\": r.get(\"status\",\"\")} for r in data]\nprint(json.dumps(regions))\nPYEOF\n) || REGIONS_JSON='[]'\nprintf '%s' \"$REGIONS_JSON\" > \"$TMP_REGIONS_JSON\"\n\n# ── step 5: list active compartments ─────────────────────────────────────────\n\nRAW_COMPARTMENTS=$(oci iam compartment list \\\n --compartment-id \"$TENANCY_OCID\" \\\n --compartment-id-in-subtree true \\\n --all \\\n --lifecycle-state ACTIVE \\\n --output json 2>/dev/null) || RAW_COMPARTMENTS='{\"data\":[]}'\nprintf '%s' \"$RAW_COMPARTMENTS\" > \"$TMP_COMPARTMENTS_JSON\"\n\nCOMPARTMENTS_JSON=$(python3 - \"$TMP_COMPARTMENTS_JSON\" \"$TENANCY_OCID\" \u003c\u003c'PYEOF'\nimport json\nimport sys\nfrom pathlib import Path\nraw = Path(sys.argv[1]).read_text() or '{\"data\":[]}'\ndata = json.loads(raw).get(\"data\", [])\ntenancy_ocid = sys.argv[2]\ncompartments = [{\"name\": c.get(\"name\",\"\"), \"ocid\": c.get(\"id\",\"\"), \"parent\": c.get(\"compartment-id\",\"\")} for c in data]\n# prepend root compartment (the tenancy itself)\ncompartments.insert(0, {\"name\": \"root (tenancy)\", \"ocid\": tenancy_ocid, \"parent\": \"\"})\nprint(json.dumps(compartments))\nPYEOF\n) || COMPARTMENTS_JSON='[]'\nprintf '%s' \"$COMPARTMENTS_JSON\" > \"$TMP_COMPARTMENTS_OUT_JSON\"\n\n# ── step 6: emit structured output ───────────────────────────────────────────\n\npython3 - \"$TENANCY_OCID\" \"$HOME_REGION\" \"$TMP_REGIONS_JSON\" \"$TMP_COMPARTMENTS_OUT_JSON\" \u003c\u003c'PYEOF'\nimport sys, json\nfrom pathlib import Path\nregions_raw = Path(sys.argv[3]).read_text()\ncompartments_raw = Path(sys.argv[4]).read_text()\nresult = {\n \"tenancy_ocid\": sys.argv[1],\n \"home_region\": sys.argv[2],\n \"regions\": json.loads(regions_raw),\n \"compartments\": json.loads(compartments_raw)\n}\nprint(json.dumps(result, indent=2))\nPYEOF\n\nexit 0\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":5977,"content_sha256":"d2d8e8a163dcca1035dda8804f15d88e22312bedf9fe3e7c206d752f899d8723"},{"filename":"oke/scripts/validate-cidr.sh","content":"#!/usr/bin/env bash\n# validate-cidr.sh\n# Detect CIDR overlap between 2 or more CIDR blocks\n#\n# Usage: bash validate-cidr.sh \u003ccidr1> \u003ccidr2> [cidr3 ...]\n# Exit: 0 = no overlaps found\n# 1 = overlaps detected\n# 2 = invalid input (bad CIDR format or insufficient arguments)\n#\n# Stderr: JSON {error_code, message, remediation, docs_url} (exit 2 only)\n# Stdout: JSON {valid: bool, overlaps: [{cidr_a, cidr_b}], cidrs_checked: []}\n\nset -euo pipefail\n\nemit_error() {\n local exit_code=\"$1\"\n local error_code=\"$2\"\n local message=\"$3\"\n local remediation=\"$4\"\n printf '{\"error_code\":\"%s\",\"message\":\"%s\",\"remediation\":\"%s\",\"docs_url\":\"\"}\\n' \\\n \"$error_code\" \"$message\" \"$remediation\" >&2\n exit \"$exit_code\"\n}\n\nif [ \"$#\" -lt 2 ]; then\n emit_error 2 \\\n \"INSUFFICIENT_ARGUMENTS\" \\\n \"validate-cidr.sh requires at least 2 CIDR arguments.\" \\\n \"Usage: bash validate-cidr.sh 10.0.0.0/16 10.244.0.0/16 10.96.0.0/16\"\nfi\n\n# Delegate all parsing and overlap detection to Python\npython3 - \"$@\" \u003c\u003c'PYEOF'\nimport sys, json, ipaddress\n\ncidrs_raw = sys.argv[1:]\ncidrs = []\n\nfor raw in cidrs_raw:\n try:\n net = ipaddress.ip_network(raw, strict=False)\n cidrs.append((raw, net))\n except ValueError as e:\n error = {\n \"error_code\": \"INVALID_CIDR\",\n \"message\": f\"Invalid CIDR block: {raw}. {e}\",\n \"remediation\": \"Provide a valid CIDR in the form A.B.C.D/prefix (e.g. 10.0.0.0/16).\",\n \"docs_url\": \"\"\n }\n print(json.dumps(error), file=sys.stderr)\n sys.exit(2)\n\noverlaps = []\nfor i in range(len(cidrs)):\n for j in range(i + 1, len(cidrs)):\n raw_a, net_a = cidrs[i]\n raw_b, net_b = cidrs[j]\n if net_a.overlaps(net_b):\n overlaps.append({\"cidr_a\": raw_a, \"cidr_b\": raw_b})\n\nresult = {\n \"valid\": len(overlaps) == 0,\n \"overlaps\": overlaps,\n \"cidrs_checked\": cidrs_raw\n}\nprint(json.dumps(result, indent=2))\nsys.exit(1 if overlaps else 0)\nPYEOF\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":1976,"content_sha256":"a95236d9dcc5228eebd5c6865c9a8087a455289522d7a872c137a30c871d0c47"},{"filename":"oke/shared/oci-resource-map.md","content":"# OCI Resource Mapping Cheatsheet\n\nUse these command chains to relate Kubernetes objects to Oracle Cloud Infrastructure resources during troubleshooting.\n\n## Automated Correlation\nUse the object correlator first when you have a namespace and one or more target selectors. It runs read-only `kubectl` and `oci` lookups and returns a normalized graph with Kubernetes nodes, OCI nodes, and edges between them.\n\n```bash\n./scripts/oke-object-correlator.sh \\\n --namespace \u003cns> \\\n --cluster-id \u003ccluster-ocid> \\\n --compartment-id \u003ccompartment-ocid> \\\n --region \u003cregion> \\\n [--pod \u003cpod>] \\\n [--deployment \u003cdeployment>] \\\n [--service \u003cservice>] \\\n [--ingress \u003cingress>] \\\n [--pvc \u003cpvc>] \\\n [--node \u003cnode>]\n```\n\nExpected graph edges include:\n- `scheduled_on`: Pod to Kubernetes node.\n- `runs_on_instance`: Kubernetes node to OCI Compute instance.\n- `has_vnic`: OCI instance to each primary or secondary VNIC attachment.\n- `attached_to_subnet`: VNIC to subnet.\n- `uses_nsg`: VNIC to Network Security Group.\n- `uses_security_list`: Subnet to security list.\n- `uses_route_table`: Subnet to route table.\n- `routes_to`: Route table to gateway, DRG, private IP, or peering target.\n- `provisions`: Kubernetes Service or Ingress to OCI Load Balancer.\n- `bound_to` and `backs_onto`: PVC to PV to OCI Block Volume.\n\nUse the manual command chains below when the correlator cannot resolve a link or when you need to inspect a specific object in more detail.\n\n## Pod → Node → Instance\n1. Identify the node hosting the pod:\n ```bash\n kubectl get pod \u003cpod> -n \u003cns> -o jsonpath='{.spec.nodeName}'\n ```\n2. Fetch node annotations for the instance OCID:\n ```bash\n kubectl get node \u003cnode> -o jsonpath='{.metadata.annotations.\"node\\.oci\\.oraclecloud\\.com/instance-id\"}'\n ```\n3. Inspect the OCI instance:\n ```bash\n oci compute instance get --instance-id \u003cinstance-ocid>\n ```\n4. List VNIC attachments for the instance:\n ```bash\n oci compute vnic-attachment list \\\n --compartment-id \u003ccompartment-ocid> \\\n --instance-id \u003cinstance-ocid> \\\n --all\n ```\n5. Inspect each VNIC and its subnet path:\n ```bash\n oci network vnic get --vnic-id \u003cvnic-ocid>\n oci network subnet get --subnet-id \u003csubnet-ocid>\n oci network security-list get --security-list-id \u003csecurity-list-ocid>\n oci network route-table get --rt-id \u003croute-table-ocid>\n ```\n\n## Service / Ingress → Load Balancer\n1. Obtain OCI load balancer OCID from annotations:\n ```bash\n kubectl get svc \u003cservice> -n \u003cns> -o jsonpath='{.metadata.annotations.\"oci\\.oraclecloud\\.com/load-balancer-id\"}'\n ```\n2. Describe load balancer health:\n ```bash\n oci lb load-balancer get --load-balancer-id \u003clb-ocid>\n ```\n3. Review backend set health:\n ```bash\n oci lb load-balancer-health get \\\n --load-balancer-id \u003clb-ocid>\n ```\n For a specific backend set:\n ```bash\n oci lb backend-set-health get \\\n --load-balancer-id \u003clb-ocid> \\\n --backend-set-name \u003cbackend-set>\n ```\n\n## PersistentVolumeClaim → Block Volume / File System\n1. Retrieve PV name and volume handle:\n ```bash\n kubectl get pvc \u003cclaim> -n \u003cns> -o jsonpath='{.spec.volumeName} {.spec.volumeHandle}'\n ```\n2. If Block Volume:\n ```bash\n oci bv volume get --volume-id \u003cvolume-ocid>\n ```\n For attachment details:\n ```bash\n oci compute volume-attachment list --compartment-id \u003ccompartment> --volume-id \u003cvolume-ocid>\n ```\n3. If File Storage (FSS):\n ```bash\n oci fs file-system get --file-system-id \u003cfilesystem-ocid>\n ```\n\n## Namespace / Service Account → IAM Policies\n1. Locate relevant IAM policy statements:\n ```bash\n oci iam policy list --compartment-id \u003ctenancy-ocid> --all \\\n --query \"data[].{name:name, statements:statements}\"\n ```\n2. Cross-check Kubernetes service account annotations and projected token behavior:\n ```bash\n kubectl get serviceaccount \u003csa> -n \u003cns> -o yaml\n ```\n\n## Node Pool → Availability Domains\n1. Identify node pool OCID:\n ```bash\n oci ce node-pool list --compartment-id \u003ccompartment> --cluster-id \u003ccluster-ocid>\n ```\n2. Fetch node pool details:\n ```bash\n oci ce node-pool get --node-pool-id \u003cnodepool-ocid>\n ```\n3. Map to AD and subnet IDs; confirm subnet health via:\n ```bash\n oci network subnet get --subnet-id \u003csubnet-ocid>\n ```\n\nKeep this document in sync with any future automation covering additional OCI resource types.\n\n## Sources\n\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/home.htm\n- https://docs.oracle.com/en-us/iaas/Content/Balance/home.htm\n- https://docs.oracle.com/en-us/iaas/Content/Block/Concepts/overview.htm\n- https://docs.oracle.com/en-us/iaas/Content/File/Concepts/filestorageoverview.htm\n- https://docs.oracle.com/en-us/iaas/Content/Identity/policieshow/Policy_Basics.htm\n- https://docs.oracle.com/en-us/iaas/tools/oci-cli/latest/oci_cli_docs/\n- https://kubernetes.io/docs/reference/kubectl/\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4867,"content_sha256":"b05657e1a35f3d206f5f00d85c606175ffbd03c73bee18801067cb33c060fcf6"},{"filename":"oke/skills/AGENTS.md","content":"# Skills Directory Instructions\n\n## Skill Structure\n\nEvery skill must have:\n\n- `SKILL.md` with YAML frontmatter containing `name` and `description`.\n- A concise body that explains the core workflow and points to references or scripts only when needed.\n\nOptional files:\n\n- `references/` for longer notes, examples, constraints, and troubleshooting details.\n- `scripts/` for deterministic helpers that are easier to test than prose-only workflows.\n- `agents/openai.yaml` for Codex-facing skill UI metadata when useful.\n\n## Skill Authoring Rules\n\n- Keep `SKILL.md` focused on procedure, guardrails, and when to load extra resources.\n- Put large examples, detailed troubleshooting notes, and edge-case explanations in `references/`.\n- Prefer discovery helpers over asking users for raw OCIDs when the environment can enumerate choices.\n- Make scripts safe to run offline where possible; destructive OCI or Kubernetes actions must require explicit user confirmation in the skill workflow.\n- Public examples must use placeholders instead of real tenancy values, cluster OCIDs, private IPs, or customer names.\n- When a skill relies on another skill, state the handoff clearly. For example, multihome pod validation should tell users to run the GVA deployer first if the node pool does not yet have GVA secondary VNIC profiles.\n\n## Validation\n\nAfter changing skill scripts, run:\n\n```bash\nbash tests/scripts-smoke.sh\n```\n\nIf adding a new OKE skill, update `oci/SKILL.md`, the root `README.md`, and the relevant `oci/oke/*.md` routing file.\n\n## Sources\n\n- https://github.com/oracle/skills/blob/main/SKILL_AUTHORING_GUIDE.md\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/home.htm\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1676,"content_sha256":"f38554029545a4ba2ceefdce431f997c7ef3ffef6e9dd26f9467d692f0aef44c"},{"filename":"oke/tests/golden/gva-multihome-pods.yaml","content":"apiVersion: v1\nkind: Namespace\nmetadata:\n name: gva-multihome-test\n---\napiVersion: k8s.cni.cncf.io/v1\nkind: NetworkAttachmentDefinition\nmetadata:\n name: gva-default-network\n namespace: kube-system\nspec:\n config: |\n {\n \"name\": \"gva-default-network\",\n \"cniVersion\": \"0.3.1\",\n \"plugins\": [\n {\n \"cniVersion\": \"0.3.1\",\n \"type\": \"oci-ipvlan\",\n \"mode\": \"l2\",\n \"ipam\": {\n \"type\": \"oci-ipam\",\n \"deviceSelector\": {\n \"interfaceName\": \"enp1s0\"\n }\n }\n },\n {\n \"cniVersion\": \"0.3.1\",\n \"type\": \"oci-ptp\",\n \"containerInterface\": \"ptp-veth0\",\n \"mtu\": 9000\n }\n ]\n }\n---\napiVersion: k8s.cni.cncf.io/v1\nkind: NetworkAttachmentDefinition\nmetadata:\n name: gva-secondary-network\n namespace: gva-multihome-test\nspec:\n config: |\n {\n \"cniVersion\": \"0.3.1\",\n \"plugins\": [\n {\n \"type\": \"ipvlan\",\n \"mode\": \"l2\",\n \"master\": \"enp2s0\",\n \"ipam\": {\n \"type\": \"oci-ipam\",\n \"deviceSelector\": {\n \"interfaceName\": \"enp2s0\"\n }\n }\n }\n ]\n }\n---\napiVersion: v1\nkind: Pod\nmetadata:\n name: gva-multihome-a\n namespace: gva-multihome-test\n labels:\n app: gva-multihome\n annotations:\n v1.multus-cni.io/default-network: kube-system/gva-default-network\n k8s.v1.cni.cncf.io/networks: gva-multihome-test/gva-secondary-network\nspec:\n nodeName: \"node-a\"\n containers:\n - name: netshoot\n image: \"docker.io/nicolaka/netshoot:v0.13\"\n command:\n - sleep\n - \"3600\"\n---\napiVersion: v1\nkind: Pod\nmetadata:\n name: gva-multihome-b\n namespace: gva-multihome-test\n labels:\n app: gva-multihome\n annotations:\n v1.multus-cni.io/default-network: kube-system/gva-default-network\n k8s.v1.cni.cncf.io/networks: gva-multihome-test/gva-secondary-network\nspec:\n nodeName: \"node-b\"\n containers:\n - name: netshoot\n image: \"docker.io/nicolaka/netshoot:v0.13\"\n command:\n - sleep\n - \"3600\"\n","content_type":"application/yaml; charset=utf-8","language":"yaml","size":2103,"content_sha256":"731139005584dd087e4432f2c07708f632c917da7d02f38f0b6686ab348b11ef"},{"filename":"oke/tests/golden/gva-node-pool-command.sh","content":"oci ce node-pool create \\\n --compartment-id \"\u003ccompartment_ocid>\" \\\n --cluster-id \"\u003ccluster_ocid>\" \\\n --name \"gva-workers\" \\\n --kubernetes-version \"v1.31.1\" \\\n --node-shape \"VM.Standard.E5.Flex\" \\\n --node-shape-config '{\"ocpus\":2,\"memoryInGBs\":16}' \\\n --size 3 \\\n --cni-type OCI_VCN_IP_NATIVE \\\n --placement-configs '[{\"availabilityDomain\":\"GrCh:US-ASHBURN-AD-1\",\"subnetId\":\"\u003cprimary_subnet_ocid>\"}]' \\\n --node-source-details '{\"sourceType\":\"IMAGE\",\"imageId\":\"\u003cimage_ocid>\"}' \\\n --secondary-vnics '[{\"createVnicDetails\":{\"ipCount\":32,\"applicationResources\":[\"frontend\"],\"assignPublicIp\":false,\"displayName\":\"frontend-vnic\",\"nsgIds\":null,\"subnetId\":\"\u003csecondary_subnet_ocid>\",\"skipSourceDestCheck\":false},\"displayName\":\"frontend-vnic\"}]'\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":746,"content_sha256":"3c31c5633997cc37282673b02c6cf11dfab04bbce558475cae8c9c7140e24e12"},{"filename":"oke/tests/scripts-smoke.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nREPO_ROOT=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\n\nassert_eq() {\n local expected=\"$1\"\n local actual=\"$2\"\n local msg=\"$3\"\n if [[ \"$expected\" != \"$actual\" ]]; then\n echo \"FAIL: $msg (expected=$expected actual=$actual)\" >&2\n exit 1\n fi\n}\n\nassert_contains() {\n local haystack=\"$1\"\n local needle=\"$2\"\n local msg=\"$3\"\n if [[ \"$haystack\" != *\"$needle\"* ]]; then\n echo \"FAIL: $msg (missing: $needle)\" >&2\n exit 1\n fi\n}\n\nassert_file_matches() {\n local expected=\"$1\"\n local actual=\"$2\"\n local msg=\"$3\"\n if ! diff -u \"$expected\" \"$actual\"; then\n echo \"FAIL: $msg\" >&2\n exit 1\n fi\n}\n\nassert_json_expr() {\n local json=\"$1\"\n local expr=\"$2\"\n local msg=\"$3\"\n if ! JSON_INPUT=\"$json\" EXPR=\"$expr\" python3 - \u003c\u003c'PY'\nimport json\nimport os\n\nobj = json.loads(os.environ[\"JSON_INPUT\"])\nexpr = os.environ[\"EXPR\"]\nif not eval(expr, {\"obj\": obj}):\n raise SystemExit(1)\nPY\n then\n echo \"FAIL: $msg\" >&2\n exit 1\n fi\n}\n\nmake_mocks() {\n local dir=\"$1\"\n mkdir -p \"$dir\"\n\n cat > \"$dir/oci\" \u003c\u003c'MOCK_OCI'\n#!/usr/bin/env bash\nset -euo pipefail\n\nargs=(\"$@\")\ntrimmed=()\nidx=0\nwhile [[ $idx -lt ${#args[@]} ]]; do\n case \"${args[$idx]}\" in\n --region|--profile)\n idx=$((idx+2))\n ;;\n *)\n trimmed+=(\"${args[$idx]}\")\n idx=$((idx+1))\n ;;\n esac\ndone\n\ncmd=\"${trimmed[*]}\"\n\nif [[ \"$cmd\" == iam\\ region-subscription\\ list* ]]; then\n if [[ \"${MOCK_OCI_AUTH_FAIL:-0}\" == \"1\" ]]; then\n echo \"Not authenticated\" >&2\n exit 1\n fi\n cat \u003c\u003c'JSON'\n{\"data\":[{\"region-name\":\"us-ashburn-1\",\"status\":\"READY\",\"is-home-region\":true,\"tenancy-id\":\"ocid1.tenancy.oc1..tenancy\"}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == iam\\ compartment\\ list* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":[{\"name\":\"team-a\",\"id\":\"ocid1.compartment.oc1..a\",\"compartment-id\":\"ocid1.tenancy.oc1..tenancy\"}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == ce\\ cluster\\ get* ]]; then\n if [[ \"${MOCK_OCI_CLUSTER_GET_FAIL:-0}\" == \"1\" ]]; then\n echo \"cluster get failed\" >&2\n exit 2\n fi\n cluster_id=\"\"\n for ((i=0; i\u003c${#trimmed[@]}; i++)); do\n if [[ \"${trimmed[$i]}\" == \"--cluster-id\" && $((i+1)) -lt ${#trimmed[@]} ]]; then\n cluster_id=\"${trimmed[$((i+1))]}\"\n break\n fi\n done\n name=\"cluster-from-get\"\n if [[ -n \"${MOCK_OCI_CLUSTER_GET_NAME:-}\" ]]; then\n name=\"${MOCK_OCI_CLUSTER_GET_NAME}\"\n fi\n cat \u003c\u003cJSON\n{\"name\":\"$name\",\"k8s\":\"v1.31.1\",\"compartment\":\"ocid1.compartment.oc1..a\",\"data\":{\"name\":\"$name\",\"kubernetes-version\":\"v1.31.1\",\"compartment-id\":\"ocid1.compartment.oc1..a\",\"id\":\"$cluster_id\"}}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == ce\\ cluster\\ list* ]]; then\n if [[ -n \"${MOCK_OCI_CLUSTER_LIST_JSON:-}\" ]]; then\n printf '%s\\n' \"$MOCK_OCI_CLUSTER_LIST_JSON\"\n else\n cat \u003c\u003c'JSON'\n{\"data\":[{\"name\":\"default-cluster\",\"id\":\"ocid1.cluster.oc1..default\",\"kubernetes-version\":\"v1.30.1\"}]}\nJSON\n fi\n exit 0\nfi\n\nif [[ \"$cmd\" == ce\\ node-pool\\ list* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":[{\"name\":\"np-general\",\"id\":\"ocid1.nodepool.oc1..np\",\"size\":2,\"node-shape\":\"VM.Standard.E5.Flex\"}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == compute\\ instance\\ get* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.instance.oc1..inst\",\"lifecycle-state\":\"RUNNING\",\"shape\":\"VM.Standard.E5.Flex\",\"availability-domain\":\"GrCh:US-ASHBURN-AD-1\"}}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == compute\\ vnic-attachment\\ list* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":[{\"id\":\"ocid1.vnicattachment.oc1..va\",\"vnic-id\":\"ocid1.vnic.oc1..vnic\",\"subnet-id\":\"ocid1.subnet.oc1..node\",\"lifecycle-state\":\"ATTACHED\",\"display-name\":\"primary-vnic\"},{\"id\":\"ocid1.vnicattachment.oc1..vasecondary\",\"vnic-id\":\"ocid1.vnic.oc1..secondary\",\"subnet-id\":\"ocid1.subnet.oc1..secondary\",\"lifecycle-state\":\"ATTACHED\",\"display-name\":\"secondary-vnic\"}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == network\\ vnic\\ get* ]]; then\n if [[ \"$cmd\" == *\"ocid1.vnic.oc1..secondary\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.vnic.oc1..secondary\",\"display-name\":\"secondary-vnic\",\"lifecycle-state\":\"AVAILABLE\",\"subnet-id\":\"ocid1.subnet.oc1..secondary\",\"nsg-ids\":[\"ocid1.networksecuritygroup.oc1..secondary\"],\"private-ip\":\"10.0.2.10\",\"is-primary\":false}}\nJSON\n else\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.vnic.oc1..vnic\",\"display-name\":\"primary-vnic\",\"lifecycle-state\":\"AVAILABLE\",\"subnet-id\":\"ocid1.subnet.oc1..node\",\"nsg-ids\":[\"ocid1.networksecuritygroup.oc1..node\"],\"private-ip\":\"10.0.1.10\",\"is-primary\":true}}\nJSON\n fi\n exit 0\nfi\n\nif [[ \"$cmd\" == network\\ subnet\\ get* ]]; then\n if [[ \"$cmd\" == *\"ocid1.subnet.oc1..secondary\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.subnet.oc1..secondary\",\"display-name\":\"secondary-subnet\",\"cidr-block\":\"10.0.2.0/24\",\"vcn-id\":\"ocid1.vcn.oc1..vcn\",\"route-table-id\":\"ocid1.routetable.oc1..secondary\",\"security-list-ids\":[\"ocid1.securitylist.oc1..secondary\"]}}\nJSON\n else\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.subnet.oc1..node\",\"display-name\":\"node-subnet\",\"cidr-block\":\"10.0.1.0/24\",\"vcn-id\":\"ocid1.vcn.oc1..vcn\",\"route-table-id\":\"ocid1.routetable.oc1..node\",\"security-list-ids\":[\"ocid1.securitylist.oc1..node\"]}}\nJSON\n fi\n exit 0\nfi\n\nif [[ \"$cmd\" == network\\ nsg\\ get* ]]; then\n if [[ \"$cmd\" == *\"ocid1.networksecuritygroup.oc1..secondary\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.networksecuritygroup.oc1..secondary\",\"display-name\":\"secondary-nsg\",\"vcn-id\":\"ocid1.vcn.oc1..vcn\",\"lifecycle-state\":\"AVAILABLE\"}}\nJSON\n else\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.networksecuritygroup.oc1..node\",\"display-name\":\"node-nsg\",\"vcn-id\":\"ocid1.vcn.oc1..vcn\",\"lifecycle-state\":\"AVAILABLE\"}}\nJSON\n fi\n exit 0\nfi\n\nif [[ \"$cmd\" == network\\ security-list\\ get* ]]; then\n if [[ \"$cmd\" == *\"ocid1.securitylist.oc1..secondary\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.securitylist.oc1..secondary\",\"display-name\":\"secondary-sl\",\"vcn-id\":\"ocid1.vcn.oc1..vcn\",\"ingress-security-rules\":[{}],\"egress-security-rules\":[{}],\"lifecycle-state\":\"AVAILABLE\"}}\nJSON\n else\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.securitylist.oc1..node\",\"display-name\":\"node-sl\",\"vcn-id\":\"ocid1.vcn.oc1..vcn\",\"ingress-security-rules\":[{}],\"egress-security-rules\":[{}],\"lifecycle-state\":\"AVAILABLE\"}}\nJSON\n fi\n exit 0\nfi\n\nif [[ \"$cmd\" == network\\ route-table\\ get* ]]; then\n if [[ \"$cmd\" == *\"ocid1.routetable.oc1..secondary\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.routetable.oc1..secondary\",\"display-name\":\"secondary-rt\",\"vcn-id\":\"ocid1.vcn.oc1..vcn\",\"route-rules\":[{\"destination\":\"10.1.0.0/16\",\"network-entity-id\":\"ocid1.localpeeringgateway.oc1..lpg\"}],\"lifecycle-state\":\"AVAILABLE\"}}\nJSON\n else\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.routetable.oc1..node\",\"display-name\":\"node-rt\",\"vcn-id\":\"ocid1.vcn.oc1..vcn\",\"route-rules\":[{\"destination\":\"0.0.0.0/0\",\"network-entity-id\":\"ocid1.natgateway.oc1..nat\"}],\"lifecycle-state\":\"AVAILABLE\"}}\nJSON\n fi\n exit 0\nfi\n\nif [[ \"$cmd\" == bv\\ volume\\ get* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":{\"id\":\"ocid1.volume.oc1..vol\",\"lifecycle-state\":\"AVAILABLE\",\"availability-domain\":\"GrCh:US-ASHBURN-AD-1\",\"size-in-gbs\":50}}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == compute\\ volume-attachment\\ list* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":[{\"id\":\"ocid1.volumeattachment.oc1..attach\",\"volume-id\":\"ocid1.volume.oc1..vol\",\"instance-id\":\"ocid1.instance.oc1..inst\",\"lifecycle-state\":\"ATTACHED\"}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == monitoring\\ alarm-status\\ list-alarms-status* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":[{\"display-name\":\"node-cpu-high\",\"status\":\"FIRING\",\"timestamp-triggered\":\"2026-04-29T20:00:00Z\"}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == artifacts\\ container\\ repository\\ list* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":[{\"display-name\":\"team/web\",\"id\":\"ocid1.containerrepo.oc1..repo\"}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == iam\\ dynamic-group\\ list* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":[{\"name\":\"oke-workloads\",\"matching-rule\":\"ALL {resource.type = 'cluster'}\"}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == iam\\ policy\\ list* ]]; then\n cat \u003c\u003c'JSON'\n{\"data\":[{\"name\":\"oke-workload-policy\",\"statements\":[\"Allow any-user to read buckets in tenancy where all {request.principal.type = 'workload'}\"]}]}\nJSON\n exit 0\nfi\n\nif [[ \"$cmd\" == network\\ subnet\\ list* ]]; then\n echo '{\"data\":[]}'\n exit 0\nfi\n\nif [[ \"$cmd\" == network\\ nsg\\ list* ]]; then\n echo '{\"data\":[]}'\n exit 0\nfi\n\necho \"unexpected oci args: $*\" >&2\nexit 9\nMOCK_OCI\n\n cat > \"$dir/kubectl\" \u003c\u003c'MOCK_KUBECTL'\n#!/usr/bin/env bash\nset -euo pipefail\n\nif [[ -n \"${MOCK_KUBECTL_LOG:-}\" ]]; then\n printf '%s\\n' \"$*\" >> \"$MOCK_KUBECTL_LOG\"\nfi\n\nif [[ \"$*\" == *\" debug node/\"* ]]; then\n echo \"Creating debugging pod nd-123 with container debugger on node.\"\n echo \"Running node doctor...\"\n echo \"PASS kernel\"\n echo \"0 Signal(s) generated\"\n exit 0\nfi\n\nif [[ \"$*\" == *\" get pod nd-123 -o jsonpath={.status.phase}\"* || \"$*\" == *\" get pod nd-123 -o jsonpath='{.status.phase}'\"* ]]; then\n echo \"Succeeded\"\n exit 0\nfi\n\nif [[ \"$*\" == *\" logs nd-123\"* ]]; then\n echo \"Running node doctor...\"\n echo \"PASS kernel\"\n echo \"0 Signal(s) generated\"\n exit 0\nfi\n\nif [[ \"$*\" == *\" delete pod \"* ]]; then\n echo \"pod \\\"nd-123\\\" deleted\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get pods -o wide\"* ]]; then\n cat \u003c\u003c'OUT'\nNAME READY STATUS NODE\ncoredns-abc 1/1 Running node-a\noci-csi-controller-abc 1/1 Running node-a\nOUT\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get deploy -o wide\"* ]]; then\n cat \u003c\u003c'OUT'\nNAME READY UP-TO-DATE AVAILABLE\ncoredns 2/2 2 2\nOUT\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get ds -o wide\"* ]]; then\n cat \u003c\u003c'OUT'\nNAME DESIRED CURRENT READY\noci-cni 2 2 2\nOUT\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get deploy coredns -o wide\"* ]]; then\n echo \"coredns 2/2 2 2\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get events --field-selector type=Warning\"* ]]; then\n echo \"No resources found in kube-system namespace.\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get pod web-0 -o wide\"* ]]; then\n echo \"web-0 0/1 ContainerCreating node-a\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default describe pod web-0\"* ]]; then\n echo \"Warning FailedCreatePodSandBox failed to find plugin \\\"ipvlan\\\" in path [/opt/cni/bin]\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get pod web-0 -o jsonpath={.metadata.annotations.k8s\\\\.v1\\\\.cni\\\\.cncf\\\\.io/network-status}\"* || \"$*\" == *\"-n default get pod web-0 -o jsonpath='{.metadata.annotations.k8s\\\\.v1\\\\.cni\\\\.cncf\\\\.io/network-status}'\"* ]]; then\n echo '[{\"name\":\"default/secondary\",\"interface\":\"net1\"}]'\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get pod web-0 -o jsonpath={.spec.nodeName}\"* || \"$*\" == *\"-n default get pod web-0 -o jsonpath='{.spec.nodeName}'\"* ]]; then\n echo \"node-a\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get pod storage-pod -o json\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"metadata\":{\"name\":\"storage-pod\",\"namespace\":\"default\"},\"spec\":{\"nodeName\":\"node-no-annotation\",\"serviceAccountName\":\"default\",\"volumes\":[{\"name\":\"data\",\"persistentVolumeClaim\":{\"claimName\":\"data\"}}]}}\nJSON\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get pvc data -o json\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"metadata\":{\"name\":\"data\",\"namespace\":\"default\"},\"spec\":{\"volumeName\":\"pv-data\"},\"status\":{\"phase\":\"Bound\"}}\nJSON\n exit 0\nfi\n\nif [[ \"$*\" == *\"get pv pv-data -o json\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"metadata\":{\"name\":\"pv-data\"},\"spec\":{\"storageClassName\":\"oci-bv\",\"csi\":{\"driver\":\"blockvolume.csi.oraclecloud.com\",\"volumeHandle\":\"ocid1.volume.oc1..vol\"}}}\nJSON\n exit 0\nfi\n\nif [[ \"$*\" == *\"get node node-no-annotation -o json\"* ]]; then\n cat \u003c\u003c'JSON'\n{\"metadata\":{\"name\":\"node-no-annotation\",\"labels\":{\"oke.oraclecloud.com/nodepool\":\"np-general\"},\"annotations\":{}},\"spec\":{\"providerID\":\"oci://ocid1.instance.oc1..inst\"}}\nJSON\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get events --field-selector type=Warning\"* ]]; then\n echo \"Warning FailedCreatePodSandBox failed to setup network\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get pods -l app=oci-cni -o wide\"* ]]; then\n echo \"oci-cni-abc 1/1 Running node-a\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get pods -l name=multus -o wide\"* ]]; then\n echo \"kube-multus-ds-abc 1/1 Running node-a\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"get network-attachment-definitions -A\"* ]]; then\n echo \"default secondary\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get pods --field-selector=status.phase=Pending -o wide\"* ]]; then\n echo \"web-0 0/1 Pending \u003cnone>\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get events --field-selector reason=FailedScheduling\"* ]]; then\n echo \"Warning FailedScheduling 0/2 nodes available: Insufficient cpu\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get deploy cluster-autoscaler -o wide\"* ]]; then\n echo \"cluster-autoscaler 1/1 1 1\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system logs deployment/cluster-autoscaler --tail=200\"* ]]; then\n echo \"NotTriggerScaleUp pod didn't trigger scale-up: max node group size reached\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default describe deployment web\"* ]]; then\n echo \"Deployment web has 3 unavailable replicas\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get hpa --selector app=web -o wide\"* ]]; then\n echo \"No resources found in default namespace.\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get pods -l k8s-app=kube-dns -o wide\"* ]]; then\n echo \"coredns-abc 1/1 Running node-a\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get configmap coredns -o yaml\"* ]]; then\n echo \"kind: ConfigMap\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system logs deployment/coredns --tail=200\"* ]]; then\n echo \"plugin/errors: 2 web.default.svc.cluster.local. A: read udp timeout\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get svc web -o yaml\"* ]]; then\n echo \"kind: Service\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get endpoints web -o yaml\"* ]]; then\n echo \"subsets: []\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get endpointslices -l kubernetes.io/service-name=web -o yaml\"* ]]; then\n echo \"items: []\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default exec web-0 -- nslookup web.default.svc.cluster.local\"* ]]; then\n echo \";; connection timed out; no servers could be reached\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"config current-context\"* ]]; then\n echo \"context-oke\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"cluster-info\"* ]]; then\n echo \"Kubernetes control plane is running at https://private-endpoint\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"get --raw=/readyz?verbose\"* ]]; then\n echo \"[-]poststarthook/start-kube-apiserver-admission-initializer failed: reason withheld\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"version --client\"* ]]; then\n echo \"Client Version: v1.31.0\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get pod web-0 -o yaml\"* ]]; then\n cat \u003c\u003c'OUT'\nspec:\n serviceAccountName: workload-sa\n imagePullSecrets:\n - name: ocirsecret\n containers:\n - image: iad.ocir.io/ns/team/web:bad\nOUT\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get events --field-selector involvedObject.name=web-0\"* ]]; then\n echo \"Warning Failed Failed to pull image iad.ocir.io/ns/team/web:bad: unauthorized\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get serviceaccount -o yaml\"* ]]; then\n echo \"kind: ServiceAccount\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get secret -o yaml\"* ]]; then\n echo \"kind: Secret\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get serviceaccount workload-sa -o yaml\"* ]]; then\n echo \"metadata: {name: workload-sa, annotations: {oracle.com/oci-workload-identity: enabled}}\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default logs web-0 --tail=200\"* ]]; then\n echo \"NotAuthorizedOrNotFound: principal lacks policy\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"get ingressclass -o yaml\"* ]]; then\n echo \"kind: IngressClass\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get ingress web -o yaml\"* ]]; then\n echo \"kind: Ingress\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default describe ingress web\"* ]]; then\n echo \"Warning ReconcileFailed listener certificate mismatch\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system get pods -l app.kubernetes.io/name=oci-native-ingress-controller -o wide\"* ]]; then\n echo \"oci-native-ingress-controller 1/1 Running node-a\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n kube-system logs -l app.kubernetes.io/name=oci-native-ingress-controller --tail=200\"* ]]; then\n echo \"Error syncing ingress default/web: backend set unhealthy\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default get events --sort-by=.lastTimestamp\"* ]]; then\n echo \"10m Warning FailedScheduling pod/web-0 Insufficient cpu\"\n echo \"5m Normal Started pod/web-0 Started container\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default rollout history deployment/web\"* ]]; then\n echo \"REVISION CHANGE-CAUSE\"\n echo \"2 kubectl set image\"\n exit 0\nfi\n\nif [[ \"$*\" == *\"-n default describe service web\"* ]]; then\n echo \"Warning Unhealthy backend\"\n exit 0\nfi\n\necho \"unexpected kubectl args: $*\" >&2\nexit 8\nMOCK_KUBECTL\n\n chmod +x \"$dir/oci\" \"$dir/kubectl\"\n}\n\nwith_temp_home() {\n local home_dir=\"$1\"\n mkdir -p \"$home_dir/.oci\"\n cat > \"$home_dir/.oci/config\" \u003c\u003c'CFG'\n[DEFAULT]\nuser=ocid1.user.oc1..u\nfingerprint=00:11\nkey_file=/tmp/fake.pem\ntenancy=ocid1.tenancy.oc1..tenancy\nregion=us-ashburn-1\nCFG\n}\n\nrun_test_preflight() {\n echo \"- preflight-check JSON contract\"\n local out err rc\n set +e\n out=\"$(\"$REPO_ROOT/scripts/preflight-check.sh\" 2>\"$TMPDIR_BASE/t1.err\")\"\n rc=$?\n set -e\n if [[ \"$rc\" != \"0\" ]]; then\n echo \"preflight stderr:\"\n cat \"$TMPDIR_BASE/t1.err\"\n fi\n assert_eq \"0\" \"$rc\" \"preflight-check exits 0\"\n err=\"$(cat \"$TMPDIR_BASE/t1.err\")\"\n assert_eq \"\" \"$err\" \"preflight-check stderr empty on success\"\n assert_json_expr \"$out\" \"obj['tenancy_ocid'].startswith('ocid1.tenancy')\" \"preflight has tenancy_ocid\"\n assert_json_expr \"$out\" \"len(obj['regions']) >= 1\" \"preflight has regions\"\n assert_json_expr \"$out\" \"obj['compartments'][0]['name'] == 'root (tenancy)'\" \"preflight prepends root compartment\"\n}\n\nrun_test_gva_discover_cluster_get_failure() {\n echo \"- gva-discover handles cluster get failure without crashing\"\n local out rc\n set +e\n out=\"$(MOCK_OCI_CLUSTER_GET_FAIL=1 \"$REPO_ROOT/scripts/gva-discover.sh\" --cluster ocid1.cluster.oc1..abc 2>\"$TMPDIR_BASE/t2.err\")\"\n rc=$?\n set -e\n assert_eq \"0\" \"$rc\" \"gva-discover still exits 0 on cluster-get failure path\"\n assert_json_expr \"$out\" \"obj['cluster']['id'] == 'ocid1.cluster.oc1..abc'\" \"gva-discover preserves cluster id\"\n assert_json_expr \"$out\" \"obj['cluster']['region'] == 'us-ashburn-1'\" \"gva-discover preserves region\"\n}\n\nrun_test_oke_discover_cluster_get_failure() {\n echo \"- oke-discover returns partial context on cluster get failure\"\n local out rc\n set +e\n out=\"$(MOCK_OCI_CLUSTER_GET_FAIL=1 \"$REPO_ROOT/scripts/oke-discover.sh\" --cluster ocid1.cluster.oc1..abc 2>\"$TMPDIR_BASE/t3.err\")\"\n rc=$?\n set -e\n assert_eq \"0\" \"$rc\" \"oke-discover exits 0 with partial context\"\n assert_json_expr \"$out\" \"obj['cluster']['id'] == 'ocid1.cluster.oc1..abc'\" \"oke-discover preserves cluster id\"\n assert_json_expr \"$out\" \"obj['cluster']['name'] == 'ocid1.cluster.oc1..abc'\" \"oke-discover uses ref name on failure\"\n}\n\nrun_test_node_doctor_namespace() {\n echo \"- node-doctor uses namespace consistently and no -it\"\n local log_file out rc\n log_file=\"$TMPDIR_BASE/kubectl.log\"\n : > \"$log_file\"\n set +e\n out=\"$(MOCK_KUBECTL_LOG=\"$log_file\" \"$REPO_ROOT/scripts/node-doctor-run.sh\" --node n1 --image img --namespace kube-system --cleanup true 2>\"$TMPDIR_BASE/t4.err\")\"\n rc=$?\n set -e\n assert_eq \"0\" \"$rc\" \"node-doctor exits 0\"\n assert_json_expr \"$out\" \"obj['node_doctor_namespace'] == 'kube-system'\" \"node-doctor JSON namespace\"\n local logs\n logs=\"$(cat \"$log_file\")\"\n assert_contains \"$logs\" \"-n kube-system debug node/n1\" \"kubectl debug uses requested namespace\"\n assert_contains \"$logs\" \"-n kube-system delete pod nd-123\" \"cleanup uses same namespace\"\n if [[ \"$logs\" == *\" -it \"* ]]; then\n echo \"FAIL: kubectl debug should not include -it\" >&2\n exit 1\n fi\n}\n\nrun_test_gva_discover_quoted_cluster_name() {\n echo \"- gva-discover resolves cluster names containing single quotes\"\n local out rc cluster_list_json\n cluster_list_json='{\"data\":[{\"name\":\"prod'\\''cluster\",\"id\":\"ocid1.cluster.oc1..quoted\",\"kubernetes-version\":\"v1.31.1\"}]}'\n set +e\n out=\"$(MOCK_OCI_CLUSTER_LIST_JSON=\"$cluster_list_json\" \"$REPO_ROOT/scripts/gva-discover.sh\" --cluster \"prod'cluster\" --compartment-id ocid1.compartment.oc1..a --region us-ashburn-1 2>\"$TMPDIR_BASE/t5.err\")\"\n rc=$?\n set -e\n assert_eq \"0\" \"$rc\" \"gva-discover handles quoted cluster name\"\n assert_json_expr \"$out\" \"obj['cluster']['id'] == 'ocid1.cluster.oc1..quoted'\" \"quoted cluster resolved correctly\"\n}\n\nrun_test_gva_cli_resolve_uses_env_override() {\n echo \"- gva-cli-resolve honors OKE_GVA_CLI_HOME\"\n local custom_home out\n custom_home=\"$TMPDIR_BASE/custom-gva-cli\"\n mkdir -p \"$custom_home/bin\"\n touch \"$custom_home/bin/activate\"\n touch \"$custom_home/oci_cli-3.65.2+preview.1.1355-py2.py3-none-any.whl\"\n out=\"$(OKE_GVA_CLI_HOME=\"$custom_home\" \"$REPO_ROOT/scripts/gva-cli-resolve.sh\" --json)\"\n assert_json_expr \"$out\" \"obj['home'].endswith('/custom-gva-cli')\" \"gva-cli-resolve prefers env override\"\n assert_json_expr \"$out\" \"obj['has_activate'] is True\" \"gva-cli-resolve sees activate path\"\n assert_json_expr \"$out\" \"obj['has_wheel'] is True\" \"gva-cli-resolve sees wheel path\"\n if rg -q \"codex_oke_plugin|/Users/.*/Desktop/projects\" \"$REPO_ROOT/scripts/gva-cli-resolve.sh\"; then\n echo \"FAIL: gva-cli-resolve should not include workstation-specific fallback paths\" >&2\n exit 1\n fi\n}\n\nrun_test_gva_menu_rejects_invalid_ipcount() {\n echo \"- gva-menu rejects ipCount values outside 1..256\"\n local out rc\n set +e\n out=\"$(printf 'cluster-a\\nus-ashburn-1\\n\\nocid1.cluster.oc1..abc\\nGrCh:US-ASHBURN-AD-1\\nocid1.vcn.oc1..vcn\\nocid1.subnet.oc1..primary\\npool1\\nVM.Standard.E5.Flex\\n2\\n16\\n3\\nocid1.image.oc1..img\\n1\\nfrontend\\nocid1.subnet.oc1..secondary\\n257\\n256\\n\\nfrontend-vnic\\n2\\n\\ny\\n2\\n' | \"$REPO_ROOT/scripts/gva-menu.sh\" 2>\"$TMPDIR_BASE/t6.err\")\"\n rc=$?\n set -e\n assert_eq \"0\" \"$rc\" \"gva-menu still completes after retrying ipCount\"\n assert_contains \"$out\" \"ipCount must be an integer between 1 and 256.\" \"gva-menu warns on invalid ipCount\"\n assert_contains \"$out\" \"Using cluster Kubernetes version: v1.31.1\" \"gva-menu still consumes discovery output\"\n assert_contains \"$out\" \"--secondary-vnics\" \"gva-menu still prints command after correction\"\n}\n\nrun_test_gva_menu_cni_failure_json() {\n echo \"- gva-menu emits JSON for unsupported CNI\"\n local rc err\n set +e\n printf 'cluster-a\\nus-ashburn-1\\n\\nocid1.cluster.oc1..abc\\nGrCh:US-ASHBURN-AD-1\\nocid1.vcn.oc1..vcn\\nocid1.subnet.oc1..primary\\npool1\\nVM.Standard.E5.Flex\\n2\\n16\\n3\\nocid1.image.oc1..img\\n2\\n' | \\\n \"$REPO_ROOT/scripts/gva-menu.sh\" >/dev/null 2>\"$TMPDIR_BASE/t7.err\"\n rc=$?\n set -e\n assert_eq \"1\" \"$rc\" \"gva-menu exits expected error when CNI unsupported\"\n err=\"$(cat \"$TMPDIR_BASE/t7.err\")\"\n assert_json_expr \"$err\" \"obj['error_code'] == 'GVA_REQUIRES_VCN_NATIVE_CNI'\" \"gva-menu emits structured JSON error\"\n}\n\nrun_test_multihome_python_errors_are_json() {\n echo \"- multihome Python helpers emit JSON errors\"\n local rc err mock_bin\n set +e\n python3 \"$REPO_ROOT/skills/oke-multihome-deployer/scripts/generate-multihome-manifest.py\" \\\n --namespace default 1>/dev/null 2>\"$TMPDIR_BASE/t8.err\"\n rc=$?\n set -e\n assert_eq \"2\" \"$rc\" \"manifest generator exits 2 for invalid arguments\"\n err=\"$(cat \"$TMPDIR_BASE/t8.err\")\"\n assert_json_expr \"$err\" \"obj['error_code'] == 'INVALID_ARGUMENT'\" \"manifest generator emits JSON argparse error\"\n\n mock_bin=\"$TMPDIR_BASE/no-kube\"\n mkdir -p \"$mock_bin\"\n cat > \"$mock_bin/kubectl\" \u003c\u003c'MOCK_KUBECTL_FAIL'\n#!/usr/bin/env bash\nexit 1\nMOCK_KUBECTL_FAIL\n chmod +x \"$mock_bin/kubectl\"\n set +e\n PATH=\"$mock_bin:$PATH\" python3 \"$REPO_ROOT/skills/oke-multihome-deployer/scripts/discover-oke-multihome.py\" \\\n --cluster-name missing 1>/dev/null 2>\"$TMPDIR_BASE/t9.err\"\n rc=$?\n set -e\n assert_eq \"1\" \"$rc\" \"multihome discovery exits 1 for unresolved cluster\"\n err=\"$(cat \"$TMPDIR_BASE/t9.err\")\"\n assert_json_expr \"$err\" \"obj['error_code'] == 'CLUSTER_OCID_NOT_RESOLVED'\" \"multihome discovery emits JSON expected error\"\n\n mock_bin=\"$TMPDIR_BASE/no-oci\"\n mkdir -p \"$mock_bin\"\n set +e\n PATH=\"$mock_bin\" \"$(command -v python3)\" \"$REPO_ROOT/skills/oke-multihome-deployer/scripts/discover-oke-multihome.py\" \\\n --cluster-id ocid1.cluster.oc1..abc 1>/dev/null 2>\"$TMPDIR_BASE/t10.err\"\n rc=$?\n set -e\n assert_eq \"1\" \"$rc\" \"multihome discovery exits 1 when OCI CLI is missing\"\n err=\"$(cat \"$TMPDIR_BASE/t10.err\")\"\n assert_json_expr \"$err\" \"obj['error_code'] == 'OCI_CLI_NOT_FOUND'\" \"multihome discovery emits explicit missing OCI CLI error\"\n}\n\nrun_test_troubleshooter_skill_has_local_fallback() {\n echo \"- troubleshooter skill documents local fallback when delegation is unavailable\"\n local body\n body=\"$(cat \"$REPO_ROOT/skills/oke-troubleshooter/SKILL.md\")\"\n assert_contains \"$body\" \"Default to **local execution in the parent skill**.\" \"troubleshooter skill declares local execution default\"\n assert_contains \"$body\" 'If delegation is available, you may hand the payload to `oke-evidence-collector`.' \"troubleshooter skill treats evidence agent as optional\"\n assert_contains \"$body\" \"Otherwise rank hypotheses locally using this rubric:\" \"troubleshooter skill includes local ranking rubric\"\n}\n\nrun_test_troubleshooter_control_plane_recipe_uses_readyz() {\n echo \"- troubleshooter control-plane recipe uses readyz/livez instead of kubectl get cs\"\n local body\n body=\"$(cat \"$REPO_ROOT/skills/oke-troubleshooter/evidence-collectors.md\")\"\n assert_contains \"$body\" \"kubectl get --raw='/readyz?verbose'\" \"control-plane recipe uses readyz\"\n assert_contains \"$body\" \"kubectl get --raw='/livez?verbose'\" \"control-plane recipe uses livez\"\n if [[ \"$body\" == *\"kubectl get cs\"* ]]; then\n echo \"FAIL: control-plane recipe should not use deprecated kubectl get cs\" >&2\n exit 1\n fi\n}\n\nrun_test_multihome_manifest_generator() {\n echo \"- multihome manifest generator emits NADs and pinned pods\"\n local out\n out=\"$(python3 \"$REPO_ROOT/skills/oke-multihome-deployer/scripts/generate-multihome-manifest.py\" \\\n --namespace gva-multihome-test \\\n --default-interface enp1s0 \\\n --secondary-interface enp2s0 \\\n --pod gva-multihome-a=node-a \\\n --pod gva-multihome-b=node-b)\"\n assert_contains \"$out\" \"kind: NetworkAttachmentDefinition\" \"manifest includes NADs\"\n assert_contains \"$out\" \"type\\\": \\\"oci-ipvlan\\\"\" \"default NAD uses oci-ipvlan\"\n assert_contains \"$out\" \"type\\\": \\\"ipvlan\\\"\" \"secondary NAD uses ipvlan\"\n assert_contains \"$out\" \"v1.multus-cni.io/default-network: kube-system/gva-default-network\" \"pod uses default Multus annotation\"\n assert_contains \"$out\" \"k8s.v1.cni.cncf.io/networks: gva-multihome-test/gva-secondary-network\" \"pod uses secondary Multus annotation\"\n assert_contains \"$out\" \"nodeName: \\\"node-a\\\"\" \"pod is pinned to node-a\"\n assert_contains \"$out\" \"image: \\\"docker.io/nicolaka/netshoot:v0.13\\\"\" \"image is fully qualified\"\n}\n\nrun_test_multihome_python_syntax() {\n echo \"- multihome Python helpers compile\"\n python3 -m py_compile \\\n \"$REPO_ROOT/skills/oke-multihome-deployer/scripts/discover-oke-multihome.py\" \\\n \"$REPO_ROOT/skills/oke-multihome-deployer/scripts/generate-multihome-manifest.py\"\n}\n\nrun_test_golden_outputs() {\n echo \"- golden outputs match Terraform, GVA, and Multus examples\"\n local generated_manifest\n generated_manifest=\"$TMPDIR_BASE/gva-multihome-pods.yaml\"\n\n assert_file_matches \\\n \"$REPO_ROOT/tests/golden/oke-cluster-main.tf\" \\\n \"$REPO_ROOT/examples/outputs/oke-cluster-generator/main.tf\" \\\n \"cluster generator Terraform example changed\"\n\n assert_file_matches \\\n \"$REPO_ROOT/tests/golden/gva-node-pool-command.sh\" \\\n \"$REPO_ROOT/examples/outputs/oke-gva-deployer/node-pool-command.sh\" \\\n \"GVA command example changed\"\n\n python3 \"$REPO_ROOT/skills/oke-multihome-deployer/scripts/generate-multihome-manifest.py\" \\\n --namespace gva-multihome-test \\\n --default-interface enp1s0 \\\n --secondary-interface enp2s0 \\\n --pod gva-multihome-a=node-a \\\n --pod gva-multihome-b=node-b \\\n > \"$generated_manifest\"\n\n assert_file_matches \\\n \"$REPO_ROOT/tests/golden/gva-multihome-pods.yaml\" \\\n \"$generated_manifest\" \\\n \"generated Multus manifest changed\"\n\n assert_file_matches \\\n \"$REPO_ROOT/tests/golden/gva-multihome-pods.yaml\" \\\n \"$REPO_ROOT/examples/outputs/oke-multihome-deployer/gva-multihome-pods.yaml\" \\\n \"Multus manifest example changed\"\n}\n\nrun_test_skill_consistency_text() {\n echo \"- skill trigger and terminology consistency checks\"\n if rg -q \"Oracle Kubernetes Engine\" \"$REPO_ROOT/skills\"; then\n echo \"FAIL: use OCI Kubernetes Engine terminology in public skill text\" >&2\n exit 1\n fi\n\n local cluster_skill gva_skill multihome_skill troubleshooter_refs\n cluster_skill=\"$(cat \"$REPO_ROOT/skills/oke-cluster-generator/SKILL.md\")\"\n gva_skill=\"$(cat \"$REPO_ROOT/skills/oke-gva-deployer/SKILL.md\")\"\n multihome_skill=\"$(cat \"$REPO_ROOT/skills/oke-multihome-deployer/SKILL.md\")\"\n troubleshooter_refs=\"$(cat \"$REPO_ROOT/skills/oke-troubleshooter/evidence-collectors.md\")\"\n\n assert_contains \"$cluster_skill\" \"FAST_PATH_MODE = true\" \"cluster generator parses fast-path token\"\n assert_contains \"$cluster_skill\" \"runtime without \\`AskUserQuestion\\`\" \"cluster generator documents Codex prompt fallback\"\n assert_contains \"$cluster_skill\" \"TODO(live validation): confirm this add-on option command\" \"cluster generator flags unverified add-on command\"\n assert_contains \"$gva_skill\" \"TODO/live validation\" \"GVA skill labels unproven image compatibility\"\n assert_contains \"$multihome_skill\" \"For broad incident RCA\" \"multihome trigger avoids broad troubleshooter overlap\"\n assert_contains \"$multihome_skill\" \"TODO(live validation): pin this manifest\" \"multihome manifest pinning is tracked as live-validation TODO\"\n assert_contains \"$troubleshooter_refs\" \"Capacity and Service Limits\" \"troubleshooter avoids broad quota-only framing\"\n}\n\nrun_test_oke_troubleshooter_helpers() {\n echo \"- OKE troubleshooting helper scripts return JSON\"\n local out\n\n out=\"$(\"$REPO_ROOT/scripts/oke-addon-health.sh\")\"\n assert_json_expr \"$out\" \"obj['domain'] == 'OKE Add-ons Health'\" \"addon helper domain\"\n assert_json_expr \"$out\" \"obj['namespace'] == 'kube-system'\" \"addon helper namespace\"\n\n out=\"$(\"$REPO_ROOT/scripts/oke-pod-network-check.sh\" --namespace default --pod web-0)\"\n assert_json_expr \"$out\" \"obj['domain'] == 'Pod Networking / OCI CNI / IPAM'\" \"pod network helper domain\"\n assert_json_expr \"$out\" \"any('ipvlan' in item for item in obj['anomalies'])\" \"pod network helper surfaces ipvlan issue\"\n\n out=\"$(\"$REPO_ROOT/scripts/oke-autoscaler-check.sh\" --namespace default --deployment web --cluster-id ocid1.cluster.oc1..abc --compartment-id ocid1.compartment.oc1..a --region us-ashburn-1)\"\n assert_json_expr \"$out\" \"obj['domain'] == 'Cluster Autoscaler / Node Pool Scaling'\" \"autoscaler helper domain\"\n assert_json_expr \"$out\" \"any('NotTriggerScaleUp' in item or 'max node group size' in item for item in obj['anomalies'])\" \"autoscaler helper surfaces scale-up issue\"\n\n out=\"$(\"$REPO_ROOT/scripts/oke-dns-check.sh\" --namespace default --service web --pod web-0 --lookup web.default.svc.cluster.local)\"\n assert_json_expr \"$out\" \"obj['domain'] == 'DNS / Service Discovery'\" \"dns helper domain\"\n assert_json_expr \"$out\" \"any('timeout' in item.lower() for item in obj['anomalies'])\" \"dns helper surfaces timeout\"\n}\n\nrun_test_oke_deep_troubleshooter_helpers() {\n echo \"- deep OKE troubleshooting helper scripts return JSON\"\n local out\n\n out=\"$(\"$REPO_ROOT/scripts/oke-private-endpoint-check.sh\" --cluster-id ocid1.cluster.oc1..abc --region us-ashburn-1 --compartment-id ocid1.compartment.oc1..a)\"\n assert_json_expr \"$out\" \"obj['domain'] == 'Private Cluster / API Endpoint Connectivity'\" \"private endpoint helper domain\"\n assert_json_expr \"$out\" \"any('readyz' in item.lower() or 'endpoint' in item.lower() for item in obj['raw_snippets'])\" \"private endpoint helper captures API signals\"\n\n out=\"$(\"$REPO_ROOT/scripts/oke-ocir-image-pull-check.sh\" --namespace default --pod web-0 --image iad.ocir.io/ns/team/web:bad --compartment-id ocid1.compartment.oc1..a --region us-ashburn-1)\"\n assert_json_expr \"$out\" \"obj['domain'] == 'OCIR / Image Pull'\" \"ocir helper domain\"\n assert_json_expr \"$out\" \"any('unauthorized' in item.lower() for item in obj['anomalies'])\" \"ocir helper surfaces unauthorized pull\"\n\n out=\"$(\"$REPO_ROOT/scripts/oke-workload-identity-check.sh\" --namespace default --serviceaccount workload-sa --pod web-0 --tenancy-id ocid1.tenancy.oc1..tenancy)\"\n assert_json_expr \"$out\" \"obj['domain'] == 'Workload Identity / OCI API From Pods'\" \"workload identity helper domain\"\n assert_json_expr \"$out\" \"any('notauthorized' in item.lower() or 'policy' in item.lower() for item in obj['anomalies'])\" \"workload identity helper surfaces auth issue\"\n\n out=\"$(\"$REPO_ROOT/scripts/oke-ingress-check.sh\" --namespace default --ingress web --region us-ashburn-1)\"\n assert_json_expr \"$out\" \"obj['domain'] == 'Ingress / OCI Native Ingress'\" \"ingress helper domain\"\n assert_json_expr \"$out\" \"any('certificate' in item.lower() or 'backend' in item.lower() for item in obj['anomalies'])\" \"ingress helper surfaces ingress issue\"\n\n out=\"$(\"$REPO_ROOT/scripts/oke-incident-timeline.sh\" --namespace default --pod web-0 --deployment web --service web --compartment-id ocid1.compartment.oc1..a --region us-ashburn-1)\"\n assert_json_expr \"$out\" \"obj['domain'] == 'Incident Timeline'\" \"timeline helper domain\"\n assert_json_expr \"$out\" \"len(obj['timeline']) >= 1\" \"timeline helper emits events\"\n}\n\nrun_test_oke_object_correlator_storage_providerid() {\n echo \"- object correlator links storage and node placement via providerID\"\n local out\n\n out=\"$(\"$REPO_ROOT/scripts/oke-object-correlator.sh\" \\\n --namespace default \\\n --cluster-id ocid1.cluster.oc1..abc \\\n --compartment-id ocid1.compartment.oc1..a \\\n --region us-ashburn-1 \\\n --pod storage-pod)\"\n\n assert_json_expr \"$out\" \"obj['fallback_used'] is False\" \"object correlator storage path has no fallback\"\n assert_json_expr \"$out\" \"any(e['from'] == 'k8s:pod:default/storage-pod' and e['to'] == 'k8s:pvc:default/data' and e['relation'] == 'mounts_claim' for e in obj['graph']['edges'])\" \"pod mounts PVC edge\"\n assert_json_expr \"$out\" \"any(e['from'] == 'k8s:pvc:default/data' and e['to'] == 'k8s:pv:pv-data' and e['relation'] == 'bound_to' for e in obj['graph']['edges'])\" \"PVC bound to PV edge\"\n assert_json_expr \"$out\" \"any(e['from'] == 'k8s:pv:pv-data' and e['to'] == 'oci:volume:ocid1.volume.oc1..vol' and e['relation'] == 'backs_onto' for e in obj['graph']['edges'])\" \"PV backs block volume edge\"\n assert_json_expr \"$out\" \"any(e['from'] == 'k8s:pod:default/storage-pod' and e['to'] == 'k8s:node:node-no-annotation' and e['relation'] == 'scheduled_on' for e in obj['graph']['edges'])\" \"pod scheduled on node edge\"\n assert_json_expr \"$out\" \"any(e['from'] == 'k8s:node:node-no-annotation' and e['to'] == 'oci:instance:ocid1.instance.oc1..inst' and e['relation'] == 'runs_on_instance' and e.get('evidence') == 'node.spec.providerID' for e in obj['graph']['edges'])\" \"node providerID maps to OCI instance edge\"\n assert_json_expr \"$out\" \"any(e['from'] == 'oci:volume:ocid1.volume.oc1..vol' and e['to'] == 'oci:instance:ocid1.instance.oc1..inst' and e['relation'] == 'attached_to_instance' for e in obj['graph']['edges'])\" \"volume attachment maps block volume to instance\"\n assert_json_expr \"$out\" \"sum(1 for e in obj['graph']['edges'] if e['from'] == 'oci:instance:ocid1.instance.oc1..inst' and e['relation'] == 'has_vnic') == 2\" \"object correlator maps all VNIC attachments\"\n assert_json_expr \"$out\" \"any(e['from'] == 'oci:vnic:ocid1.vnic.oc1..secondary' and e['to'] == 'oci:subnet:ocid1.subnet.oc1..secondary' and e['relation'] == 'attached_to_subnet' for e in obj['graph']['edges'])\" \"secondary VNIC maps to secondary subnet\"\n assert_json_expr \"$out\" \"any(e['from'] == 'oci:vnic:ocid1.vnic.oc1..secondary' and e['to'] == 'oci:nsg:ocid1.networksecuritygroup.oc1..secondary' and e['relation'] == 'uses_nsg' for e in obj['graph']['edges'])\" \"secondary VNIC maps to NSG\"\n assert_json_expr \"$out\" \"any(e['from'] == 'oci:subnet:ocid1.subnet.oc1..secondary' and e['to'] == 'oci:security-list:ocid1.securitylist.oc1..secondary' and e['relation'] == 'uses_security_list' for e in obj['graph']['edges'])\" \"secondary subnet maps to security list\"\n assert_json_expr \"$out\" \"any(e['from'] == 'oci:subnet:ocid1.subnet.oc1..secondary' and e['to'] == 'oci:route-table:ocid1.routetable.oc1..secondary' and e['relation'] == 'uses_route_table' for e in obj['graph']['edges'])\" \"secondary subnet maps to route table\"\n assert_json_expr \"$out\" \"any(e['from'] == 'oci:route-table:ocid1.routetable.oc1..secondary' and e['to'] == 'oci:network-entity:ocid1.localpeeringgateway.oc1..lpg' and e['relation'] == 'routes_to' for e in obj['graph']['edges'])\" \"route table maps to peering path\"\n assert_json_expr \"$out\" \"any(n['id'] == 'oci:instance:ocid1.instance.oc1..inst' and n['type'] == 'oci.compute.instance' for n in obj['graph']['oci'])\" \"OCI compute instance node exists\"\n}\n\nmain() {\n TMPDIR_BASE=\"$(mktemp -d)\"\n if [[ \"${KEEP_TMPDIR:-0}\" == \"1\" ]]; then\n echo \"KEEP_TMPDIR at: $TMPDIR_BASE\"\n else\n trap 'rm -rf \"$TMPDIR_BASE\"' EXIT\n fi\n\n make_mocks \"$TMPDIR_BASE/mockbin\"\n with_temp_home \"$TMPDIR_BASE/home\"\n\n export PATH=\"$TMPDIR_BASE/mockbin:$PATH\"\n export HOME=\"$TMPDIR_BASE/home\"\n\n run_test_preflight\n run_test_gva_discover_cluster_get_failure\n run_test_oke_discover_cluster_get_failure\n run_test_node_doctor_namespace\n run_test_gva_discover_quoted_cluster_name\n run_test_gva_cli_resolve_uses_env_override\n run_test_gva_menu_rejects_invalid_ipcount\n run_test_gva_menu_cni_failure_json\n run_test_multihome_python_errors_are_json\n run_test_troubleshooter_skill_has_local_fallback\n run_test_troubleshooter_control_plane_recipe_uses_readyz\n run_test_multihome_manifest_generator\n run_test_multihome_python_syntax\n run_test_golden_outputs\n run_test_skill_consistency_text\n run_test_oke_troubleshooter_helpers\n run_test_oke_deep_troubleshooter_helpers\n run_test_oke_object_correlator_storage_providerid\n\n echo \"All smoke tests passed.\"\n}\n\nmain \"$@\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":37952,"content_sha256":"9d32b7f10e3642470914992cda941f7ba12d85354b0f2edb3e45977b19c26501"},{"filename":"oke/troubleshooting.md","content":"# OKE Troubleshooting\n\n## Overview\n\nUse this skill to troubleshoot OCI Kubernetes Engine (OKE) workload and cluster symptoms. Start with read-only evidence collection, map Kubernetes objects to OCI resources where possible, then rank likely causes before recommending fixes.\n\nUse this for symptoms such as Pending pods, unhealthy add-ons, CNI failures, DNS issues, LoadBalancer or Ingress problems, OCIR image pulls, Workload Identity errors, private endpoint access failures, storage issues, and node-pool scaling failures.\n\nFor full operational behavior, load `oci/oke/skills/oke-troubleshooter/SKILL.md` and its supporting files before collecting evidence. That skill preserves the OKE troubleshooting workflow, including symptom triage, evidence collectors, Kubernetes-to-OCI object correlation, optional helper agents, and final report structure.\n\nSupporting tools and references:\n\n- `oci/oke/scripts/oke-discover.sh`\n- `oci/oke/scripts/oke-object-correlator.sh`\n- `oci/oke/scripts/oke-addon-health.sh`\n- `oci/oke/scripts/oke-pod-network-check.sh`\n- `oci/oke/scripts/oke-autoscaler-check.sh`\n- `oci/oke/scripts/oke-dns-check.sh`\n- `oci/oke/scripts/oke-ingress-check.sh`\n- `oci/oke/scripts/oke-private-endpoint-check.sh`\n- `oci/oke/scripts/oke-ocir-image-pull-check.sh`\n- `oci/oke/scripts/oke-workload-identity-check.sh`\n- `oci/oke/scripts/oke-incident-timeline.sh`\n- `oci/oke/scripts/node-doctor-run.sh`\n- `oci/oke/skills/oke-troubleshooter/evidence-collectors.md`\n- `oci/oke/skills/oke-troubleshooter/symptom-triage.md`\n\n## Tool Use\n\nUse the included tools to collect evidence instead of relying only on ad hoc commands:\n\n| Situation | Tool | When to run |\n|-----------|------|-------------|\n| Start of troubleshooting | `oci/oke/scripts/oke-discover.sh` | Run after the user identifies the target cluster and region. Use it to resolve cluster OCID, compartment, Kubernetes version, and deployment hints. |\n| Object relationship mapping | `oci/oke/scripts/oke-object-correlator.sh` | Run after namespace and at least one target object are known. Use it to map pods, nodes, Services, Ingresses, PVCs, and OCI resources. |\n| Add-on symptoms | `oci/oke/scripts/oke-addon-health.sh` | Run for unhealthy kube-system add-ons such as CoreDNS, CNI, CSI, metrics, or controllers. |\n| Pod networking, CNI, Multus, sandbox errors | `oci/oke/scripts/oke-pod-network-check.sh` | Run for `FailedCreatePodSandBox`, missing `network-status`, OCI CNI/IPAM, Multus, or pod interface symptoms. |\n| Pending pods or autoscaler failures | `oci/oke/scripts/oke-autoscaler-check.sh` | Run when pods are Pending, Cluster Autoscaler is not scaling, or node-pool capacity is suspect. |\n| DNS or service discovery failures | `oci/oke/scripts/oke-dns-check.sh` | Run for DNS timeout, `NXDOMAIN`, `SERVFAIL`, or service resolution issues. |\n| Ingress failures | `oci/oke/scripts/oke-ingress-check.sh` | Run for OCI Native Ingress controller, route, listener, certificate, or backend health issues. |\n| Private API endpoint failures | `oci/oke/scripts/oke-private-endpoint-check.sh` | Run for private cluster API connectivity, kubeconfig, endpoint, or auth path symptoms. |\n| OCIR image pull failures | `oci/oke/scripts/oke-ocir-image-pull-check.sh` | Run for `ImagePullBackOff`, `ErrImagePull`, unauthorized pulls, or repository path issues. |\n| Workload Identity failures | `oci/oke/scripts/oke-workload-identity-check.sh` | Run when pods cannot call OCI APIs using OKE Workload Identity. |\n| Timeline reconstruction | `oci/oke/scripts/oke-incident-timeline.sh` | Run after target objects are known to merge Kubernetes events, rollout history, object descriptions, and OCI alarms. |\n| Node-level diagnostics | `oci/oke/scripts/node-doctor-run.sh` | Run only after explicit approval because it uses `kubectl debug` to create a temporary debug pod and may delete it during cleanup. |\n\nExample discovery:\n\n```bash\nbash oci/oke/scripts/oke-discover.sh --cluster \u003ccluster-name-or-ocid> --region \u003cregion>\n```\n\nExample object correlation:\n\n```bash\nbash oci/oke/scripts/oke-object-correlator.sh \\\n --namespace \u003cnamespace> \\\n --cluster-id \u003ccluster_ocid> \\\n --compartment-id \u003ccompartment_ocid> \\\n --region \u003cregion> \\\n --pod \u003cpod-name>\n```\n\nExample symptom-specific check:\n\n```bash\nbash oci/oke/scripts/oke-pod-network-check.sh --namespace \u003cnamespace> --pod \u003cpod-name>\n```\n\nTreat these tool outputs as evidence. Summarize findings, anomalies, raw snippets, and fallback flags in the diagnosis report.\n\nAsk for explicit approval before running `node-doctor-run.sh`.\n\n## Safety Rules\n\n- Run read-only commands first.\n- Ask before changing OCI or Kubernetes resources.\n- Do not restart, scale, drain, delete, patch, apply, or update resources without explicit user approval.\n- Keep the investigation scoped to the user-provided cluster, namespace, and workload.\n- If `kubectl` or OCI CLI access is unavailable, continue with partial evidence and clearly mark the gap.\n\n## Intake\n\nCollect or infer:\n\n| Field | Examples |\n|-------|----------|\n| Symptom | `pods stuck Pending`, `service has no IP`, `ImagePullBackOff` |\n| Cluster | OKE cluster name, OCID, or kubeconfig context |\n| Region | `us-ashburn-1` |\n| Namespace | `default`, `prod`, `payments` |\n| Workload | Deployment, Pod, Service, Ingress, PVC, or ServiceAccount |\n| Time window | `15m`, `1h`, `24h` |\n| Impact | production, lower environment, single workload, cluster-wide |\n\nConfirm the active kubeconfig context before collecting evidence:\n\n```bash\nkubectl config current-context\nkubectl config get-contexts\n```\n\nIf the context does not match the target cluster, stop and ask the user to switch context or provide the correct kubeconfig.\n\n## Symptom Routing\n\n| Symptom | Primary evidence |\n|---------|------------------|\n| Pods Pending or Unschedulable | Pod events, node capacity, taints/tolerations, node selectors, autoscaler logs, OCI limits |\n| ImagePullBackOff or ErrImagePull | Pod events, image path, imagePullSecrets, service account, OCIR repository and IAM policy |\n| CrashLoopBackOff or OOMKilled | Pod logs, previous logs, resource requests/limits, node pressure |\n| CNI or pod sandbox failures | Pod events, kube-system CNI pods, OCI VCN-native CNI logs, subnet IP capacity, Multus NADs, all node VNICs, NSGs, security lists, route tables, gateways, and peering paths |\n| Service has no LoadBalancer IP | Service events, OCI load balancer state, subnets, NSGs, security lists, route tables, gateways, peering paths, and quota |\n| Ingress failure | Ingress events, OCI Native Ingress controller logs, listener/certificate/backend health, subnets, NSGs, security lists, route tables, gateways, and peering paths |\n| DNS timeout or NXDOMAIN | CoreDNS health, Service and EndpointSlice state, pod lookup tests |\n| PVC Pending or attach failure | PVC/PV events, CSI controller logs, volume AD, node AD, block volume limits |\n| Workload cannot call OCI API | ServiceAccount, Workload Identity policy, SDK provider, namespace and cluster identity |\n| Private API endpoint unreachable | Kubeconfig, endpoint subnet, NSGs, security lists, route tables, gateways, peering paths, bastion, or operator host path |\n\n## Evidence Collection Pattern\n\nStart broad, then narrow.\n\n1. Confirm client tools:\n\n```bash\nkubectl version --client\noci --version\n```\n\n2. Capture cluster and namespace state:\n\n```bash\nkubectl get nodes -o wide\nkubectl get pods -A\nkubectl get events -A --sort-by=.lastTimestamp\nkubectl get deploy,rs,pod,svc,ingress,pvc -n \u003cnamespace> -o wide\n```\n\n3. Inspect the failing object:\n\n```bash\nkubectl describe pod \u003cpod> -n \u003cnamespace>\nkubectl logs \u003cpod> -n \u003cnamespace> --previous\nkubectl describe service \u003cservice> -n \u003cnamespace>\nkubectl describe ingress \u003cingress> -n \u003cnamespace>\nkubectl describe pvc \u003cpvc> -n \u003cnamespace>\n```\n\n4. Check OKE system components:\n\n```bash\nkubectl get pods -n kube-system -o wide\nkubectl get daemonset,deployment -n kube-system\n```\n\n5. Use OCI CLI for matching OCI-side state when the cluster OCID, compartment OCID, and region are known:\n\n```bash\noci ce cluster get --cluster-id \u003ccluster_ocid> --region \u003cregion>\noci ce node-pool list --compartment-id \u003ccompartment_ocid> --cluster-id \u003ccluster_ocid> --region \u003cregion>\n```\n\nOnly run broader OCI inventory commands when they are needed to explain the Kubernetes symptom.\n\n## Kubernetes to OCI Correlation\n\nWhen enough selectors are known, map Kubernetes objects to OCI resources:\n\n| Kubernetes object | OCI resource to check |\n|-------------------|-----------------------|\n| Node | Compute instance, all VNIC attachments including primary and secondary VNICs, attached subnets, VNIC NSGs, subnet security lists, route tables, gateways or peering paths, node pool |\n| Service type `LoadBalancer` | OCI Load Balancer, backend set, listeners, subnets, NSGs, security lists, route tables, gateways or peering path |\n| Ingress | OCI Load Balancer resources managed by the ingress controller, subnets, NSGs, security lists, route tables, gateways or peering path |\n| PVC/PV | Block Volume or File Storage resource and attachment state |\n| Pod with VCN-native networking | Pod subnet IP capacity, OCI CNI allocation path, NSGs, security lists, route table, gateways or peering path |\n| Pod using Workload Identity | Cluster, namespace, service account, IAM policy conditions |\n\nUse this correlation to avoid treating every symptom as only a Kubernetes problem.\n\n## Diagnosis Report\n\nPresent findings in this order:\n\n1. Confirmed context:\n - Cluster\n - Region\n - Namespace\n - Workload\n - Time window\n\n2. Top findings:\n - Evidence\n - Why it matters\n - Confidence\n\n3. Likely causes:\n - Rank from most likely to least likely.\n - Link each cause to the evidence that supports it.\n\n4. Recommended actions:\n - Separate read-only validation from mutating remediation.\n - Ask before any remediation command.\n\n5. Residual risks:\n - Missing permissions\n - Missing logs\n - Time-window gaps\n - OCI visibility gaps\n\n## Common Mistakes\n\n- Troubleshooting the current kubeconfig context before confirming it is the target OKE cluster.\n- Treating a Pending pod as only a Kubernetes scheduler issue without checking node-pool limits, OCI quota, and shape availability.\n- Debugging LoadBalancer Services without checking the OCI load balancer, backend health, subnets, NSGs, and service events together.\n- Assuming DNS is broken before checking Service selectors and EndpointSlices.\n- Rotating image pull secrets before confirming the image path, region key, OCIR namespace, and repository permissions.\n- Mixing Workload Identity with dynamic group assumptions. OKE Workload Identity uses workload identity policy conditions, not dynamic groups.\n- Applying fixes before preserving the events and logs needed to explain the incident.\n- Checking NSGs but forgetting subnet security lists, route tables, gateways, and peering paths.\n\n## Sources\n\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/home.htm\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengdownloadkubeconfigfile.htm\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengpodnetworking.htm\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengpodnetworking_topic-OCI_CNI_plugin.htm\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringclusteraddons.htm\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contenggrantingworkloadaccesstoresources.htm\n- https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengAttaching_Multiple_VNICs.htm\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11511,"content_sha256":"9c2b37ae20b7408d9704b20191b9ffa1a71eb7915ee49d99453fbc1581d138ee"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Oracle Cloud Infrastructure Skills","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this domain for practical Oracle Cloud Infrastructure guidance. Current content covers OCI Kubernetes Engine (OKE): cluster design, operational troubleshooting, Generic VNIC Attachment (GVA), and Multus multi-interface pod validation. It covers OCI Internet of Things Platform resource discovery, digital twin lifecycle workflows, device publish flows, and optional MCP-assisted operation. It covers OCI Functions local deployment and diagnosis-first troubleshooting. It includes a constrained Terraform Configuration generator for OCI Security Zone recipes and Security Zones. It also includes a constrained Terraform Configuration generator for OCI Exadata Database Service on Dedicated Infrastructure. It also includes a constrained Terraform Configuration generator for OCI IAM compartments, groups, dynamic groups, policies, and Identity Domains resources. It also includes a constrained Terraform configuration generator for OCI networking resources. It also covers Enterprise AI because that work is built around OCI Generative AI, OCI networking, IAM, cost estimation, hosted applications, and OCI platform integrations.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"How to Use This Domain","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Start with the routing table below.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read only the smallest service file or nested skill that matches the user's task.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer official Oracle documentation and live read-only discovery commands before making design or remediation recommendations.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask before running commands that create, update, delete, restart, scale, drain, or otherwise mutate OCI or Kubernetes resources.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep OCI-owned design decisions here, and route database-owned SQL, vector, and Select AI implementation details to ","type":"text"},{"text":"db/features/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Directory Structure","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"oci/\n├── SKILL.md\n├── enterprise-ai/\n│ ├── SKILL.md\n│ ├── models/\n│ ├── agent-workflows/\n│ ├── governance/\n│ ├── data/\n│ ├── cost/\n│ └── integrations/\n├── functions/\n│ ├── oci-functions-deploy/\n│ │ ├── SKILL.md\n│ │ ├── agents/\n│ │ ├── references/\n│ │ ├── scripts/\n│ │ └── tests/\n│ └── oci-functions-troubleshoot/\n│ ├── SKILL.md\n│ ├── agents/\n│ └── references/\n├── iot-platform/\n│ ├── SKILL.md\n│ ├── agents/\n│ ├── references/\n│ ├── scripts/\n│ ├── templates/\n│ └── tests/\n├── oci-landing-zone-terraform-module/\n│ ├── terraform-oci-module-security-zone-skill/\n│ │ ├── SKILL.md\n│ │ ├── agents/\n│ │ └── references/\n│ ├── terraform-oci-modules-exadata-database-skill/\n│ │ ├── SKILL.md\n│ │ ├── agents/\n│ │ └── references/\n│ ├── terraform-oci-modules-iam-skill/\n│ │ ├── SKILL.md\n│ │ ├── agents/\n│ │ └── references/\n│ └── terraform-oci-modules-networking-skill/\n│ ├── SKILL.md\n│ ├── agents/\n│ └── references/\n└── oke/\n ├── cluster-design.md\n ├── troubleshooting.md\n ├── gva-node-pools.md\n ├── multus-multihome.md\n ├── skills/\n ├── scripts/\n ├── agents/\n ├── shared/\n ├── examples/\n └── tests/","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Category Routing","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Topic","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Start With","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Design or scaffold an OKE cluster, Terraform stack, or OCI Resource Manager stack","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Start with ","type":"text"},{"text":"oci/oke/cluster-design.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", then load ","type":"text"},{"text":"oci/oke/skills/oke-cluster-generator/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Troubleshoot OKE workloads, pods, services, DNS, add-ons, ingress, load balancers, image pulls, storage, Workload Identity, or cluster access","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Start with ","type":"text"},{"text":"oci/oke/troubleshooting.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", then load ","type":"text"},{"text":"oci/oke/skills/oke-troubleshooter/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Configure OKE managed node pools with Generic VNIC Attachment secondary VNIC profiles and Application Resources","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Start with ","type":"text"},{"text":"oci/oke/gva-node-pools.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", then load ","type":"text"},{"text":"oci/oke/skills/oke-gva-deployer/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Deploy or validate Multus NetworkAttachmentDefinitions and multi-interface pods on OKE","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Start with ","type":"text"},{"text":"oci/oke/multus-multihome.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", then load ","type":"text"},{"text":"oci/oke/skills/oke-multihome-deployer/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Deploy an OCI Function from a local macOS or Linux workstation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/functions/oci-functions-deploy/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Troubleshoot OCI Functions setup, deployment, invocation, or observability","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/functions/oci-functions-troubleshoot/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate or update Terraform Configuration for OCI Security Zone recipes or Security Zones","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-module-security-zone-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate or update Terraform Configuration for OCI Exadata Database Service on Dedicated Infrastructure","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-exadata-database-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate or update Terraform Configuration for OCI IAM compartments, groups, dynamic groups, policies, or Identity Domains","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-iam-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate or update Terraform configuration for OCI VCNs, subnets, routes, gateways, security lists, NSGs, or ZPR security-attribute assignments","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-networking-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OCI IoT domains, domain groups, digital twin models, adapters, instances, relationships, raw commands, Data API access, or HTTPS publish flows","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/iot-platform/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OCI Generative AI models, custom/imported models, endpoints, or private endpoints","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OCI Responses API agents, tools, memory, File Search, Code Interpreter, MCP, or SQL Search","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OCI Generative AI and OCI Generative AI Agents cost estimation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/cost/cost-estimation.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OCI Enterprise AI governance, IAM, API keys, OAuth, guardrails, or ZPR","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/governance/private-endpoints-and-governance.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Key Starting Points","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oke/cluster-design.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oke/troubleshooting.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oke/gva-node-pools.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oke/multus-multihome.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/functions/oci-functions-deploy/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/functions/oci-functions-troubleshoot/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/functions/oci-functions-deploy/references/oci-functions-quickstart.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/functions/oci-functions-troubleshoot/references/error-patterns.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-module-security-zone-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-exadata-database-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-iam-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-networking-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/iot-platform/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/iot-platform/references/cli-workflows.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/iot-platform/references/mcp-optional-use.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/SKILL.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/models/enterprise-ai-models.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/agent-workflows/responses-api-agents.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/cost/cost-estimation.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/enterprise-ai/governance/private-endpoints-and-governance.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Operational Tools","type":"text"}]},{"type":"paragraph","content":[{"text":"The OKE operational skills include deterministic helper tools under ","type":"text"},{"text":"oci/oke/scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":" and skill-specific helper scripts under ","type":"text"},{"text":"oci/oke/skills/*/scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read-only discovery and evidence tools may be used to collect context.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate-only tools may produce manifests, commands, Terraform snippets, or reports.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Any tool or command that creates, updates, deletes, patches, restarts, scales, drains, debugs, assigns IPs, applies manifests, or otherwise mutates OCI or Kubernetes resources requires explicit user approval first.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oke/scripts/gva-menu.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" is allowed to create an OKE node pool for the GVA workflow only after the user approves execution and completes its final ","type":"text"},{"text":"CREATE","type":"text","marks":[{"type":"code_inline"}]},{"text":" confirmation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oci/oke/scripts/node-doctor-run.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" requires approval before execution because it creates a temporary debug pod and may delete that pod during cleanup.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Multi-Step Flows","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Task","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Recommended Sequence","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Plan a production OKE cluster","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oke/cluster-design.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Diagnose an OKE service with no load balancer IP","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oke/troubleshooting.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Build a node pool with workload-specific secondary VNIC profiles","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oke/gva-node-pools.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"oke/multus-multihome.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" if pods need multiple interfaces","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Validate Multus pod networking on GVA-enabled nodes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oke/multus-multihome.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"oke/troubleshooting.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" if symptoms remain","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Investigate OKE workload access to OCI APIs","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oke/troubleshooting.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Deploy a local function","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"functions/oci-functions-deploy/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> preflight -> Fn context validation -> OCIR auth check -> app selection -> scaffold -> deploy","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Troubleshoot a failed function deploy","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"functions/oci-functions-troubleshoot/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"functions/oci-functions-troubleshoot/references/error-patterns.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"functions/oci-functions-troubleshoot/references/deploy.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Troubleshoot function invocation failures","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"functions/oci-functions-troubleshoot/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"functions/oci-functions-troubleshoot/references/invoke.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> logs, traces, metrics, and limits","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate OCI Security Zone Terraform Configuration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci-landing-zone-terraform-module/terraform-oci-module-security-zone-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> validate inputs and authorization read-only -> generate ","type":"text"},{"text":"main.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"variables.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"terraform.tfvars","type":"text","marks":[{"type":"code_inline"}]},{"text":" without running Terraform","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate OCI Exadata Database Service Terraform Configuration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci-landing-zone-terraform-module/terraform-oci-modules-exadata-database-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> validate only requested-object inputs read-only -> generate ","type":"text"},{"text":"main.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"variables.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"terraform.tfvars","type":"text","marks":[{"type":"code_inline"}]},{"text":" without running Terraform","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate OCI IAM Terraform Configuration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci-landing-zone-terraform-module/terraform-oci-modules-iam-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> determine supported module -> collect and validate inputs -> generate ","type":"text"},{"text":"main.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"variables.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"terraform.tfvars","type":"text","marks":[{"type":"code_inline"}]},{"text":" without running Terraform","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate OCI networking Terraform configuration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oci-landing-zone-terraform-module/terraform-oci-modules-networking-skill/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> determine support -> collect and validate inputs -> generate a compatible configuration change without running Terraform","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Explore or update OCI IoT digital twin resources","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"iot-platform/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"iot-platform/references/cli-workflows.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"iot-platform/references/resilience-guidance.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Publish test telemetry to an OCI IoT twin","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"iot-platform/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"iot-platform/references/cli-workflows.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"iot-platform/templates/publish-curl.template.sh","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Build a governed enterprise assistant","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"enterprise-ai/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"enterprise-ai/agent-workflows/agent-tools.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"enterprise-ai/data/rag-and-search.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> ","type":"text"},{"text":"enterprise-ai/governance/private-endpoints-and-governance.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scope Boundaries","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep OCI service, networking, IAM, agent hosting, and cost-estimation guidance in this domain.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Route OCI Security Zone recipes and Security Zone Terraform configuration to ","type":"text"},{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-module-security-zone-skill/","type":"text","marks":[{"type":"code_inline"}]},{"text":"; it is generate-only and does not deploy or manage other Cloud Guard, IAM, or network resources.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Route Exadata Database Service Terraform configuration to ","type":"text"},{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-exadata-database-skill/","type":"text","marks":[{"type":"code_inline"}]},{"text":"; it supports only Cloud Exadata Infrastructure, Cloud VM Clusters, DB Homes, Container Databases, and Pluggable Databases, and does not create IAM or network prerequisites.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Route OCI IAM Terraform configuration to ","type":"text"},{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-iam-skill/","type":"text","marks":[{"type":"code_inline"}]},{"text":"; it supports only the upstream compartments, groups, dynamic groups, policies, and Identity Domains modules, and does not create standalone classic users or memberships.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Route OCI networking Terraform configuration to ","type":"text"},{"text":"oci/oci-landing-zone-terraform-module/terraform-oci-modules-networking-skill/","type":"text","marks":[{"type":"code_inline"}]},{"text":"; it supports only ","type":"text"},{"text":"oci-landing-zones/terraform-oci-modules-networking","type":"text","marks":[{"type":"code_inline"}]},{"text":" and does not manage ZPR policies.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Route OCI IoT domain, digital twin, adapter, device publish, raw command, and Data API workflows to ","type":"text"},{"text":"oci/iot-platform/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Route Oracle Database-owned implementation details to ","type":"text"},{"text":"db/features/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Route APEX artifact generation to ","type":"text"},{"text":"apex/apexlang/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer official Oracle documentation for OCI service limits, IAM verbs, endpoint formats, regions, and pricing inputs because these change frequently.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Sources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/ContEng/home.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengAttaching_Multiple_VNICs.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contenggrantingworkloadaccesstoresources.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://github.com/oracle-terraform-modules/terraform-oci-oke","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://github.com/oci-landing-zones/terraform-oci-modules-oracle-database/tree/main/exadata-database","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://github.com/oci-landing-zones/terraform-oci-modules-iam","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://github.com/oci-landing-zones/terraform-oci-modules-networking","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/internet-of-things/home.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://github.com/oracle-samples/oci-iot-samples","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/generative-ai/overview.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/generative-ai/home.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/Functions/home.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/Functions/Tasks/functionsquickstartlocalhost.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/Functions/Tasks/functionscreatingapps.htm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"https://docs.oracle.com/en-us/iaas/Content/Functions/Tasks/functionstroubleshooting.htm","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"oci","author":"@skillopedia","source":{"stars":910,"repo_name":"skills","origin_url":"https://github.com/oracle/skills/blob/HEAD/oci/SKILL.md","repo_owner":"oracle","body_sha256":"7663f82396eb1d38244c2e2432e8923133c1aba03ae5ed884b5f563f4d503174","cluster_key":"b6bde7f0e05ce3aa99701d184e6b945be25bfdbcdf39249b8a1397922a7c6e7f","clean_bundle":{"format":"clean-skill-bundle-v1","source":"oracle/skills/oci/SKILL.md","text_bytes":253941,"source_commit":"0779f030fdee393ee418a3a76f6a4b09bf1408db","skills_sh_rank":19129,"attachment_count":36,"text_attachments":36,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"oci/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"devops-infrastructure","category_label":"DevOps"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"devops-infrastructure","import_tag":"skills-sh-top20k-2026-11","description":"Oracle Cloud Infrastructure guidance for designing, operating, and troubleshooting OCI services, including OCI Kubernetes Engine (OKE), OCI Internet of Things Platform, OCI Functions deployment and troubleshooting, OCI Exadata Database Service Terraform configuration, OCI IAM Terraform configuration, OCI networking Terraform configuration, OCI Security Zones Terraform configuration, and Enterprise AI workflows for OCI Generative AI models, Responses API agents, RAG, cost estimation, governance, private endpoints, hosted agentic applications, and Oracle platform integrations. Use when the user asks about OKE cluster design, Terraform or Resource Manager planning, OCI Security Zone recipes or Security Zones, OCI compartment hierarchy, classic IAM groups or group membership assignment, dynamic groups, IAM policies, OCI Identity Domains resources, Cloud Exadata Infrastructure, Cloud VM Clusters, DB Homes, Container Databases, Pluggable Databases, OCI VCNs, subnets, routes, gateways, security lists, NSGs, ZPR security-attribute assignments, OKE incident troubleshooting, Generic VNIC Attachment, Multus, pod networking, node pools, add-ons, ingress, load balancers, OCIR image pulls, Workload Identity, Kubernetes workloads on OCI, OCI IoT domains or digital twins, device publish flows, OCI Functions setup, deployment, invocation, or troubleshooting, OCI Generative AI, Enterprise AI Models, Enterprise AI Agents, governed GenAI applications, agentic workflows, RAG on Oracle Cloud, or OCI Generative AI pricing."}},"renderedAt":1793661426776}

Oracle Cloud Infrastructure Skills Use this domain for practical Oracle Cloud Infrastructure guidance. Current content covers OCI Kubernetes Engine (OKE): cluster design, operational troubleshooting, Generic VNIC Attachment (GVA), and Multus multi-interface pod validation. It covers OCI Internet of Things Platform resource discovery, digital twin lifecycle workflows, device publish flows, and optional MCP-assisted operation. It covers OCI Functions local deployment and diagnosis-first troubleshooting. It includes a constrained Terraform Configuration generator for OCI Security Zone recipes an…