Files
tf_provider/docs/MIGRATION_PLAN_FOR_AGENT.md
T

21 KiB
Raw Blame History

План миграции в Nubes Managed Kubernetes — Инструкции для агента

Создан: 2026-03-13 (Opus 4.6)
Исполнитель: Sonnet 4.6
Статус: Ожидает исполнения

ВАЖНО: Этот документ — пошаговый план. Каждый пункт содержит:

  • ЧТО делать (цель)
  • ГДЕ делать (файлы)
  • КАК делать (конкретные инструкции)
  • ОГРАНИЧЕНИЯ (что нельзя трогать)

Контекст

Terraform-провайдер Nubes Cloud сейчас работает на self-managed K8s (registry.kube5s.ru). Цель: подготовить инфраструктуру к передаче в Nubes Managed Kubernetes под управление их DevOps.

Nubes (nubes.ru) — российский cloud-провайдер, собственный DC Tier III (Москва), имеет Managed K8s, Harbor, S3.

Репозиторий: /home/naeel/remote_dev/terraform

Обязательно прочитать перед работой:

  • REPO_CONTENTS.md — карта репозитория
  • .github/copilot-instructions.md — правила работы (IMMUTABILITY POLICY)
  • docs/CODEBASE_ANALYSIS_AND_ROADMAP.md — анализ кодовой базы

Группа A: Подготовительные задачи (делать ПЕРВЫМИ)

A1. Helm Chart для всех K8s-манифестов

Цель: Конвертировать raw YAML манифесты в Helm chart.

Исходные файлы (ТОЛЬКО ЧИТАТЬ, НЕ МЕНЯТЬ):

  • k8s/registry-deployment-new.yaml
  • k8s/registry-ingress-new.yaml
  • operator/manifests/00-namespace.yaml
  • operator/manifests/00-rbac.yaml
  • operator/manifests/02-crd.yaml
  • operator/manifests/03-build-script.yaml
  • operator/manifests/04-operator-deployment.yaml
  • operator/manifests/05-registry-server.yaml
  • operator/manifests/06-registry-server-docs.yaml

Создать:

charts/
  terraform-registry/
    Chart.yaml
    values.yaml
    values-dev.yaml
    values-prod.yaml
    templates/
      _helpers.tpl
      namespace.yaml
      rbac.yaml
      crd.yaml
      build-script-configmap.yaml
      operator-deployment.yaml
      registry-server-deployment.yaml
      registry-server-service.yaml
      docs-server-deployment.yaml      (optional)
      ingress.yaml
      pdb.yaml
      networkpolicy.yaml

Требования к values.yaml:

global:
  registryHostname: "registry.kube5s.ru"       # Переопределяется при миграции
  namespace: "terraform-registry"

registry:
  image:
    repository: "naeel/terraform-registry-server"  # → Harbor при миграции
    tag: "latest"
    pullPolicy: IfNotPresent
  replicas: 1                             # → 2 в prod
  resources:
    requests:
      cpu: 100m
      memory: 128Mi
    limits:
      cpu: 500m
      memory: 256Mi
  service:
    port: 80
    targetPort: 8080
  healthcheck:
    enabled: true
    path: /healthz
    port: 8080

operator:
  image:
    repository: "naeel/terraform-registry-operator"
    tag: "latest"
  replicas: 1
  resources:
    requests:
      cpu: 50m
      memory: 64Mi
    limits:
      cpu: 500m
      memory: 128Mi

ingress:
  enabled: true
  className: "nginx"
  host: "registry.kube5s.ru"                   # Переопределяется
  tls:
    enabled: true
    issuer: "letsencrypt-prod"           # НЕ ТРОГАТЬ LetsEncrypt issuer!
    secretName: "registry-tls"
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"

s3:
  endpoint: "s3.msk-1.ngcloud.ru"
  bucket: "terraform-registry"
  useSSL: true
  # credentials через existingSecret
  existingSecret: "s3-credentials"
  accessKeyField: "access-key"
  secretKeyField: "secret-key"

pdb:
  enabled: false                          # → true в prod
  minAvailable: 1

networkPolicy:
  enabled: false                          # → true при миграции

Требования к values-dev.yaml:

global:
  registryHostname: "registry.kube5s.ru"
registry:
  replicas: 1
pdb:
  enabled: false

Требования к values-prod.yaml:

global:
  registryHostname: "registry.nubes.ru"  # Целевой домен
registry:
  image:
    repository: "pearlharbor.registryk8s.services.ngcloud.ru/terraform/registry-server"
  replicas: 2
operator:
  image:
    repository: "pearlharbor.registryk8s.services.ngcloud.ru/terraform/registry-operator"
pdb:
  enabled: true
  minAvailable: 1
networkPolicy:
  enabled: true

Ограничения:

  • НЕ менять исходные YAML в k8s/ и operator/manifests/ (они могут ещё использоваться)
  • НЕ трогать CRD-ресурсы с LetsEncrypt issuerRef
  • НЕ запускать helm install/upgrade — только создать файлы
  • Helm chart — НОВЫЕ файлы в charts/ (APPEND ONLY)

A2. Externalize hardcoded values

Цель: Убрать все hardcoded пути и домены, заменить на env vars.

Файл 1: internal/core/client.go строка ~18

// СЕЙЧАС:
f, err := os.OpenFile("/home/naeel/terra/debug_nubes.log", ...)

// НОВЫЙ КОД (добавить новую функцию в КОНЕЦ файла):
func debugLogPath() string {
    if p := os.Getenv("NUBES_DEBUG_LOG"); p != "" {
        return p
    }
    return filepath.Join(os.TempDir(), "nubes_debug.log")
}

Ограничение: НЕ менять строку 18 напрямую. Добавить функцию debugLogPath() в КОНЕЦ файла. Спросить оператора перед заменой вызова.

Файл 2: internal/provider/provider.go строка ~103

// СЕЙЧАС:
InsecureSkipVerify: true,

// НУЖНО: Сделать конфигурируемым через provider schema + env var

Инструкция:

  1. Добавить атрибут insecure в schema провайдера (Optional, bool, default false)
  2. Добавить чтение env var NUBES_INSECURE
  3. InsecureSkipVerify = config_value || env_value || false
  4. Код добавлять В КОНЕЦ секции Configure(), не рефакторить существующий

Файл 3: universal_rebuild/internal/provider/provider.go

  • Проверить аналогичную проблему с InsecureSkipVerify
  • Применить тот же паттерн

Ограничения:

  • НЕ менять сигнатуры существующих функций
  • Новый код — APPEND ONLY
  • InsecureSkipVerify=false по умолчанию (breaking change для текущих юзеров — СПРОСИТЬ оператора)

A3. Health endpoints для registry-server

Цель: Добавить /healthz, /readyz, /metrics endpoints.

Файл: registry-server-build/main.go

Инструкция:

  1. Прочитать текущий main.go полностью
  2. Добавить в КОНЕЦ файла (новые handler-функции):
func healthzHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("ok"))
}

func readyzHandler(w http.ResponseWriter, r *http.Request) {
    // Проверить доступность S3
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("ok"))
}
  1. Зарегистрировать handlers в main() — СПРОСИТЬ оператора перед добавлением в mux

Ограничения:

  • НЕ менять существующие handlers
  • НЕ запускать docker build
  • Новые функции — APPEND ONLY

A4. Operaционная документация

Цель: Создать набор операционных документов для DevOps Nubes.

Создать файлы:

docs/ops/RUNBOOK.md:

# Runbook: Terraform Provider Registry

## Предпосылки
- Kubernetes cluster ≥ 1.27
- Helm ≥ 3.12
- Доступ к S3 (s3.msk-1.ngcloud.ru)
- Harbor registry (для образов)

## Установка
helm install terraform-registry ./charts/terraform-registry \
  -f charts/terraform-registry/values-prod.yaml \
  -n terraform-registry --create-namespace

## Обновление версии
1. Собрать новый образ (CI pipeline)
2. Обновить tag в values
3. helm upgrade terraform-registry ./charts/terraform-registry -f values-prod.yaml

## Проверка здоровья
kubectl -n terraform-registry get pods
curl https://<REGISTRY_HOST>/healthz
curl https://<REGISTRY_HOST>/.well-known/terraform.json

## Компоненты
- Registry Server — HTTP-сервер протокола Terraform Registry
- Operator — K8s controller для сборки provider binaries
- S3 — хранилище артефактов (бинарники + документация)

docs/ops/TROUBLESHOOTING.md:

# Troubleshooting

## Registry Server не отвечает
1. kubectl -n terraform-registry get pods -l app=registry-server
2. kubectl -n terraform-registry logs -l app=registry-server --tail=100
3. Проверить ingress: kubectl get ingress -n terraform-registry
4. Проверить S3: curl -s https://s3.msk-1.ngcloud.ru (bucket access)

## Provider binary не скачивается
1. Проверить наличие в S3: s3cmd ls s3://terraform-registry/terraform-providers/...
2. Проверить SHA256SUMS сигнатуру
3. Проверить GPG ключ

## Operator не создаёт build job
1. kubectl -n terraform-registry get terraformproviderrelease
2. kubectl -n terraform-registry describe terraformproviderrelease <name>
3. kubectl -n terraform-registry get jobs
4. Проверить RBAC: operator ServiceAccount должен иметь права на jobs и secrets

## TLS / Certificate проблемы
- Проверить cert-manager: kubectl get certificates -n terraform-registry
- ⚠️ НЕ пересоздавать certificates с LetsEncrypt issuer (rate limits!)
- Для отладки использовать self-signed issuer

docs/ops/MONITORING.md:

# Мониторинг

## Ключевые метрики
- registry_http_requests_total — кол-во запросов к registry
- registry_http_request_duration_seconds — latency
- registry_s3_operations_total — операции с S3
- registry_s3_errors_total — ошибки S3

## Алерты (Prometheus)
- RegistryDown: up == 0 (>2 min)
- RegistryHighLatency: p99 > 5s (>5 min)
- RegistryS3Errors: rate > 0.1/s (>5 min)
- RegistryPodRestart: увеличение restart count

## Grafana Dashboard
- Import dashboard ID: (создать при установке мониторинга)

docs/ops/UPGRADE.md:

# Процедура обновления

## Provider version update (без downtime)
1. CI собирает новый provider binary
2. Создать TerraformProviderRelease CR с новой версией
3. Operator создаёт build job → артефакты в S3
4. Старые версии остаются доступны (immutable artifacts)

## Registry Server update (rolling)
1. Обновить image tag в Helm values
2. helm upgrade --set registry.image.tag=<new> terraform-registry ./charts/...
3. Проверить: kubectl rollout status deployment/registry-server -n terraform-registry
4. Rollback: helm rollback terraform-registry 1

## Operator update
1. Обновить operator image tag
2. helm upgrade ...
3. Проверить CRD compatibility: kubectl get crd terraformproviderreleases.terra.core.nubes.ru

docs/ops/ROLLBACK.md:

# Процедура отката

## Helm rollback
helm rollback terraform-registry <revision>
helm history terraform-registry -n terraform-registry

## Emergency: Direct image rollback
kubectl -n terraform-registry set image deployment/registry-server \
  registry-server=<HARBOR>/terraform/registry-server:<PREV_TAG>

## S3 artifacts (immutable — откат не нужен)
Все версии provider binary хранятся бессрочно.
Удаление только вручную через s3cmd.

Группа B: Инфраструктурная подготовка

B1. Dockerfile оптимизация

Цель: Убедиться что Dockerfile для registry-server и operator готовы к Harbor.

Инструкция:

  1. Прочитать registry-server-build/ и operator/build/
  2. Проверить что Dockerfile использует multi-stage build
  3. Проверить что нет hardcoded путей
  4. Убедиться что base image — official (golang:1.24 + alpine/scratch)
  5. НЕ запускать docker build — только проверить файлы

Создать (если отсутствует): registry-server-build/.dockerignore, operator/.dockerignore


B2. CI Pipeline definition

Цель: Создать файл CI pipeline (GitLab CI / Tekton) для автосборки.

Создать: devops/ci/pipeline.yaml

# GitLab CI - пример (адаптировать под конкретный CI Nubes)
stages:
  - test
  - build
  - sign
  - publish

variables:
  HARBOR_HOST: "pearlharbor.registryk8s.services.ngcloud.ru"
  S3_BUCKET: "terraform-registry"
  PROVIDER_NAME: "nubes"
  PROVIDER_NAMESPACE: "nubes"

test:
  stage: test
  image: golang:1.24
  script:
    - cd universal_rebuild
    - go test ./...
    - go vet ./...

build-provider:
  stage: build
  image: golang:1.24
  script:
    - cd universal_rebuild
    - GOOS=linux GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_linux_amd64
    - GOOS=darwin GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_darwin_amd64
    - GOOS=windows GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_windows_amd64.exe
  artifacts:
    paths: [universal_rebuild/bin/]

build-images:
  stage: build
  script:
    - docker build -t ${HARBOR_HOST}/terraform/registry-server:${CI_COMMIT_TAG} registry-server-build/
    - docker build -t ${HARBOR_HOST}/terraform/registry-operator:${CI_COMMIT_TAG} operator/
    - docker push ${HARBOR_HOST}/terraform/registry-server:${CI_COMMIT_TAG}
    - docker push ${HARBOR_HOST}/terraform/registry-operator:${CI_COMMIT_TAG}

sign:
  stage: sign
  script:
    - cd universal_rebuild/bin
    - sha256sum terraform-provider-* > SHA256SUMS
    - gpg --import $GPG_PRIVATE_KEY
    - gpg --detach-sign SHA256SUMS

publish-to-s3:
  stage: publish
  script:
    - s3cmd put bin/* s3://${S3_BUCKET}/terraform-providers/...

B3. NetworkPolicy template

Цель: Подготовить NetworkPolicy для изоляции namespace.

Включить в Helm chart: charts/terraform-registry/templates/networkpolicy.yaml

{{- if .Values.networkPolicy.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ include "terraform-registry.fullname" . }}-netpol
  namespace: {{ .Values.global.namespace }}
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - port: {{ .Values.registry.service.targetPort }}
          protocol: TCP
  egress:
    - to: []
      ports:
        - port: 443    # S3, Vault
          protocol: TCP
        - port: 53     # DNS
          protocol: UDP
        - port: 53
          protocol: TCP
{{- end }}

Группа C: Domain Migration Plan

C1. Domain migration (порядок действий)

Это НЕ код — это инструкция для DevOps. Записать в docs/ops/DOMAIN_MIGRATION.md:

# Миграция домена registry.kube5s.ru → registry.nubes.ru

## Фаза 1: Dual-domain (параллельная работа)
1. Настроить Ingress с двумя hosts: registry.kube5s.ru + registry.nubes.ru
2. Оба домена указывают на один Registry Server
3. Обновить provider main.go: Address → registry.nubes.ru
4. Старый адрес registry.kube5s.ru продолжает работать

## Фаза 2: Миграция клиентов
1. Документировать новый registry address для пользователей
2. .terraformrc mirror config для переходного периода:
   provider_installation {
     direct {
       exclude = ["registry.kube5s.ru/*/*"]
     }
     network_mirror {
       url = "https://registry.nubes.ru/v1/providers/"
     }
   }

## Фаза 3: Редирект
1. registry.kube5s.ru Ingress → 301 redirect на registry.nubes.ru
2. Мониторинг: отслеживать запросы на старый домен

## Фаза 4: Деком (через 6+ месяцев)
1. Убрать registry.kube5s.ru из Ingress
2. DNS → удалить A/CNAME запись

Группа D: Code Quality (из CODEBASE_ANALYSIS_AND_ROADMAP.md)

D1. Deprecated-маркеры для legacy CreateGenericInstance

Файл: internal/core/client.go

Инструкция: Добавить КОММЕНТАРИИ (не код) перед каждым методом V1-V5:

// Deprecated: Use CreateGenericInstanceUniversalV5 instead.
// This method is kept for backward compatibility and will be removed in v3.0.
func (c *UniversalClient) CreateGenericInstance(...) ...

Методы для пометки:

  • CreateGenericInstance (V1)
  • CreateGenericInstanceUniversal (V2)
  • CreateGenericInstanceUniversalV2 (V3)
  • CreateGenericInstanceUniversalV3 (V4)
  • CreateGenericInstanceUniversalV4 (V5)

Ограничения: ТОЛЬКО комментарии. НЕ менять код методов. НЕ удалять.


D2. .gitignore для secrets

Файл: .gitignore (корень репозитория)

Добавить в КОНЕЦ файла:

# Secrets (should be in Vault, not in git)
secrets/*.asc
secrets/*.token
secrets/*.key
!secrets/.gitkeep

Создать: secrets/.gitkeep (пустой файл, чтобы директория осталась в git)


D3. Unit tests для core layer

Цель: Создать минимальный набор тестов.

Создать файлы:

  • universal_rebuild/internal/core/client_test.go
  • universal_rebuild/internal/resources_core/crud_test.go

Минимальные тесты для client_test.go:

  • TestNormalizeValue_EmptyString
  • TestNormalizeValue_NullString
  • TestNormalizeValue_MapType
  • TestNormalizeValue_ArrayType
  • TestNormalizeValue_TrimSpace

Минимальные тесты для crud_test.go:

  • TestIsStatusSuspended
  • TestIsStatusNonAdoptable
  • TestDeleteBehaviorDefault

Ограничения:

  • Тесты — НОВЫЕ файлы (не менять существующие)
  • Использовать стандартный testing пакет Go
  • НЕ запускать тесты (go test) без разрешения оператора

Порядок выполнения

ФАЗА 0 (быстрые wins):
  D1 → Deprecated комментарии            [5 мин]
  D2 → .gitignore для secrets            [2 мин]
  A2 → debugLogPath() function           [10 мин]

ФАЗА 1 (Helm chart):
  A1 → Полный Helm chart                 [30-60 мин]

ФАЗА 2 (Ops docs):
  A4 → RUNBOOK, TROUBLESHOOTING и др.    [20 мин]
  C1 → DOMAIN_MIGRATION.md              [10 мин]

ФАЗА 3 (Code quality):
  D3 → Unit tests                        [30 мин]
  A3 → Health endpoints                  [15 мин]

ФАЗА 4 (CI/CD):
  B2 → CI pipeline definition            [15 мин]
  B1 → Dockerfile audit                  [10 мин]

Правила для агента (напоминание)

  1. IMMUTABILITY POLICY — НЕ менять существующий рабочий код
  2. APPEND ONLY — новый код только в конец файла
  3. Комментарии — ЭТО НЕ ПРАВКА КОДА, их можно и нужно добавлять
  4. НЕ запускать docker build, kubectl apply, helm install
  5. НЕ трогать ресурсы с LetsEncrypt issuerRef
  6. Спрашивать разрешения перед изменением сигнатур функций
  7. Secrets в коде — КАТЕГОРИЧЕСКИ нет
  8. Перед любой работой — прочитать REPO_CONTENTS.md
  9. Файлы в internal/resources_gen/ — НЕ МЕНЯТЬ ВРУЧНУЮ (только через генератор)
  10. Минимальные ресурсы — при создании K8s ресурсов ставить минимальный CPU/memory