add: HAR traces
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Cloud Instance Status Checker
|
||||
Проверяет статус ресурсов в облаке по манифесту Terraform
|
||||
"""
|
||||
|
||||
import json
|
||||
import requests
|
||||
import sys
|
||||
from typing import Dict, Optional, List
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class InstanceState(Enum):
|
||||
"""Возможные состояния инстанса"""
|
||||
NOT_CREATED = "not_created"
|
||||
CREATING = "creating"
|
||||
RUNNING = "running"
|
||||
MODIFYING = "modifying"
|
||||
SUSPENDING = "suspending"
|
||||
SUSPENDED = "suspended"
|
||||
RESUMING = "resuming"
|
||||
DELETING = "deleting"
|
||||
DELETED = "deleted"
|
||||
OPERATION_PENDING = "operation_pending"
|
||||
OPERATION_FAILED = "operation_failed"
|
||||
UNKNOWN = "unknown"
|
||||
|
||||
|
||||
@dataclass
|
||||
class InstanceInfo:
|
||||
"""Информация об инстансе"""
|
||||
uid: str
|
||||
name: str
|
||||
state: InstanceState
|
||||
is_created: bool
|
||||
is_deleted: bool
|
||||
is_suspended: bool
|
||||
uptime_seconds: float
|
||||
operation_in_progress: bool
|
||||
operation_pending: bool
|
||||
last_operation: Optional[str]
|
||||
last_operation_status: Optional[str]
|
||||
error_log: Optional[str]
|
||||
timestamp: str
|
||||
|
||||
|
||||
class CloudInstanceChecker:
|
||||
"""Проверяет статус инстансов в облаке"""
|
||||
|
||||
BASE_URL = "https://deck-api.ngcloud.ru/api/v1/index.cfm"
|
||||
|
||||
def __init__(self, token: str):
|
||||
"""
|
||||
Инициализация чекера
|
||||
|
||||
Args:
|
||||
token: JWT токен для аутентификации в API
|
||||
"""
|
||||
self.token = token
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json"
|
||||
})
|
||||
|
||||
def find_instance(self, display_name: str, service_type: Optional[str] = None) -> Optional[Dict]:
|
||||
"""
|
||||
Найти инстанс по отображаемому имени
|
||||
|
||||
Args:
|
||||
display_name: Имя ресурса в облаке
|
||||
service_type: Тип сервиса (опционально, для фильтрации)
|
||||
|
||||
Returns:
|
||||
Данные инстанса или None если не найден
|
||||
"""
|
||||
try:
|
||||
params = {
|
||||
"fields": "instanceConfigDtCreated,instanceUid,displayName,svc,uptime,explainedStatus,svcExtendedName,updaterLogin,updaterShortname,operationIsInProgress,operationIsPending,monitoringUrl",
|
||||
"search": display_name,
|
||||
"isAuxiliary": "false",
|
||||
"isDeleted": "false",
|
||||
"page": "1",
|
||||
"pageSize": "100"
|
||||
}
|
||||
|
||||
response = self.session.get(f"{self.BASE_URL}/instances", params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
results = data.get("results", [])
|
||||
|
||||
# Поиск точного совпадения по имени
|
||||
for instance in results:
|
||||
if instance.get("displayName") == display_name:
|
||||
if service_type is None or instance.get("svc") == service_type:
|
||||
return instance
|
||||
|
||||
return None
|
||||
except requests.RequestException as e:
|
||||
print(f"❌ Ошибка при поиске инстанса '{display_name}': {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def get_instance_details(self, instance_uid: str) -> Optional[Dict]:
|
||||
"""
|
||||
Получить полную информацию об инстансе
|
||||
|
||||
Args:
|
||||
instance_uid: UUID инстанса
|
||||
|
||||
Returns:
|
||||
Полные данные инстанса или None при ошибке
|
||||
"""
|
||||
try:
|
||||
params = {
|
||||
"fields": "instanceConfigDtCreated,instanceUid,displayName,descr,svc,state,operations,availableOperations,uptime,isDeleted,updaterLogin,updaterShortname,explainedStatus,man,dependencies,dependentInstances,svcExtendedName"
|
||||
}
|
||||
|
||||
response = self.session.get(f"{self.BASE_URL}/instances/{instance_uid}", params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json().get("instance")
|
||||
except requests.RequestException as e:
|
||||
print(f"❌ Ошибка при получении информации об инстансе {instance_uid}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def determine_state(self, instance_data: Dict) -> InstanceState:
|
||||
"""
|
||||
Определить состояние инстанса на основе флагов
|
||||
|
||||
Args:
|
||||
instance_data: Данные инстанса от API
|
||||
|
||||
Returns:
|
||||
InstanceState enum
|
||||
"""
|
||||
# Проверка удаления
|
||||
if instance_data.get("isDeleted"):
|
||||
return InstanceState.DELETED
|
||||
|
||||
# Проверка создания
|
||||
if not instance_data.get("isCreated"):
|
||||
return InstanceState.NOT_CREATED
|
||||
|
||||
# Проверка операции в процессе
|
||||
if instance_data.get("operationIsInProgress"):
|
||||
operations = instance_data.get("operations", [])
|
||||
if operations:
|
||||
last_op = operations[-1]
|
||||
operation = last_op.get("operation", "unknown")
|
||||
|
||||
if operation == "create":
|
||||
return InstanceState.CREATING
|
||||
elif operation == "modify":
|
||||
return InstanceState.MODIFYING
|
||||
elif operation == "delete":
|
||||
return InstanceState.DELETING
|
||||
elif operation == "suspend":
|
||||
return InstanceState.SUSPENDING
|
||||
elif operation == "resume":
|
||||
return InstanceState.RESUMING
|
||||
|
||||
# Проверка ожидающей операции
|
||||
if instance_data.get("operationIsPending"):
|
||||
operations = instance_data.get("operations", [])
|
||||
if operations:
|
||||
last_op = operations[-1]
|
||||
if not last_op.get("dtStart"):
|
||||
return InstanceState.OPERATION_PENDING
|
||||
|
||||
# Проверка приостановки
|
||||
if instance_data.get("isSuspended"):
|
||||
return InstanceState.SUSPENDED
|
||||
|
||||
# Проверка основного статуса
|
||||
if instance_data.get("explainedStatus") == "running":
|
||||
return InstanceState.RUNNING
|
||||
|
||||
# Проверка ошибок в операциях
|
||||
operations = instance_data.get("operations", [])
|
||||
if operations:
|
||||
last_op = operations[-1]
|
||||
if last_op.get("isSuccessful") == False:
|
||||
return InstanceState.OPERATION_FAILED
|
||||
|
||||
return InstanceState.UNKNOWN
|
||||
|
||||
def check_instance(self, resource_name: str, service_type: Optional[str] = None) -> Optional[InstanceInfo]:
|
||||
"""
|
||||
Проверить статус инстанса
|
||||
|
||||
Args:
|
||||
resource_name: Имя ресурса в манифесте
|
||||
service_type: Тип сервиса (опционально)
|
||||
|
||||
Returns:
|
||||
InstanceInfo с информацией о статусе
|
||||
"""
|
||||
# Поиск инстанса
|
||||
instance_short = self.find_instance(resource_name, service_type)
|
||||
|
||||
if not instance_short:
|
||||
return InstanceInfo(
|
||||
uid="unknown",
|
||||
name=resource_name,
|
||||
state=InstanceState.NOT_CREATED,
|
||||
is_created=False,
|
||||
is_deleted=False,
|
||||
is_suspended=False,
|
||||
uptime_seconds=0,
|
||||
operation_in_progress=False,
|
||||
operation_pending=False,
|
||||
last_operation=None,
|
||||
last_operation_status=None,
|
||||
error_log=None,
|
||||
timestamp=datetime.now().isoformat()
|
||||
)
|
||||
|
||||
# Получить полную информацию
|
||||
instance_uid = instance_short.get("instanceUid")
|
||||
instance_full = self.get_instance_details(instance_uid)
|
||||
|
||||
if not instance_full:
|
||||
instance_full = instance_short
|
||||
|
||||
# Определить состояние
|
||||
state = self.determine_state(instance_full)
|
||||
|
||||
# Извлечь информацию об операции
|
||||
operations = instance_full.get("operations", [])
|
||||
last_operation = None
|
||||
last_operation_status = None
|
||||
error_log = None
|
||||
|
||||
if operations:
|
||||
last_op = operations[-1]
|
||||
last_operation = last_op.get("operation")
|
||||
last_operation_status = "success" if last_op.get("isSuccessful") == True else "pending" if last_op.get("isSuccessful") is None else "failed"
|
||||
error_log = last_op.get("errorLog")
|
||||
|
||||
return InstanceInfo(
|
||||
uid=instance_uid,
|
||||
name=resource_name,
|
||||
state=state,
|
||||
is_created=instance_full.get("isCreated", False),
|
||||
is_deleted=instance_full.get("isDeleted", False),
|
||||
is_suspended=instance_full.get("isSuspended", False),
|
||||
uptime_seconds=instance_full.get("uptime", 0),
|
||||
operation_in_progress=instance_full.get("operationIsInProgress", False),
|
||||
operation_pending=instance_full.get("operationIsPending", False),
|
||||
last_operation=last_operation,
|
||||
last_operation_status=last_operation_status,
|
||||
error_log=error_log,
|
||||
timestamp=datetime.now().isoformat()
|
||||
)
|
||||
|
||||
def format_output(self, info: InstanceInfo) -> str:
|
||||
"""Форматировать результат для вывода"""
|
||||
|
||||
# Выбрать эмодзи в зависимости от состояния
|
||||
emoji_map = {
|
||||
InstanceState.NOT_CREATED: "⚪",
|
||||
InstanceState.CREATING: "🔄",
|
||||
InstanceState.RUNNING: "✅",
|
||||
InstanceState.MODIFYING: "🔧",
|
||||
InstanceState.SUSPENDING: "⏸️",
|
||||
InstanceState.SUSPENDED: "⏹️",
|
||||
InstanceState.RESUMING: "▶️",
|
||||
InstanceState.DELETING: "🗑️",
|
||||
InstanceState.DELETED: "❌",
|
||||
InstanceState.OPERATION_PENDING: "⏳",
|
||||
InstanceState.OPERATION_FAILED: "💥",
|
||||
InstanceState.UNKNOWN: "❓",
|
||||
}
|
||||
|
||||
emoji = emoji_map.get(info.state, "?")
|
||||
|
||||
result = f"{emoji} {info.name}: {info.state.value.upper()}"
|
||||
|
||||
if info.uid != "unknown":
|
||||
result += f" (UID: {info.uid[:8]}...)"
|
||||
|
||||
if info.last_operation:
|
||||
result += f" | Last op: {info.last_operation} ({info.last_operation_status})"
|
||||
|
||||
if info.uptime_seconds > 0:
|
||||
uptime_hours = info.uptime_seconds / 3600
|
||||
result += f" | Uptime: {uptime_hours:.1f}h"
|
||||
|
||||
if info.error_log:
|
||||
result += f"\n ⚠️ Error: {info.error_log[:100]}"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
"""Главная функция"""
|
||||
|
||||
# Пример использования
|
||||
token_file = "/home/naeel/remote_dev/terraform/secrets/prod.token"
|
||||
|
||||
try:
|
||||
with open(token_file, 'r') as f:
|
||||
token = f.read().strip()
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Токен не найден: {token_file}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
checker = CloudInstanceChecker(token)
|
||||
|
||||
# Примеры ресурсов для проверки из PROD_STAND/PG1/resources.tf
|
||||
resources_to_check = [
|
||||
("pg-testing", "nubes_postgres"),
|
||||
("lucy11", "nubes_lucee"),
|
||||
("node_0111", "nubes_nodejs"),
|
||||
("baba-buck-test2", "nubes_s3bucket"),
|
||||
]
|
||||
|
||||
print("=" * 80)
|
||||
print("🔍 Cloud Instance Status Report")
|
||||
print("=" * 80)
|
||||
print()
|
||||
|
||||
for resource_name, service_type in resources_to_check:
|
||||
print(f"Checking: {resource_name}")
|
||||
info = checker.check_instance(resource_name, service_type)
|
||||
if info:
|
||||
print(checker.format_output(info))
|
||||
print()
|
||||
|
||||
print("=" * 80)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create namespace if missing and apply kustomize overlay for dev
|
||||
kubectl create namespace terra-dev --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl apply -k k8s/overlays/dev
|
||||
|
||||
echo "Applied kustomize overlay for terra-dev."
|
||||
echo "To init terraform backend for dev: terraform init -reconfigure -backend-config=terraform/backend-dev.hcl"
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Cleanup
|
||||
rm -rf build_artifacts
|
||||
mkdir -p build_artifacts
|
||||
|
||||
VERSION="${1:-2.0.1}"
|
||||
|
||||
# Function to build and zip
|
||||
build_and_zip() {
|
||||
OS=$1
|
||||
ARCH=$2
|
||||
echo "Building for $OS/$ARCH..."
|
||||
|
||||
BINARY_NAME="terraform-provider-nubes_v${VERSION}"
|
||||
if [ "$OS" == "windows" ]; then
|
||||
BINARY_NAME="${BINARY_NAME}.exe"
|
||||
fi
|
||||
|
||||
env CGO_ENABLED=0 GOOS=$OS GOARCH=$ARCH go build -o build_artifacts/${BINARY_NAME} .
|
||||
|
||||
cd build_artifacts
|
||||
ZIP_NAME="terraform-provider-nubes_${VERSION}_${OS}_${ARCH}.zip"
|
||||
|
||||
# Zip the binary
|
||||
if [[ "$OS" == "windows" ]]; then
|
||||
# For windows, we might need zip to handle the .exe extension properly if we were relying on unix permissions, but zip handles it ok.
|
||||
# Using python zipfile as 'zip' command was missing earlier
|
||||
python3 -m zipfile -c $ZIP_NAME $BINARY_NAME
|
||||
else
|
||||
# Ensure executable permission
|
||||
chmod +x $BINARY_NAME
|
||||
python3 -m zipfile -c $ZIP_NAME $BINARY_NAME
|
||||
fi
|
||||
|
||||
# Remove binary to save space/confusion (optional, but cleaner)
|
||||
rm $BINARY_NAME
|
||||
cd ..
|
||||
}
|
||||
|
||||
# Build for all targets
|
||||
build_and_zip linux amd64
|
||||
build_and_zip windows amd64
|
||||
build_and_zip darwin amd64
|
||||
build_and_zip darwin arm64
|
||||
|
||||
echo "Calculating SHA256SUMS..."
|
||||
cd build_artifacts
|
||||
sha256sum *.zip > terraform-provider-nubes_${VERSION}_SHA256SUMS
|
||||
|
||||
echo "Signing SHA256SUMS..."
|
||||
# Detached binary signature
|
||||
gpg --batch --detach-sign --default-key 866FD93D456DCA800F2448413EC4673EB798238A --output terraform-provider-nubes_${VERSION}_SHA256SUMS.sig terraform-provider-nubes_${VERSION}_SHA256SUMS
|
||||
|
||||
echo "Uploading to S3..."
|
||||
# Base S3 path
|
||||
S3_PATH="registry/terraform-registry/terra.k8c.ru/nubes/nubes/${VERSION}/"
|
||||
|
||||
# Upload zips
|
||||
for f in *.zip; do
|
||||
mc cp $f $S3_PATH
|
||||
done
|
||||
|
||||
# Upload sums and sig
|
||||
mc cp terraform-provider-nubes_${VERSION}_SHA256SUMS $S3_PATH
|
||||
mc cp terraform-provider-nubes_${VERSION}_SHA256SUMS.sig $S3_PATH
|
||||
|
||||
echo "Done!"
|
||||
Executable
+196
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Publish a single rendered docs page (or a small file set) to a specific docs version path.
|
||||
#
|
||||
# Usage examples:
|
||||
# ./scripts/publish-doc-page.sh \
|
||||
# --profile devops/profiles/test \
|
||||
# --version 5.0.17 \
|
||||
# --page 30_registry/guides/getting-started/index.html
|
||||
#
|
||||
# ./scripts/publish-doc-page.sh \
|
||||
# --profile devops/profiles/prod \
|
||||
# --version 2.1.26 \
|
||||
# --page 30_registry/guides/getting-started/
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}"
|
||||
PROFILE_DIR=""
|
||||
SITE_DIR="${ROOT_DIR}/site"
|
||||
PAGE_REL=""
|
||||
VERSION=""
|
||||
REGISTRY_HOST="${REGISTRY_HOST:-terra.k8c.ru}"
|
||||
NAMESPACE="${NAMESPACE:-nubes}"
|
||||
PROVIDER_NAME="${PROVIDER_NAME:-nubes}"
|
||||
S3CFG_REGISTRY="${S3CFG_REGISTRY:-${ROOT_DIR}/secrets/.s3cfg_registry}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
publish-doc-page.sh --profile <path> --version <ver> --page <relative-path> [--site-dir <dir>]
|
||||
|
||||
Required:
|
||||
--profile Path to stand profile dir (e.g. devops/profiles/test)
|
||||
--version Docs version (e.g. 5.0.17)
|
||||
--page Relative path inside built site dir, e.g.:
|
||||
30_registry/guides/getting-started/index.html
|
||||
30_registry/guides/getting-started/ (auto -> index.html)
|
||||
|
||||
Optional:
|
||||
--site-dir Built MkDocs site directory (default: <repo>/site)
|
||||
|
||||
Behavior:
|
||||
Uploads exactly one file to:
|
||||
docs/<namespace>/<provider>/<version>/<page>
|
||||
in S3 bucket terraform-registry.
|
||||
EOF
|
||||
}
|
||||
|
||||
resolve_root_path() {
|
||||
local path_value="$1"
|
||||
if [[ -z "$path_value" ]]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
if [[ "$path_value" = /* ]]; then
|
||||
echo "$path_value"
|
||||
return
|
||||
fi
|
||||
echo "${ROOT_DIR}/${path_value}"
|
||||
}
|
||||
|
||||
load_s3cfg_registry() {
|
||||
local cfg="$1"
|
||||
if [[ ! -f "$cfg" ]]; then
|
||||
echo "Error: S3 config not found: $cfg" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
local access_key secret_key host_base use_https endpoint
|
||||
access_key=$(awk -F '=' '/^\s*access_key\s*=/ {gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2; exit}' "$cfg")
|
||||
secret_key=$(awk -F '=' '/^\s*secret_key\s*=/ {gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2; exit}' "$cfg")
|
||||
host_base=$(awk -F '=' '/^\s*host_base\s*=/ {gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2; exit}' "$cfg")
|
||||
use_https=$(awk -F '=' '/^\s*use_https\s*=/ {gsub(/^[ \t]+|[ \t]+$/, "", $2); print tolower($2); exit}' "$cfg")
|
||||
|
||||
if [[ -z "${S3_ACCESS_KEY:-}" && -n "$access_key" ]]; then
|
||||
export S3_ACCESS_KEY="$access_key"
|
||||
fi
|
||||
if [[ -z "${S3_SECRET_KEY:-}" && -n "$secret_key" ]]; then
|
||||
export S3_SECRET_KEY="$secret_key"
|
||||
fi
|
||||
|
||||
if [[ -z "${S3_ENDPOINT:-}" && -n "$host_base" ]]; then
|
||||
if [[ "$host_base" == http* ]]; then
|
||||
endpoint="$host_base"
|
||||
else
|
||||
if [[ "$use_https" == "false" || "$use_https" == "0" || "$use_https" == "no" ]]; then
|
||||
endpoint="http://${host_base}"
|
||||
else
|
||||
endpoint="https://${host_base}"
|
||||
fi
|
||||
fi
|
||||
export S3_ENDPOINT="$endpoint"
|
||||
fi
|
||||
|
||||
if [[ -z "${S3_ENDPOINT:-}" || -z "${S3_ACCESS_KEY:-}" || -z "${S3_SECRET_KEY:-}" ]]; then
|
||||
echo "Error: S3 credentials are not set (S3_ENDPOINT/S3_ACCESS_KEY/S3_SECRET_KEY)" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--profile)
|
||||
PROFILE_DIR="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--version)
|
||||
VERSION="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--page)
|
||||
PAGE_REL="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--site-dir)
|
||||
SITE_DIR="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Error: unknown argument: $1" >&2
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$PROFILE_DIR" || -z "$VERSION" || -z "$PAGE_REL" ]]; then
|
||||
echo "Error: --profile, --version and --page are required" >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
PROFILE_DIR="$(resolve_root_path "$PROFILE_DIR")"
|
||||
SITE_DIR="$(resolve_root_path "$SITE_DIR")"
|
||||
S3CFG_REGISTRY="$(resolve_root_path "$S3CFG_REGISTRY")"
|
||||
|
||||
if [[ ! -d "$PROFILE_DIR" ]]; then
|
||||
echo "Error: profile directory not found: $PROFILE_DIR" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
PROFILE_ENV_FILE="${PROFILE_DIR}/profile.env"
|
||||
if [[ -f "$PROFILE_ENV_FILE" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$PROFILE_ENV_FILE"
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Re-apply defaults from profile if provided
|
||||
REGISTRY_HOST="${REGISTRY_HOSTNAME:-${REGISTRY_HOST:-$REGISTRY_HOST}}"
|
||||
NAMESPACE="${NAMESPACE:-$NAMESPACE}"
|
||||
PROVIDER_NAME="${PROVIDER_NAME:-$PROVIDER_NAME}"
|
||||
if [[ -n "${S3CFG_REGISTRY:-}" ]]; then
|
||||
S3CFG_REGISTRY="$(resolve_root_path "${S3CFG_REGISTRY}")"
|
||||
fi
|
||||
|
||||
if [[ ! -d "$SITE_DIR" ]]; then
|
||||
echo "Error: site dir not found: $SITE_DIR" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
page="${PAGE_REL#/}"
|
||||
if [[ "$page" == */ ]]; then
|
||||
page="${page}index.html"
|
||||
fi
|
||||
if [[ "$page" != *.html && "$page" != *.xml && "$page" != *.txt && "$page" != *.json && "$page" != *.css && "$page" != *.js ]]; then
|
||||
# convenience: if user passed folder-like path without trailing slash
|
||||
if [[ -f "$SITE_DIR/${page}/index.html" ]]; then
|
||||
page="${page}/index.html"
|
||||
fi
|
||||
fi
|
||||
|
||||
SOURCE_FILE="${SITE_DIR}/${page}"
|
||||
if [[ ! -f "$SOURCE_FILE" ]]; then
|
||||
echo "Error: page file not found: $SOURCE_FILE" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
load_s3cfg_registry "$S3CFG_REGISTRY"
|
||||
|
||||
MC_ALIAS="registry"
|
||||
mc alias set "$MC_ALIAS" "$S3_ENDPOINT" "$S3_ACCESS_KEY" "$S3_SECRET_KEY" --api S3v4 >/dev/null
|
||||
|
||||
TARGET="${MC_ALIAS}/terraform-registry/docs/${NAMESPACE}/${PROVIDER_NAME}/${VERSION}/${page}"
|
||||
mc cp "$SOURCE_FILE" "$TARGET"
|
||||
|
||||
PUBLIC_TARGET="${MC_ALIAS}/terraform-registry/docs/${NAMESPACE}/${PROVIDER_NAME}/${VERSION}/"
|
||||
mc policy set public "$PUBLIC_TARGET" >/dev/null || true
|
||||
|
||||
echo "Published single page: https://${REGISTRY_HOST}/docs/${NAMESPACE}/${PROVIDER_NAME}/${VERSION}/${page}"
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Usage: publish-docs.sh <site-dir> <registry-host> <namespace> <name> <version>
|
||||
SITE_DIR=${1:-site}
|
||||
REGISTRY_HOST=${2:-terra.k8c.ru}
|
||||
NAMESPACE=${3:-nubes}
|
||||
NAME=${4:-nubes}
|
||||
VERSION=${5:-dev}
|
||||
|
||||
# S3-only variables
|
||||
ENDPOINT=${S3_ENDPOINT:-}
|
||||
ACCESS_KEY=${S3_ACCESS_KEY:-}
|
||||
SECRET_KEY=${S3_SECRET_KEY:-}
|
||||
|
||||
if [ -z "$ENDPOINT" ] || [ -z "$ACCESS_KEY" ] || [ -z "$SECRET_KEY" ]; then
|
||||
echo "Error: S3_ENDPOINT/S3_ACCESS_KEY/S3_SECRET_KEY must be set"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
MC_ALIAS=registry
|
||||
mc alias set $MC_ALIAS "$ENDPOINT" "$ACCESS_KEY" "$SECRET_KEY" --api S3v4
|
||||
TARGET="${MC_ALIAS}/terraform-registry/docs/${NAMESPACE}/${NAME}/${VERSION}/"
|
||||
|
||||
# Create target bucket path if needed (mc will create directories implicitly when copying)
|
||||
mc cp --recursive "$SITE_DIR/" "$TARGET"
|
||||
# Optionally set public policy
|
||||
mc policy set public "$TARGET" || true
|
||||
|
||||
echo "Published docs to: https://${REGISTRY_HOST}/docs/${NAMESPACE}/${NAME}/${VERSION}/"
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Пример настройки bucket notifications через mc (S3-compatible)
|
||||
# Запускать НА УДАЛЁННОЙ машине.
|
||||
#
|
||||
# Пример:
|
||||
# export S3CFG=/home/naeel/terra/terraform/secrets/.s3cfg_registry
|
||||
# export BUCKET=buck
|
||||
# export TARGET_ARN='arn:minio:sqs::primary:webhook'
|
||||
# export EVENTS='put,delete'
|
||||
# bash scripts/s3_notification_example.sh
|
||||
#
|
||||
# Где TARGET_ARN — уже существующий target в конфигурации S3/MinIO.
|
||||
|
||||
S3CFG="${S3CFG:-/home/naeel/terra/terraform/secrets/.s3cfg_registry}"
|
||||
BUCKET="${BUCKET:-}"
|
||||
TARGET_ARN="${TARGET_ARN:-}"
|
||||
EVENTS="${EVENTS:-put}" # put | delete | get | put,delete
|
||||
PREFIX="${PREFIX:-}" # опционально, напр. uploads/
|
||||
SUFFIX="${SUFFIX:-}" # опционально, напр. .jpg
|
||||
ALIAS_NAME="${ALIAS_NAME:-regnotif}"
|
||||
|
||||
if ! command -v mc >/dev/null 2>&1; then
|
||||
echo "ERROR: mc не установлен"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$S3CFG" ]]; then
|
||||
echo "ERROR: не найден S3CFG: $S3CFG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$BUCKET" ]]; then
|
||||
echo "ERROR: задайте BUCKET"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$TARGET_ARN" ]]; then
|
||||
echo "ERROR: задайте TARGET_ARN (например arn:minio:sqs::primary:webhook)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Читаем access_key/secret_key/host_base из .s3cfg
|
||||
# shellcheck disable=SC2016
|
||||
readarray -t CFG_LINES < <(python3 - <<PY
|
||||
import configparser
|
||||
cfg = configparser.RawConfigParser()
|
||||
cfg.read("$S3CFG")
|
||||
sec = cfg["default"]
|
||||
print(sec.get("access_key", ""))
|
||||
print(sec.get("secret_key", ""))
|
||||
print(sec.get("host_base", ""))
|
||||
PY
|
||||
)
|
||||
ACCESS_KEY="${CFG_LINES[0]:-}"
|
||||
SECRET_KEY="${CFG_LINES[1]:-}"
|
||||
HOST_BASE="${CFG_LINES[2]:-}"
|
||||
|
||||
if [[ -z "$ACCESS_KEY" || -z "$SECRET_KEY" || -z "$HOST_BASE" ]]; then
|
||||
echo "ERROR: в $S3CFG нет access_key/secret_key/host_base"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mc alias rm "$ALIAS_NAME" >/dev/null 2>&1 || true
|
||||
mc alias set "$ALIAS_NAME" "https://$HOST_BASE" "$ACCESS_KEY" "$SECRET_KEY" >/dev/null
|
||||
|
||||
echo "[1/3] Текущие notification-правила в бакете $BUCKET:"
|
||||
mc event ls "$ALIAS_NAME/$BUCKET" || true
|
||||
|
||||
echo "[2/3] Добавление правила: events=$EVENTS target=$TARGET_ARN"
|
||||
EVENT_FLAGS=()
|
||||
IFS=',' read -r -a EVENT_ARRAY <<< "$EVENTS"
|
||||
for ev in "${EVENT_ARRAY[@]}"; do
|
||||
EVENT_FLAGS+=("--event" "$ev")
|
||||
done
|
||||
|
||||
FILTER_FLAGS=()
|
||||
if [[ -n "$PREFIX" ]]; then
|
||||
FILTER_FLAGS+=("--prefix" "$PREFIX")
|
||||
fi
|
||||
if [[ -n "$SUFFIX" ]]; then
|
||||
FILTER_FLAGS+=("--suffix" "$SUFFIX")
|
||||
fi
|
||||
|
||||
mc event add "$ALIAS_NAME/$BUCKET" "$TARGET_ARN" "${EVENT_FLAGS[@]}" "${FILTER_FLAGS[@]}"
|
||||
|
||||
echo "[3/3] Проверка после добавления:"
|
||||
mc event ls "$ALIAS_NAME/$BUCKET"
|
||||
|
||||
echo "OK: правило уведомлений настроено"
|
||||
Reference in New Issue
Block a user