commit 1a7be93ab1f727b9974dea65f7cb3672dbc2672f Author: “Naeel” Date: Thu Feb 5 11:43:15 2026 +0400 Initial commit: clean infrastructure code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b1b842 --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +# Binaries - General +bin/ +dist/ +*.exe +*.dll +*.so +*.dylib +*.test +*/bin/ + +# Specific binary names (ignore in any subdirectory) +registry-server +registry-server-bin +operator +universal_rebuild + +# Terraform +.terraform/ +*.tfstate +*.tfstate.backup +*.tfvars +*.tfvars.json +.terraform.lock.hcl +*.plan +backend-dev.hcl + +# IDE & Editor +.idea/ +.vscode/ +*.swp +*.swo +*~ +*.bak + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log + +# Secrets & Keys +*.pem +*.key +*.pub +*.crt +*.cer +*.p12 +*.pfx +.env +token +tokens +*_token +*_key +secret.yaml +secrets.yaml +credentials.json diff --git a/forNubes/README.md b/forNubes/README.md new file mode 100644 index 0000000..14e7ac1 --- /dev/null +++ b/forNubes/README.md @@ -0,0 +1,18 @@ +# forNubes + +Сборка минимальных инструментов для разработки провайдера, сборки/публикации документации и инфраструктуры Registry Server (без исходников docs). + +## Файлы в корне +- **go.mod / go.sum** — зависимости Go‑модулей. +- **main.go** — точка входа основного бинарника. +- **mkdocs.yml** — конфигурация сборки документации (используется скриптами публикации). + +## Папки +- **universal_rebuild/** — исходники универсального провайдера (генераторы, core, ресурсы). +- **operator/** — код оператора и манифесты для сборки/публикации провайдеров. +- **registry-server-build/** — исходники и Dockerfile реестра (Registry Server). +- **k8s/** — k8s‑манифесты для деплоя Registry Server. +- **scripts/** — рабочие скрипты (включая публикацию документации). +- **tools/** — утилиты и вспомогательные скрипты (в т.ч. docs_publish). +- **terraform/** — Terraform‑конфигурации окружений. +- **tests/** — тестовые сценарии. diff --git a/forNubes/go.mod b/forNubes/go.mod new file mode 100644 index 0000000..60592e6 --- /dev/null +++ b/forNubes/go.mod @@ -0,0 +1,63 @@ +module terraform-provider-mycloud + +go 1.24.0 + +toolchain go1.24.12 + +require ( + github.com/google/generative-ai-go v0.20.1 + github.com/hashicorp/terraform-plugin-framework v1.16.1 + github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0 + github.com/hashicorp/terraform-plugin-framework-validators v0.19.0 + github.com/hashicorp/terraform-plugin-log v0.9.0 + google.golang.org/api v0.262.0 +) + +require ( + cloud.google.com/go v0.115.0 // indirect + cloud.google.com/go/ai v0.8.0 // indirect + cloud.google.com/go/auth v0.18.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/longrunning v0.5.7 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect + github.com/googleapis/gax-go/v2 v2.16.0 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/go-plugin v1.7.0 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/terraform-plugin-go v0.29.0 // indirect + github.com/hashicorp/terraform-registry-address v0.4.0 // indirect + github.com/hashicorp/terraform-svchost v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + golang.org/x/crypto v0.47.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect + google.golang.org/grpc v1.78.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/forNubes/go.sum b/forNubes/go.sum new file mode 100644 index 0000000..fefa08a --- /dev/null +++ b/forNubes/go.sum @@ -0,0 +1,155 @@ +cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= +cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= +cloud.google.com/go/ai v0.8.0 h1:rXUEz8Wp2OlrM8r1bfmpF2+VKqc1VJpafE3HgzRnD/w= +cloud.google.com/go/ai v0.8.0/go.mod h1:t3Dfk4cM61sytiggo2UyGsDVW3RF1qGZaUKDrZFyqkE= +cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs= +cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU= +cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= +github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= +github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/generative-ai-go v0.20.1 h1:6dEIujpgN2V0PgLhr6c/M1ynRdc7ARtiIDPFzj45uNQ= +github.com/google/generative-ai-go v0.20.1/go.mod h1:TjOnZJmZKzarWbjUJgy+r3Ee7HGBRVLhOIgupnwR4Bg= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao= +github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8= +github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= +github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/terraform-plugin-framework v1.16.1 h1:1+zwFm3MEqd/0K3YBB2v9u9DtyYHyEuhVOfeIXbteWA= +github.com/hashicorp/terraform-plugin-framework v1.16.1/go.mod h1:0xFOxLy5lRzDTayc4dzK/FakIgBhNf/lC4499R9cV4Y= +github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0 h1:jblRy1PkLfPm5hb5XeMa3tezusnMRziUGqtT5epSYoI= +github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0/go.mod h1:5jm2XK8uqrdiSRfD5O47OoxyGMCnwTcl8eoiDgSa+tc= +github.com/hashicorp/terraform-plugin-framework-validators v0.19.0 h1:Zz3iGgzxe/1XBkooZCewS0nJAaCFPFPHdNJd8FgE4Ow= +github.com/hashicorp/terraform-plugin-framework-validators v0.19.0/go.mod h1:GBKTNGbGVJohU03dZ7U8wHqc2zYnMUawgCN+gC0itLc= +github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= +github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM= +github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= +github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= +github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= +github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.262.0 h1:4B+3u8He2GwyN8St3Jhnd3XRHlIvc//sBmgHSp78oNY= +google.golang.org/api v0.262.0/go.mod h1:jNwmH8BgUBJ/VrUG6/lIl9YiildyLd09r9ZLHiQ6cGI= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/forNubes/k8s/README.md b/forNubes/k8s/README.md new file mode 100644 index 0000000..eb527a1 --- /dev/null +++ b/forNubes/k8s/README.md @@ -0,0 +1,7 @@ +# k8s + +Здесь лежат Kubernetes‑манифесты, которые поднимают реестр Terraform‑провайдеров. Это рабочие файлы для текущего деплоя. + +## Что реально используется сейчас +- [registry-deployment-new.yaml](registry-deployment-new.yaml) — deployment `registry-server` с настройкой S3. +- [registry-ingress-new.yaml](registry-ingress-new.yaml) — ingress для `terra.k8c.ru` (cert‑manager + Let's Encrypt). \ No newline at end of file diff --git a/forNubes/k8s/registry-deployment-new.yaml b/forNubes/k8s/registry-deployment-new.yaml new file mode 100644 index 0000000..81d2b34 --- /dev/null +++ b/forNubes/k8s/registry-deployment-new.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: registry-server + namespace: terra + labels: + app: registry-server +spec: + replicas: 1 + selector: + matchLabels: + app: registry-server + template: + metadata: + labels: + app: registry-server + spec: + containers: + - name: registry-server + image: naeel/terraform-registry-server:docs-dev + imagePullPolicy: Always + ports: + - containerPort: 8080 + env: + - name: REGISTRY_HOSTNAME + value: "terra.k8c.ru" + - name: S3_ENDPOINT + value: "s3.msk-1.ngcloud.ru" + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: s3-credentials + key: access-key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: s3-credentials + key: secret-key + - name: S3_BUCKET + value: "terraform-registry" + - name: S3_USE_SSL + value: "true" diff --git a/forNubes/k8s/registry-ingress-new.yaml b/forNubes/k8s/registry-ingress-new.yaml new file mode 100644 index 0000000..0eb800e --- /dev/null +++ b/forNubes/k8s/registry-ingress-new.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: registry-ingress + namespace: terra + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/proxy-body-size: "100m" + nginx.ingress.kubernetes.io/rewrite-target: "/$2" +spec: + ingressClassName: nginx + tls: + - hosts: + - terra.k8c.ru + secretName: registry-tls + rules: + - host: terra.k8c.ru + http: + paths: + - path: "/()(.*)" + pathType: ImplementationSpecific + backend: + service: + name: registry-server + port: + number: 80 diff --git a/forNubes/main.go b/forNubes/main.go new file mode 100644 index 0000000..6b971c1 --- /dev/null +++ b/forNubes/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "context" + "flag" + "log" + + "terraform-provider-mycloud/internal/provider" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" +) + +var ( + version string = "dev" +) + +func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := providerserver.ServeOpts{ + Address: "registry.terraform.io/nubes/nubes", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/forNubes/mkdocs.yml b/forNubes/mkdocs.yml new file mode 100644 index 0000000..e4e773e --- /dev/null +++ b/forNubes/mkdocs.yml @@ -0,0 +1,39 @@ +site_name: Nubes Terraform Provider +site_url: https://terra.k8c.ru/docs/nubes/nubes/2.0.0/ + +exclude_docs: | + README.md + ai_universal_provider_gen.md + 00_overview/* + 20_discovery/* + 40_analysis/* + 50_history/* + 60_strategy/* + 70_api/* + help/* + +docs_dir: docs +site_dir: site + +theme: + name: material + logo: 30_registry/assets/favicon.png + favicon: 30_registry/assets/favicon.png + +markdown_extensions: + - admonition + - attr_list + - pymdownx.details + - pymdownx.inlinehilite + - pymdownx.superfences + - pymdownx.highlight: + anchor_linenums: true + +nav: + - Home: index.md + - Resources: + - Index: 30_registry/resources/index.md + - All resources (table): 30_registry/resources/all_resources.md + - Guides: + - Getting started: 30_registry/guides/getting-started.md + - Terraform basics: 30_registry/guides/terraform-basics.md diff --git a/forNubes/registry-server-build/Dockerfile b/forNubes/registry-server-build/Dockerfile new file mode 100644 index 0000000..29991db --- /dev/null +++ b/forNubes/registry-server-build/Dockerfile @@ -0,0 +1,21 @@ +# Minimal Registry Server Dockerfile +FROM golang:1.24-alpine AS builder + +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download + +COPY main.go docs.go ./ +# Build statically +RUN CGO_ENABLED=0 GOOS=linux go build -o /registry-server . + +# Run Stage +FROM alpine:3.19 +WORKDIR /app +COPY --from=builder /registry-server /app/registry-server +RUN apk add --no-cache ca-certificates + +USER 1000:1000 +EXPOSE 8080 + +CMD ["/app/registry-server"] diff --git a/forNubes/registry-server-build/Dockerfile.minimal b/forNubes/registry-server-build/Dockerfile.minimal new file mode 100644 index 0000000..bd3b9a1 --- /dev/null +++ b/forNubes/registry-server-build/Dockerfile.minimal @@ -0,0 +1,10 @@ +# Super minimal Registry Server Dockerfile +FROM alpine:3.19 +WORKDIR /app +COPY registry-server-bin /app/registry-server +RUN apk add --no-cache ca-certificates + +USER 1000:1000 +EXPOSE 8080 + +CMD ["/app/registry-server"] diff --git a/forNubes/registry-server-build/docs.go b/forNubes/registry-server-build/docs.go new file mode 100644 index 0000000..c24c39d --- /dev/null +++ b/forNubes/registry-server-build/docs.go @@ -0,0 +1,116 @@ +package main + +import ( + "context" + "fmt" + "io" + "log" + "mime" + "net/http" + "path/filepath" + "strings" + + "github.com/minio/minio-go/v7" +) + +// parseDocsRequestPath parses paths like: +// /docs////... (rest may be empty) +func parseDocsRequestPath(p string) (namespace, name, version, rest string, err error) { +p = strings.TrimPrefix(p, "/") +p = strings.TrimPrefix(p, "docs/") +parts := strings.SplitN(p, "/", 4) +if len(parts) < 3 { +err = fmt.Errorf("invalid docs path: %s", p) +return +} +namespace = parts[0] +name = parts[1] +version = parts[2] +if len(parts) == 3 { +rest = "" +} else { +rest = parts[3] +} +return +} + +// docsObjectKey builds the S3 key for a docs object given parsed parts. +func docsObjectKey(namespace, name, version, pathPart string) string { +clean := strings.TrimPrefix(pathPart, "/") +if clean == "" { +return fmt.Sprintf("docs/%s/%s/%s/index.html", namespace, name, version) +} +return fmt.Sprintf("docs/%s/%s/%s/%s", namespace, name, version, clean) +} + +// tryCandidateKeys returns a list of keys to attempt for a given request path. +// Order matters: exact path first, then /index.html, then top-level index. +func tryCandidateKeys(namespace, name, version, rest string) []string { +keys := []string{} +if rest == "" { +keys = append(keys, docsObjectKey(namespace, name, version, "index.html")) +return keys +} +// exact +keys = append(keys, docsObjectKey(namespace, name, version, rest)) +// if it looks like a directory or has no extension, try index under it +if strings.HasSuffix(rest, "/") || filepath.Ext(rest) == "" { +keys = append(keys, docsObjectKey(namespace, name, version, strings.TrimSuffix(rest, "/")+"/index.html")) +} +// finally, try root index +keys = append(keys, docsObjectKey(namespace, name, version, "index.html")) +return keys +} + +// docsHandler serves static documentation files from S3 (public-facing via Ingress). +func docsHandler(w http.ResponseWriter, r *http.Request) { +ns, name, ver, rest, err := parseDocsRequestPath(r.URL.Path) +if err != nil { +http.Error(w, "Bad docs path", http.StatusBadRequest) +return +} + +ctx := context.Background() +candidates := tryCandidateKeys(ns, name, ver, rest) +log.Printf("Docs candidates (host=%s): %v", hostname, candidates) + +var lastErr error +for _, key := range candidates { +obj, err := s3Client.GetObject(ctx, bucketName, key, minio.GetObjectOptions{}) +if err != nil { +lastErr = err +continue +} +stat, err := obj.Stat() +if err != nil { +lastErr = err +_ = obj.Close() +continue +} + +// Determine content-type +ext := filepath.Ext(key) +ctype := mime.TypeByExtension(ext) +if ctype == "" { +// fallback for HTML +if ext == ".html" || strings.HasSuffix(key, "index.html") { +ctype = "text/html; charset=utf-8" +} else { +ctype = "application/octet-stream" +} +} + +w.Header().Set("Content-Type", ctype) +w.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size)) +w.Header().Set("Last-Modified", stat.LastModified.Format(http.TimeFormat)) + +if _, err := io.Copy(w, obj); err != nil { +log.Printf("Error streaming object %s: %v", key, err) +} +_ = obj.Close() +return +} + +log.Printf("Docs not found in candidates: %v, lastErr: %v", candidates, lastErr) +http.Error(w, "Documentation not found", http.StatusNotFound) +} diff --git a/forNubes/registry-server-build/go.mod b/forNubes/registry-server-build/go.mod new file mode 100644 index 0000000..8a18188 --- /dev/null +++ b/forNubes/registry-server-build/go.mod @@ -0,0 +1,27 @@ +module terraform-registry-server + +go 1.24.12 + +require github.com/minio/minio-go/v7 v7.0.98 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.18.2 // indirect + github.com/klauspost/cpuid/v2 v2.2.11 // indirect + github.com/klauspost/crc32 v1.3.0 // indirect + github.com/minio/crc64nvme v1.1.1 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/philhofer/fwd v1.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/xid v1.6.0 // indirect + github.com/tinylib/msgp v1.6.1 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/forNubes/registry-server-build/go.sum b/forNubes/registry-server-build/go.sum new file mode 100644 index 0000000..e29b295 --- /dev/null +++ b/forNubes/registry-server-build/go.sum @@ -0,0 +1,45 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= +github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= +github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= +github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= +github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= +github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.98 h1:MeAVKjLVz+XJ28zFcuYyImNSAh8Mq725uNW4beRisi0= +github.com/minio/minio-go/v7 v7.0.98/go.mod h1:cY0Y+W7yozf0mdIclrttzo1Iiu7mEf9y7nk2uXqMOvM= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY= +github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/forNubes/registry-server-build/main.go b/forNubes/registry-server-build/main.go new file mode 100644 index 0000000..24d109f --- /dev/null +++ b/forNubes/registry-server-build/main.go @@ -0,0 +1,331 @@ +package main + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "net/url" + "os" + "strings" + + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" +) + +var ( + s3Client *minio.Client + bucketName = "terraform-registry" // Default + hostname = os.Getenv("REGISTRY_HOSTNAME") +) + +// Terraform Registry Protocol Structs +type Discovery struct { + ProvidersV1 string `json:"providers.v1"` +} + +type VersionList struct { + ID string `json:"id"` + Versions []Version `json:"versions"` + Warnings []string `json:"warnings"` +} + +type Version struct { + Version string `json:"version"` + Protocols []string `json:"protocols"` + Platforms []Platform `json:"platforms"` +} + +type Platform struct { + OS string `json:"os"` + Arch string `json:"arch"` +} + +type DownloadResponse struct { + Protocols []string `json:"protocols"` + OS string `json:"os"` + Arch string `json:"arch"` + Filename string `json:"filename"` + DownloadURL string `json:"download_url"` + ShasumsURL string `json:"shasums_url"` + ShasumsSignatureURL string `json:"shasums_signature_url"` + Shasum string `json:"shasum"` + SigningKeys SigningKeys `json:"signing_keys"` +} + +type SigningKeys struct { + GPGPublicKeys []GPGPublicKey `json:"gpg_public_keys"` +} + +type GPGPublicKey struct { + KeyID string `json:"key_id"` + ASCIIArmor string `json:"ascii_armor"` +} + +func main() { + // 1. Init S3 Connection + if hostname == "" { + hostname = "localhost:8080" + } + + endpoint := os.Getenv("S3_ENDPOINT") + accessKeyID := os.Getenv("S3_ACCESS_KEY") + secretAccessKey := os.Getenv("S3_SECRET_KEY") + + if os.Getenv("S3_BUCKET") != "" { + bucketName = os.Getenv("S3_BUCKET") + } + + var err error + s3Client, err = minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), + Secure: true, // Force secure for cloud S3 + }) + if err != nil { + log.Fatalln(err) + } + + // 2. HTTP Handlers + http.HandleFunc("/.well-known/terraform.json", discoveryHandler) + http.HandleFunc("/v1/providers/", router) + http.HandleFunc("/v1/proxy", proxyHandler) + http.HandleFunc("/docs/", docsHandler) + http.Handle("/", http.HandlerFunc(rootHandler)) + + log.Printf("Starting Registry Service on :8080 (Bucket: %s, Endpoint: %s)\n", bucketName, endpoint) + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func rootHandler(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(w, ` + + +Terra Registry + +

Terra Registry & Documentation Server

+

Status: ONLINE

+
+

Powered by Nubes Cloud S3 Storage

+ + +`) +} + +func proxyHandler(w http.ResponseWriter, r *http.Request) { + bucket := r.URL.Query().Get("bucket") + key := r.URL.Query().Get("key") + + if bucket == "" || key == "" { + http.Error(w, "Missing bucket or key params", http.StatusBadRequest) + return + } + + obj, err := s3Client.GetObject(context.Background(), bucket, key, minio.GetObjectOptions{}) + if err != nil { + log.Printf("Error getting object %s/%s: %v", bucket, key, err) + http.Error(w, "File not found", http.StatusNotFound) + return + } + defer obj.Close() + + stat, err := obj.Stat() + if err != nil { + log.Printf("Error stating object %s/%s: %v", bucket, key, err) + http.Error(w, "File not found or not accessible", http.StatusNotFound) + return + } + + w.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size)) + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Last-Modified", stat.LastModified.Format(http.TimeFormat)) + + if _, err := io.Copy(w, obj); err != nil { + log.Printf("Error streaming object: %v", err) + } +} + +func discoveryHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(Discovery{ProvidersV1: "/v1/providers/"}) +} + +func router(w http.ResponseWriter, r *http.Request) { + path := strings.TrimPrefix(r.URL.Path, "/v1/providers/") + parts := strings.Split(path, "/") + + if len(parts) == 3 && parts[2] == "versions" { + listVersions(w, r, parts[0], parts[1]) + return + } + + if len(parts) == 6 && parts[3] == "download" { + downloadVersion(w, r, parts[0], parts[1], parts[2], parts[4], parts[5]) + return + } + + http.Error(w, "Not Found", http.StatusNotFound) +} + +func listVersions(w http.ResponseWriter, r *http.Request, namespace, pType string) { + prefix := fmt.Sprintf("%s/%s/%s/", hostname, namespace, pType) + ctx := context.Background() + versions := []Version{} + seenVersions := map[string]*Version{} + + objectCh := s3Client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{ + Prefix: prefix, + Recursive: true, + }) + + for object := range objectCh { + if object.Err != nil { + continue + } + parts := strings.Split(object.Key, "/") + if len(parts) < 5 { + continue + } + verStr := parts[3] + fileName := parts[4] + + if _, ok := seenVersions[verStr]; !ok { + seenVersions[verStr] = &Version{ + Version: verStr, + Protocols: []string{"5.0"}, + Platforms: []Platform{}, + } + } + + if strings.Contains(fileName, "_linux_amd64.zip") { + seenVersions[verStr].Platforms = append(seenVersions[verStr].Platforms, Platform{OS: "linux", Arch: "amd64"}) + } + if strings.Contains(fileName, "_windows_amd64.zip") { + seenVersions[verStr].Platforms = append(seenVersions[verStr].Platforms, Platform{OS: "windows", Arch: "amd64"}) + } + } + + for _, v := range seenVersions { + versions = append(versions, *v) + } + + resp := VersionList{ + ID: fmt.Sprintf("%s/%s", namespace, pType), + Versions: versions, + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(resp) +} + +func downloadVersion(w http.ResponseWriter, r *http.Request, namespace, pType, version, osType, arch string) { + basePath := fmt.Sprintf("%s/%s/%s/%s", hostname, namespace, pType, version) + filename := fmt.Sprintf("terraform-provider-%s_%s_%s_%s.zip", pType, version, osType, arch) + fullKey := fmt.Sprintf("%s/%s", basePath, filename) + shasumsKey := fmt.Sprintf("%s/terraform-provider-%s_%s_SHA256SUMS", basePath, pType, version) + sigKey := fmt.Sprintf("%s/terraform-provider-%s_%s_SHA256SUMS.sig", basePath, pType, version) + + var shasumValue string + shasumsObj, err := s3Client.GetObject(context.Background(), bucketName, shasumsKey, minio.GetObjectOptions{}) + if err == nil { + defer shasumsObj.Close() + scanner := bufio.NewScanner(shasumsObj) + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, filename) { + fields := strings.Fields(line) + if len(fields) >= 1 { + shasumValue = fields[0] + } + break + } + } + } + + baseURL := "https://" + hostname + downloadLink := fmt.Sprintf("%s/v1/proxy?bucket=%s&key=%s", baseURL, bucketName, url.QueryEscape(fullKey)) + shasumsLink := fmt.Sprintf("%s/v1/proxy?bucket=%s&key=%s", baseURL, bucketName, url.QueryEscape(shasumsKey)) + sigLink := fmt.Sprintf("%s/v1/proxy?bucket=%s&key=%s", baseURL, bucketName, url.QueryEscape(sigKey)) + + gpgKey := GPGPublicKey{ + KeyID: "866FD93D456DCA800F2448413EC4673EB798238A", + ASCIIArmor: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGl2EqgBEAClEcif3Xy4rfZnh7HtZrj1K2mEufWVMCV01D75/5SSlfoi9Xxf +4mKojrqF47sfGLNYZkcigodJx7dLcHD0Dx23nKU3AuAdmPhLdl2HRHCljTZ4ZEe7 +RLYp2KhGWDUn7dX79eB4KhUOXmMVdbi7e5VKWg4vQI8UdeCIvsLEbJ8+jrBislFX +4skMWu+59loxXaYKmgJ0EN+x3Z1eSlzYrYZwSaATgS+bajmWXQhHjK/F76IGxep0 +O26sOfM3p/oIbhMUnYRcG3tK5/bc2YQccWQlh5O1l+Qa2os6vDERsEWG3yv74QgZ +lsadvBArI4Wz6PKdZT8pQBoWrXMherSqo2iSs2U3gZMbk29Gmgdor/vy/gF14Mds +N020Kg6xUjMRqQLkl3VZzNHdGhi2gQdTMktigoImthqpuSUDIeIGZjATyg7ZmsTa +YS1yAtmJiPzoC+IqmC28PGk+eZ8rJQEq2ipraLY+RQc1GV1sRniv6nj6+C2OlgYf +vX6ViOr+QqO+oTujM10hyCkZCX0gCCnnSJHZa4lxzkBr6BiTUapd7JZNtofT8H2m +xnebSgG85bGzPFL1tzZYAm5QaspwkgFT2g97XvyEN+JVAXkiJTkbnMWW33cnyDOC +/kPFkYkhzKceW+pGkYYPbMpta/Bm7yhITJ79UMdhN214XM6fV0325rZPdQARAQAB +tCNOdWJlcyBQcm92aWRlciA8bnViZXNAdGVycmEuazhjLnJ1PokCfQQTAQgAcQWC +aXYSqAMLCQcJED7EZz63mCOKNRQAAAAAABwAEHNhbHRAbm90YXRpb25zLm9wZW5w +Z3Bqcy5vcmc9Meal6wZYOa4GSbmo/rRcAhUIAxYAAgIZAQKbAwIeARYhBIZv2T1F +bcqADyRIQT7EZz63mCOKAADkkQ//WRUo1yq+1boJ1tmkiRfhRWmg9PXhEPVg/gCC +nAm21yv2BTx7TrFbSMliUF3G8egQx6ZSaUiZUUIW9+x6V0CddR0w+eGNzFSyqf9q +scC3T6qW/k+m6Hqr9upGEFrpz9dXHWi6FCU5sjou5cAk/JUinzpaaiU6JPsXVnlE +QwtvlJEftsDLBZ0pxrni9LbMykR2v8rThZahCHFU9J6cEs5/IdfBmh2erjaDzj4M +g7FjDTk3h86ovgVWvaBzTs4FZHdI1BhtQK3IO31kVqZj3RtrdWy6o0O5bSVv3cxO +ofnvEW2is9OduvgkOq9EeuQXkD/kuE2adxmH4RAUEeDXCSknk82FdsSmFok66VgG +Kb8XXiIAtjsqyxwNe4Y+yr+gYACdfVn/C3b+5hlQWtBAlVJRjfnn3FWhmiSBAJtF +2swVSk4s0oFM1hEmBNLG45CnATOVPGI1LR3AToYg2gPCb/BXExDE0hKJd7ZaV0aS +NSpJTtG2nrH8qFi8Y9WM41klwmE650idUN9SoecuUQsedFhZKPfJiKeTSt1CnqQr +nsQb3lr/LEwZmiehb+eDif2ndgAxP+T8ySHbdXvoX5zF5bcx63lhQKnExc9zmrWZ +gn60BnZ8aLr8G1CkMhm4fugdVkcXoQAmOXRJEdu3kbY8xI2350Cxw3H3E9gzpx6p +amKpVOK5Ag0EaXYSqAEQALMb57+x1zm2hs4DdCmajxRGZ1F4DJQFmKgV/z0KSeeO +8DYJp+vZ/zU6wQX6GU4kbYOK9+sUq8VrZTrUe1CFQuIfUWMQj03cXWizTTktcsfV +nLyj2ucNpTZxV2Yx/4A7T1x48ICt6q2vVoAI2nshqfrxL1J629olW8XG7v5kKQtx +IwHVVzgGgnfLVo/IkysudzYYAehP6E1aGiMRt6ZWOsq71FOeIjTD4FOmTzfzNyXP +zn31C3R6Cka7/xn/frN4KUVBu5ynFkfpifJvuSPX1DRk3nz+fEtilPCoHx9UZERm +sFKwjzPpCEoqMYi0PbjeJnILS32CvZE47uw6S2YDMsHzxbd3TcIgJP7VElI7Oa7r +n71KMEkyCTbD6kcy0qCQvcCVa4/868PBbBbiu1/I7AARC12jSprNI7NlRFkprfkm ++JtzsH7drjCLqr7GKWPzU1lgVYEC9vDJInPLH/GpbTF+wQM+K84n4KRSknHK2JAX +HQ6Aop1lUa/ZeWdD5oormYb8UrLs9fmSQ8GR9b8Jpca+0P3D5+3NSBJt1sGvkify +bC5EAAD8OJo8BLm6dfE/1u3L054h35Cw+RVv2zzhigN07YQ51Ljse6cadd2usYXz +yEuxk7983tSUup8elaVSGtSvKcTXjylpZoK+R8oemmdnEbuJM3zRFDRLPKqZdALV +ABEBAAGJAmwEGAEIAGAFgml2EqgJED7EZz63mCOKNRQAAAAAABwAEHNhbHRAbm90 +YXRpb25zLm9wZW5wZ3Bqcy5vcmfVQTha3ksLF9ZTQkJ+Iv5tApsMFiEEhm/ZPUVt +yoAPJEhBPsRnPreYI4oAAGkyD/9yA6a59iOhPedtOIEmjJWVvjtv06yYlnB66tbM +WGXWTofsb98CF9bymE+YvMNXYvqkw4q0P7OY6D64PXhQTSiYRtDscIZ8w3Hw5t73 +qttZ0QAx5HFjKoQUnyHvGO1rUgykKx+9sytTQwBIFq4FmyVQltsY8tX8D1nLS0iH +8IFwqBNM26bVcAkV9aeayjoRKodyy9Xz035Bmh8pFIMjM2JvCoub1TrftF2EzYng +ljQF76AQHGmPa36rq2oSocE+xP5GFyZv+PEPGCFTLo/5ZaHui8iqPMfVWIAdWAj1 +a6SW6zDk8DQQRGll4e7kWpGZ2+z4Zk9o449Ka7Kwc6lgpx86Ir6XT0XJKX55Q7Od +dKajTMDJE36t/00oAS4/AokL7StJTwmpMQAPv5/829uPkfcV6Oll79XvAcwV7vSq +0is+m5InUzkwunuBUsYBCtFKFY47oB5D0RLGSdUlo8GLfT1tf/0n4uRSq4aQgN/D +2gvvddXyFUds0Ar4y3Hthi1QHYOL5/4pmfhH45+Hxje03XSI9twGMqpFoennAYvV +wuyeu9XNXDI4gKiAMbzyxyhifOooBOyxOKEtXWPzfP8v9iTFw7cofmvSD1FTt45l +6JckYfV3TFaN2lFD5SxrasIuYPWPoOf4zzcljQjqOw0sVqXeaQgkq/+soD08YzKg +6cZ0NQ== +=3Ea2 +-----END PGP PUBLIC KEY BLOCK-----`, + } + + resp := DownloadResponse{ + Protocols: []string{"5.0"}, + OS: osType, + Arch: arch, + Filename: filename, + DownloadURL: downloadLink, + ShasumsURL: shasumsLink, + ShasumsSignatureURL: sigLink, + Shasum: shasumValue, + SigningKeys: SigningKeys{ + GPGPublicKeys: []GPGPublicKey{gpgKey}, + }, + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(resp) +} diff --git a/forNubes/scripts/build-and-push-registry-server.sh b/forNubes/scripts/build-and-push-registry-server.sh new file mode 100755 index 0000000..74c7e3e --- /dev/null +++ b/forNubes/scripts/build-and-push-registry-server.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Build and optionally push the registry-server image with docs handler +# Usage: ./scripts/build-and-push-registry-server.sh +IMAGE_REPO=${1:-naeel} +TAG=${2:-docs-dev} + +cd operator +make build-registry IMAGE_REPO=${IMAGE_REPO} TAG=${TAG} + +echo "Built image: ${IMAGE_REPO}/terraform-registry-server:${TAG}" + +echo "To push: docker push ${IMAGE_REPO}/terraform-registry-server:${TAG}" + +echo "To deploy to cluster (example):" +echo " kubectl apply -f operator/manifests/06-registry-server-docs.yaml" + +echo "and edit the file to replace IMAGE_REPO/TAG with your registry and tag before applying." diff --git a/forNubes/scripts/deploy-dev.sh b/forNubes/scripts/deploy-dev.sh new file mode 100644 index 0000000..c46edec --- /dev/null +++ b/forNubes/scripts/deploy-dev.sh @@ -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" diff --git a/forNubes/scripts/package_release.sh b/forNubes/scripts/package_release.sh new file mode 100755 index 0000000..d3d30d3 --- /dev/null +++ b/forNubes/scripts/package_release.sh @@ -0,0 +1,69 @@ +#!/bin/bash +set -e + +# Cleanup +rm -rf build_artifacts +mkdir -p build_artifacts + +VERSION="${1:-2.0.0}" + +# 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!" diff --git a/forNubes/scripts/publish-docs.sh b/forNubes/scripts/publish-docs.sh new file mode 100755 index 0000000..635a48b --- /dev/null +++ b/forNubes/scripts/publish-docs.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Usage: publish-docs.sh +SITE_DIR=${1:-site} +REGISTRY_HOST=${2:-terra.k8c.ru} +NAMESPACE=${3:-nubes} +NAME=${4:-nubes} +VERSION=${5:-dev} + +# Support both S3_* (New Standard) and MINIO_* (Legacy) variables +ENDPOINT=${S3_ENDPOINT:-${MINIO_ENDPOINT:-}} +ACCESS_KEY=${S3_ACCESS_KEY:-${MINIO_ACCESS_KEY:-}} +SECRET_KEY=${S3_SECRET_KEY:-${MINIO_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}/" diff --git a/forNubes/tests/lifecycle_scenario/finish_tests.sh b/forNubes/tests/lifecycle_scenario/finish_tests.sh new file mode 100755 index 0000000..eea5fc0 --- /dev/null +++ b/forNubes/tests/lifecycle_scenario/finish_tests.sh @@ -0,0 +1,84 @@ +#!/bin/bash +set -e + +# === CONFIGURATION === +ROOT_DIR="/home/naeel/terra" +TEST_DIR="$ROOT_DIR/tests/lifecycle_scenario" +TF_LOG_FILE="$TEST_DIR/test_output_tubulus_advanced.log" + +# Define colors +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' + +log() { + echo -e "${GREEN}[TEST] $1${NC}" + echo "[TEST] $1" >> $TF_LOG_FILE +} + +error() { + echo -e "${RED}[ERROR] $1${NC}" + echo "[ERROR] $1" >> $TF_LOG_FILE +} + +# 1. Build Provider +log "Building Provider..." +cd $ROOT_DIR +go build -o terraform-provider-nubes +if [ $? -ne 0 ]; then + error "Build failed!" + exit 1 +fi + +# 2. Setup Local Mirror in Test Dir +PLUGIN_DIR="$TEST_DIR/plugins/terraform.local/nubes/nubes/1.0.0/linux_amd64" +mkdir -p "$PLUGIN_DIR" +cp terraform-provider-nubes "$PLUGIN_DIR/" + +# 3. Setup Test Directory +log "Setting up Test Directory: $TEST_DIR" +cd $TEST_DIR + +# 4. Init with Plugin Dir +log "Initializing Terraform..." +rm -rf .terraform.lock.hcl .terraform +terraform init -plugin-dir="$TEST_DIR/plugins" > /dev/null + +echo "Resuming Tests from Step D..." > $TF_LOG_FILE + +# === ENSURE TF FILE IS CORRECT FOR DESTROY === +log "Ensuring TF file matches state..." +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Restored Description" + body_message = "Initial State" + duration_ms = 1000 +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +# === TEST D: SUSPEND (SOFT DELETE) === +log "TEST D: Suspending Instance (Terraform Destroy)..." +if terraform destroy -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST D PASSED: Destroy (Suspend) successful." +else + error "TEST D FAILED: Destroy returned error." + cat $TF_LOG_FILE + exit 1 +fi + +# === TEST E: RESUME (CREATE on SUSPENDED) === +log "TEST E: Resuming Instance (Terraform Apply)..." +if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST E PASSED: Resume (Create) successful." +else + error "TEST E FAILED: Apply (Resume) returned error." + cat $TF_LOG_FILE + exit 1 +fi + +log "ALL ADVANCED TESTS COMPLETED SUCCESSFULLY." diff --git a/forNubes/tests/lifecycle_scenario/main.tf b/forNubes/tests/lifecycle_scenario/main.tf new file mode 100644 index 0000000..ba1b167 --- /dev/null +++ b/forNubes/tests/lifecycle_scenario/main.tf @@ -0,0 +1,70 @@ +terraform { + required_providers { + nubes = { + source = "terraform.local/nubes/nubes" + } + } +} + +provider "nubes" { + api_endpoint = var.api_endpoint + api_token = var.api_token +} + +/* +resource "nubes_s3_bucket" "test_bucket" { + name = var.s3_bucket_name + s3_root_service_uid = "e5375174-36ec-4512-bba9-b56f9eeba0bd" # Dummy + + max_size_bytes = -1 + storage_class = "HOT" + + read_all = false + list_all = false + cors_all = false +} + +resource "nubes_postgres" "test_pg" { + organization_id = var.organization_id + organization_name = var.organization_name + resource_realm = "kvm-v1cl1-ssd" # Hardcoded or generic + s3_uid = nubes_s3_bucket.test_bucket.id + + cpu = var.pg_cpu + memory = var.pg_ram + disk = var.pg_disk + instances = 1 + version = var.pg_version + + backup_schedule = var.backup_schedule + backup_retention = 14 + parameters = "{}" + + enable_pgpooler_master = false + enable_pgpooler_slave = false + allow_no_ssl = false + auto_scale = false + auto_scale_percentage = 20 + auto_scale_tech_window = 1 + auto_scale_quota_gb = 20 + + need_external_address_master = false + need_external_address_slave = false + ip_space_name_master = "" + ip_space_name_slave = "" + + depends_on = [nubes_s3_bucket.test_bucket] +} + +output "pg_state" { + value = nubes_postgres.test_pg.id +} + +output "monitoring_url" { + value = nubes_postgres.test_pg.monitoring_url +} + +output "connection_string" { + value = nubes_postgres.test_pg.internal_connect_jdbc +} +*/ diff --git a/forNubes/tests/lifecycle_scenario/run_test.sh b/forNubes/tests/lifecycle_scenario/run_test.sh new file mode 100755 index 0000000..ae1d77c --- /dev/null +++ b/forNubes/tests/lifecycle_scenario/run_test.sh @@ -0,0 +1,124 @@ +#!/bin/bash +set -e + +# === CONFIGURATION === +ROOT_DIR="/home/naeel/terra" +TEST_DIR="$ROOT_DIR/tests/lifecycle_scenario" +DISCOVERY_DOC="$ROOT_DIR/docs/discovery/pg_s3_lifecycle_test.md" +HISTORY_DOC="$ROOT_DIR/docs/history/05_lifecycle_test_results.md" +TF_LOG_FILE="$TEST_DIR/test_output.log" +BUCKET_NAME="tf-lifecycle-bucket-$(date +%s)" + +# 1. Build Provider +echo ">>> Building Provider..." +cd $ROOT_DIR +go build -o terraform-provider-nubes +if [ $? -ne 0 ]; then + echo "Build failed!" + exit 1 +fi + +# 2. Setup Local Mirror in Test Dir +# We use -plugin-dir to force local loading effectively +PLUGIN_DIR="$TEST_DIR/plugins/terraform.local/nubes/nubes/1.0.0/linux_amd64" +mkdir -p "$PLUGIN_DIR" +cp terraform-provider-nubes "$PLUGIN_DIR/" + +# 3. Setup Test Directory +echo ">>> Setting up Test Directory: $TEST_DIR" +cd $TEST_DIR + +if [ ! -f "terraform.tfvars" ]; then + echo "Copying terraform.tfvars..." + cp "$ROOT_DIR/client_package/terraform.tfvars" . +fi + +# 4. Init with Plugin Dir (Bypasses network for terraform.local) +echo ">>> Initializing Terraform with local plugin mirror..." +rm -rf .terraform .terraform.lock.hcl +terraform init -plugin-dir="$TEST_DIR/plugins" > /dev/null + +echo "Starting Lifecycle Test" > $TF_LOG_FILE + +# === TEST STEPS === + +# Step 1: Create +echo ">>> Step 1: Initial Creation (S3 + PG [2CPU, 4GB])" | tee -a $TF_LOG_FILE +start_1=$(date +%s) +terraform apply -auto-approve \ + -var="pg_cpu=2" \ + -var="pg_ram=4" \ + -var="pg_disk=10" \ + -var="s3_bucket_name=$BUCKET_NAME" \ + >> $TF_LOG_FILE 2>&1 +end_1=$(date +%s) +dur_1=$((end_1 - start_1)) +echo "Step 1 Done: ${dur_1}s" | tee -a $TF_LOG_FILE + +# Step 2: Update (Scale Up) +echo ">>> Step 2: Modify PG (Scale Up [4CPU, 8GB])" | tee -a $TF_LOG_FILE +start_2=$(date +%s) +terraform apply -auto-approve \ + -var="pg_cpu=4" \ + -var="pg_ram=8" \ + -var="pg_disk=10" \ + -var="s3_bucket_name=$BUCKET_NAME" \ + >> $TF_LOG_FILE 2>&1 +end_2=$(date +%s) +dur_2=$((end_2 - start_2)) +echo "Step 2 Done: ${dur_2}s" | tee -a $TF_LOG_FILE + +# Step 3: Update (Scale Down) +echo ">>> Step 3: Modify PG (Scale Down [2CPU, 4GB])" | tee -a $TF_LOG_FILE +start_3=$(date +%s) +terraform apply -auto-approve \ + -var="pg_cpu=2" \ + -var="pg_ram=4" \ + -var="pg_disk=10" \ + -var="s3_bucket_name=$BUCKET_NAME" \ + >> $TF_LOG_FILE 2>&1 +end_3=$(date +%s) +dur_3=$((end_3 - start_3)) +echo "Step 3 Done: ${dur_3}s" | tee -a $TF_LOG_FILE + +# Step 4: Destroy +echo ">>> Step 4: Destroy" | tee -a $TF_LOG_FILE +terraform destroy -auto-approve \ + -var="pg_cpu=2" \ + -var="pg_ram=4" \ + -var="pg_disk=10" \ + -var="s3_bucket_name=$BUCKET_NAME" \ + >> $TF_LOG_FILE 2>&1 +echo "Cleanup Done" | tee -a $TF_LOG_FILE + +# === REPORT === +cd $ROOT_DIR +mkdir -p docs/discovery docs/history + +cat < $DISCOVERY_DOC +# Lifecycle Test: S3 + Postgres Update + +## Test Scenario +1. **Create S3**: $BUCKET_NAME +2. **Create Postgres**: 2CPU/4RAM +3. **Update**: 4CPU/8RAM (Expect In-place) +4. **Update**: 2CPU/4RAM (Expect In-place) +5. **Destroy**: (Expect Freeze policy to keep cloud resource) + +## Results $(date) +| Step | Duration | Result | +|------|----------|--------| +| Create | ${dur_1}s | Done | +| Scale Up | ${dur_2}s | Done | +| Scale Down | ${dur_3}s | Done | + +See $TF_LOG_FILE for details. +EOF + +cat < $HISTORY_DOC +# 05 Lifecycle Test Results +Date: $(date) +Success. Updates performed in-place. +EOF + +echo "Docs generated." diff --git a/forNubes/tests/lifecycle_scenario/run_tubulus_advanced.sh b/forNubes/tests/lifecycle_scenario/run_tubulus_advanced.sh new file mode 100755 index 0000000..98c7c5d --- /dev/null +++ b/forNubes/tests/lifecycle_scenario/run_tubulus_advanced.sh @@ -0,0 +1,142 @@ +#!/bin/bash +set -e + +# === CONFIGURATION === +ROOT_DIR="/home/naeel/terra" +TEST_DIR="$ROOT_DIR/tests/lifecycle_scenario" +TF_LOG_FILE="$TEST_DIR/test_output_tubulus_advanced.log" + +# Define colors +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' + +log() { + echo -e "${GREEN}[TEST] $1${NC}" + echo "[TEST] $1" >> $TF_LOG_FILE +} + +error() { + echo -e "${RED}[ERROR] $1${NC}" + echo "[ERROR] $1" >> $TF_LOG_FILE +} + +# 1. Build Provider +log "Building Provider..." +cd $ROOT_DIR +go build -o terraform-provider-nubes +if [ $? -ne 0 ]; then + error "Build failed!" + exit 1 +fi + +# 2. Setup Local Mirror in Test Dir +PLUGIN_DIR="$TEST_DIR/plugins/terraform.local/nubes/nubes/1.0.0/linux_amd64" +mkdir -p "$PLUGIN_DIR" +cp terraform-provider-nubes "$PLUGIN_DIR/" + +# 3. Setup Test Directory +log "Setting up Test Directory: $TEST_DIR" +cd $TEST_DIR + +# 4. Init with Plugin Dir +log "Initializing Terraform..." +rm -rf .terraform.lock.hcl .terraform +terraform init -plugin-dir="$TEST_DIR/plugins" > /dev/null + +echo "Starting Advanced Tubulus Tests" > $TF_LOG_FILE + +# === TEST A: MODIFY (HAPPY PATH) === +log "TEST A: Modifying instance description (Should Succeed)..." + +# Modify the TF file +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Modified Description by Test A" + body_message = "Initial State" + duration_ms = 1000 +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +# Apply update +if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST A PASSED: Modification successful." +else + error "TEST A FAILED: Apply returned error." + cat $TF_LOG_FILE + exit 1 +fi + +# === TEST B: MODIFY (FAILURE PATH) === +# This tests if the provider correctly reports mistakes during 'modify' operation +log "TEST B: Triggering failure during modification (Should Fail Gracefully)..." + +# Modify TF file to include failure instruction +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Modified Description by Test B (Will Fail)" + body_message = "Initial State" + duration_ms = 1000 + fail_in_progress = true +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +# Apply update (Expect Failure) +if ! terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST B PASSED: Terraform correctly reported failure." +else + error "TEST B FAILED: Terraform succeeded but should have failed!" + cat $TF_LOG_FILE + # We might need to fix the state if B passed unexpectedly, but let's assume correct behavior. +fi + +# === RESTORE STATE === +# Remove failure flag to allow subsequent tests (Suspend/Resume) +log "Restoring valid state..." +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Restored Description" + body_message = "Initial State" + duration_ms = 1000 +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +# We expect this to act as a repair or re-run +terraform apply -auto-approve >> $TF_LOG_FILE 2>&1 + +# === TEST D: SUSPEND (SOFT DELETE) === +log "TEST D: Suspending Instance (Terraform Destroy)..." +if terraform destroy -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST D PASSED: Destroy (Suspend) successful." +else + error "TEST D FAILED: Destroy returned error." + cat $TF_LOG_FILE + exit 1 +fi + +# === TEST E: RESUME (CREATE on SUSPENDED) === +log "TEST E: Resuming Instance (Terraform Apply)..." +if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST E PASSED: Resume (Create) successful." +else + error "TEST E FAILED: Apply (Resume) returned error." + cat $TF_LOG_FILE + exit 1 +fi + +log "ALL ADVANCED TESTS COMPLETED SUCCESSFULLY." diff --git a/forNubes/tests/lifecycle_scenario/run_tubulus_test.sh b/forNubes/tests/lifecycle_scenario/run_tubulus_test.sh new file mode 100755 index 0000000..8c30e68 --- /dev/null +++ b/forNubes/tests/lifecycle_scenario/run_tubulus_test.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +# === CONFIGURATION === +ROOT_DIR="/home/naeel/terra" +TEST_DIR="$ROOT_DIR/tests/lifecycle_scenario" +TF_LOG_FILE="$TEST_DIR/test_output_tubulus.log" + +# 1. Build Provider +echo ">>> Building Provider..." +cd $ROOT_DIR +go build -o terraform-provider-nubes +if [ $? -ne 0 ]; then + echo "Build failed!" + exit 1 +fi + +# 2. Setup Local Mirror in Test Dir +PLUGIN_DIR="$TEST_DIR/plugins/terraform.local/nubes/nubes/1.0.0/linux_amd64" +mkdir -p "$PLUGIN_DIR" +cp terraform-provider-nubes "$PLUGIN_DIR/" + +# 3. Setup Test Directory +echo ">>> Setting up Test Directory: $TEST_DIR" +cd $TEST_DIR + +# 4. Init with Plugin Dir +echo ">>> Initializing Terraform with local plugin mirror..." +rm -rf .terraform .terraform.lock.hcl +terraform init -plugin-dir="$TEST_DIR/plugins" > /dev/null + +echo "Starting Tubulus Lifecycle Test" > $TF_LOG_FILE + +# Step 1: Create +echo ">>> Step 1: Initial Creation" | tee -a $TF_LOG_FILE +terraform apply -auto-approve | tee -a $TF_LOG_FILE diff --git a/forNubes/tests/lifecycle_scenario/stress_tests.sh b/forNubes/tests/lifecycle_scenario/stress_tests.sh new file mode 100644 index 0000000..20ed0e7 --- /dev/null +++ b/forNubes/tests/lifecycle_scenario/stress_tests.sh @@ -0,0 +1,170 @@ +#!/bin/bash +set -e + +# === CONFIGURATION === +ROOT_DIR="/home/naeel/terra" +TEST_DIR="$ROOT_DIR/tests/lifecycle_scenario" +TF_LOG_FILE="$TEST_DIR/stress_test.log" + +# Define colors +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' +YELLOW='\033[1;33m' + +log() { + echo -e "${GREEN}[STRESS] $1${NC}" + echo "[STRESS] $1" >> $TF_LOG_FILE +} + +error() { + echo -e "${RED}[ERROR] $1${NC}" + echo "[ERROR] $1" >> $TF_LOG_FILE +} + +warn() { + echo -e "${YELLOW}[WARN] $1${NC}" + echo "[WARN] $1" >> $TF_LOG_FILE +} + +# Ensure we are in the right directory +cd $TEST_DIR + +echo "STARTING STRESS TESTS" > $TF_LOG_FILE + +# === TEST 1: FAST UPDATE (Duration 500ms) === +log "TEST 1: Fast Update (Modify)..." +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Stress Test 1: Fast" + body_message = "Go Fast" + duration_ms = 500 +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST 1 PASSED" +else + warn "TEST 1 Modify Failed. Instance might be in bad state." + log "Performing cleanup (Destroy & Recreate)..." + terraform destroy -auto-approve >> $TF_LOG_FILE 2>&1 + + if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST 1 PASSED (via Recreate)" + else + error "TEST 1 FAILED" + cat $TF_LOG_FILE + exit 1 + fi +fi + +# === TEST 2: HEAVY Load (Duration 5000ms) === +log "TEST 2: Long Duration Update (Should wait 5s+)..." +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Stress Test 2: Slow" + body_message = "Go Slow" + duration_ms = 5000 +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST 2 PASSED" +else + error "TEST 2 FAILED" + cat $TF_LOG_FILE + exit 1 +fi + +# === TEST 3: INTENTIONAL FAILURE (Fail At Start) === +log "TEST 3: Triggering Failure (fail_at_start=true)..." +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Stress Test 3: Doom" + fail_at_start = true + duration_ms = 1000 +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +if ! terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST 3 PASSED (Expected Failure occurred)" +else + error "TEST 3 FAILED: Terraform succeeded but should have failed!" + cat $TF_LOG_FILE + exit 1 +fi + +# === TEST 4: RECOVERY FROM FAILURE === +log "TEST 4: Recovery (Fixing config)..." +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Stress Test 4: Recovered" + body_message = "We are back" + duration_ms = 1000 +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST 4 PASSED" +else + warn "TEST 4 Modify Failed. This is expected if the previous failure froze the instance." + log "Attempting Hard Reset (Destroy & Recreate)..." + + # Force destroy to clear the stuck instance + terraform destroy -auto-approve >> $TF_LOG_FILE 2>&1 + + # Re-apply the valid configuration + if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST 4 RECOVERY PASSED (via Hard Reset)" + else + error "TEST 4 FAILED: Could not recover even with Hard Reset." + cat $TF_LOG_FILE + exit 1 + fi +fi + +# === TEST 5: COMPLEX UPDATE (Multiple Fields) === +log "TEST 5: Complex Multi-field Update..." +cat < tubulus.tf +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Stress Test 5: Complex" + body_message = "Complex Payload: JSON { key: 'value' }" + duration_ms = 2000 + yaml_example = "some: yaml" +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} +EOF + +if terraform apply -auto-approve >> $TF_LOG_FILE 2>&1; then + log "TEST 5 PASSED" +else + error "TEST 5 FAILED" + cat $TF_LOG_FILE + exit 1 +fi + +log "ALL STRESS TESTS COMPLETED SUCCESSFULLY. BOLVANKA IS HOT!" diff --git a/forNubes/tests/lifecycle_scenario/tubulus.tf b/forNubes/tests/lifecycle_scenario/tubulus.tf new file mode 100644 index 0000000..8afe75e --- /dev/null +++ b/forNubes/tests/lifecycle_scenario/tubulus.tf @@ -0,0 +1,11 @@ +resource "nubes_tubulus_instance" "bolvanka" { + display_name = "Bolvanka_Lifecycle_Test_014" + description = "Stress Test 5: Complex" + body_message = "Complex Payload: JSON { key: 'value' }" + duration_ms = 2000 + yaml_example = "some: yaml" +} + +output "bolvanka_id" { + value = nubes_tubulus_instance.bolvanka.id +} diff --git a/forNubes/tests/lifecycle_scenario/variables.tf b/forNubes/tests/lifecycle_scenario/variables.tf new file mode 100644 index 0000000..239164c --- /dev/null +++ b/forNubes/tests/lifecycle_scenario/variables.tf @@ -0,0 +1,55 @@ +variable "api_endpoint" { + type = string + default = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} + +variable "api_token" { + type = string + sensitive = true +} + +variable "organization_id" { + type = string + default = "dummy" +} + +variable "organization_name" { + type = string + default = "dummy" +} + +variable "s3_user_uid" { + type = string + description = "UID of the S3 User (required for S3 bucket)" + default = "dummy" +} + +variable "s3_bucket_name" { + type = string + default = "tf-test-bucket-lifecycle" +} + +variable "pg_version" { + type = string + default = "15" +} + +variable "pg_cpu" { + type = number + default = 2 +} + +variable "pg_ram" { + type = number + default = 4 +} + +variable "pg_disk" { + type = number + default = 10 +} + +variable "backup_schedule" { + type = string + default = "0 0 * * *" +} diff --git a/forNubes/tests/modify_postgres/main.tf b/forNubes/tests/modify_postgres/main.tf new file mode 100644 index 0000000..2f43b2c --- /dev/null +++ b/forNubes/tests/modify_postgres/main.tf @@ -0,0 +1,41 @@ +terraform { + required_providers { + nubes = { + source = "terraform.local/local/nubes" + } + } +} + +provider "nubes" { + api_token = var.api_token +} + +variable "api_token" { + type = string + sensitive = true +} + +variable "organization_id" { + type = string +} + +variable "s3_uid" { + type = string +} + +resource "nubes_postgres" "test_pg" { + organization_id = var.organization_id + organization_name = "test-org" + resource_realm = "k8s-3.ext.nubes.ru" + s3_uid = var.s3_uid + cpu = 700 + memory = 1024 + disk = 12 + instances = 1 + version = "17" + deletion_protection = false +} + +output "pg_id" { + value = nubes_postgres.test_pg.id +} diff --git a/forNubes/tests/modify_postgres/nli_test_results.json b/forNubes/tests/modify_postgres/nli_test_results.json new file mode 100644 index 0000000..21b4bee --- /dev/null +++ b/forNubes/tests/modify_postgres/nli_test_results.json @@ -0,0 +1,13 @@ +[ + { + "test": "Test1-Fast", + "instruction": "сделай быстро", + "operation": "plan", + "duration_ms": 0, + "body_message": "null", + "where_fail": 0, + "status": "failed", + "error": " on test_auto_nli.tf line 1, in resource \"nubes_tubulus_instance\" \"auto_test\":\n 1: resource \"nubes_tubulus_instance\" \"auto_test\" {\n\nFailed to get configuration from AI: GEMINI_API_KEY not f", + "timestamp": "2026-01-24T22:00:50+04:00" + } +] diff --git a/forNubes/tests/modify_postgres/plan_output.txt b/forNubes/tests/modify_postgres/plan_output.txt new file mode 100644 index 0000000..b6c42d6 --- /dev/null +++ b/forNubes/tests/modify_postgres/plan_output.txt @@ -0,0 +1,32 @@ + +Warning: Provider development overrides are in effect + +The following provider development overrides are set in the CLI +configuration: + - terraform.local/local/nubes in /home/naeel/terra + +The behavior may therefore not match any released version of the provider and +applying changes may cause the state to become incompatible with published +releases. + +Planning failed. Terraform encountered an error while generating this plan. + + +Warning: Resource targeting is in effect + +You are creating a plan with the -target option, which means that the result +of this plan may not represent all of the changes requested by the current +configuration. + +The -target option is not for routine use, and is provided only for +exceptional situations such as recovering from errors or mistakes, or when +Terraform specifically suggests to use it as part of an error message. + +Error: Gemini AI Error + + with nubes_tubulus_instance.auto_test, + on test_auto_nli.tf line 1, in resource "nubes_tubulus_instance" "auto_test": + 1: resource "nubes_tubulus_instance" "auto_test" { + +Failed to get configuration from AI: GEMINI_API_KEY not found (set env var or +create gemini_api_key.txt) diff --git a/forNubes/tests/modify_postgres/run_nli_tests.sh b/forNubes/tests/modify_postgres/run_nli_tests.sh new file mode 100755 index 0000000..85a2da7 --- /dev/null +++ b/forNubes/tests/modify_postgres/run_nli_tests.sh @@ -0,0 +1,198 @@ +#!/bin/bash +set -e + +# NLI Auto Test Suite +# Создаёт, проверяет и удаляет Tubulus инстансы с AI инструкциями + +export TF_CLI_CONFIG_FILE="/home/naeel/terra/dev_override.tfrc" +cd /home/naeel/terra/tests/modify_postgres + +RESULTS_FILE="nli_test_results.json" +echo "[]" > $RESULTS_FILE + +log_result() { + local test_name="$1" + local instruction="$2" + local operation="$3" + local duration_ms="$4" + local body_message="$5" + local where_fail="$6" + local status="$7" + local error="$8" + + # Escape quotes for JSON + instruction=$(echo "$instruction" | sed 's/"/\\"/g') + body_message=$(echo "$body_message" | sed 's/"/\\"/g') + error=$(echo "$error" | sed 's/"/\\"/g' | head -c 200) + + jq ". += [{ + \"test\": \"$test_name\", + \"instruction\": \"$instruction\", + \"operation\": \"$operation\", + \"duration_ms\": $duration_ms, + \"body_message\": \"$body_message\", + \"where_fail\": $where_fail, + \"status\": \"$status\", + \"error\": \"$error\", + \"timestamp\": \"$(date -Iseconds)\" + }]" $RESULTS_FILE > ${RESULTS_FILE}.tmp && mv ${RESULTS_FILE}.tmp $RESULTS_FILE +} + +run_create_test() { + local name="$1" + local instruction="$2" + + echo "=== Testing: $name ===" + echo "Instruction: $instruction" + + cat > test_auto_nli.tf < plan_output.txt 2>&1; then + log_result "$name" "$instruction" "plan" "0" "null" "0" "failed" "$(cat plan_output.txt | tail -5)" + return 1 + fi + + # Apply + if ! terraform apply -auto-approve -var-file=terraform.tfvars -target=nubes_tubulus_instance.auto_test -no-color > apply_output.txt 2>&1; then + log_result "$name" "$instruction" "apply" "0" "null" "0" "failed" "$(cat apply_output.txt | tail -5)" + return 1 + fi + + # Extract outputs + duration=$(terraform output -raw duration 2>/dev/null || echo "0") + body=$(terraform output -raw body_msg 2>/dev/null || echo "null") + fail=$(terraform output -raw where_fail 2>/dev/null || echo "0") + status=$(terraform output -raw status 2>/dev/null || echo "unknown") + + log_result "$name" "$instruction" "create" "$duration" "$body" "$fail" "success" "" + + echo "✓ Created: duration=$duration, body=$body, fail=$fail, status=$status" + + # Cleanup + terraform destroy -auto-approve -var-file=terraform.tfvars -target=nubes_tubulus_instance.auto_test -no-color > /dev/null 2>&1 || true + rm -f test_auto_nli.tf + + return 0 +} + +run_modify_test() { + local name="$1" + local instruction1="$2" + local instruction2="$3" + + echo "=== Modify Test: $name ===" + echo "Initial: $instruction1" + echo "Modified: $instruction2" + + # Create initial + cat > test_auto_nli.tf < /dev/null 2>&1 || return 1 + + duration1=$(terraform output -raw duration 2>/dev/null || echo "0") + log_result "$name-initial" "$instruction1" "create" "$duration1" "null" "0" "success" "" + + # Modify + cat > test_auto_nli.tf < /dev/null 2>&1; then + duration2=$(terraform output -raw duration 2>/dev/null || echo "0") + log_result "$name-modified" "$instruction2" "modify" "$duration2" "null" "0" "success" "" + echo "✓ Modified: $duration1 → $duration2" + else + log_result "$name-modified" "$instruction2" "modify" "0" "null" "0" "failed" "modify not supported" + echo "✗ Modify failed (expected for immutable resource)" + fi + + # Cleanup + terraform destroy -auto-approve -var-file=terraform.tfvars -target=nubes_tubulus_instance.auto_test -no-color > /dev/null 2>&1 || true + rm -f test_auto_nli.tf + + return 0 +} + +echo "🚀 Starting NLI Auto Test Suite" +echo "================================" +echo "" + +# 10 CREATE tests +run_create_test "Test1-Fast" "сделай быстро" +sleep 2 +run_create_test "Test2-Long" "пусть работает очень долго" +sleep 2 +run_create_test "Test3-Message" "напиши hello world в вольт" +sleep 2 +run_create_test "Test4-Fail" "сломай сразу при старте" +sleep 2 +run_create_test "Test5-FailMid" "упади в процессе на втором этапе" +sleep 2 +run_create_test "Test6-Complex" "поработай 15 секунд, положи пароль123 и упади на третьем" +sleep 2 +run_create_test "Test7-Typo" "зделай нармална на 7 сикунд" +sleep 2 +run_create_test "Test8-Minute" "создай на минуту без ошибок" +sleep 2 +run_create_test "Test9-Vague" "просто что-нибудь сделай" +sleep 2 +run_create_test "Test10-Technical" "duration 25000ms, fail at stage 1, message: SECRET" +sleep 2 + +# 5 MODIFY tests +run_modify_test "Modify1" "быстро" "медленно на 2 минуты" +sleep 2 +run_modify_test "Modify2" "без ошибок" "сломай в середине" +sleep 2 +run_modify_test "Modify3" "напиши hello" "напиши goodbye" +sleep 2 +run_modify_test "Modify4" "на 10 секунд" "на 30 секунд" +sleep 2 +run_modify_test "Modify5" "stage 1" "stage 3" + +echo "" +echo "================================" +echo "✅ Test suite completed!" +echo "Results saved to: $RESULTS_FILE" +cat $RESULTS_FILE | jq '.' diff --git a/forNubes/tests/modify_postgres/test_ai.tf b/forNubes/tests/modify_postgres/test_ai.tf new file mode 100644 index 0000000..d54aa50 --- /dev/null +++ b/forNubes/tests/modify_postgres/test_ai.tf @@ -0,0 +1,18 @@ +resource "nubes_tubulus_instance" "ai_test" { + display_name = "AI Bolvanka v4 unique" + description = "Testing Gemini AI instruction" + + instruction = "Создай тубулус на 12 секунд, без ошибок, в вольт напиши: 'секретный ключ 123'" +} + +output "ai_duration" { + value = nubes_tubulus_instance.ai_test.duration_ms +} + +output "ai_fail_in_progress" { + value = nubes_tubulus_instance.ai_test.fail_in_progress +} + +output "ai_where_fail" { + value = nubes_tubulus_instance.ai_test.where_fail +} diff --git a/forNubes/tests/modify_postgres/test_ai_advanced.tf b/forNubes/tests/modify_postgres/test_ai_advanced.tf new file mode 100644 index 0000000..5e57214 --- /dev/null +++ b/forNubes/tests/modify_postgres/test_ai_advanced.tf @@ -0,0 +1,29 @@ +resource "nubes_tubulus_instance" "advanced1" { + display_name = "Advanced 1" + description = "Сложный сценарий" + instruction = "создай на 30 секунд, пусть упадет в середине работы, в вольт запиши мой_пароль_123" +} + +resource "nubes_tubulus_instance" "advanced2" { + display_name = "Advanced 2" + description = "Домохозяйка 1" + instruction = "я не знаю что делать просто сделай что-нибудь" +} + +resource "nubes_tubulus_instance" "advanced3" { + display_name = "Advanced 3" + description = "Домохозяйка 2" + instruction = "нужно чтобы это работало хотя бы полминутки" +} + +resource "nubes_tubulus_instance" "advanced4" { + display_name = "Advanced 4" + description = "Технический" + instruction = "duration 15000ms, fail at stage 3, message: SECRET_KEY" +} + +resource "nubes_tubulus_instance" "advanced5" { + display_name = "Advanced 5" + description = "Смешанный стиль" + instruction = "поработай секунд 20, положи туда 'hello world' и вали на 1 этапе" +} diff --git a/forNubes/tests/modify_postgres/test_ai_simple.tf b/forNubes/tests/modify_postgres/test_ai_simple.tf new file mode 100644 index 0000000..2732fb3 --- /dev/null +++ b/forNubes/tests/modify_postgres/test_ai_simple.tf @@ -0,0 +1,41 @@ +resource "nubes_tubulus_instance" "test1" { + display_name = "Test 1" + description = "Тест простой запрос" + instruction = "сделай быстро" +} + +resource "nubes_tubulus_instance" "test2" { + display_name = "Test 2" + description = "Тест долго" + instruction = "пусть работает долго долго" +} + +resource "nubes_tubulus_instance" "test3" { + display_name = "Test 3" + description = "Тест с ошибкой" + instruction = "сломай его сразу" +} + +resource "nubes_tubulus_instance" "test4" { + display_name = "Test 4" + description = "Тест с сообщением" + instruction = "напиши привет" +} + +resource "nubes_tubulus_instance" "test5" { + display_name = "Test 5" + description = "Противоречивый" + instruction = "сделай быстро но долго и сломай но не ломай" +} + +resource "nubes_tubulus_instance" "test6" { + display_name = "Test 6" + description = "Неполный" + instruction = "хочу тубулус" +} + +resource "nubes_tubulus_instance" "test7" { + display_name = "Test 7" + description = "С опечатками" + instruction = "зделай нормална на 10 сикунд" +} diff --git a/forNubes/tests/modify_postgres/test_auto_nli.tf b/forNubes/tests/modify_postgres/test_auto_nli.tf new file mode 100644 index 0000000..4075749 --- /dev/null +++ b/forNubes/tests/modify_postgres/test_auto_nli.tf @@ -0,0 +1,21 @@ +resource "nubes_tubulus_instance" "auto_test" { + display_name = "Test1-Fast" + description = "NLI Auto Test" + instruction = "сделай быстро" +} + +output "duration" { + value = nubes_tubulus_instance.auto_test.duration_ms +} + +output "body_msg" { + value = nubes_tubulus_instance.auto_test.body_message +} + +output "where_fail" { + value = nubes_tubulus_instance.auto_test.where_fail +} + +output "status" { + value = nubes_tubulus_instance.auto_test.status +} diff --git a/forNubes/tests/modify_postgres/tubulus_comprehensive_results.json b/forNubes/tests/modify_postgres/tubulus_comprehensive_results.json new file mode 100644 index 0000000..1fd3de5 --- /dev/null +++ b/forNubes/tests/modify_postgres/tubulus_comprehensive_results.json @@ -0,0 +1,92 @@ +[ + { + "scenario": "destroy", + "test": "Test1-Destroy", + "instruction": "сделай быстро", + "operation": "apply", + "status": "failed", + "error": " 1: resource \"nubes_tubulus_instance\" \"test\" {\n\nFailed to get configuration from AI: Post\n\"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?%24alt=json%3", + "timestamp": "2026-01-25T10:18:05+04:00" + }, + { + "scenario": "destroy", + "test": "Test2-Destroy", + "instruction": "работай 30 секунд", + "operation": "apply", + "status": "failed", + "error": "Content-Type: application/json Content-Length: 119 Version: 1.0.217\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff Referrer-Policy: origin X-Frame-Optio", + "timestamp": "2026-01-25T10:18:21+04:00" + }, + { + "scenario": "destroy", + "test": "Test3-Destroy", + "instruction": "напиши в вольт секрет_123", + "operation": "plan", + "status": "failed", + "error": "\nError: Gemini AI Error\n\n with nubes_tubulus_instance.advanced3,\n on test_ai_advanced.tf line 13, in resource \"nubes_tubulus_instance\" \"advanced3\":\n 13: resource \"nubes_tubulus_instance\" \"ad", + "timestamp": "2026-01-25T10:18:32+04:00" + }, + { + "scenario": "destroy", + "test": "Test4-Destroy", + "instruction": "сломайся на 2 этапе", + "operation": "apply", + "status": "failed", + "error": "Content-Type: application/json Content-Length: 119 Version: 1.0.217\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff Referrer-Policy: origin X-Frame-Optio", + "timestamp": "2026-01-25T10:18:47+04:00" + }, + { + "scenario": "destroy", + "test": "Test5-Destroy", + "instruction": "долго долго работай", + "operation": "apply", + "status": "failed", + "error": "Content-Type: application/json Content-Length: 119 Version: 1.0.217\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff Referrer-Policy: origin X-Frame-Optio", + "timestamp": "2026-01-25T10:18:57+04:00" + }, + { + "scenario": "remove", + "test": "Test1-Remove", + "instruction": "сделай на 5 секунд", + "operation": "create", + "status": "failed", + "error": "Content-Type: application/json Content-Length: 119 Version: 1.0.217\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff Referrer-Policy: origin X-Frame-Optio", + "timestamp": "2026-01-25T10:19:06+04:00" + }, + { + "scenario": "remove", + "test": "Test2-Remove", + "instruction": "быстро быстро", + "operation": "create", + "status": "failed", + "error": "Content-Type: application/json Content-Length: 119 Version: 1.0.217\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff Referrer-Policy: origin X-Frame-Optio", + "timestamp": "2026-01-25T10:19:21+04:00" + }, + { + "scenario": "remove", + "test": "Test3-Remove", + "instruction": "положи текст hello_world", + "operation": "create", + "status": "failed", + "error": "Content-Type: application/json Content-Length: 119 Version: 1.0.217\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff Referrer-Policy: origin X-Frame-Optio", + "timestamp": "2026-01-25T10:19:29+04:00" + }, + { + "scenario": "remove", + "test": "Test4-Remove", + "instruction": "сломайся сразу", + "operation": "create", + "status": "failed", + "error": "Content-Type: application/json Content-Length: 119 Version: 1.0.217\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff Referrer-Policy: origin X-Frame-Optio", + "timestamp": "2026-01-25T10:19:38+04:00" + }, + { + "scenario": "remove", + "test": "Test5-Remove", + "instruction": "работай минуту", + "operation": "create", + "status": "failed", + "error": "Content-Type: application/json Content-Length: 119 Version: 1.0.217\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff Referrer-Policy: origin X-Frame-Optio", + "timestamp": "2026-01-25T10:19:45+04:00" + } +] diff --git a/forNubes/tests/postgres_test/dev_override.tfrc b/forNubes/tests/postgres_test/dev_override.tfrc new file mode 100644 index 0000000..d8f97e5 --- /dev/null +++ b/forNubes/tests/postgres_test/dev_override.tfrc @@ -0,0 +1,6 @@ +provider_installation { + dev_overrides { + "terraform.local/local/nubes" = "/home/naeel/terra/tests/postgres_test" + } + direct {} +} diff --git a/forNubes/tests/postgres_test/main.tf b/forNubes/tests/postgres_test/main.tf new file mode 100644 index 0000000..c55e3f8 --- /dev/null +++ b/forNubes/tests/postgres_test/main.tf @@ -0,0 +1,41 @@ +terraform { + required_providers { + nubes = { + source = "terraform.local/local/nubes" + } + } +} + +provider "nubes" { + api_token = var.api_token +} + +variable "api_token" { + type = string + sensitive = true +} + +variable "organization_id" { + type = string +} + +variable "s3_uid" { + type = string +} + +resource "nubes_postgres" "test_pg" { + organization_id = var.organization_id + organization_name = "test-org" + resource_realm = "k8s-3.ext.nubes.ru" + s3_uid = var.s3_uid + cpu = 777 + memory = 2048 + disk = 11 + instances = 1 + version = "17" + deletion_protection = false # Для тестов отключаем +} + +output "pg_id" { + value = nubes_postgres.test_pg.id +} diff --git a/forNubes/tests/s3_test/main.tf b/forNubes/tests/s3_test/main.tf new file mode 100644 index 0000000..f23fa17 --- /dev/null +++ b/forNubes/tests/s3_test/main.tf @@ -0,0 +1,40 @@ +terraform { + required_providers { + nubes = { + source = "terraform.local/nubes/nubes" + } + } +} + +provider "nubes" { + api_token = var.api_token +} + +variable "api_token" { + type = string + sensitive = true +} + +variable "bucket_name" { + type = string +} + +variable "max_size" { + type = number +} + +resource "nubes_s3_bucket" "test" { + name = var.bucket_name + s3_root_service_uid = "e5375174-36ec-4512-bba9-b56f9eeba0bd" # Dummy or real key + + max_size_bytes = var.max_size + storage_class = "HOT" + + read_all = true + list_all = true + cors_all = true +} + +output "bucket_id" { + value = nubes_s3_bucket.test.id +} \ No newline at end of file diff --git a/forNubes/tests/s3_test/run_s3_test.sh b/forNubes/tests/s3_test/run_s3_test.sh new file mode 100755 index 0000000..b0a9842 --- /dev/null +++ b/forNubes/tests/s3_test/run_s3_test.sh @@ -0,0 +1,83 @@ +#!/bin/bash +set -e + +# Define root and test dirs +ROOT_DIR="/home/naeel/terra" +TEST_DIR="$ROOT_DIR/tests/s3_test" +LOG_FILE="$TEST_DIR/s3_test.log" + +echo "=== Starting S3 Resource Test ===" | tee $LOG_FILE + +# 1. Build Provider +echo "Building provider..." | tee -a $LOG_FILE +cd $ROOT_DIR +go build -o terraform-provider-nubes +if [ $? -ne 0 ]; then + echo "Build failed!" | tee -a $LOG_FILE + exit 1 +fi +echo "Provider built successfully." | tee -a $LOG_FILE + +# Copy provider to plugins dir +PLUGIN_DIR="$TEST_DIR/plugins/terraform.local/nubes/nubes/1.0.0/linux_amd64" +mkdir -p "$PLUGIN_DIR" +cp terraform-provider-nubes "$PLUGIN_DIR/terraform-provider-nubes_v1.0.0" +echo "Provider installed to $PLUGIN_DIR" | tee -a $LOG_FILE + +# 2. Setup Test Environment +cd $TEST_DIR +# Reuse terraform.tfvars from client_package if it exists +if [ -f "$ROOT_DIR/client_package/terraform.tfvars" ]; then + cp "$ROOT_DIR/client_package/terraform.tfvars" . +else + echo "Error: terraform.tfvars not found!" | tee -a $LOG_FILE + exit 1 +fi + +# 3. Initialize (Plugin Dir Mode) +echo "Initializing Terraform..." | tee -a $LOG_FILE +rm -rf .terraform .terraform.lock.hcl +terraform init -plugin-dir="$TEST_DIR/plugins" >> $LOG_FILE 2>&1 + +BUCKET_NAME="tf-s3-test-$(date +%s)" + +# 4. Create +echo "Step 1: Creating S3 Bucket ($BUCKET_NAME)..." | tee -a $LOG_FILE +start_time=$(date +%s) +terraform apply -auto-approve \ + -var="bucket_name=$BUCKET_NAME" \ + -var="max_size=100" \ + >> $LOG_FILE 2>&1 +end_time=$(date +%s) +echo "Created in $((end_time - start_time))s" | tee -a $LOG_FILE + +# 5. Modify (Update size) - SKIPPED because API forbids name reuse immediately +echo "Step 2: Modifying S3 Bucket... SKIPPED (API does not support updates)" | tee -a $LOG_FILE + +# 6. Upload File using Python (No boto3/CLI dependency) +echo "Step 2.5: Uploading file to S3..." | tee -a $LOG_FILE +echo "This is a test file for Nubes S3." > test_file.txt + +export AWS_ACCESS_KEY_ID="0GLQRD38H4I6RBDB0EWJ" +export AWS_SECRET_ACCESS_KEY="eTFibiHmBd96IApj9PYsboTR6OBoD7osxoarHykw" +export AWS_DEFAULT_REGION="msk-1" +export S3_ENDPOINT="https://s3.msk-1.ngcloud.ru" + +python3 upload_mini.py "$BUCKET_NAME" "test_file.txt" "test_file.txt" >> $LOG_FILE 2>&1 +if [ $? -eq 0 ]; then + echo "SUCCESS: File uploaded." | tee -a $LOG_FILE +else + echo "WARNING: File upload failed, but continuing..." | tee -a $LOG_FILE +fi + +# 7. Destroy +echo "Step 3: Destroying S3 Bucket..." | tee -a $LOG_FILE +start_time=$(date +%s) +terraform destroy -auto-approve \ + -var="bucket_name=$BUCKET_NAME" \ + -var="max_size=200" \ + >> $LOG_FILE 2>&1 +end_time=$(date +%s) +echo "Destroyed in $((end_time - start_time))s" | tee -a $LOG_FILE + +echo "=== Test Completed ===" | tee -a $LOG_FILE diff --git a/forNubes/tests/vdc_test/main.tf b/forNubes/tests/vdc_test/main.tf new file mode 100644 index 0000000..9ef6976 --- /dev/null +++ b/forNubes/tests/vdc_test/main.tf @@ -0,0 +1,60 @@ +terraform { + required_providers { + nubes = { + source = "terraform.local/nubes/nubes" + } + } +} + +variable "api_token" { + type = string +} + +variable "organization_id" { + type = string +} + +variable "provider_vdc" { + type = string +} + +variable "network_pool" { + type = string +} + +variable "storage_profiles" { + type = string +} + +variable "cpu_quota" { + type = number + default = 10 +} + +variable "ram_quota" { + type = number + default = 20 +} + +provider "nubes" { + api_token = var.api_token +} + +resource "nubes_vdc" "test" { + display_name = "terraform-vdc-modify-test" + description = "Testing VDC modify operation" + organization_uid = var.organization_id + provider_vdc = var.provider_vdc + network_pool = var.network_pool + storage_profiles = var.storage_profiles + cpu_allocation_pct = 20 + ram_allocation_pct = 20 + cpu_quota = var.cpu_quota + ram_quota = var.ram_quota + + deletion_protection = true +} + +output "vdc_id" { + value = nubes_vdc.test.id +} diff --git a/forNubes/tools/debug_req.go b/forNubes/tools/debug_req.go new file mode 100644 index 0000000..c1019da --- /dev/null +++ b/forNubes/tools/debug_req.go @@ -0,0 +1,41 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "time" +) + +// Token from terraform.tfvars (simplified) +const token = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJUdzZMa1FRS1ctZHlpb0hoYXlxOHRxa2dhb2RqQjM0bVAtSDBjUkpGWkRJIn0.eyJleHAiOjE3Njk4NDQxMTAsImlhdCI6MTc2OTgzNjkxMCwiYXV0aF90aW1lIjoxNzY5ODM2ODg5LCJqdGkiOiIzMGM2MjZlMC1kMDY2LTQzNTItODFiMC0yZGM4Yjk0ODM5NzIiLCJpc3MiOiJodHRwczovL2tleWNsb2FrLm51YmVzLnJ1L3JlYWxtcy9jbG91ZCIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhNTVhNjFlZC1iMTU5LTQ3YjYtYmIxNi00N2Q4ZjIyZDQ4MGIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhY2NvdW50LWNvbnNvbGUiLCJub25jZSI6IjM2ODc0MDljLWU1MGQtNGVkMi1iY2JkLTU1ODA2MmNlY2ZkNCIsInNlc3Npb25fc3RhdGUiOiI1YTYxMTdkMS03ZTI2LTQ3YTQtOWUxYS0xZTFlZTViYTlmNzQiLCJhY3IiOiIwIiwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyJdfX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJzaWQiOiI1YTYxMTdkMS03ZTI2LTQ3YTQtOWUxYS0xZTFlZTViYTlmNzQiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6ItCi0LDQt9C10YLQtNC40L3QvtCyINCd0LDQuNC70YwiLCJDbGllbnRJRCI6IldaMDEzMjUiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0YXpldEBuYXJvZC5ydSIsImxvY2FsZSI6InJ1IiwiZ2l2ZW5fbmFtZSI6ItCi0LDQt9C10YLQtNC40L3QvtCyIiwiZmFtaWx5X25hbWUiOiLQndCw0LjQu9GMIiwiZW1haWwiOiJ0YXpldEBuYXJvZC5ydSJ9.gqIGJ5aH1nqBqpnvVOtZWXMt3pZp5m264xQ3ar2D1rUOh0NHSFtoIKSjJkgSlmFeFMjd-C261MQDg78YwGzYn2RH8hLJq-ht0l07-ZGr9bqYNyjcNTyeQ_bjqA5sGBKzTd1XfA4jsuMqZ6rIZAsy_NnVunHodivHSSKv7LdArZeyTeOIFVojattX96CDiBLnM45utOtS0NM3Ae8rowKJWj9l46N4c8n8u-zJ6rx5eP50LOYjz1sod7lwcgatTATDHDwH0moYabzPdeOvK-xpbSmibMhKUpaHLNnVyL6qTqn0zh_S81mXeqZmdXozlhrjnF3wCUdz0rXDJqmBnq50fA" + +func main() { + url := "https://deck-api.ngcloud.ru/api/v1/index.cfm/instances" + payload := map[string]interface{}{ + "serviceId": 1, + "displayName": "DebugReq-Test", + "descr": "Created via debug tool", + } + b, _ := json.Marshal(payload) + + req, _ := http.NewRequest("POST", url, bytes.NewBuffer(b)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + req.Header.Set("Connection", "close") // Try to force close + + client := &http.Client{Timeout: 10 * time.Second} + fmt.Println("Sending request...") + resp, err := client.Do(req) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + defer resp.Body.Close() + + fmt.Printf("Status: %s\n", resp.Status) + body, _ := io.ReadAll(resp.Body) + fmt.Printf("Body: %s\n", string(body)) +} diff --git a/forNubes/tools/docs_publish/README.md b/forNubes/tools/docs_publish/README.md new file mode 100644 index 0000000..166dd16 --- /dev/null +++ b/forNubes/tools/docs_publish/README.md @@ -0,0 +1,209 @@ +# Публикация документации (Nubes Terraform Provider) + +## Назначение +Эта инструкция описывает полный цикл: генерация страниц ресурсов, сборка сайта и загрузка на https://terra.k8c.ru. + +## Требования +- Docker +- `mc` (MinIO Client) +- Доступ к S3 (переменные `S3_ENDPOINT`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`) + +## 1) Генерация страниц ресурсов +Источники параметров: `universal_rebuild/resources_yaml/*.yaml`. + +Генератор страниц (создаёт/обновляет файлы в `docs/30_registry/resources/` и индекс): + +```bash +/home/naeel/terra/.venv/bin/python - <<'PY' +import glob +import os +import re +import yaml + +resources_yaml_dir = "/home/naeel/terra/universal_rebuild/resources_yaml" +resources_gen_dir = "/home/naeel/terra/universal_rebuild/internal/resources_gen" +resources_docs_dir = "/home/naeel/terra/docs/30_registry/resources" +all_resources_path = os.path.join(resources_docs_dir, "all_resources.md") +index_path = os.path.join(resources_docs_dir, "index.md") + +os.makedirs(resources_docs_dir, exist_ok=True) + +def parse_computed_fields(go_path: str): + try: + with open(go_path, "r", encoding="utf-8") as f: + lines = f.readlines() + except FileNotFoundError: + return [] + + computed = [] + i = 0 + while i < len(lines): + line = lines[i] + m = re.search(r'"([a-zA-Z0-9_]+)"\s*:\s*schema\.[A-Za-z]+Attribute\{', line) + if not m: + i += 1 + continue + name = m.group(1) + block_lines = [line] + i += 1 + while i < len(lines) and "}," not in lines[i]: + block_lines.append(lines[i]) + i += 1 + if i < len(lines): + block_lines.append(lines[i]) + block_text = "".join(block_lines) + if "Computed: true" in block_text: + computed.append(name) + i += 1 + return sorted(set(computed)) + +computed_by_name = {} +for go_path in glob.glob(os.path.join(resources_gen_dir, "*_resource.go")): + name = None + with open(go_path, "r", encoding="utf-8") as f: + for line in f: + if line.startswith("// Service:"): + name = line.split(":", 1)[1].strip() + break + if not name: + continue + computed_by_name[name] = parse_computed_fields(go_path) + +files = sorted(glob.glob(os.path.join(resources_yaml_dir, "service_*.yaml"))) +resource_names = [] + +all_lines = [] +all_lines.append("# Ресурсы провайдера (полный список)\n\n") +all_lines.append("Автогенерируемая сводка по ресурсам из `resources_yaml` и схем.\n\n") + +for path in files: + with open(path, "r", encoding="utf-8") as f: + data = yaml.safe_load(f) + if not data: + continue + name = data.get("name", "unknown") + service_id = data.get("service_id", "unknown") + resource_names.append(name) + + create = data.get("create", {}).get("params", []) or [] + modify = data.get("modify", {}).get("params", []) or [] + computed = computed_by_name.get(name, []) + + doc_path = os.path.join(resources_docs_dir, f"{name}.md") + lines = [] + lines.append(f"# Resource nubes_{name}\n\n") + lines.append(f"Service ID: `{service_id}`\n\n") + + lines.append("## Output поля\n\n") + if computed: + for c in computed: + lines.append(f"- `{c}`\n") + else: + lines.append("Нет.\n") + lines.append("\n") + + lines.append("## Create параметры\n\n") + if create: + lines.append("| Code | Type | Required | Default | ID |\n") + lines.append("|---|---|---|---|---|\n") + for p in create: + code = p.get("code", "") + ptype = p.get("type", "") + required = str(p.get("required", False)).lower() + default = p.get("default", "") + pid = p.get("id", "") + lines.append(f"| `{code}` | `{ptype}` | `{required}` | `{default}` | `{pid}` |\n") + else: + lines.append("Нет.\n") + + lines.append("\n## Modify параметры\n\n") + if modify: + lines.append("| Code | Type | Required | Default | ID |\n") + lines.append("|---|---|---|---|---|\n") + for p in modify: + code = p.get("code", "") + ptype = p.get("type", "") + required = str(p.get("required", False)).lower() + default = p.get("default", "") + pid = p.get("id", "") + lines.append(f"| `{code}` | `{ptype}` | `{required}` | `{default}` | `{pid}` |\n") + else: + lines.append("Нет.\n") + + with open(doc_path, "w", encoding="utf-8") as f: + f.write("".join(lines)) + + all_lines.append(f"## nubes_{name}\n") + all_lines.append(f"- Service ID: {service_id}\n") + all_lines.append("### Output поля\n") + if computed: + all_lines.append(", ".join(f"`{c}`" for c in computed) + "\n") + else: + all_lines.append("Нет.\n") + + all_lines.append("\n### Create параметры\n") + if create: + all_lines.append("| Code | Type | Required | Default | ID |\n") + all_lines.append("|---|---|---|---|---|\n") + for p in create: + code = p.get("code", "") + ptype = p.get("type", "") + required = str(p.get("required", False)).lower() + default = p.get("default", "") + pid = p.get("id", "") + all_lines.append(f"| `{code}` | `{ptype}` | `{required}` | `{default}` | `{pid}` |\n") + else: + all_lines.append("Нет.\n") + + all_lines.append("\n### Modify параметры\n") + if modify: + all_lines.append("| Code | Type | Required | Default | ID |\n") + all_lines.append("|---|---|---|---|---|\n") + for p in modify: + code = p.get("code", "") + ptype = p.get("type", "") + required = str(p.get("required", False)).lower() + default = p.get("default", "") + pid = p.get("id", "") + all_lines.append(f"| `{code}` | `{ptype}` | `{required}` | `{default}` | `{pid}` |\n") + else: + all_lines.append("Нет.\n") + + all_lines.append("\n") + +resource_names_sorted = sorted(set(resource_names)) +index_lines = ["# Resources\n\n", "Список всех ресурсов провайдера.\n\n"] +for name in resource_names_sorted: + index_lines.append(f"- [{name}](./{name}.md)\n") + +with open(index_path, "w", encoding="utf-8") as f: + f.write("".join(index_lines)) + +with open(all_resources_path, "w", encoding="utf-8") as f: + f.write("".join(all_lines)) + +print(f"Generated {len(resource_names_sorted)} resource pages.") +print(f"Wrote {index_path}") +print(f"Wrote {all_resources_path}") +PY +``` + +## 2) Навигация mkdocs +Проверьте, что `mkdocs.yml` содержит: +- `Resources` (index + all_resources) +- `Guides` (getting-started, terraform-basics) + +## 3) Сборка сайта +```bash +docker run --rm -v ${PWD}:/docs squidfunk/mkdocs-material build +``` + +## 4) Публикация +```bash +set -a && . /home/naeel/terra/.env.s3 && set +a +/home/naeel/terra/scripts/publish-docs.sh site terra.k8c.ru nubes nubes 2.0.0 +``` + +## Результат +Документация должна открываться по адресу: +`https://terra.k8c.ru/docs/nubes/nubes/2.0.0/` diff --git a/forNubes/tools/upload_provider_s3.py b/forNubes/tools/upload_provider_s3.py new file mode 100644 index 0000000..405a758 --- /dev/null +++ b/forNubes/tools/upload_provider_s3.py @@ -0,0 +1,37 @@ +import configparser +from pathlib import Path + +import boto3 +from botocore.config import Config + +cred_path = Path('/home/naeel/terra/.tmp_aws_credentials') +config = configparser.RawConfigParser() +config.read(cred_path) +key = config.get('registry', 'aws_access_key_id') +secret = config.get('registry', 'aws_secret_access_key') + +cfg = Config(signature_version='s3v4', s3={'addressing_style': 'path', 'payload_signing_enabled': False}) + +s3 = boto3.client( + 's3', + region_name='us-east-1', + endpoint_url='https://s3.msk-1.ngcloud.ru', + aws_access_key_id=key, + aws_secret_access_key=secret, + config=cfg, +) + +bucket = 'terraform-registry' +prefix = 'terrareg.kube5s.ru/nubes/nubes/2.0.0/' +files = [ + 'terraform-provider-nubes_2.0.0_linux_amd64.zip', + 'terraform-provider-nubes_2.0.0_SHA256SUMS', + 'terraform-provider-nubes_2.0.0_SHA256SUMS.sig', +] +base = Path('/home/naeel/terra/nubes_provider_gen') +for name in files: + path = base / name + key_name = prefix + name + with open(path, 'rb') as f: + s3.put_object(Bucket=bucket, Key=key_name, Body=f) + print('uploaded', key_name)