fix: builder and sdk changes (#81)
## Description
<!-- **Please link some issue here describing what you are trying to achieve.**
In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->
relates to #1234
## Checklist
- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-authored-by: marcel.henselin <marcel.henselin@stackit.cloud>
Reviewed-on: #81
This commit is contained in:
parent
635a9abf20
commit
1033d7e034
145 changed files with 5944 additions and 5298 deletions
148
generator/cmd/build/templates/data_source_scaffold.gotmpl
Normal file
148
generator/cmd/build/templates/data_source_scaffold.gotmpl
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package {{.PackageName}}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
|
||||
{{.PackageName}}Pkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
|
||||
|
||||
{{.PackageName}}Gen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/datasources_gen"
|
||||
)
|
||||
|
||||
var _ datasource.DataSource = (*{{.NameCamel}}DataSource)(nil)
|
||||
|
||||
const errorPrefix = "[{{.PackageNamePascal}} - {{.NamePascal}}]"
|
||||
|
||||
func New{{.NamePascal}}DataSource() datasource.DataSource {
|
||||
return &{{.NameCamel}}DataSource{}
|
||||
}
|
||||
|
||||
type dsModel struct {
|
||||
{{.PackageName}}Gen.{{.NamePascal}}Model
|
||||
TfId types.String `tfsdk:"id"`
|
||||
}
|
||||
|
||||
type {{.NameCamel}}DataSource struct{
|
||||
client *{{.PackageName}}Pkg.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (d *{{.NameCamel}}DataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_{{.PackageName}}_{{.NameSnake}}"
|
||||
}
|
||||
|
||||
func (d *{{.NameCamel}}DataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = {{.PackageName}}Gen.{{.NamePascal}}DataSourceSchema(ctx)
|
||||
resp.Schema.Attributes["id"] = schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The terraform internal identifier.",
|
||||
MarkdownDescription: "The terraform internal identifier.",
|
||||
}
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the data source.
|
||||
func (d *{{.NameCamel}}DataSource) Configure(
|
||||
ctx context.Context,
|
||||
req datasource.ConfigureRequest,
|
||||
resp *datasource.ConfigureResponse,
|
||||
) {
|
||||
var ok bool
|
||||
d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClientConfigOptions := []config.ConfigurationOption{
|
||||
config.WithCustomAuth(d.providerData.RoundTripper),
|
||||
utils.UserAgentConfigOption(d.providerData.Version),
|
||||
}
|
||||
if d.providerData.{{.PackageNamePascal}}CustomEndpoint != "" {
|
||||
apiClientConfigOptions = append(
|
||||
apiClientConfigOptions,
|
||||
config.WithEndpoint(d.providerData.{{.PackageNamePascal}}CustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClientConfigOptions = append(
|
||||
apiClientConfigOptions,
|
||||
config.WithRegion(d.providerData.GetRegion()),
|
||||
)
|
||||
}
|
||||
apiClient, err := {{.PackageName}}Pkg.NewAPIClient(apiClientConfigOptions...)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error configuring API client",
|
||||
fmt.Sprintf(
|
||||
"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
|
||||
err,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, fmt.Sprintf("%s client configured", errorPrefix))
|
||||
}
|
||||
|
||||
func (d *{{.NameCamel}}DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data dsModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := data.ProjectId.ValueString()
|
||||
region := d.providerData.GetRegionWithOverride(data.Region)
|
||||
{{.NameCamel}}Id := data.{{.NamePascal}}Id.ValueString()
|
||||
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// TODO: implement needed fields
|
||||
ctx = tflog.SetField(ctx, "{{.NameCamel}}_id", {{.NameCamel}}Id)
|
||||
|
||||
// TODO: refactor to correct implementation
|
||||
{{.NameCamel}}Resp, err := d.client.Get{{.NamePascal}}Request(ctx, projectId, region, {{.NameCamel}}Id).Execute()
|
||||
if err != nil {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
err,
|
||||
"Reading {{.NameCamel}}",
|
||||
fmt.Sprintf("{{.NameCamel}} with ID %q does not exist in project %q.", {{.NameCamel}}Id, projectId),
|
||||
map[int]string{
|
||||
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
||||
},
|
||||
)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
|
||||
data.TfId = utils.BuildInternalTerraformId(projectId, region, ..)
|
||||
|
||||
// TODO: fill remaining fields
|
||||
{{- range .Fields }}
|
||||
// data.{{.}} = types.Sometype(apiResponse.Get{{.}}())
|
||||
{{- end -}}
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
tflog.Info(ctx, fmt.Sprintf("%s read successful", errorPrefix))
|
||||
}
|
||||
98
generator/cmd/build/templates/functions_scaffold.gotmpl
Normal file
98
generator/cmd/build/templates/functions_scaffold.gotmpl
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
package {{.PackageName}}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
|
||||
{{.PackageName}} "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
|
||||
{{.PackageName}}ResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/instance/resources_gen"
|
||||
)
|
||||
|
||||
func mapResponseToModel(
|
||||
ctx context.Context,
|
||||
resp *{{.PackageName}}.Get{{.NamePascal}}Response,
|
||||
m *{{.PackageName}}ResGen.{{.NamePascal}}Model,
|
||||
tfDiags diag.Diagnostics,
|
||||
) error {
|
||||
// TODO: complete and refactor
|
||||
m.Id = types.StringValue(resp.GetId())
|
||||
|
||||
/*
|
||||
sampleList, diags := types.ListValueFrom(ctx, types.StringType, resp.GetList())
|
||||
tfDiags.Append(diags...)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf(
|
||||
"error converting list response value",
|
||||
)
|
||||
}
|
||||
sample, diags := {{.PackageName}}ResGen.NewSampleValue(
|
||||
{{.PackageName}}ResGen.SampleValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"field": types.StringValue(string(resp.GetField())),
|
||||
},
|
||||
)
|
||||
tfDiags.Append(diags...)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf(
|
||||
"error converting sample response value",
|
||||
"sample",
|
||||
types.StringValue(string(resp.GetField())),
|
||||
)
|
||||
}
|
||||
m.Sample = sample
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleEncryption(
|
||||
m *{{.PackageName}}ResGen.{{.NamePascal}}Model,
|
||||
resp *{{.PackageName}}.Get{{.NamePascal}}Response,
|
||||
) {{.PackageName}}ResGen.EncryptionValue {
|
||||
if !resp.HasEncryption() ||
|
||||
resp.Encryption == nil ||
|
||||
resp.Encryption.KekKeyId == nil ||
|
||||
resp.Encryption.KekKeyRingId == nil ||
|
||||
resp.Encryption.KekKeyVersion == nil ||
|
||||
resp.Encryption.ServiceAccount == nil {
|
||||
|
||||
if m.Encryption.IsNull() || m.Encryption.IsUnknown() {
|
||||
return {{.PackageName}}ResGen.NewEncryptionValueNull()
|
||||
}
|
||||
return m.Encryption
|
||||
}
|
||||
|
||||
enc := {{.PackageName}}ResGen.NewEncryptionValueNull()
|
||||
if kVal, ok := resp.Encryption.GetKekKeyIdOk(); ok {
|
||||
enc.KekKeyId = types.StringValue(kVal)
|
||||
}
|
||||
if kkVal, ok := resp.Encryption.GetKekKeyRingIdOk(); ok {
|
||||
enc.KekKeyRingId = types.StringValue(kkVal)
|
||||
}
|
||||
if kkvVal, ok := resp.Encryption.GetKekKeyVersionOk(); ok {
|
||||
enc.KekKeyVersion = types.StringValue(kkvVal)
|
||||
}
|
||||
if sa, ok := resp.Encryption.GetServiceAccountOk(); ok {
|
||||
enc.ServiceAccount = types.StringValue(sa)
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
func toCreatePayload(
|
||||
ctx context.Context,
|
||||
model *{{.PackageName}}ResGen.{{.NamePascal}}Model,
|
||||
) (*{{.PackageName}}.Create{{.NamePascal}}RequestPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
return &{{.PackageName}}.Create{{.NamePascal}}RequestPayload{
|
||||
// TODO: fill fields
|
||||
}, nil
|
||||
}
|
||||
39
generator/cmd/build/templates/provider_scaffold.gotmpl
Normal file
39
generator/cmd/build/templates/provider_scaffold.gotmpl
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package {{.PackageName}}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/provider"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
)
|
||||
|
||||
var _ provider.Provider = (*{{.NameCamel}}Provider)(nil)
|
||||
|
||||
func New() func() provider.Provider {
|
||||
return func() provider.Provider {
|
||||
return &{{.NameCamel}}Provider{}
|
||||
}
|
||||
}
|
||||
|
||||
type {{.NameCamel}}Provider struct{}
|
||||
|
||||
func (p *{{.NameCamel}}Provider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
|
||||
|
||||
}
|
||||
|
||||
func (p *{{.NameCamel}}Provider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
|
||||
|
||||
}
|
||||
|
||||
func (p *{{.NameCamel}}Provider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
|
||||
resp.TypeName = "{{.NameSnake}}"
|
||||
}
|
||||
|
||||
func (p *{{.NameCamel}}Provider) DataSources(ctx context.Context) []func() datasource.DataSource {
|
||||
return []func() datasource.DataSource{}
|
||||
}
|
||||
|
||||
func (p *{{.NameCamel}}Provider) Resources(ctx context.Context) []func() resource.Resource {
|
||||
return []func() resource.Resource{}
|
||||
}
|
||||
429
generator/cmd/build/templates/resource_scaffold.gotmpl
Normal file
429
generator/cmd/build/templates/resource_scaffold.gotmpl
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
package {{.PackageName}}
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
|
||||
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
|
||||
{{.PackageName}}ResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/resources_gen"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &{{.NameCamel}}Resource{}
|
||||
_ resource.ResourceWithConfigure = &{{.NameCamel}}Resource{}
|
||||
_ resource.ResourceWithImportState = &{{.NameCamel}}Resource{}
|
||||
_ resource.ResourceWithModifyPlan = &{{.NameCamel}}Resource{}
|
||||
_ resource.ResourceWithIdentity = &{{.NameCamel}}Resource{}
|
||||
)
|
||||
|
||||
func New{{.NamePascal}}Resource() resource.Resource {
|
||||
return &{{.NameCamel}}Resource{}
|
||||
}
|
||||
|
||||
type {{.NameCamel}}Resource struct{
|
||||
client *{{.PackageName}}.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
// resourceModel represents the Terraform resource state
|
||||
type resourceModel = {{.PackageName}}.{{.NamePascal}}Model
|
||||
|
||||
type {{.NamePascal}}ResourceIdentityModel struct {
|
||||
ProjectID types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
// TODO: implement further needed parts
|
||||
{{.NamePascal}}ID types.String `tfsdk:"{{.NameSnake}}_id"`
|
||||
}
|
||||
|
||||
// Metadata defines terraform resource name
|
||||
func (r *{{.NameCamel}}Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_{{.PackageName}}_{{.NameSnake}}"
|
||||
}
|
||||
|
||||
//go:embed planModifiers.yaml
|
||||
var modifiersFileByte []byte
|
||||
|
||||
// Schema loads the schema from generated files and adds plan modifiers
|
||||
func (r *{{.NameCamel}}Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
schema = {{.PackageName}}ResGen.{{.NamePascal}}ResourceSchema(ctx)
|
||||
|
||||
fields, err := {{.PackageName}}Utils.ReadModifiersConfig(modifiersFileByte)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("error during read modifiers config file", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = {{.PackageName}}Utils.AddPlanModifiersToResourceSchema(fields, &schema)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = schema
|
||||
}
|
||||
|
||||
// IdentitySchema defines the identity schema
|
||||
func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
|
||||
resp.IdentitySchema = identityschema.Schema{
|
||||
Attributes: map[string]identityschema.Attribute{
|
||||
"project_id": identityschema.StringAttribute{
|
||||
RequiredForImport: true, // must be set during import by the practitioner
|
||||
},
|
||||
"region": identityschema.StringAttribute{
|
||||
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||
},
|
||||
"instance_id": identityschema.StringAttribute{
|
||||
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||
},
|
||||
// TODO: implement remaining schema parts
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the resource.
|
||||
func (r *{{.NameCamel}}Resource) Configure(
|
||||
ctx context.Context,
|
||||
req resource.ConfigureRequest,
|
||||
resp *resource.ConfigureResponse,
|
||||
) {
|
||||
var ok bool
|
||||
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClientConfigOptions := []config.ConfigurationOption{
|
||||
config.WithCustomAuth(r.providerData.RoundTripper),
|
||||
utils.UserAgentConfigOption(r.providerData.Version),
|
||||
}
|
||||
if r.providerData.{{.PackageNamePascal}}CustomEndpoint != "" {
|
||||
apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(r.providerData.{{.PackageName}}CustomEndpoint))
|
||||
} else {
|
||||
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(r.providerData.GetRegion()))
|
||||
}
|
||||
apiClient, err := {{.PackageName}}.NewAPIClient(apiClientConfigOptions...)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error configuring API client",
|
||||
fmt.Sprintf(
|
||||
"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
|
||||
err,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
r.client = apiClient
|
||||
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} client configured")
|
||||
}
|
||||
|
||||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||
// Use the modifier to set the effective region in the current plan.
|
||||
func (r *{{.NameCamel}}Resource) ModifyPlan(
|
||||
ctx context.Context,
|
||||
req resource.ModifyPlanRequest,
|
||||
resp *resource.ModifyPlanResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
var configModel {{.PackageName}}ResGen.{{.NamePascal}}Model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Plan.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
var planModel {{.PackageName}}ResGen.{{.NamePascal}}Model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates a new resource
|
||||
func (r *{{.NameCamel}}Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data {{.PackageName}}ResGen.{{.NamePascal}}Model
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := data.ProjectId.ValueString()
|
||||
region := data.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
// TODO: add remaining fields
|
||||
|
||||
// TODO: Create API call logic
|
||||
/*
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(ctx, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating {{.NamePascal}}",
|
||||
fmt.Sprintf("Creating API payload: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
// Create new {{.NamePascal}}
|
||||
createResp, err := r.client.Create{{.NamePascal}}Request(
|
||||
ctx,
|
||||
projectId,
|
||||
region,
|
||||
).Create{{.NamePascal}}RequestPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating {{.NamePascal}}", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
{{.NamePascal}}Id := *createResp.Id
|
||||
*/
|
||||
|
||||
// Example data value setting
|
||||
data.{{.NameCamel | ucfirst}}Id = types.StringValue("id-from-response")
|
||||
|
||||
// TODO: Set data returned by API in identity
|
||||
identity := {{.NamePascal}}ResourceIdentityModel{
|
||||
ProjectID: types.StringValue(projectId),
|
||||
Region: types.StringValue(region),
|
||||
// TODO: add missing values
|
||||
{{.NamePascal}}ID: types.StringValue({{.NamePascal}}Id),
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: implement wait handler if needed
|
||||
/*
|
||||
|
||||
waitResp, err := wait.Create{{.NamePascal}}WaitHandler(
|
||||
ctx,
|
||||
r.client,
|
||||
projectId,
|
||||
{{.NamePascal}}Id,
|
||||
region,
|
||||
).SetSleepBeforeWait(
|
||||
30 * time.Second,
|
||||
).SetTimeout(
|
||||
90 * time.Minute,
|
||||
).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating {{.NamePascal}}",
|
||||
fmt.Sprintf("{{.NamePascal}} creation waiting: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if waitResp.Id == nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating {{.NamePascal}}",
|
||||
"{{.NamePascal}} creation waiting: returned id is nil",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapResponseToModel(ctx, waitResp, &model, resp.Diagnostics)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating {{.NamePascal}}",
|
||||
fmt.Sprintf("Processing API payload: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} created")
|
||||
}
|
||||
|
||||
func (r *{{.NameCamel}}Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data {{.PackageName}}ResGen.{{.NamePascal}}Model
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData {{.NamePascal}}ResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := identityData.ProjectID.ValueString()
|
||||
region := identityData.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// TODO: Read API call logic
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
// TODO: Set data returned by API in identity
|
||||
identity := {{.NamePascal}}ResourceIdentityModel{
|
||||
ProjectID: types.StringValue(projectId),
|
||||
Region: types.StringValue(region),
|
||||
// InstanceID: types.StringValue(instanceId),
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} read")
|
||||
}
|
||||
|
||||
func (r *{{.NameCamel}}Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data {{.PackageName}}ResGen.{{.NamePascal}}Model
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := data.ProjectId.ValueString()
|
||||
region := data.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// TODO: Update API call logic
|
||||
|
||||
// TODO: Set data returned by API in identity
|
||||
identity := {{.NamePascal}}ResourceIdentityModel{
|
||||
ProjectID: types.StringValue(projectId),
|
||||
Region: types.StringValue(region),
|
||||
// TODO: add missing values
|
||||
{{.NamePascal}}ID: types.StringValue({{.NamePascal}}Id),
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} updated")
|
||||
}
|
||||
|
||||
func (r *{{.NameCamel}}Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data {{.PackageName}}ResGen.{{.NamePascal}}Model
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData {{.NamePascal}}ResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := identityData.ProjectID.ValueString()
|
||||
region := identityData.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// TODO: Delete API call logic
|
||||
|
||||
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} deleted")
|
||||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
|
||||
func (r *{{.NameCamel}}Resource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
// TODO: Import logic
|
||||
// TODO: fix len and parts itself
|
||||
if len(idParts) < 2 || idParts[0] == "" || idParts[1] == "" {
|
||||
core.LogAndAddError(
|
||||
ctx, &resp.Diagnostics,
|
||||
"Error importing database",
|
||||
fmt.Sprintf(
|
||||
"Expected import identifier with format [project_id],[region],..., got %q",
|
||||
req.ID,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
|
||||
// ... more ...
|
||||
|
||||
core.LogAndAddWarning(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"{{.PackageName | ucfirst}} database imported with empty password",
|
||||
"The database password is not imported as it is only available upon creation of a new database. The password field will be empty.",
|
||||
)
|
||||
tflog.Info(ctx, "{{.PackageName | ucfirst}} {{.NameCamel}} state imported")
|
||||
}
|
||||
47
generator/cmd/build/templates/util.gotmpl
Normal file
47
generator/cmd/build/templates/util.gotmpl
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
{{.PackageName}} "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
)
|
||||
|
||||
func ConfigureClient(
|
||||
ctx context.Context,
|
||||
providerData *core.ProviderData,
|
||||
diags *diag.Diagnostics,
|
||||
) *{{.PackageName}}.APIClient {
|
||||
apiClientConfigOptions := []config.ConfigurationOption{
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
utils.UserAgentConfigOption(providerData.Version),
|
||||
}
|
||||
if providerData.{{.PackageName}}CustomEndpoint != "" {
|
||||
apiClientConfigOptions = append(
|
||||
apiClientConfigOptions,
|
||||
config.WithEndpoint(providerData.{{.PackageName}}CustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(providerData.GetRegion()))
|
||||
}
|
||||
apiClient, err := {{.PackageName}}.NewAPIClient(apiClientConfigOptions...)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
diags,
|
||||
"Error configuring API client",
|
||||
fmt.Sprintf(
|
||||
"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
|
||||
err,
|
||||
),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
return apiClient
|
||||
}
|
||||
97
generator/cmd/build/templates/util_test.gotmpl
Normal file
97
generator/cmd/build/templates/util_test.gotmpl
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
sdkClients "github.com/stackitcloud/stackit-sdk-go/core/clients"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
{{.PackageName}} "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
|
||||
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
testVersion = "1.2.3"
|
||||
testCustomEndpoint = "https://sqlserverflex-custom-endpoint.api.stackit.cloud"
|
||||
)
|
||||
|
||||
func TestConfigureClient(t *testing.T) {
|
||||
/* mock authentication by setting service account token env variable */
|
||||
os.Clearenv()
|
||||
err := os.Setenv(sdkClients.ServiceAccountToken, "mock-val")
|
||||
if err != nil {
|
||||
t.Errorf("error setting env variable: %v", err)
|
||||
}
|
||||
|
||||
type args struct {
|
||||
providerData *core.ProviderData
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
expected *sqlserverflex.APIClient
|
||||
}{
|
||||
{
|
||||
name: "default endpoint",
|
||||
args: args{
|
||||
providerData: &core.ProviderData{
|
||||
Version: testVersion,
|
||||
},
|
||||
},
|
||||
expected: func() *sqlserverflex.APIClient {
|
||||
apiClient, err := sqlserverflex.NewAPIClient(
|
||||
config.WithRegion("eu01"),
|
||||
utils.UserAgentConfigOption(testVersion),
|
||||
)
|
||||
if err != nil {
|
||||
t.Errorf("error configuring client: %v", err)
|
||||
}
|
||||
return apiClient
|
||||
}(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom endpoint",
|
||||
args: args{
|
||||
providerData: &core.ProviderData{
|
||||
Version: testVersion,
|
||||
SQLServerFlexCustomEndpoint: testCustomEndpoint,
|
||||
},
|
||||
},
|
||||
expected: func() *sqlserverflex.APIClient {
|
||||
apiClient, err := sqlserverflex.NewAPIClient(
|
||||
utils.UserAgentConfigOption(testVersion),
|
||||
config.WithEndpoint(testCustomEndpoint),
|
||||
)
|
||||
if err != nil {
|
||||
t.Errorf("error configuring client: %v", err)
|
||||
}
|
||||
return apiClient
|
||||
}(),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
diags := diag.Diagnostics{}
|
||||
|
||||
actual := ConfigureClient(ctx, tt.args.providerData, &diags)
|
||||
if diags.HasError() != tt.wantErr {
|
||||
t.Errorf("ConfigureClient() error = %v, want %v", diags.HasError(), tt.wantErr)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, tt.expected) {
|
||||
t.Errorf("ConfigureClient() = %v, want %v", actual, tt.expected)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue