Initial commit: clean infrastructure code
This commit is contained in:
commit
1a7be93ab1
57
.gitignore
vendored
Normal file
57
.gitignore
vendored
Normal file
@ -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
|
||||||
18
forNubes/README.md
Normal file
18
forNubes/README.md
Normal file
@ -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/** — тестовые сценарии.
|
||||||
63
forNubes/go.mod
Normal file
63
forNubes/go.mod
Normal file
@ -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
|
||||||
|
)
|
||||||
155
forNubes/go.sum
Normal file
155
forNubes/go.sum
Normal file
@ -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=
|
||||||
7
forNubes/k8s/README.md
Normal file
7
forNubes/k8s/README.md
Normal file
@ -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).
|
||||||
42
forNubes/k8s/registry-deployment-new.yaml
Normal file
42
forNubes/k8s/registry-deployment-new.yaml
Normal file
@ -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"
|
||||||
26
forNubes/k8s/registry-ingress-new.yaml
Normal file
26
forNubes/k8s/registry-ingress-new.yaml
Normal file
@ -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
|
||||||
32
forNubes/main.go
Normal file
32
forNubes/main.go
Normal file
@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
39
forNubes/mkdocs.yml
Normal file
39
forNubes/mkdocs.yml
Normal file
@ -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
|
||||||
21
forNubes/registry-server-build/Dockerfile
Normal file
21
forNubes/registry-server-build/Dockerfile
Normal file
@ -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"]
|
||||||
10
forNubes/registry-server-build/Dockerfile.minimal
Normal file
10
forNubes/registry-server-build/Dockerfile.minimal
Normal file
@ -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"]
|
||||||
116
forNubes/registry-server-build/docs.go
Normal file
116
forNubes/registry-server-build/docs.go
Normal file
@ -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/<namespace>/<name>/<version>/... (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 <path>/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)
|
||||||
|
}
|
||||||
27
forNubes/registry-server-build/go.mod
Normal file
27
forNubes/registry-server-build/go.mod
Normal file
@ -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
|
||||||
|
)
|
||||||
45
forNubes/registry-server-build/go.sum
Normal file
45
forNubes/registry-server-build/go.sum
Normal file
@ -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=
|
||||||
331
forNubes/registry-server-build/main.go
Normal file
331
forNubes/registry-server-build/main.go
Normal file
@ -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, `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><title>Terra Registry</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Terra Registry & Documentation Server</h1>
|
||||||
|
<p>Status: <span style="color: green">ONLINE</span></p>
|
||||||
|
<hr>
|
||||||
|
<p>Powered by Nubes Cloud S3 Storage</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
19
forNubes/scripts/build-and-push-registry-server.sh
Executable file
19
forNubes/scripts/build-and-push-registry-server.sh
Executable file
@ -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> <tag>
|
||||||
|
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."
|
||||||
9
forNubes/scripts/deploy-dev.sh
Normal file
9
forNubes/scripts/deploy-dev.sh
Normal file
@ -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"
|
||||||
69
forNubes/scripts/package_release.sh
Executable file
69
forNubes/scripts/package_release.sh
Executable file
@ -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!"
|
||||||
30
forNubes/scripts/publish-docs.sh
Executable file
30
forNubes/scripts/publish-docs.sh
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Usage: publish-docs.sh <site-dir> <registry-host> <namespace> <name> <version>
|
||||||
|
SITE_DIR=${1:-site}
|
||||||
|
REGISTRY_HOST=${2:-terra.k8c.ru}
|
||||||
|
NAMESPACE=${3:-nubes}
|
||||||
|
NAME=${4:-nubes}
|
||||||
|
VERSION=${5:-dev}
|
||||||
|
|
||||||
|
# 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}/"
|
||||||
84
forNubes/tests/lifecycle_scenario/finish_tests.sh
Executable file
84
forNubes/tests/lifecycle_scenario/finish_tests.sh
Executable file
@ -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 <<EOF > 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."
|
||||||
70
forNubes/tests/lifecycle_scenario/main.tf
Normal file
70
forNubes/tests/lifecycle_scenario/main.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
|
*/
|
||||||
124
forNubes/tests/lifecycle_scenario/run_test.sh
Executable file
124
forNubes/tests/lifecycle_scenario/run_test.sh
Executable file
@ -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 <<EOF > $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 <<EOF > $HISTORY_DOC
|
||||||
|
# 05 Lifecycle Test Results
|
||||||
|
Date: $(date)
|
||||||
|
Success. Updates performed in-place.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Docs generated."
|
||||||
142
forNubes/tests/lifecycle_scenario/run_tubulus_advanced.sh
Executable file
142
forNubes/tests/lifecycle_scenario/run_tubulus_advanced.sh
Executable file
@ -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 <<EOF > 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 <<EOF > 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 <<EOF > 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."
|
||||||
36
forNubes/tests/lifecycle_scenario/run_tubulus_test.sh
Executable file
36
forNubes/tests/lifecycle_scenario/run_tubulus_test.sh
Executable file
@ -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
|
||||||
170
forNubes/tests/lifecycle_scenario/stress_tests.sh
Normal file
170
forNubes/tests/lifecycle_scenario/stress_tests.sh
Normal file
@ -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 <<EOF > 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 <<EOF > 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 <<EOF > 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 <<EOF > 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 <<EOF > 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!"
|
||||||
11
forNubes/tests/lifecycle_scenario/tubulus.tf
Normal file
11
forNubes/tests/lifecycle_scenario/tubulus.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
55
forNubes/tests/lifecycle_scenario/variables.tf
Normal file
55
forNubes/tests/lifecycle_scenario/variables.tf
Normal file
@ -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 * * *"
|
||||||
|
}
|
||||||
41
forNubes/tests/modify_postgres/main.tf
Normal file
41
forNubes/tests/modify_postgres/main.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
13
forNubes/tests/modify_postgres/nli_test_results.json
Normal file
13
forNubes/tests/modify_postgres/nli_test_results.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
32
forNubes/tests/modify_postgres/plan_output.txt
Normal file
32
forNubes/tests/modify_postgres/plan_output.txt
Normal file
@ -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)
|
||||||
198
forNubes/tests/modify_postgres/run_nli_tests.sh
Executable file
198
forNubes/tests/modify_postgres/run_nli_tests.sh
Executable file
@ -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 <<EOF
|
||||||
|
resource "nubes_tubulus_instance" "auto_test" {
|
||||||
|
display_name = "$name"
|
||||||
|
description = "NLI Auto Test"
|
||||||
|
instruction = "$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
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Plan
|
||||||
|
if ! terraform plan -var-file=terraform.tfvars -target=nubes_tubulus_instance.auto_test -no-color > 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 <<EOF
|
||||||
|
resource "nubes_tubulus_instance" "auto_test" {
|
||||||
|
display_name = "$name"
|
||||||
|
description = "NLI Modify Test"
|
||||||
|
instruction = "$instruction1"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "duration" {
|
||||||
|
value = nubes_tubulus_instance.auto_test.duration_ms
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
terraform apply -auto-approve -var-file=terraform.tfvars -target=nubes_tubulus_instance.auto_test -no-color > /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 <<EOF
|
||||||
|
resource "nubes_tubulus_instance" "auto_test" {
|
||||||
|
display_name = "$name"
|
||||||
|
description = "NLI Modify Test"
|
||||||
|
instruction = "$instruction2"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "duration" {
|
||||||
|
value = nubes_tubulus_instance.auto_test.duration_ms
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if terraform apply -auto-approve -var-file=terraform.tfvars -target=nubes_tubulus_instance.auto_test -no-color > /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 '.'
|
||||||
18
forNubes/tests/modify_postgres/test_ai.tf
Normal file
18
forNubes/tests/modify_postgres/test_ai.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
29
forNubes/tests/modify_postgres/test_ai_advanced.tf
Normal file
29
forNubes/tests/modify_postgres/test_ai_advanced.tf
Normal file
@ -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 этапе"
|
||||||
|
}
|
||||||
41
forNubes/tests/modify_postgres/test_ai_simple.tf
Normal file
41
forNubes/tests/modify_postgres/test_ai_simple.tf
Normal file
@ -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 сикунд"
|
||||||
|
}
|
||||||
21
forNubes/tests/modify_postgres/test_auto_nli.tf
Normal file
21
forNubes/tests/modify_postgres/test_auto_nli.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
6
forNubes/tests/postgres_test/dev_override.tfrc
Normal file
6
forNubes/tests/postgres_test/dev_override.tfrc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
provider_installation {
|
||||||
|
dev_overrides {
|
||||||
|
"terraform.local/local/nubes" = "/home/naeel/terra/tests/postgres_test"
|
||||||
|
}
|
||||||
|
direct {}
|
||||||
|
}
|
||||||
41
forNubes/tests/postgres_test/main.tf
Normal file
41
forNubes/tests/postgres_test/main.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
40
forNubes/tests/s3_test/main.tf
Normal file
40
forNubes/tests/s3_test/main.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
83
forNubes/tests/s3_test/run_s3_test.sh
Executable file
83
forNubes/tests/s3_test/run_s3_test.sh
Executable file
@ -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
|
||||||
60
forNubes/tests/vdc_test/main.tf
Normal file
60
forNubes/tests/vdc_test/main.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
41
forNubes/tools/debug_req.go
Normal file
41
forNubes/tools/debug_req.go
Normal file
@ -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))
|
||||||
|
}
|
||||||
209
forNubes/tools/docs_publish/README.md
Normal file
209
forNubes/tools/docs_publish/README.md
Normal file
@ -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/`
|
||||||
37
forNubes/tools/upload_provider_s3.py
Normal file
37
forNubes/tools/upload_provider_s3.py
Normal file
@ -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)
|
||||||
Loading…
Reference in New Issue
Block a user