From e91e10e29abcdf646d3dd4879711ecd0970897ae Mon Sep 17 00:00:00 2001 From: "Marcel S. Henselin" Date: Wed, 21 Jan 2026 16:29:22 +0100 Subject: [PATCH] chore: updated files - work save --- .goreleaser.yaml | 2 + .../sqlserverflexalpha_database.md | 31 +++ docs/resources/sqlserverflexalpha_database.md | 36 +++ pkg/.gitkeep | 0 pkg/albbeta/client.go | 5 +- pkg/albwafalpha/client.go | 5 +- pkg/cdnbeta/client.go | 5 +- pkg/certificatesbeta/client.go | 5 +- pkg/edgebeta/client.go | 5 +- pkg/gitbeta/client.go | 5 +- pkg/gitbeta/model_instance.go | 83 +++++-- pkg/iaasalpha/client.go | 5 +- pkg/iaasbeta/client.go | 5 +- pkg/intakebeta/client.go | 5 +- pkg/kmsbeta/client.go | 5 +- pkg/logsalpha/client.go | 5 +- pkg/logsbeta/client.go | 5 +- pkg/postgresflexalpha/client.go | 5 +- .../model_create_instance_request_payload.go | 22 ++ pkg/runcommandbeta/client.go | 5 +- pkg/sfsbeta/client.go | 5 +- pkg/sqlserverflexalpha/client.go | 5 +- pkg/vpnalpha/client.go | 5 +- sample/postgres/postresql.tf | 2 +- .../sqlserverflex_database_config.yml | 7 + .../postgresflexalpha/instance/resource.go | 2 +- .../sqlserverflexalpha/backup/datasource.go | 48 ++++ .../collation/datasource.go | 48 ++++ .../sqlserverflexalpha/database/datasource.go | 49 ++++ .../sqlserverflexalpha/database/resource.go | 217 ++++++++++++++++++ .../sqlserverflexalpha/version/datasource.go | 48 ++++ stackit/provider.go | 3 + tools/formats.go | 53 +++++ tools/main.go | 207 ++++++++++++++--- tools/templates/data_source_scaffold.gotmpl | 49 ++++ tools/templates/provider_scaffold.gotmpl | 39 ++++ tools/templates/resource_scaffold.gotmpl | 208 +++++++++++++++++ 37 files changed, 1121 insertions(+), 118 deletions(-) create mode 100644 docs/data-sources/sqlserverflexalpha_database.md create mode 100644 docs/resources/sqlserverflexalpha_database.md delete mode 100644 pkg/.gitkeep create mode 100644 stackit/internal/services/sqlserverflexalpha/backup/datasource.go create mode 100644 stackit/internal/services/sqlserverflexalpha/collation/datasource.go create mode 100644 stackit/internal/services/sqlserverflexalpha/database/datasource.go create mode 100644 stackit/internal/services/sqlserverflexalpha/database/resource.go create mode 100644 stackit/internal/services/sqlserverflexalpha/version/datasource.go create mode 100644 tools/formats.go create mode 100644 tools/templates/data_source_scaffold.gotmpl create mode 100644 tools/templates/provider_scaffold.gotmpl create mode 100644 tools/templates/resource_scaffold.gotmpl diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 55baab60..95b11695 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -29,6 +29,8 @@ builds: ignore: - goos: darwin goarch: '386' + - goos: windows + goarch: 'arm6' binary: '{{ .ProjectName }}_v{{ .Version }}' archives: - formats: [ 'zip' ] diff --git a/docs/data-sources/sqlserverflexalpha_database.md b/docs/data-sources/sqlserverflexalpha_database.md new file mode 100644 index 00000000..4aab99cc --- /dev/null +++ b/docs/data-sources/sqlserverflexalpha_database.md @@ -0,0 +1,31 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackitprivatepreview_sqlserverflexalpha_database Data Source - stackitprivatepreview" +subcategory: "" +description: |- + +--- + +# stackitprivatepreview_sqlserverflexalpha_database (Data Source) + + + + + + +## Schema + +### Required + +- `database_name` (String) The name of the database. +- `instance_id` (String) The ID of the instance. +- `project_id` (String) The STACKIT project ID. +- `region` (String) The region which should be addressed + +### Read-Only + +- `collation_name` (String) The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint. +- `compatibility_level` (Number) CompatibilityLevel of the Database. +- `id` (Number) The id of the database. +- `name` (String) The name of the database. +- `owner` (String) The owner of the database. diff --git a/docs/resources/sqlserverflexalpha_database.md b/docs/resources/sqlserverflexalpha_database.md new file mode 100644 index 00000000..fd6ba0fd --- /dev/null +++ b/docs/resources/sqlserverflexalpha_database.md @@ -0,0 +1,36 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackitprivatepreview_sqlserverflexalpha_database Resource - stackitprivatepreview" +subcategory: "" +description: |- + +--- + +# stackitprivatepreview_sqlserverflexalpha_database (Resource) + + + + + + +## Schema + +### Required + +- `name` (String) The name of the database. +- `owner` (String) The owner of the database. + +### Optional + +- `collation` (String) The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint. +- `compatibility` (Number) CompatibilityLevel of the Database. +- `database_name` (String) The name of the database. +- `instance_id` (String) The ID of the instance. +- `project_id` (String) The STACKIT project ID. +- `region` (String) The region which should be addressed + +### Read-Only + +- `collation_name` (String) The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint. +- `compatibility_level` (Number) CompatibilityLevel of the Database. +- `id` (Number) The id of the database. diff --git a/pkg/.gitkeep b/pkg/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/pkg/albbeta/client.go b/pkg/albbeta/client.go index 5b35b745..1059e022 100644 --- a/pkg/albbeta/client.go +++ b/pkg/albbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/albwafalpha/client.go b/pkg/albwafalpha/client.go index 50ba2873..4a3605d5 100644 --- a/pkg/albwafalpha/client.go +++ b/pkg/albwafalpha/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/cdnbeta/client.go b/pkg/cdnbeta/client.go index de5cffb3..01316883 100644 --- a/pkg/cdnbeta/client.go +++ b/pkg/cdnbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/certificatesbeta/client.go b/pkg/certificatesbeta/client.go index 993b7b42..56be2ebb 100644 --- a/pkg/certificatesbeta/client.go +++ b/pkg/certificatesbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/edgebeta/client.go b/pkg/edgebeta/client.go index 8d089622..0ad796cc 100644 --- a/pkg/edgebeta/client.go +++ b/pkg/edgebeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/gitbeta/client.go b/pkg/gitbeta/client.go index 1f80279e..fa9de508 100644 --- a/pkg/gitbeta/client.go +++ b/pkg/gitbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/gitbeta/model_instance.go b/pkg/gitbeta/model_instance.go index d782bfae..f00d5e51 100644 --- a/pkg/gitbeta/model_instance.go +++ b/pkg/gitbeta/model_instance.go @@ -82,22 +82,42 @@ type InstanceGetConsumedObjectStorageArgType = string type InstanceGetConsumedObjectStorageRetType = string /* - types and functions for created_at + types and functions for created */ // isDateTime -type InstanceGetCreatedAtAttributeType = *time.Time -type InstanceGetCreatedAtArgType = time.Time -type InstanceGetCreatedAtRetType = time.Time +type InstanceGetCreatedAttributeType = *time.Time +type InstanceGetCreatedArgType = time.Time +type InstanceGetCreatedRetType = time.Time -func getInstanceGetCreatedAtAttributeTypeOk(arg InstanceGetCreatedAtAttributeType) (ret InstanceGetCreatedAtRetType, ok bool) { +func getInstanceGetCreatedAttributeTypeOk(arg InstanceGetCreatedAttributeType) (ret InstanceGetCreatedRetType, ok bool) { if arg == nil { return ret, false } return *arg, true } -func setInstanceGetCreatedAtAttributeType(arg *InstanceGetCreatedAtAttributeType, val InstanceGetCreatedAtRetType) { +func setInstanceGetCreatedAttributeType(arg *InstanceGetCreatedAttributeType, val InstanceGetCreatedRetType) { + *arg = &val +} + +/* + types and functions for feature_toggle +*/ + +// isModel +type InstanceGetFeatureToggleAttributeType = *FeatureToggle +type InstanceGetFeatureToggleArgType = FeatureToggle +type InstanceGetFeatureToggleRetType = FeatureToggle + +func getInstanceGetFeatureToggleAttributeTypeOk(arg InstanceGetFeatureToggleAttributeType) (ret InstanceGetFeatureToggleRetType, ok bool) { + if arg == nil { + return ret, false + } + return *arg, true +} + +func setInstanceGetFeatureToggleAttributeType(arg *InstanceGetFeatureToggleAttributeType, val InstanceGetFeatureToggleRetType) { *arg = &val } @@ -351,7 +371,9 @@ type Instance struct { ConsumedObjectStorage InstanceGetConsumedObjectStorageAttributeType `json:"consumed_object_storage" required:"true"` // The date and time the creation of the STACKIT Git instance was triggered. // REQUIRED - CreatedAt InstanceGetCreatedAtAttributeType `json:"created_at" required:"true"` + Created InstanceGetCreatedAttributeType `json:"created" required:"true"` + // REQUIRED + FeatureToggle InstanceGetFeatureToggleAttributeType `json:"feature_toggle" required:"true"` // Instance flavor. // REQUIRED Flavor InstanceGetFlavorAttributeType `json:"flavor" required:"true"` @@ -378,12 +400,13 @@ type _Instance Instance // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewInstance(acl InstanceGetAclArgType, consumedDisk InstanceGetConsumedDiskArgType, consumedObjectStorage InstanceGetConsumedObjectStorageArgType, createdAt InstanceGetCreatedAtArgType, flavor InstanceGetFlavorArgType, id InstanceGetIdArgType, name InstanceGetNameArgType, state InstanceGetStateArgType, url InstanceGetUrlArgType, version InstanceGetVersionArgType) *Instance { +func NewInstance(acl InstanceGetAclArgType, consumedDisk InstanceGetConsumedDiskArgType, consumedObjectStorage InstanceGetConsumedObjectStorageArgType, created InstanceGetCreatedArgType, featureToggle InstanceGetFeatureToggleArgType, flavor InstanceGetFlavorArgType, id InstanceGetIdArgType, name InstanceGetNameArgType, state InstanceGetStateArgType, url InstanceGetUrlArgType, version InstanceGetVersionArgType) *Instance { this := Instance{} setInstanceGetAclAttributeType(&this.Acl, acl) setInstanceGetConsumedDiskAttributeType(&this.ConsumedDisk, consumedDisk) setInstanceGetConsumedObjectStorageAttributeType(&this.ConsumedObjectStorage, consumedObjectStorage) - setInstanceGetCreatedAtAttributeType(&this.CreatedAt, createdAt) + setInstanceGetCreatedAttributeType(&this.Created, created) + setInstanceGetFeatureToggleAttributeType(&this.FeatureToggle, featureToggle) setInstanceGetFlavorAttributeType(&this.Flavor, flavor) setInstanceGetIdAttributeType(&this.Id, id) setInstanceGetNameAttributeType(&this.Name, name) @@ -452,21 +475,38 @@ func (o *Instance) SetConsumedObjectStorage(v InstanceGetConsumedObjectStorageRe setInstanceGetConsumedObjectStorageAttributeType(&o.ConsumedObjectStorage, v) } -// GetCreatedAt returns the CreatedAt field value -func (o *Instance) GetCreatedAt() (ret InstanceGetCreatedAtRetType) { - ret, _ = o.GetCreatedAtOk() +// GetCreated returns the Created field value +func (o *Instance) GetCreated() (ret InstanceGetCreatedRetType) { + ret, _ = o.GetCreatedOk() return ret } -// GetCreatedAtOk returns a tuple with the CreatedAt field value +// GetCreatedOk returns a tuple with the Created field value // and a boolean to check if the value has been set. -func (o *Instance) GetCreatedAtOk() (ret InstanceGetCreatedAtRetType, ok bool) { - return getInstanceGetCreatedAtAttributeTypeOk(o.CreatedAt) +func (o *Instance) GetCreatedOk() (ret InstanceGetCreatedRetType, ok bool) { + return getInstanceGetCreatedAttributeTypeOk(o.Created) } -// SetCreatedAt sets field value -func (o *Instance) SetCreatedAt(v InstanceGetCreatedAtRetType) { - setInstanceGetCreatedAtAttributeType(&o.CreatedAt, v) +// SetCreated sets field value +func (o *Instance) SetCreated(v InstanceGetCreatedRetType) { + setInstanceGetCreatedAttributeType(&o.Created, v) +} + +// GetFeatureToggle returns the FeatureToggle field value +func (o *Instance) GetFeatureToggle() (ret InstanceGetFeatureToggleRetType) { + ret, _ = o.GetFeatureToggleOk() + return ret +} + +// GetFeatureToggleOk returns a tuple with the FeatureToggle field value +// and a boolean to check if the value has been set. +func (o *Instance) GetFeatureToggleOk() (ret InstanceGetFeatureToggleRetType, ok bool) { + return getInstanceGetFeatureToggleAttributeTypeOk(o.FeatureToggle) +} + +// SetFeatureToggle sets field value +func (o *Instance) SetFeatureToggle(v InstanceGetFeatureToggleRetType) { + setInstanceGetFeatureToggleAttributeType(&o.FeatureToggle, v) } // GetFlavor returns the Flavor field value @@ -582,8 +622,11 @@ func (o Instance) ToMap() (map[string]interface{}, error) { if val, ok := getInstanceGetConsumedObjectStorageAttributeTypeOk(o.ConsumedObjectStorage); ok { toSerialize["ConsumedObjectStorage"] = val } - if val, ok := getInstanceGetCreatedAtAttributeTypeOk(o.CreatedAt); ok { - toSerialize["CreatedAt"] = val + if val, ok := getInstanceGetCreatedAttributeTypeOk(o.Created); ok { + toSerialize["Created"] = val + } + if val, ok := getInstanceGetFeatureToggleAttributeTypeOk(o.FeatureToggle); ok { + toSerialize["FeatureToggle"] = val } if val, ok := getInstanceGetFlavorAttributeTypeOk(o.Flavor); ok { toSerialize["Flavor"] = val diff --git a/pkg/iaasalpha/client.go b/pkg/iaasalpha/client.go index f994a657..ecb8c6bc 100644 --- a/pkg/iaasalpha/client.go +++ b/pkg/iaasalpha/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/iaasbeta/client.go b/pkg/iaasbeta/client.go index ccfc8f2f..e4377132 100644 --- a/pkg/iaasbeta/client.go +++ b/pkg/iaasbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/intakebeta/client.go b/pkg/intakebeta/client.go index 0f6930b0..82b728f9 100644 --- a/pkg/intakebeta/client.go +++ b/pkg/intakebeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/kmsbeta/client.go b/pkg/kmsbeta/client.go index 5ef31839..0d13c206 100644 --- a/pkg/kmsbeta/client.go +++ b/pkg/kmsbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/logsalpha/client.go b/pkg/logsalpha/client.go index c01c446b..137e252b 100644 --- a/pkg/logsalpha/client.go +++ b/pkg/logsalpha/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/logsbeta/client.go b/pkg/logsbeta/client.go index 8b200a38..05e2cbb6 100644 --- a/pkg/logsbeta/client.go +++ b/pkg/logsbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/postgresflexalpha/client.go b/pkg/postgresflexalpha/client.go index f5ab16f9..d2c38194 100644 --- a/pkg/postgresflexalpha/client.go +++ b/pkg/postgresflexalpha/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/postgresflexalpha/model_create_instance_request_payload.go b/pkg/postgresflexalpha/model_create_instance_request_payload.go index 96d88b0c..da35858a 100644 --- a/pkg/postgresflexalpha/model_create_instance_request_payload.go +++ b/pkg/postgresflexalpha/model_create_instance_request_payload.go @@ -17,6 +17,26 @@ import ( // checks if the CreateInstanceRequestPayload type satisfies the MappedNullable interface at compile time var _ MappedNullable = &CreateInstanceRequestPayload{} +/* + types and functions for acl +*/ + +// isModel +type CreateInstancePayloadGetAclAttributeType = *[]string +type CreateInstancePayloadGetAclArgType = *[]string +type CreateInstancePayloadGetAclRetType = []string + +func getCreateInstancePayloadGetAclAttributeTypeOk(arg CreateInstancePayloadGetAclAttributeType) (ret CreateInstancePayloadGetAclRetType, ok bool) { + if arg == nil { + return ret, false + } + return *arg, true +} + +func setCreateInstancePayloadGetAclAttributeType(arg *CreateInstancePayloadGetAclAttributeType, val CreateInstancePayloadGetAclRetType) { + *arg = &val +} + /* types and functions for backupSchedule */ @@ -203,6 +223,8 @@ type CreateInstanceRequestPayloadGetVersionRetType = string // CreateInstanceRequestPayload struct for CreateInstanceRequestPayload type CreateInstanceRequestPayload struct { + // REQUIRED + Acl CreateInstancePayloadGetAclAttributeType `json:"acl" required:"true"` // The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule. // REQUIRED BackupSchedule CreateInstanceRequestPayloadGetBackupScheduleAttributeType `json:"backupSchedule" required:"true"` diff --git a/pkg/runcommandbeta/client.go b/pkg/runcommandbeta/client.go index 711f0007..183ad9ca 100644 --- a/pkg/runcommandbeta/client.go +++ b/pkg/runcommandbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/sfsbeta/client.go b/pkg/sfsbeta/client.go index 344a757a..46d371e9 100644 --- a/pkg/sfsbeta/client.go +++ b/pkg/sfsbeta/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/sqlserverflexalpha/client.go b/pkg/sqlserverflexalpha/client.go index f8a9ddbe..50dcb452 100644 --- a/pkg/sqlserverflexalpha/client.go +++ b/pkg/sqlserverflexalpha/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/pkg/vpnalpha/client.go b/pkg/vpnalpha/client.go index daced0da..3ad32f07 100644 --- a/pkg/vpnalpha/client.go +++ b/pkg/vpnalpha/client.go @@ -501,10 +501,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/sample/postgres/postresql.tf b/sample/postgres/postresql.tf index 89937c4b..db395a4a 100644 --- a/sample/postgres/postresql.tf +++ b/sample/postgres/postresql.tf @@ -51,7 +51,7 @@ resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" { } resource "stackitprivatepreview_postgresflexalpha_database" "example" { - count = 25 + count = 5 depends_on = [stackitprivatepreview_postgresflexalpha_user.ptlsdbadminuser] project_id = var.project_id instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example.instance_id diff --git a/service_specs/sqlserverflex_database_config.yml b/service_specs/sqlserverflex_database_config.yml index 5421e9d3..f6a437a9 100644 --- a/service_specs/sqlserverflex_database_config.yml +++ b/service_specs/sqlserverflex_database_config.yml @@ -3,6 +3,10 @@ provider: resources: database: + schema: + attributes: + aliases: + id: database_id create: path: /v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases method: POST @@ -21,6 +25,9 @@ data_sources: method: GET database: + attributes: + aliases: + id: database_id read: path: /v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases/{databaseName} method: GET diff --git a/stackit/internal/services/postgresflexalpha/instance/resource.go b/stackit/internal/services/postgresflexalpha/instance/resource.go index 8cb30519..603e7182 100644 --- a/stackit/internal/services/postgresflexalpha/instance/resource.go +++ b/stackit/internal/services/postgresflexalpha/instance/resource.go @@ -211,7 +211,7 @@ func (r *instanceResource) Create( func modelToCreateInstancePayload(netAcl []string, model postgresflexalpha.InstanceModel, replVal int32) postgresflex.CreateInstanceRequestPayload { payload := postgresflex.CreateInstanceRequestPayload{ - // Acl: &netAcl, + Acl: &netAcl, BackupSchedule: model.BackupSchedule.ValueStringPointer(), Encryption: &postgresflex.InstanceEncryption{ KekKeyId: model.Encryption.KekKeyId.ValueStringPointer(), diff --git a/stackit/internal/services/sqlserverflexalpha/backup/datasource.go b/stackit/internal/services/sqlserverflexalpha/backup/datasource.go new file mode 100644 index 00000000..24f99500 --- /dev/null +++ b/stackit/internal/services/sqlserverflexalpha/backup/datasource.go @@ -0,0 +1,48 @@ +package sqlserverflexalpha + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ datasource.DataSource = (*backupDataSource)(nil) + +func NewBackupDataSource() datasource.DataSource { + return &backupDataSource{} +} + +type backupDataSource struct{} + +type backupDataSourceModel struct { + Id types.String `tfsdk:"id"` +} + +func (d *backupDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "sqlserverflexalpha_backup" +} + +func (d *backupDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = sqlserverflexalphaGen.BackupDataSourceSchema(ctx) +} + +func (d *backupDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data sqlserverflexalphaGen.backupDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Read API call logic + + // Example data value setting + data.Id = types.StringValue("example-id") + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/stackit/internal/services/sqlserverflexalpha/collation/datasource.go b/stackit/internal/services/sqlserverflexalpha/collation/datasource.go new file mode 100644 index 00000000..f55291c6 --- /dev/null +++ b/stackit/internal/services/sqlserverflexalpha/collation/datasource.go @@ -0,0 +1,48 @@ +package sqlserverflexalpha + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ datasource.DataSource = (*collationDataSource)(nil) + +func NewCollationDataSource() datasource.DataSource { + return &collationDataSource{} +} + +type collationDataSource struct{} + +type collationDataSourceModel struct { + Id types.String `tfsdk:"id"` +} + +func (d *collationDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "sqlserverflexalpha_collation" +} + +func (d *collationDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = sqlserverflexalphaGen.CollationDataSourceSchema(ctx) +} + +func (d *collationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data sqlserverflexalphaGen.collationDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Read API call logic + + // Example data value setting + data.Id = types.StringValue("example-id") + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/stackit/internal/services/sqlserverflexalpha/database/datasource.go b/stackit/internal/services/sqlserverflexalpha/database/datasource.go new file mode 100644 index 00000000..6cca0141 --- /dev/null +++ b/stackit/internal/services/sqlserverflexalpha/database/datasource.go @@ -0,0 +1,49 @@ +package sqlserverflexalpha + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" + + sqlserverflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/datasources_gen" +) + +var _ datasource.DataSource = (*databaseDataSource)(nil) + +func NewDatabaseDataSource() datasource.DataSource { + return &databaseDataSource{} +} + +type databaseDataSource struct{} + +type databaseDataSourceModel struct { + Id types.String `tfsdk:"id"` +} + +func (d *databaseDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database" +} + +func (d *databaseDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = sqlserverflexalphaGen.DatabaseDataSourceSchema(ctx) +} + +func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data sqlserverflexalphaGen.DatabaseModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Read API call logic + + // Example data value setting + // data.Id = types.StringValue("example-id") + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/stackit/internal/services/sqlserverflexalpha/database/resource.go b/stackit/internal/services/sqlserverflexalpha/database/resource.go new file mode 100644 index 00000000..3a073f82 --- /dev/null +++ b/stackit/internal/services/sqlserverflexalpha/database/resource.go @@ -0,0 +1,217 @@ +package sqlserverflexalpha + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha" + "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion" + "github.com/stackitcloud/stackit-sdk-go/core/config" + + "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core" + "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils" + + sqlserverflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/resources_gen" +) + +var ( + _ resource.Resource = &databaseResource{} + _ resource.ResourceWithConfigure = &databaseResource{} + _ resource.ResourceWithImportState = &databaseResource{} + _ resource.ResourceWithModifyPlan = &databaseResource{} +) + +func NewDatabaseResource() resource.Resource { + return &databaseResource{} +} + +type databaseResource struct { + client *sqlserverflexalpha.APIClient + providerData core.ProviderData +} + +func (r *databaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database" +} + +func (r *databaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = sqlserverflexalphaGen.DatabaseResourceSchema(ctx) +} + +// Configure adds the provider configured client to the resource. +func (r *databaseResource) 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.PostgresFlexCustomEndpoint != "" { + apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(r.providerData.PostgresFlexCustomEndpoint)) + } else { + apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(r.providerData.GetRegion())) + } + apiClient, err := sqlserverflexalpha.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, "sqlserverflexalpha.Database client configured") +} + +func (r *databaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data sqlserverflexalphaGen.DatabaseModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // TODO: Create API call logic + + // Example data value setting + //data.DatabaseId = types.StringValue("id-from-response") + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + + tflog.Info(ctx, "sqlserverflexalpha.Database created") +} + +func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data sqlserverflexalphaGen.DatabaseModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Read API call logic + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + + tflog.Info(ctx, "sqlserverflexalpha.Database read") +} + +func (r *databaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data sqlserverflexalphaGen.DatabaseModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Update API call logic + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + + tflog.Info(ctx, "sqlserverflexalpha.Database updated") +} + +func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data sqlserverflexalphaGen.DatabaseModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Delete API call logic + + tflog.Info(ctx, "sqlserverflexalpha.Database deleted") +} + +// ModifyPlan implements resource.ResourceWithModifyPlan. +// Use the modifier to set the effective region in the current plan. +func (r *databaseResource) ModifyPlan( + ctx context.Context, + req resource.ModifyPlanRequest, + resp *resource.ModifyPlanResponse, +) { // nolint:gocritic // function signature required by Terraform + var configModel sqlserverflexalphaGen.DatabaseModel + // skip initial empty configuration to avoid follow-up errors + if req.Config.Raw.IsNull() { + return + } + resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...) + if resp.Diagnostics.HasError() { + return + } + + var planModel sqlserverflexalphaGen.DatabaseModel + 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 + } +} + +// 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 *databaseResource) ImportState( + ctx context.Context, + req resource.ImportStateRequest, + resp *resource.ImportStateResponse, +) { + idParts := strings.Split(req.ID, core.Separator) + + // Todo: Import logic + 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, + "Sqlserverflexalpha 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, "Sqlserverflexalpha database state imported") +} diff --git a/stackit/internal/services/sqlserverflexalpha/version/datasource.go b/stackit/internal/services/sqlserverflexalpha/version/datasource.go new file mode 100644 index 00000000..1fb9466b --- /dev/null +++ b/stackit/internal/services/sqlserverflexalpha/version/datasource.go @@ -0,0 +1,48 @@ +package sqlserverflexalpha + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ datasource.DataSource = (*versionDataSource)(nil) + +func NewVersionDataSource() datasource.DataSource { + return &versionDataSource{} +} + +type versionDataSource struct{} + +type versionDataSourceModel struct { + Id types.String `tfsdk:"id"` +} + +func (d *versionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "sqlserverflexalpha_version" +} + +func (d *versionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = sqlserverflexalphaGen.VersionDataSourceSchema(ctx) +} + +func (d *versionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data sqlserverflexalphaGen.versionDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Read API call logic + + // Example data value setting + data.Id = types.StringValue("example-id") + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/stackit/provider.go b/stackit/provider.go index 8550fc7b..ad8bd47d 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -22,6 +22,7 @@ import ( postgresFlexAlphaFlavor "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/flavor" postgresFlexAlphaInstance "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance" postgresFlexAlphaUser "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/user" + sqlserverflexalpha "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database" sqlserverFlexAlphaFlavor "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavor" sqlServerFlexAlphaInstance "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance" sqlserverFlexAlphaUser "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user" @@ -499,6 +500,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource sqlserverFlexAlphaFlavor.NewFlavorDataSource, sqlServerFlexAlphaInstance.NewInstanceDataSource, sqlserverFlexAlphaUser.NewUserDataSource, + sqlserverflexalpha.NewDatabaseDataSource, } } @@ -510,6 +512,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource { postgresFlexAlphaUser.NewUserResource, sqlServerFlexAlphaInstance.NewInstanceResource, sqlserverFlexAlphaUser.NewUserResource, + sqlserverflexalpha.NewDatabaseResource, } return resources } diff --git a/tools/formats.go b/tools/formats.go new file mode 100644 index 00000000..8b73a42a --- /dev/null +++ b/tools/formats.go @@ -0,0 +1,53 @@ +package tools + +import ( + "regexp" + "strings" + "unicode" + "unicode/utf8" +) + +// snakeLetters will match to the first letter and an underscore followed by a letter +var snakeLetters = regexp.MustCompile("(^[a-z])|_[a-z0-9]") + +func ToPascalCase(in string) string { + inputSplit := strings.Split(in, ".") + + var ucName string + + for _, v := range inputSplit { + if len(v) < 1 { + continue + } + + firstChar := v[0:1] + ucFirstChar := strings.ToUpper(firstChar) + + if len(v) < 2 { + ucName += ucFirstChar + continue + } + + ucName += ucFirstChar + v[1:] + } + + return snakeLetters.ReplaceAllStringFunc(ucName, func(s string) string { + return strings.ToUpper(strings.Replace(s, "_", "", -1)) + }) +} + +func ToCamelCase(in string) string { + pascal := ToPascalCase(in) + + // Grab first rune and lower case it + firstLetter, size := utf8.DecodeRuneInString(pascal) + if firstLetter == utf8.RuneError && size <= 1 { + return pascal + } + + return string(unicode.ToLower(firstLetter)) + pascal[size:] +} + +func ValidateSnakeCase(in string) bool { + return snakeLetters.MatchString(string(in)) +} diff --git a/tools/main.go b/tools/main.go index ed989800..3a0eef6b 100644 --- a/tools/main.go +++ b/tools/main.go @@ -14,7 +14,7 @@ import ( "regexp" "strconv" "strings" - "time" + "text/template" "github.com/ldez/go-git-cmd-wrapper/v2/clone" "github.com/ldez/go-git-cmd-wrapper/v2/git" @@ -50,6 +50,12 @@ func Build() error { return err } + slog.Info("Cleaning up old packages directory") + err = os.RemoveAll(path.Join(*root, "pkg")) + if err != nil { + return err + } + slog.Info("Creating generator dir", "dir", fmt.Sprintf("%s/%s", *root, GEN_REPO_NAME)) genDir, err := createGeneratorDir(*root, GEN_REPO, GEN_REPO_NAME) if err != nil { @@ -162,6 +168,10 @@ func Build() error { } slog.Info("Rearranging package directories") + err = os.MkdirAll(path.Join(*root, "pkg"), 0755) + if err != nil { + return err + } srcDir := path.Join(genDir, "sdk-repo-updated", "services") items, err := os.ReadDir(srcDir) if err != nil { @@ -171,27 +181,30 @@ func Build() error { if item.IsDir() { slog.Info(" -> package", "name", item.Name()) tgtDir := path.Join(*root, "pkg", item.Name()) - bakName := fmt.Sprintf("%s.%s", item.Name(), time.Now().Format("20060102-150405")) - if _, err = os.Stat(tgtDir); !os.IsNotExist(err) { - err = os.Rename( - tgtDir, - path.Join(*root, "pkg", bakName), - ) - if err != nil { - return err - } - } + // no backup needed as we generate new + //bakName := fmt.Sprintf("%s.%s", item.Name(), time.Now().Format("20060102-150405")) + //if _, err = os.Stat(tgtDir); !os.IsNotExist(err) { + // err = os.Rename( + // tgtDir, + // path.Join(*root, "pkg", bakName), + // ) + // if err != nil { + // return err + // } + //} err = os.Rename(path.Join(srcDir, item.Name()), tgtDir) if err != nil { return err } - if _, err = os.Stat(path.Join(*root, "pkg", bakName, "wait")); !os.IsNotExist(err) { - slog.Info(" Copying wait subfolder") - err = os.Rename(path.Join(*root, "pkg", bakName, "wait"), path.Join(tgtDir, "wait")) - if err != nil { - return err - } - } + + // wait is placed outside now + //if _, err = os.Stat(path.Join(*root, "pkg", bakName, "wait")); !os.IsNotExist(err) { + // slog.Info(" Copying wait subfolder") + // err = os.Rename(path.Join(*root, "pkg", bakName, "wait"), path.Join(tgtDir, "wait")) + // if err != nil { + // return err + // } + //} } } @@ -216,19 +229,17 @@ func Build() error { return err } - // TODO - for _, testDir := range []string{ - path.Join(*root, "stackit", "internal", "services"), - } { - fmt.Println(testDir) + err = createBoilerplate(*root, path.Join(*root, "stackit", "internal", "services")) + if err != nil { + return err } slog.Info("Finally removing temporary files and directories") - err = os.RemoveAll(path.Join(*root, "generated")) - if err != nil { - slog.Error("RemoveAll", "dir", path.Join(*root, "generated"), "err", err) - return err - } + //err = os.RemoveAll(path.Join(*root, "generated")) + //if err != nil { + // slog.Error("RemoveAll", "dir", path.Join(*root, "generated"), "err", err) + // return err + //} err = os.RemoveAll(path.Join(*root, GEN_REPO_NAME)) if err != nil { @@ -240,6 +251,146 @@ func Build() error { return nil } +type templateData struct { + PackageName string + NameCamel string + NamePascal string + NameSnake string +} + +func fileExists(path string) bool { + _, err := os.Stat(path) + if os.IsNotExist(err) { + return false + } + if err != nil { + panic(err) + } + return true +} + +func createBoilerplate(rootFolder, folder string) error { + services, err := os.ReadDir(folder) + if err != nil { + return err + } + for _, svc := range services { + if !svc.IsDir() { + continue + } + resources, err := os.ReadDir(path.Join(folder, svc.Name())) + if err != nil { + return err + } + + handleDS := false + handleRes := false + foundDS := false + foundRes := false + for _, res := range resources { + if !res.IsDir() { + continue + } + + resourceName := res.Name() + + dsFile := path.Join(folder, svc.Name(), res.Name(), "datasources_gen", fmt.Sprintf("%s_data_source_gen.go", res.Name())) + handleDS = fileExists(dsFile) + + resFile := path.Join(folder, svc.Name(), res.Name(), "resources_gen", fmt.Sprintf("%s_resource_gen.go", res.Name())) + handleRes = fileExists(resFile) + + dsGoFile := path.Join(folder, svc.Name(), res.Name(), "datasource.go") + foundDS = fileExists(dsGoFile) + + resGoFile := path.Join(folder, svc.Name(), res.Name(), "resource.go") + foundRes = fileExists(resGoFile) + + if handleDS && !foundDS { + slog.Info("Creating missing datasource.go", "service", svc.Name(), "resource", resourceName) + if !ValidateSnakeCase(resourceName) { + return errors.New("resource name is invalid") + } + + tplName := "data_source_scaffold.gotmpl" + err = writeTemplateToFile( + tplName, + path.Join(rootFolder, "tools", "templates", tplName), + path.Join(folder, svc.Name(), res.Name(), "datasource.go"), + &templateData{ + PackageName: svc.Name(), + NameCamel: ToCamelCase(resourceName), + NamePascal: ToPascalCase(resourceName), + NameSnake: resourceName, + }, + ) + if err != nil { + panic(err) + } + } + + if handleRes && !foundRes { + slog.Info("Creating missing resource.go", "service", svc.Name(), "resource", resourceName) + if !ValidateSnakeCase(resourceName) { + return errors.New("resource name is invalid") + } + + tplName := "resource_scaffold.gotmpl" + err = writeTemplateToFile( + tplName, + path.Join(rootFolder, "tools", "templates", tplName), + path.Join(folder, svc.Name(), res.Name(), "resource.go"), + &templateData{ + PackageName: svc.Name(), + NameCamel: ToCamelCase(resourceName), + NamePascal: ToPascalCase(resourceName), + NameSnake: resourceName, + }, + ) + if err != nil { + return err + } + } + } + } + return nil +} + +func ucfirst(s string) string { + if len(s) == 0 { + return "" + } + return strings.ToUpper(s[:1]) + s[1:] +} + +func writeTemplateToFile(tplName, tplFile, outFile string, data *templateData) error { + fn := template.FuncMap{ + "ucfirst": ucfirst, + } + + tmpl, err := template.New(tplName).Funcs(fn).ParseFiles(tplFile) + if err != nil { + return err + } + + var f *os.File + f, err = os.Create(outFile) + if err != nil { + return err + } + + err = tmpl.Execute(f, *data) + if err != nil { + return err + } + + err = f.Close() + if err != nil { + return err + } + return nil +} + func generateServiceFiles(rootDir, generatorDir string) error { // slog.Info("Generating specs folder") err := os.MkdirAll(path.Join(rootDir, "generated", "specs"), 0755) diff --git a/tools/templates/data_source_scaffold.gotmpl b/tools/templates/data_source_scaffold.gotmpl new file mode 100644 index 00000000..bcfb65ac --- /dev/null +++ b/tools/templates/data_source_scaffold.gotmpl @@ -0,0 +1,49 @@ +package {{.PackageName}} + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + + {{.PackageName}}Gen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/datasources_gen" +) + +var _ datasource.DataSource = (*{{.NameCamel}}DataSource)(nil) + +func New{{.NamePascal}}DataSource() datasource.DataSource { + return &{{.NameCamel}}DataSource{} +} + +type {{.NameCamel}}DataSource struct{ + client *{{.PackageName}}.APIClient + providerData core.ProviderData +} + +func (d *{{.NameCamel}}DataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_{{.PackageName}}_{{.NameSnake}}" +} + +func (d *{{.NameCamel}}DataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = {{.PackageName}}Gen.{{.NamePascal}}DataSourceSchema(ctx) +} + +func (d *{{.NameCamel}}DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data {{.PackageName}}Gen.{{.NameCamel}}Model + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Read API call logic + + // Example data value setting + // data.Id = types.StringValue("example-id") + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/tools/templates/provider_scaffold.gotmpl b/tools/templates/provider_scaffold.gotmpl new file mode 100644 index 00000000..8c141ccc --- /dev/null +++ b/tools/templates/provider_scaffold.gotmpl @@ -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{} +} diff --git a/tools/templates/resource_scaffold.gotmpl b/tools/templates/resource_scaffold.gotmpl new file mode 100644 index 00000000..39ea764e --- /dev/null +++ b/tools/templates/resource_scaffold.gotmpl @@ -0,0 +1,208 @@ +package {{.PackageName}} + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core" + "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils" + + {{.PackageName}}Gen "github.com/mhenselin/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{} +) + +func New{{.NamePascal}}Resource() resource.Resource { + return &{{.NameCamel}}Resource{} +} + +type {{.NameCamel}}Resource struct{ + client *{{.PackageName}}.APIClient + providerData core.ProviderData +} + +func (r *{{.NameCamel}}Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_{{.PackageName}}_{{.NameSnake}}" +} + +func (r *{{.NameCamel}}Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = {{.PackageName}}Gen.{{.NamePascal}}ResourceSchema(ctx) +} + +// 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.PostgresFlexCustomEndpoint != "" { + apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(r.providerData.PostgresFlexCustomEndpoint)) + } 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") +} + +func (r *{{.NameCamel}}Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data {{.PackageName}}Gen.{{.NamePascal}}Model + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // TODO: Create API call logic + + // Example data value setting + data.{{.NameCamel | ucfirst}}Id = types.StringValue("id-from-response") + + // 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}}Gen.{{.NamePascal}}Model + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Read API call logic + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + + tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} read") +} + +func (r *{{.NameCamel}}Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data {{.PackageName}}Gen.{{.NamePascal}}Model + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Update API call logic + + // 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}}Gen.{{.NamePascal}}Model + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Todo: Delete API call logic + + tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} deleted") +} + +// 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 + var configModel {{.PackageName}}Gen.{{.NamePascal}}Model + // skip initial empty configuration to avoid follow-up errors + if req.Config.Raw.IsNull() { + return + } + resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...) + if resp.Diagnostics.HasError() { + return + } + + var planModel {{.PackageName}}Gen.{{.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 + } +} + +// 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 + 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") +}