198 lines
7.5 KiB
Go
198 lines
7.5 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
|
"github.com/hashicorp/terraform-plugin-framework/provider"
|
|
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
|
|
"terraform-provider-mycloud/internal/core"
|
|
"terraform-provider-mycloud/internal/generated"
|
|
)
|
|
|
|
var _ provider.Provider = &NubesProvider{}
|
|
|
|
type NubesProvider struct {
|
|
version string
|
|
}
|
|
|
|
// NubesProviderModel — конфигурация провайдера из HCL-блока provider {}.
|
|
// Каждое поле соответствует атрибуту в Schema() ниже.
|
|
// Поля Optional: если не указаны — берутся из env-переменных или defaults.
|
|
type NubesProviderModel struct {
|
|
ApiEndpoint types.String `tfsdk:"api_endpoint"`
|
|
ApiToken types.String `tfsdk:"api_token"`
|
|
// Insecure отключает проверку TLS-сертификата сервера.
|
|
// НЕ использовать в продакшн! Только для dev-стендов с самоподписанным сертом.
|
|
// Может быть задан также через env NUBES_INSECURE=true.
|
|
Insecure types.Bool `tfsdk:"insecure"`
|
|
// LogLevel задаёт уровень вывода этапов операций: "none" (default) | "info" | "debug".
|
|
// Может быть переопределён на уровне ресурса через атрибут log_level ресурса.
|
|
LogLevel types.String `tfsdk:"log_level"`
|
|
}
|
|
|
|
type NubesClient struct {
|
|
HttpClient *http.Client
|
|
ApiEndpoint string
|
|
ApiToken string
|
|
}
|
|
|
|
func New(version string) func() provider.Provider {
|
|
return func() provider.Provider {
|
|
return &NubesProvider{
|
|
version: version,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *NubesProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
|
|
resp.TypeName = "nubes"
|
|
resp.Version = p.version
|
|
}
|
|
|
|
func (p *NubesProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
|
|
resp.Schema = schema.Schema{
|
|
Attributes: map[string]schema.Attribute{
|
|
"api_endpoint": schema.StringAttribute{
|
|
MarkdownDescription: "API Gateway endpoint for Nubes Cloud",
|
|
Optional: true,
|
|
},
|
|
"api_token": schema.StringAttribute{
|
|
MarkdownDescription: "API authentication token",
|
|
Optional: true,
|
|
Sensitive: true,
|
|
},
|
|
// insecure: отключает проверку TLS-сертификата Nubes API.
|
|
// По умолчанию false — сертификат проверяется (безопасно).
|
|
// Устанавливать true только на dev-стенде с самоподписанным сертом.
|
|
// Альтернатива: env NUBES_INSECURE=true (не требует правки .tf файлов).
|
|
"insecure": schema.BoolAttribute{
|
|
MarkdownDescription: "Disable TLS certificate verification. Use only for dev environments with self-signed certs. Can also be set via NUBES_INSECURE env var.",
|
|
Optional: true,
|
|
},
|
|
// log_level: уровень вывода этапов операций во время terraform apply/destroy.
|
|
// "none" (default) — не выводить ничего.
|
|
// "info" — выводить строку на каждый этап: [OK ] Валидация — 74.8 sec
|
|
// "debug" — как info + детали каждого этапа без timestamp-мусора.
|
|
// Может быть переопределён на уровне ресурса через атрибут log_level.
|
|
"log_level": schema.StringAttribute{
|
|
MarkdownDescription: "Operation stages log level: none (default), info, debug.",
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (p *NubesProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
|
|
var config NubesProviderModel
|
|
|
|
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
// Default values
|
|
apiEndpoint := "https://deck-api.ngcloud.ru/api/v1/index.cfm"
|
|
apiToken := ""
|
|
|
|
if !config.ApiEndpoint.IsNull() {
|
|
apiEndpoint = config.ApiEndpoint.ValueString()
|
|
}
|
|
|
|
if !config.ApiToken.IsNull() {
|
|
apiToken = strings.TrimSpace(config.ApiToken.ValueString())
|
|
} else {
|
|
// Try to get from environment
|
|
if token := os.Getenv("NUBES_API_TOKEN"); token != "" {
|
|
apiToken = strings.TrimSpace(token)
|
|
} else {
|
|
// Try to read from ~/.nubes_token file
|
|
homeDir, err := os.UserHomeDir()
|
|
if err == nil {
|
|
tokenFile := homeDir + "/.nubes_token"
|
|
if data, err := os.ReadFile(tokenFile); err == nil {
|
|
apiToken = strings.TrimSpace(string(data))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- TLS: определяем нужно ли пропустить проверку сертификата ---
|
|
// Приоритет: config.insecure > env NUBES_INSECURE > false (безопасный default).
|
|
// Nubes Cloud API использует валидный TLS-сертификат от доверенного CA,
|
|
// поэтому в продакшн InsecureSkipVerify должен быть false.
|
|
// true оставлен только для совместимости с dev-стендами без нормального сертификата.
|
|
insecureSkipVerify := false
|
|
if !config.Insecure.IsNull() && !config.Insecure.IsUnknown() {
|
|
// Явно задано в provider {} блоке HCL
|
|
insecureSkipVerify = config.Insecure.ValueBool()
|
|
} else if os.Getenv("NUBES_INSECURE") == "true" {
|
|
// Задано через переменную окружения (удобно для CI/CD без правки .tf файлов)
|
|
insecureSkipVerify = true
|
|
}
|
|
|
|
// Custom transport based on DefaultTransport.
|
|
// Клонируем DefaultTransport чтобы сохранить все системные настройки (proxy, timeouts).
|
|
transport := http.DefaultTransport.(*http.Transport).Clone()
|
|
transport.TLSHandshakeTimeout = 60 * time.Second
|
|
|
|
// Force HTTP/1.1: Nubes API не поддерживает HTTP/2, принудительно отключаем.
|
|
// MinVersion TLS 1.2 — минимально безопасная версия TLS.
|
|
transport.TLSClientConfig = &tls.Config{
|
|
InsecureSkipVerify: insecureSkipVerify, // false в prod, true только для dev
|
|
NextProtos: []string{"http/1.1"},
|
|
MinVersion: tls.VersionTLS12,
|
|
}
|
|
transport.ForceAttemptHTTP2 = false
|
|
|
|
// Use Core Universal Client
|
|
client := &core.UniversalClient{
|
|
HttpClient: &http.Client{
|
|
Transport: transport,
|
|
Timeout: 300 * time.Second,
|
|
},
|
|
ApiEndpoint: apiEndpoint,
|
|
ApiToken: apiToken,
|
|
LogLevel: config.LogLevel.ValueString(),
|
|
}
|
|
|
|
resp.DataSourceData = client
|
|
resp.ResourceData = client
|
|
}
|
|
|
|
func (p *NubesProvider) Resources(ctx context.Context) []func() resource.Resource {
|
|
return []func() resource.Resource{
|
|
NewTubulusResource,
|
|
NewOrganizationResource,
|
|
NewVMResource,
|
|
NewVDCResource,
|
|
NewEdgeResource,
|
|
NewVAppResource,
|
|
NewQuickStartResource,
|
|
NewPostgresResource,
|
|
NewS3BucketResource,
|
|
NewPgAdminResource,
|
|
// Generated Resources
|
|
generated.NewBolvankaResource,
|
|
generated.NewBolvankaUniversalResource,
|
|
generated.NewBolvankaUniversalLifecycleResource,
|
|
}
|
|
}
|
|
|
|
func (p *NubesProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
|
|
return []func() datasource.DataSource{
|
|
NewVDCDataSource,
|
|
NewEdgeDataSource,
|
|
NewVAppDataSource,
|
|
NewServiceInstanceDataSource,
|
|
}
|
|
}
|