chore: adjust pagination for postgres database and flavor listing (#20)

* feat: implement pagination for database listing

* fix: change database_id attribute type from string to int64

* refactor: rename getDatabase to getDatabaseById for clarity

* fix: improve error handling for database not found scenario

* feat: add validation for database_id and name attributes; implement separate functions for fetching databases by ID and name

* feat: implement database client interface and update database fetching functions

* refactor: rename matcher to filter for clarity and update pagination logic

* feat: implement flavors retrieval with pagination and filtering support

* refactor: rename flavor import for consistency and clarity

* feat: add support for InstanceStatePending in wait handler logic

* refactor: simplify GetFlavorsRequest and GetFlavorsRequestExecute by removing pagination parameters

* refactor: improve readability of test cases by formatting function signatures and restructuring test runs

* refactor: remove pagination parameters from GetFlavorsRequest in test case

* refactor: simplify function signatures and improve readability in datasource and resource files

* refactor: add descriptions for user-related attributes in datasource schema

* refactor: enhance user resource schema with additional attributes and improve logging

* refactor: delete unused file

* refactor: standardize formatting and improve function naming for user resource management

* refactor: remove skip from TestMapFields and update roles initialization in resource tests

* fix: golangci lint issues

* fix: golangci lint issues again

* fix: golangci lint issues again
This commit is contained in:
Andre_Harms 2026-01-16 16:23:10 +01:00 committed by GitHub
parent 0150fea302
commit 979220be66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 3630 additions and 2759 deletions

View file

@ -193,7 +193,7 @@ type DefaultApi interface {
@param region The region which should be addressed
@return ApiGetFlavorsRequestRequest
*/
GetFlavorsRequest(ctx context.Context, projectId string, region string, page, size *int64, sort *FlavorSort) ApiGetFlavorsRequestRequest
GetFlavorsRequest(ctx context.Context, projectId string, region string) ApiGetFlavorsRequestRequest
/*
GetFlavorsRequestExecute executes the request
@ -203,7 +203,7 @@ type DefaultApi interface {
@return GetFlavorsResponse
*/
GetFlavorsRequestExecute(ctx context.Context, projectId string, region string, page, size *int64, sort *FlavorSort) (*GetFlavorsResponse, error)
GetFlavorsRequestExecute(ctx context.Context, projectId string, region string) (*GetFlavorsResponse, error)
/*
GetInstanceRequest Get Specific Instance
Get information about a specific available instance
@ -2520,27 +2520,21 @@ Get all available flavors for a project.
@param region The region which should be addressed
@return ApiGetFlavorsRequestRequest
*/
func (a *APIClient) GetFlavorsRequest(ctx context.Context, projectId string, region string, page, size *int64, sort *FlavorSort) ApiGetFlavorsRequestRequest {
func (a *APIClient) GetFlavorsRequest(ctx context.Context, projectId string, region string) ApiGetFlavorsRequestRequest {
return GetFlavorsRequestRequest{
apiService: a.defaultApi,
ctx: ctx,
projectId: projectId,
region: region,
page: page,
size: size,
sort: sort,
}
}
func (a *APIClient) GetFlavorsRequestExecute(ctx context.Context, projectId string, region string, page, size *int64, sort *FlavorSort) (*GetFlavorsResponse, error) {
func (a *APIClient) GetFlavorsRequestExecute(ctx context.Context, projectId string, region string) (*GetFlavorsResponse, error) {
r := GetFlavorsRequestRequest{
apiService: a.defaultApi,
ctx: ctx,
projectId: projectId,
region: region,
page: page,
size: size,
sort: sort,
}
return r.Execute()
}

File diff suppressed because it is too large Load diff

View file

@ -142,6 +142,8 @@ func PartialUpdateInstanceWaitHandler(
return false, nil, nil
case InstanceStateProgressing:
return false, nil, nil
case InstanceStatePending:
return false, nil, nil
case InstanceStateTerminating:
return false, nil, nil
case InstanceStateSuccess:
@ -154,96 +156,3 @@ func PartialUpdateInstanceWaitHandler(
handler.SetTimeout(45 * time.Minute)
return handler
}
//// DeleteInstanceWaitHandler will wait for instance deletion
//func DeleteInstanceWaitHandler(
// ctx context.Context,
// a APIClientInstanceInterface,
// projectId, region, instanceId string,
//) *wait.AsyncActionHandler[struct{}] {
// handler := wait.New(
// func() (waitFinished bool, response *struct{}, err error) {
// s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
// if err != nil {
// return false, nil, err
// }
// if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
// return false, nil, nil
// }
// // TODO - maybe we want to validate status if no 404 error (only unknown or terminating should be valid)
// // switch *s.Status {
// // default:
// // return true, nil, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
// // case InstanceStateSuccess:
// // return false, nil, nil
// // case InstanceStateTerminating:
// // return false, nil, nil
// // }
//
// // TODO - add tflog for ignored cases
// oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
// if !ok {
// return false, nil, err
// }
// if oapiErr.StatusCode != 404 {
// return false, nil, err
// }
// return true, nil, nil
// },
// )
// handler.SetTimeout(5 * time.Minute)
// return handler
//}
//
//// TODO - remove
//// ForceDeleteInstanceWaitHandler will wait for instance deletion
//func ForceDeleteInstanceWaitHandler(
// ctx context.Context,
// a APIClientInstanceInterface,
// projectId, region, instanceId string,
//) *wait.AsyncActionHandler[struct{}] {
// handler := wait.New(
// func() (waitFinished bool, response *struct{}, err error) {
// _, err = a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
// if err == nil {
// return false, nil, nil
// }
// oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
// if !ok {
// return false, nil, err
// }
// if oapiErr.StatusCode != 404 {
// return false, nil, err
// }
// return true, nil, nil
// },
// )
// handler.SetTimeout(15 * time.Minute)
// return handler
//}
// DeleteUserWaitHandler will wait for delete
func DeleteUserWaitHandler(
ctx context.Context,
a APIClientUserInterface,
projectId, region, instanceId string, userId int64,
) *wait.AsyncActionHandler[struct{}] {
handler := wait.New(
func() (waitFinished bool, response *struct{}, err error) {
_, err = a.GetUserRequestExecute(ctx, projectId, region, instanceId, userId)
if err == nil {
return false, nil, nil
}
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
if !ok {
return false, nil, err
}
if oapiErr.StatusCode != 404 {
return false, nil, err
}
return true, nil, nil
},
)
handler.SetTimeout(1 * time.Minute)
return handler
}

View file

@ -23,7 +23,10 @@ type apiClientInstanceMocked struct {
usersGetErrorStatus int
}
func (a *apiClientInstanceMocked) GetInstanceRequestExecute(_ context.Context, _, _, _ string) (*postgresflex.GetInstanceResponse, error) {
func (a *apiClientInstanceMocked) GetInstanceRequestExecute(
_ context.Context,
_, _, _ string,
) (*postgresflex.GetInstanceResponse, error) {
if a.instanceGetFails {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: 500,
@ -43,7 +46,10 @@ func (a *apiClientInstanceMocked) GetInstanceRequestExecute(_ context.Context, _
}, nil
}
func (a *apiClientInstanceMocked) ListUsersRequestExecute(_ context.Context, _, _, _ string) (*postgresflex.ListUserResponse, error) {
func (a *apiClientInstanceMocked) ListUsersRequestExecute(
_ context.Context,
_, _, _ string,
) (*postgresflex.ListUserResponse, error) {
if a.usersGetErrorStatus != 0 {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: a.usersGetErrorStatus,
@ -59,31 +65,6 @@ func (a *apiClientInstanceMocked) ListUsersRequestExecute(_ context.Context, _,
}, nil
}
// Used for testing user operations
type apiClientUserMocked struct {
getFails bool
userId int64
isUserDeleted bool
}
func (a *apiClientUserMocked) GetUserRequestExecute(_ context.Context, _, _, _ string, _ int64) (*postgresflex.GetUserResponse, error) {
if a.getFails {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: 500,
}
}
if a.isUserDeleted {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: 404,
}
}
return &postgresflex.GetUserResponse{
Id: &a.userId,
}, nil
}
func TestCreateInstanceWaitHandler(t *testing.T) {
tests := []struct {
desc string
@ -213,28 +194,30 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
instanceId := "foo-bar"
t.Run(
tt.desc, func(t *testing.T) {
instanceId := "foo-bar"
apiClient := &apiClientInstanceMocked{
instanceId: instanceId,
instanceState: tt.instanceState,
instanceNetwork: tt.instanceNetwork,
instanceGetFails: tt.instanceGetFails,
usersGetErrorStatus: tt.usersGetErrorStatus,
}
apiClient := &apiClientInstanceMocked{
instanceId: instanceId,
instanceState: tt.instanceState,
instanceNetwork: tt.instanceNetwork,
instanceGetFails: tt.instanceGetFails,
usersGetErrorStatus: tt.usersGetErrorStatus,
}
handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background())
if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background())
if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
if !cmp.Equal(gotRes, tt.wantRes) {
t.Fatalf("handler gotRes = %v, want %v", gotRes, tt.wantRes)
}
})
if !cmp.Equal(gotRes, tt.wantRes) {
t.Fatalf("handler gotRes = %v, want %v", gotRes, tt.wantRes)
}
},
)
}
}
@ -324,160 +307,29 @@ func TestUpdateInstanceWaitHandler(t *testing.T) {
wantRes: nil,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
instanceId := "foo-bar"
apiClient := &apiClientInstanceMocked{
instanceId: instanceId,
instanceState: tt.instanceState,
instanceNetwork: tt.instanceNetwork,
instanceGetFails: tt.instanceGetFails,
}
handler := PartialUpdateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
if !cmp.Equal(gotRes, tt.wantRes) {
t.Fatalf("handler gotRes = %v, want %v", gotRes, tt.wantRes)
}
})
}
}
func TestDeleteInstanceWaitHandler(t *testing.T) {
tests := []struct {
desc string
instanceGetFails bool
instanceState string
wantErr bool
}{
{
desc: "delete_failed",
instanceGetFails: false,
instanceState: InstanceStateFailed,
wantErr: true,
},
{
desc: "get_fails",
instanceGetFails: true,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(
tt.desc, func(t *testing.T) {
instanceId := "foo-bar"
apiClient := &apiClientInstanceMocked{
instanceGetFails: tt.instanceGetFails,
instanceId: instanceId,
instanceState: tt.instanceState,
instanceNetwork: tt.instanceNetwork,
instanceGetFails: tt.instanceGetFails,
}
handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
handler := PartialUpdateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
if !cmp.Equal(gotRes, tt.wantRes) {
t.Fatalf("handler gotRes = %v, want %v", gotRes, tt.wantRes)
}
},
)
}
}
func TestForceDeleteInstanceWaitHandler(t *testing.T) {
tests := []struct {
desc string
instanceGetFails bool
instanceState string
wantErr bool
}{
{
desc: "delete_failed",
instanceGetFails: false,
instanceState: InstanceStateUnknown,
wantErr: true,
},
{
desc: "get_fails",
instanceGetFails: true,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(
tt.desc, func(t *testing.T) {
instanceId := "foo-bar"
apiClient := &apiClientInstanceMocked{
instanceGetFails: tt.instanceGetFails,
instanceIsForceDeleted: tt.instanceState == InstanceStateFailed,
instanceId: instanceId,
instanceState: tt.instanceState,
}
handler := ForceDeleteInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
},
)
}
}
func TestDeleteUserWaitHandler(t *testing.T) {
tests := []struct {
desc string
deleteFails bool
getFails bool
wantErr bool
}{
{
desc: "delete_succeeded",
deleteFails: false,
getFails: false,
wantErr: false,
},
{
desc: "delete_failed",
deleteFails: true,
getFails: false,
wantErr: true,
},
{
desc: "get_fails",
deleteFails: false,
getFails: true,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
userId := int64(1001)
apiClient := &apiClientUserMocked{
getFails: tt.getFails,
userId: userId,
isUserDeleted: !tt.deleteFails,
}
handler := DeleteUserWaitHandler(context.Background(), apiClient, "", "", "", userId)
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
},
)
}
}

View file

@ -26,11 +26,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService CreateDatabaseRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -88,9 +88,9 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService CreateInstanceRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -147,11 +147,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService CreateUserRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/users"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -209,13 +209,13 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService DeleteDatabaseRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases/{databaseName}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
databaseNameValue := "databaseName-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"databaseName"+"}", url.PathEscape(ParameterValueToString(databaseNameValue, "databaseName")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"databaseName"+"}", url.PathEscape(ParameterValueToString(databaseNameValue, "databaseName")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -264,11 +264,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService DeleteInstanceRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -316,13 +316,13 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService DeleteUserRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/users/{userId}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
userIdValue := int64(123)
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"userId"+"}", url.PathEscape(ParameterValueToString(userIdValue, "userId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"userId"+"}", url.PathEscape(ParameterValueToString(userIdValue, "userId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -371,13 +371,13 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService GetBackupRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/backups/{backupId}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
backupIdValue := int64(123)
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"backupId"+"}", url.PathEscape(ParameterValueToString(backupIdValue, "backupId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"backupId"+"}", url.PathEscape(ParameterValueToString(backupIdValue, "backupId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -435,11 +435,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService GetCollationsRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/collations"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -496,13 +496,13 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService GetDatabaseRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases/{databaseName}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
databaseNameValue := "databaseName-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"databaseName"+"}", url.PathEscape(ParameterValueToString(databaseNameValue, "databaseName")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"databaseName"+"}", url.PathEscape(ParameterValueToString(databaseNameValue, "databaseName")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -560,9 +560,9 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService GetFlavorsRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/flavors"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -621,11 +621,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService GetInstanceRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -682,11 +682,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService GetStoragesRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/storages/{flavorId}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
flavorIdValue := "flavorId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"flavorId"+"}", url.PathEscape(ParameterValueToString(flavorIdValue, "flavorId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"flavorId"+"}", url.PathEscape(ParameterValueToString(flavorIdValue, "flavorId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -743,13 +743,13 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService GetUserRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/users/{userId}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
userIdValue := int64(123)
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"userId"+"}", url.PathEscape(ParameterValueToString(userIdValue, "userId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"userId"+"}", url.PathEscape(ParameterValueToString(userIdValue, "userId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -807,9 +807,9 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService GetVersionsRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/versions"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -865,11 +865,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ListBackupsRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/backups"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -926,11 +926,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ListCompatibilitiesRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/compatibility"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -987,11 +987,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ListCurrentRunningRestoreJobs", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/restore-jobs"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1048,11 +1048,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ListDatabasesRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1109,9 +1109,9 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ListInstancesRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1167,11 +1167,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ListRolesRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/roles"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1228,11 +1228,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ListUsersRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/users"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1289,11 +1289,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ProtectInstanceRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/protections"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1351,13 +1351,13 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService ResetUserRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/users/{userId}/reset"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
userIdValue := int64(123)
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"userId"+"}", url.PathEscape(ParameterValueToString(userIdValue, "userId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"userId"+"}", url.PathEscape(ParameterValueToString(userIdValue, "userId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1415,13 +1415,13 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService TriggerBackupRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/backups/databases/{databaseName}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
databaseNameValue := "databaseName-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"databaseName"+"}", url.PathEscape(ParameterValueToString(databaseNameValue, "databaseName")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"databaseName"+"}", url.PathEscape(ParameterValueToString(databaseNameValue, "databaseName")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1470,13 +1470,13 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService TriggerRestoreRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/backups/databases/{databaseName}/restores"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
databaseNameValue := "databaseName-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"databaseName"+"}", url.PathEscape(ParameterValueToString(databaseNameValue, "databaseName")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"databaseName"+"}", url.PathEscape(ParameterValueToString(databaseNameValue, "databaseName")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1525,11 +1525,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService UpdateInstancePartiallyRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {
@ -1578,11 +1578,11 @@ func Test_sqlserverflexalpha_DefaultApiService(t *testing.T) {
t.Run("Test DefaultApiService UpdateInstanceRequest", func(t *testing.T) {
_apiUrlPath := "/v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}"
projectIdValue := "projectId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"projectId"+"}", url.PathEscape(ParameterValueToString(projectIdValue, "projectId")))
regionValue := "region-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"region"+"}", url.PathEscape(ParameterValueToString(regionValue, "region")))
instanceIdValue := "instanceId-value"
_apiUrlPath = strings.Replace(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")), -1)
_apiUrlPath = strings.ReplaceAll(_apiUrlPath, "{"+"instanceId"+"}", url.PathEscape(ParameterValueToString(instanceIdValue, "instanceId")))
testDefaultApiServeMux := http.NewServeMux()
testDefaultApiServeMux.HandleFunc(_apiUrlPath, func(w http.ResponseWriter, req *http.Request) {

View file

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
@ -84,12 +85,10 @@ func (r *databaseDataSource) Schema(_ context.Context, _ datasource.SchemaReques
Description: descriptions["id"],
Computed: true,
},
"database_id": schema.StringAttribute{
"database_id": schema.Int64Attribute{
Description: descriptions["database_id"],
Required: true,
Validators: []validator.String{
validate.NoSeparator(),
},
Optional: true,
Computed: true,
},
"instance_id": schema.StringAttribute{
Description: descriptions["instance_id"],
@ -109,7 +108,11 @@ func (r *databaseDataSource) Schema(_ context.Context, _ datasource.SchemaReques
},
"name": schema.StringAttribute{
Description: descriptions["name"],
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"owner": schema.StringAttribute{
Description: descriptions["owner"],
@ -137,6 +140,18 @@ func (r *databaseDataSource) Read(
return
}
// validation for exactly one of database_id or name
isIdSet := !model.DatabaseId.IsNull() && !model.DatabaseId.IsUnknown()
isNameSet := !model.Name.IsNull() && !model.Name.IsUnknown()
if (isIdSet && isNameSet) || (!isIdSet && !isNameSet) {
core.LogAndAddError(
ctx, &resp.Diagnostics,
"Invalid configuration", "Exactly one of 'database_id' or 'name' must be specified.",
)
return
}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
@ -148,7 +163,19 @@ func (r *databaseDataSource) Read(
ctx = tflog.SetField(ctx, "database_id", databaseId)
ctx = tflog.SetField(ctx, "region", region)
databaseResp, err := getDatabase(ctx, r.client, projectId, region, instanceId, databaseId)
var databaseResp *postgresflexalpha.ListDatabase
var err error
if isIdSet {
databaseId := model.DatabaseId.ValueInt64()
ctx = tflog.SetField(ctx, "database_id", databaseId)
databaseResp, err = getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId)
} else {
databaseName := model.Name.ValueString()
ctx = tflog.SetField(ctx, "name", databaseName)
databaseResp, err = getDatabaseByName(ctx, r.client, projectId, region, instanceId, databaseName)
}
if err != nil {
utils.LogError(
ctx,

View file

@ -0,0 +1,81 @@
package postgresflexalpha
import (
"context"
"fmt"
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
)
// databaseClientReader represents the contract to listing databases from postgresflex.APIClient.
type databaseClientReader interface {
ListDatabasesRequest(
ctx context.Context,
projectId string,
region string,
instanceId string,
) postgresflex.ApiListDatabasesRequestRequest
}
// getDatabaseById gets a database by its ID.
func getDatabaseById(
ctx context.Context,
client databaseClientReader,
projectId, region, instanceId string,
databaseId int64,
) (*postgresflex.ListDatabase, error) {
filter := func(db postgresflex.ListDatabase) bool {
return db.Id != nil && *db.Id == databaseId
}
return getDatabase(ctx, client, projectId, region, instanceId, filter)
}
// getDatabaseByName gets a database by its name.
func getDatabaseByName(
ctx context.Context,
client databaseClientReader,
projectId, region, instanceId, databaseName string,
) (*postgresflex.ListDatabase, error) {
filter := func(db postgresflex.ListDatabase) bool {
return db.Name != nil && *db.Name == databaseName
}
return getDatabase(ctx, client, projectId, region, instanceId, filter)
}
// getDatabase is a helper function to retrieve a database using a filter function.
// Hint: The API does not have a GetDatabase endpoint, only ListDatabases
func getDatabase(
ctx context.Context,
client databaseClientReader,
projectId, region, instanceId string,
filter func(db postgresflex.ListDatabase) bool,
) (*postgresflex.ListDatabase, error) {
if projectId == "" || region == "" || instanceId == "" {
return nil, fmt.Errorf("all parameters (project, region, instance) are required")
}
const pageSize = 25
for page := int64(1); ; page++ {
res, err := client.ListDatabasesRequest(ctx, projectId, region, instanceId).
Page(page).Size(pageSize).Sort(postgresflex.DATABASESORT_INDEX_ASC).Execute()
if err != nil {
return nil, fmt.Errorf("requesting database list (page %d): %w", page, err)
}
// If the API returns no databases, we have reached the end of the list.
if res.Databases == nil || len(*res.Databases) == 0 {
break
}
// Iterate over databases to find a match
for _, db := range *res.Databases {
if filter(db) {
foundDb := db
return &foundDb, nil
}
}
}
return nil, fmt.Errorf("database not found for instance %s", instanceId)
}

View file

@ -0,0 +1,196 @@
package postgresflexalpha
import (
"context"
"testing"
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
)
type mockRequest struct {
executeFunc func() (*postgresflex.ListDatabasesResponse, error)
}
func (m *mockRequest) Page(_ int64) postgresflex.ApiListDatabasesRequestRequest { return m }
func (m *mockRequest) Size(_ int64) postgresflex.ApiListDatabasesRequestRequest { return m }
func (m *mockRequest) Sort(_ postgresflex.DatabaseSort) postgresflex.ApiListDatabasesRequestRequest {
return m
}
func (m *mockRequest) Execute() (*postgresflex.ListDatabasesResponse, error) {
return m.executeFunc()
}
type mockDBClient struct {
executeRequest func() postgresflex.ApiListDatabasesRequestRequest
}
var _ databaseClientReader = (*mockDBClient)(nil)
func (m *mockDBClient) ListDatabasesRequest(
_ context.Context,
_, _, _ string,
) postgresflex.ApiListDatabasesRequestRequest {
return m.executeRequest()
}
func TestGetDatabase(t *testing.T) {
mockResp := func(page int64) (*postgresflex.ListDatabasesResponse, error) {
if page == 1 {
return &postgresflex.ListDatabasesResponse{
Databases: &[]postgresflex.ListDatabase{
{Id: utils.Ptr(int64(1)), Name: utils.Ptr("first")},
{Id: utils.Ptr(int64(2)), Name: utils.Ptr("second")},
},
Pagination: &postgresflex.Pagination{
Page: utils.Ptr(int64(1)),
TotalPages: utils.Ptr(int64(2)),
Size: utils.Ptr(int64(3)),
},
}, nil
}
if page == 2 {
return &postgresflex.ListDatabasesResponse{
Databases: &[]postgresflex.ListDatabase{{Id: utils.Ptr(int64(3)), Name: utils.Ptr("three")}},
Pagination: &postgresflex.Pagination{
Page: utils.Ptr(int64(2)),
TotalPages: utils.Ptr(int64(2)),
Size: utils.Ptr(int64(3)),
},
}, nil
}
return &postgresflex.ListDatabasesResponse{
Databases: &[]postgresflex.ListDatabase{},
Pagination: &postgresflex.Pagination{
Page: utils.Ptr(int64(3)),
TotalPages: utils.Ptr(int64(2)),
Size: utils.Ptr(int64(3)),
},
}, nil
}
tests := []struct {
description string
projectId string
region string
instanceId string
wantErr bool
wantDbName string
wantDbId int64
}{
{
description: "Success - Found by name on first page",
projectId: "pid", region: "reg", instanceId: "inst",
wantErr: false,
wantDbName: "second",
},
{
description: "Success - Found by id on first page",
projectId: "pid", region: "reg", instanceId: "inst",
wantErr: false,
wantDbId: 2,
},
{
description: "Success - Found by name on second page",
projectId: "pid", region: "reg", instanceId: "inst",
wantErr: false,
wantDbName: "three",
},
{
description: "Success - Found by id on second page",
projectId: "pid", region: "reg", instanceId: "inst",
wantErr: false,
wantDbId: 1,
},
{
description: "Error - API failure",
projectId: "pid", region: "reg", instanceId: "inst",
wantErr: true,
},
{
description: "Error - Missing parameters",
projectId: "", region: "reg", instanceId: "inst",
wantErr: true,
},
{
description: "Error - Search by name not found after all pages",
projectId: "pid", region: "reg", instanceId: "inst",
wantDbName: "non-existent",
wantErr: true,
},
{
description: "Error - Search by id not found after all pages",
projectId: "pid", region: "reg", instanceId: "inst",
wantDbId: 999999,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(
tt.description, func(t *testing.T) {
var currentPage int64
client := &mockDBClient{
executeRequest: func() postgresflex.ApiListDatabasesRequestRequest {
return &mockRequest{
executeFunc: func() (*postgresflex.ListDatabasesResponse, error) {
currentPage++
return mockResp(currentPage)
},
}
},
}
var actual *postgresflex.ListDatabase
var errDB error
if tt.wantDbName != "" {
actual, errDB = getDatabaseByName(
t.Context(),
client,
tt.projectId,
tt.region,
tt.instanceId,
tt.wantDbName,
)
} else if tt.wantDbId != 0 {
actual, errDB = getDatabaseById(
t.Context(),
client,
tt.projectId,
tt.region,
tt.instanceId,
tt.wantDbId,
)
} else {
actual, errDB = getDatabase(
context.Background(),
client,
tt.projectId,
tt.region,
tt.instanceId,
func(_ postgresflex.ListDatabase) bool { return false },
)
}
if (errDB != nil) != tt.wantErr {
t.Errorf("getDatabase() error = %v, wantErr %v", errDB, tt.wantErr)
return
}
if !tt.wantErr && tt.wantDbName != "" && actual != nil {
if *actual.Name != tt.wantDbName {
t.Errorf("getDatabase() got name = %v, want %v", *actual.Name, tt.wantDbName)
}
}
if !tt.wantErr && tt.wantDbId != 0 && actual != nil {
if *actual.Id != tt.wantDbId {
t.Errorf("getDatabase() got id = %v, want %v", *actual.Id, tt.wantDbId)
}
}
},
)
}
}

View file

@ -259,7 +259,7 @@ func (r *databaseResource) Create(
databaseId := *databaseResp.Id
ctx = tflog.SetField(ctx, "database_id", databaseId)
database, err := getDatabase(ctx, r.client, projectId, region, instanceId, databaseId)
database, err := getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId)
if err != nil {
core.LogAndAddError(
ctx,
@ -314,7 +314,7 @@ func (r *databaseResource) Read(
ctx = tflog.SetField(ctx, "database_id", databaseId)
ctx = tflog.SetField(ctx, "region", region)
databaseResp, err := getDatabase(ctx, r.client, projectId, region, instanceId, databaseId)
databaseResp, err := getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId)
if err != nil {
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
if (ok && oapiErr.StatusCode == http.StatusNotFound) || errors.Is(err, errDatabaseNotFound) {
@ -353,7 +353,7 @@ func (r *databaseResource) Update(
ctx context.Context,
req resource.UpdateRequest,
resp *resource.UpdateResponse,
) { // nolint:gocritic // function signature required by Terraform
) {
var model Model
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -430,7 +430,6 @@ func (r *databaseResource) Update(
return
}
tflog.Info(ctx, "Postgres Flex database updated")
}
// Delete deletes the resource and removes the Terraform state on success.
@ -530,14 +529,11 @@ func mapFields(resp *postgresflexalpha.ListDatabase, model *Model, region string
return nil
}
func mapFieldsUpdate(res *postgresflexalpha.UpdateDatabaseResponse, model *Model, region string) error {
if res == nil {
return fmt.Errorf("response is nil")
}
return mapFields(res.Database, model, region)
}
func mapFieldsUpdatePartially(res *postgresflexalpha.UpdateDatabasePartiallyResponse, model *Model, region string) error {
func mapFieldsUpdatePartially(
res *postgresflexalpha.UpdateDatabasePartiallyResponse,
model *Model,
region string,
) error {
if res == nil {
return fmt.Errorf("response is nil")
}
@ -564,26 +560,3 @@ func toCreatePayload(model *Model) (*postgresflexalpha.CreateDatabaseRequestPayl
}
var errDatabaseNotFound = errors.New("database not found")
// The API does not have a GetDatabase endpoint, only ListDatabases
func getDatabase(
ctx context.Context,
client *postgresflexalpha.APIClient,
projectId, region, instanceId string,
databaseId int64,
) (*postgresflexalpha.ListDatabase, error) {
// TODO - implement pagination handling
resp, err := client.ListDatabasesRequestExecute(ctx, projectId, region, instanceId)
if err != nil {
return nil, err
}
if resp == nil || resp.Databases == nil {
return nil, fmt.Errorf("response is nil")
}
for _, database := range *resp.Databases {
if database.Id != nil && *database.Id == databaseId {
return &database, nil
}
}
return nil, errDatabaseNotFound
}

View file

@ -7,195 +7,59 @@ import (
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
)
type flavorsClient interface {
GetFlavorsRequestExecute(
type flavorsClientReader interface {
GetFlavorsRequest(
ctx context.Context,
projectId, region string,
page, size *int64,
sort *postgresflex.FlavorSort,
) (*postgresflex.GetFlavorsResponse, error)
) postgresflex.ApiGetFlavorsRequestRequest
}
//func loadFlavorId(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel, storage *storageModel) error {
// if model == nil {
// return fmt.Errorf("nil model")
// }
// if flavor == nil {
// return fmt.Errorf("nil flavor")
// }
// cpu := flavor.CPU.ValueInt64()
// if cpu == 0 {
// return fmt.Errorf("nil CPU")
// }
// ram := flavor.RAM.ValueInt64()
// if ram == 0 {
// return fmt.Errorf("nil RAM")
// }
//
// nodeType := flavor.NodeType.ValueString()
// if nodeType == "" {
// if model.Replicas.IsNull() || model.Replicas.IsUnknown() {
// return fmt.Errorf("nil NodeType")
// }
// switch model.Replicas.ValueInt64() {
// case 1:
// nodeType = "Single"
// case 3:
// nodeType = "Replica"
// default:
// return fmt.Errorf("unknown Replicas value: %d", model.Replicas.ValueInt64())
// }
// }
//
// storageClass := conversion.StringValueToPointer(storage.Class)
// if storageClass == nil {
// return fmt.Errorf("nil StorageClass")
// }
// storageSize := conversion.Int64ValueToPointer(storage.Size)
// if storageSize == nil {
// return fmt.Errorf("nil StorageSize")
// }
//
// projectId := model.ProjectId.ValueString()
// region := model.Region.ValueString()
//
// flavorList, err := getAllFlavors(ctx, client, projectId, region)
// if err != nil {
// return err
// }
//
// avl := ""
// foundFlavorCount := 0
// var foundFlavors []string
// for _, f := range flavorList {
// if f.Id == nil || f.Cpu == nil || f.Memory == nil {
// continue
// }
// if !strings.EqualFold(*f.NodeType, nodeType) {
// continue
// }
// if *f.Cpu == cpu && *f.Memory == ram {
// var useSc *postgresflex.FlavorStorageClassesStorageClass
// for _, sc := range *f.StorageClasses {
// if *sc.Class != *storageClass {
// continue
// }
// if *storageSize < *f.MinGB || *storageSize > *f.MaxGB {
// return fmt.Errorf("storage size %d out of bounds (min: %d - max: %d)", *storageSize, *f.MinGB, *f.MaxGB)
// }
// useSc = &sc
// }
// if useSc == nil {
// return fmt.Errorf("no storage class found for %s", *storageClass)
// }
//
// flavor.Id = types.StringValue(*f.Id)
// flavor.Description = types.StringValue(*f.Description)
// foundFlavors = append(foundFlavors, fmt.Sprintf("%s (%d/%d - %s)", *f.Id, *f.Cpu, *f.Memory, *f.NodeType))
// foundFlavorCount++
// }
// for _, cls := range *f.StorageClasses {
// avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM, storage %s (min: %d - max: %d)", avl, *f.Cpu, *f.Memory, *cls.Class, *f.MinGB, *f.MaxGB)
// }
// }
// if foundFlavorCount > 1 {
// return fmt.Errorf(
// "number of flavors returned: %d\nmultiple flavors found: %d flavors\n %s",
// len(flavorList),
// foundFlavorCount,
// strings.Join(foundFlavors, "\n "),
// )
// }
// if flavor.Id.ValueString() == "" {
// return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
// }
//
// return nil
//}
func getAllFlavors(ctx context.Context, client flavorsClient, projectId, region string) ([]postgresflex.ListFlavors, error) {
if projectId == "" || region == "" {
return nil, fmt.Errorf("listing postgresflex flavors: projectId and region are required")
}
var flavorList []postgresflex.ListFlavors
page := int64(1)
size := int64(10)
sort := postgresflex.FLAVORSORT_INDEX_ASC
counter := 0
for {
res, err := client.GetFlavorsRequestExecute(ctx, projectId, region, &page, &size, &sort)
if err != nil {
return nil, fmt.Errorf("listing postgresflex flavors: %w", err)
}
if res.Flavors == nil {
return nil, fmt.Errorf("finding flavors for project %s", projectId)
}
pagination := res.GetPagination()
flavors := res.GetFlavors()
flavorList = append(flavorList, flavors...)
if *pagination.TotalRows < int64(len(flavorList)) {
return nil, fmt.Errorf("total rows is smaller than current accumulated list - that should not happen")
}
if *pagination.TotalRows == int64(len(flavorList)) {
break
}
page++
if page > *pagination.TotalPages {
break
}
// implement a breakpoint
counter++
if counter > 1000 {
panic("too many pagination results")
}
func getAllFlavors(ctx context.Context, client flavorsClientReader, projectId, region string) (
[]postgresflex.ListFlavors,
error,
) {
getAllFilter := func(_ postgresflex.ListFlavors) bool { return true }
flavorList, err := getFlavorsByFilter(ctx, client, projectId, region, getAllFilter)
if err != nil {
return nil, err
}
return flavorList, nil
}
//func getFlavorModelById(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel) error {
// if model == nil {
// return fmt.Errorf("nil model")
// }
// if flavor == nil {
// return fmt.Errorf("nil flavor")
// }
// id := conversion.StringValueToPointer(flavor.Id)
// if id == nil {
// return fmt.Errorf("nil flavor ID")
// }
//
// flavor.Id = types.StringValue("")
//
// projectId := model.ProjectId.ValueString()
// region := model.Region.ValueString()
//
// flavorList, err := getAllFlavors(ctx, client, projectId, region)
// if err != nil {
// return err
// }
//
// avl := ""
// for _, f := range flavorList {
// if f.Id == nil || f.Cpu == nil || f.Memory == nil {
// continue
// }
// if *f.Id == *id {
// flavor.Id = types.StringValue(*f.Id)
// flavor.Description = types.StringValue(*f.Description)
// flavor.CPU = types.Int64Value(*f.Cpu)
// flavor.RAM = types.Int64Value(*f.Memory)
// flavor.NodeType = types.StringValue(*f.NodeType)
// break
// }
// avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
// }
// if flavor.Id.ValueString() == "" {
// return fmt.Errorf("couldn't find flavor, available specs are: %s", avl)
// }
//
// return nil
//}
// getFlavorsByFilter is a helper function to retrieve flavors using a filtern function.
// Hint: The API does not have a GetFlavors endpoint, only ListFlavors
func getFlavorsByFilter(
ctx context.Context,
client flavorsClientReader,
projectId, region string,
filter func(db postgresflex.ListFlavors) bool,
) ([]postgresflex.ListFlavors, error) {
if projectId == "" || region == "" {
return nil, fmt.Errorf("listing postgresflex flavors: projectId and region are required")
}
const pageSize = 25
var result = make([]postgresflex.ListFlavors, 0)
for page := int64(1); ; page++ {
res, err := client.GetFlavorsRequest(ctx, projectId, region).
Page(page).Size(pageSize).Sort(postgresflex.FLAVORSORT_INDEX_ASC).Execute()
if err != nil {
return nil, fmt.Errorf("requesting flavors list (page %d): %w", page, err)
}
// If the API returns no flavors, we have reached the end of the list.
if res.Flavors == nil || len(*res.Flavors) == 0 {
break
}
for _, flavor := range *res.Flavors {
if filter(flavor) {
result = append(result, flavor)
}
}
}
return result, nil
}

View file

@ -0,0 +1,134 @@
package postgresFlexAlphaFlavor
import (
"context"
"testing"
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
)
type mockRequest struct {
executeFunc func() (*postgresflex.GetFlavorsResponse, error)
}
func (m *mockRequest) Page(_ int64) postgresflex.ApiGetFlavorsRequestRequest { return m }
func (m *mockRequest) Size(_ int64) postgresflex.ApiGetFlavorsRequestRequest { return m }
func (m *mockRequest) Sort(_ postgresflex.FlavorSort) postgresflex.ApiGetFlavorsRequestRequest {
return m
}
func (m *mockRequest) Execute() (*postgresflex.GetFlavorsResponse, error) {
return m.executeFunc()
}
type mockFlavorsClient struct {
executeRequest func() postgresflex.ApiGetFlavorsRequestRequest
}
func (m *mockFlavorsClient) GetFlavorsRequest(_ context.Context, _, _ string) postgresflex.ApiGetFlavorsRequestRequest {
return m.executeRequest()
}
var mockResp = func(page int64) (*postgresflex.GetFlavorsResponse, error) {
if page == 1 {
return &postgresflex.GetFlavorsResponse{
Flavors: &[]postgresflex.ListFlavors{
{Id: utils.Ptr("flavor-1"), Description: utils.Ptr("first")},
{Id: utils.Ptr("flavor-2"), Description: utils.Ptr("second")},
},
}, nil
}
if page == 2 {
return &postgresflex.GetFlavorsResponse{
Flavors: &[]postgresflex.ListFlavors{
{Id: utils.Ptr("flavor-3"), Description: utils.Ptr("three")},
},
}, nil
}
return &postgresflex.GetFlavorsResponse{
Flavors: &[]postgresflex.ListFlavors{},
}, nil
}
func TestGetFlavorsByFilter(t *testing.T) {
tests := []struct {
description string
projectId string
region string
mockErr error
filter func(postgresflex.ListFlavors) bool
wantCount int
wantErr bool
}{
{
description: "Success - Get all flavors (2 pages)",
projectId: "pid", region: "reg",
filter: func(_ postgresflex.ListFlavors) bool { return true },
wantCount: 3,
wantErr: false,
},
{
description: "Success - Filter flavors by description",
projectId: "pid", region: "reg",
filter: func(f postgresflex.ListFlavors) bool { return *f.Description == "first" },
wantCount: 1,
wantErr: false,
},
{
description: "Error - Missing parameters",
projectId: "", region: "reg",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(
tt.description, func(t *testing.T) {
var currentPage int64
client := &mockFlavorsClient{
executeRequest: func() postgresflex.ApiGetFlavorsRequestRequest {
return &mockRequest{
executeFunc: func() (*postgresflex.GetFlavorsResponse, error) {
currentPage++
return mockResp(currentPage)
},
}
},
}
actual, err := getFlavorsByFilter(context.Background(), client, tt.projectId, tt.region, tt.filter)
if (err != nil) != tt.wantErr {
t.Errorf("getFlavorsByFilter() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && len(actual) != tt.wantCount {
t.Errorf("getFlavorsByFilter() got %d flavors, want %d", len(actual), tt.wantCount)
}
},
)
}
}
func TestGetAllFlavors(t *testing.T) {
var currentPage int64
client := &mockFlavorsClient{
executeRequest: func() postgresflex.ApiGetFlavorsRequestRequest {
return &mockRequest{
executeFunc: func() (*postgresflex.GetFlavorsResponse, error) {
currentPage++
return mockResp(currentPage)
},
}
},
}
res, err := getAllFlavors(context.Background(), client, "pid", "reg")
if err != nil {
t.Errorf("getAllFlavors() unexpected error: %v", err)
}
if len(res) != 3 {
t.Errorf("getAllFlavors() expected 3 flavor, got %d", len(res))
}
}

View file

@ -1,79 +0,0 @@
package postgresFlexAlphaFlavor
import (
"context"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha"
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &flavorListDataSource{}
)
// NewFlavorListDataSource is a helper function to simplify the provider implementation.
func NewFlavorListDataSource() datasource.DataSource {
return &flavorListDataSource{}
}
// flavorDataSource is the data source implementation.
type flavorListDataSource struct {
client *postgresflexalpha.APIClient
providerData core.ProviderData
}
// Metadata returns the data source type name.
func (r *flavorListDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_flavorlist"
}
// Configure adds the provider configured client to the data source.
func (r *flavorListDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
var ok bool
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok {
return
}
apiClient := postgresflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}
r.client = apiClient
tflog.Info(ctx, "Postgres Flex flavors client configured")
}
func (r *flavorListDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = postgresflex.FlavorDataSourceSchema(ctx)
}
func (r *flavorListDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var model postgresflex.FlavorModel
diags := req.Config.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
region := r.providerData.GetRegionWithOverride(model.Region)
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// Set refreshed state
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Postgres Flex flavors read")
}

View file

@ -38,12 +38,20 @@ type instanceDataSource struct {
}
// Metadata returns the data source type name.
func (r *instanceDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
func (r *instanceDataSource) Metadata(
_ context.Context,
req datasource.MetadataRequest,
resp *datasource.MetadataResponse,
) {
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_instance"
}
// Configure adds the provider configured client to the data source.
func (r *instanceDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
func (r *instanceDataSource) Configure(
ctx context.Context,
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) {
var ok bool
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok {
@ -178,7 +186,11 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques
}
// Read refreshes the Terraform state with the latest data.
func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceDataSource) Read(
ctx context.Context,
req datasource.ReadRequest,
resp *datasource.ReadResponse,
) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.Config.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -239,9 +251,14 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
}
}
err = mapFields(ctx, r.client, instanceResp, &model, storage, encryption, network, region)
err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error reading instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// Set refreshed state

View file

@ -13,13 +13,8 @@ import (
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
)
type postgresflexClient interface {
GetFlavorsRequestExecute(ctx context.Context, projectId string, region string, page, size *int64, sort *postgresflex.FlavorSort) (*postgresflex.GetFlavorsResponse, error)
}
func mapFields(
ctx context.Context,
client postgresflexClient,
resp *postgresflex.GetInstanceResponse,
model *Model,
storage *storageModel,

View file

@ -1,18 +1,11 @@
package postgresflexalpha
import (
"context"
"fmt"
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
)
type postgresFlexClientMocked struct {
returnError bool
firstItem int
lastItem int
}
//nolint:unused // TODO: remove when used
type testFlavor struct {
Cpu int64
Description string
@ -24,12 +17,14 @@ type testFlavor struct {
StorageClasses []testFlavorStorageClass
}
//nolint:unused // TODO: remove when used
type testFlavorStorageClass struct {
Class string
MaxIoPerSec int64
MaxThroughInMb int64
}
//nolint:unused // TODO: remove when used
var responseList = []testFlavor{
{
Cpu: 1,
@ -415,6 +410,7 @@ var responseList = []testFlavor{
/* ......................................................... */
}
//nolint:unused // TODO: remove when used
func testFlavorListToResponseFlavorList(f []testFlavor) []postgresflex.ListFlavors {
result := make([]postgresflex.ListFlavors, len(f))
for i, flavor := range f {
@ -423,14 +419,17 @@ func testFlavorListToResponseFlavorList(f []testFlavor) []postgresflex.ListFlavo
return result
}
//nolint:unused // TODO: remove when used
func testFlavorToResponseFlavor(f testFlavor) postgresflex.ListFlavors {
var scList []postgresflex.FlavorStorageClassesStorageClass
for _, fl := range f.StorageClasses {
scList = append(scList, postgresflex.FlavorStorageClassesStorageClass{
Class: utils.Ptr(fl.Class),
MaxIoPerSec: utils.Ptr(fl.MaxIoPerSec),
MaxThroughInMb: utils.Ptr(fl.MaxThroughInMb),
})
scList = append(
scList, postgresflex.FlavorStorageClassesStorageClass{
Class: utils.Ptr(fl.Class),
MaxIoPerSec: utils.Ptr(fl.MaxIoPerSec),
MaxThroughInMb: utils.Ptr(fl.MaxThroughInMb),
},
)
}
return postgresflex.ListFlavors{
Cpu: utils.Ptr(f.Cpu),
@ -444,49 +443,7 @@ func testFlavorToResponseFlavor(f testFlavor) postgresflex.ListFlavors {
}
}
func (c postgresFlexClientMocked) GetFlavorsRequestExecute(
_ context.Context,
_, _ string,
page, size *int64,
_ *postgresflex.FlavorSort,
) (*postgresflex.GetFlavorsResponse, error) {
if c.returnError {
return nil, fmt.Errorf("get flavors failed")
}
var res postgresflex.GetFlavorsResponse
var resFlavors []postgresflex.ListFlavors
myList := responseList[c.firstItem : c.lastItem+1]
firstItem := *page**size - *size
if firstItem > int64(len(myList)) {
firstItem = int64(len(myList))
}
lastItem := firstItem + *size
if lastItem > int64(len(myList)) {
lastItem = int64(len(myList))
}
for _, flv := range myList[firstItem:lastItem] {
resFlavors = append(resFlavors, testFlavorToResponseFlavor(flv))
}
res.Flavors = &resFlavors
totPages := (int64(len(myList))-1) / *size + 1
res.Pagination = &postgresflex.Pagination{
Page: page,
Size: size,
Sort: utils.Ptr("id.asc"),
TotalPages: utils.Ptr(int64(totPages)),
TotalRows: utils.Ptr(int64(len(myList))),
}
return &res, nil
}
//func Test_getAllFlavors(t *testing.T) {
// func Test_getAllFlavors(t *testing.T) {
// type args struct {
// projectId string
// region string
@ -579,7 +536,7 @@ func (c postgresFlexClientMocked) GetFlavorsRequestExecute(
// }
//}
//func Test_loadFlavorId(t *testing.T) {
// func Test_loadFlavorId(t *testing.T) {
// type args struct {
// ctx context.Context
// model *Model

View file

@ -22,12 +22,6 @@ type Model struct {
Network types.Object `tfsdk:"network"`
}
//type IdentityModel struct {
// InstanceId types.String `tfsdk:"instance_id"`
// Region types.String `tfsdk:"region"`
// ProjectId types.String `tfsdk:"project_id"`
//}
type encryptionModel struct {
KeyRingId types.String `tfsdk:"keyring_id"`
KeyId types.String `tfsdk:"key_id"`

View file

@ -39,7 +39,7 @@ var (
_ resource.ResourceWithImportState = &instanceResource{}
_ resource.ResourceWithModifyPlan = &instanceResource{}
_ resource.ResourceWithValidateConfig = &instanceResource{}
//_ resource.ResourceWithIdentity = &instanceResource{}
// _ resource.ResourceWithIdentity = &instanceResource{}
)
// NewInstanceResource is a helper function to simplify the provider implementation.
@ -53,7 +53,11 @@ type instanceResource struct {
providerData core.ProviderData
}
func (r *instanceResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
func (r *instanceResource) ValidateConfig(
ctx context.Context,
req resource.ValidateConfigRequest,
resp *resource.ValidateConfigResponse,
) {
var data Model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
@ -73,7 +77,11 @@ func (r *instanceResource) ValidateConfig(ctx context.Context, req resource.Vali
// ModifyPlan implements resource.ResourceWithModifyPlan.
// Use the modifier to set the effective region in the current plan.
func (r *instanceResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) ModifyPlan(
ctx context.Context,
req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse,
) { // nolint:gocritic // function signature required by Terraform
var configModel Model
// skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() {
@ -107,7 +115,11 @@ func (r *instanceResource) Metadata(_ context.Context, req resource.MetadataRequ
}
// Configure adds the provider configured client to the resource.
func (r *instanceResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
func (r *instanceResource) 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 {
@ -123,7 +135,7 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure
}
// Schema defines the schema for the resource.
func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
descriptions := map[string]string{
"main": "Postgres Flex instance resource schema. Must have a `region` specified in the provider configuration.",
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".",
@ -345,7 +357,7 @@ func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest,
}
}
//func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
// func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
// resp.IdentitySchema = identityschema.Schema{
// Attributes: map[string]identityschema.Attribute{
// "project_id": identityschema.StringAttribute{
@ -420,11 +432,20 @@ func (r *instanceResource) Create(
// Generate API request body from model
payload, err := toCreatePayload(&model, storage, encryption, network)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Create new instance
createResp, err := r.client.CreateInstanceRequest(ctx, projectId, region).CreateInstanceRequestPayload(*payload).Execute()
createResp, err := r.client.CreateInstanceRequest(
ctx,
projectId,
region,
).CreateInstanceRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Calling API: %v", err))
return
@ -440,24 +461,26 @@ func (r *instanceResource) Create(
return
}
//// Set data returned by API in identity
//identity := IdentityModel{
// InstanceId: types.StringValue(instanceId),
// Region: types.StringValue(region),
// ProjectId: types.StringValue(projectId),
//}
//resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Wait handler error: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Wait handler error: %v", err),
)
return
}
// Map response body to schema
err = mapFields(ctx, r.client, waitResp, &model, storage, encryption, network, region)
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// Set state to fully populated data
@ -470,7 +493,11 @@ func (r *instanceResource) Create(
}
// Read refreshes the Terraform state with the latest data.
func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) Read(
ctx context.Context,
req resource.ReadRequest,
resp *resource.ReadResponse,
) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -478,13 +505,6 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
return
}
//// Read identity data
//var identityData IdentityModel
//resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
//if resp.Diagnostics.HasError() {
// return
//}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
@ -535,9 +555,14 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
ctx = core.LogResponse(ctx)
// Map response body to schema
err = mapFields(ctx, r.client, instanceResp, &model, &storage, &encryption, &network, region)
err = mapFields(ctx, instanceResp, &model, &storage, &encryption, &network, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error reading instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
@ -547,19 +572,15 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
return
}
//identityData.InstanceId = model.InstanceId
//identityData.Region = model.Region
//identityData.ProjectId = model.ProjectId
//resp.Diagnostics.Append(resp.Identity.Set(ctx, identityData)...)
//if resp.Diagnostics.HasError() {
// return
//}
tflog.Info(ctx, "Postgres Flex instance read")
}
// Update updates the resource and sets the updated Terraform state on success.
func (r *instanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) Update(
ctx context.Context,
req resource.UpdateRequest,
resp *resource.UpdateResponse,
) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -616,11 +637,21 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
// Generate API request body from model
payload, err := toUpdatePayload(&model, storage, network)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Creating API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error updating instance",
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Update existing instance
err = r.client.UpdateInstancePartiallyRequest(ctx, projectId, region, instanceId).UpdateInstancePartiallyRequestPayload(*payload).Execute()
err = r.client.UpdateInstancePartiallyRequest(
ctx,
projectId,
region,
instanceId,
).UpdateInstancePartiallyRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error())
return
@ -628,16 +659,32 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
ctx = core.LogResponse(ctx)
waitResp, err := wait.PartialUpdateInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).WaitWithContext(ctx)
waitResp, err := wait.PartialUpdateInstanceWaitHandler(
ctx,
r.client,
projectId,
region,
instanceId,
).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Instance update waiting: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error updating instance",
fmt.Sprintf("Instance update waiting: %v", err),
)
return
}
// Map response body to schema
err = mapFields(ctx, r.client, waitResp, &model, storage, encryption, network, region)
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error updating instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
diags = resp.State.Set(ctx, model)
@ -649,7 +696,11 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
}
// Delete deletes the resource and removes the Terraform state on success.
func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) Delete(
ctx context.Context,
req resource.DeleteRequest,
resp *resource.DeleteResponse,
) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -675,12 +726,6 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques
ctx = core.LogResponse(ctx)
//_, err = wait.DeleteInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).SetTimeout(45 * time.Minute).WaitWithContext(ctx)
//if err != nil {
// core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Instance deletion waiting: %v", err))
// return
//}
_, err = r.client.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
if err != nil {
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
@ -696,11 +741,16 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques
// ImportState imports a resource into the Terraform state on success.
// The expected format of the resource import identifier is: project_id,instance_id
func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
func (r *instanceResource) ImportState(
ctx context.Context,
req resource.ImportStateRequest,
resp *resource.ImportStateResponse,
) {
idParts := strings.Split(req.ID, core.Separator)
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
core.LogAndAddError(ctx, &resp.Diagnostics,
core.LogAndAddError(
ctx, &resp.Diagnostics,
"Error importing instance",
fmt.Sprintf("Expected import identifier with format: [project_id],[region],[instance_id] Got: %q", req.ID),
)

View file

@ -53,9 +53,11 @@ func TestMapFields(t *testing.T) {
&storageModel{},
&encryptionModel{},
&networkModel{
ACL: types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
}),
ACL: types.ListValueMust(
types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
},
),
},
testRegion,
Model{
@ -67,24 +69,32 @@ func TestMapFields(t *testing.T) {
//ACL: types.ListNull(types.StringType),
BackupSchedule: types.StringNull(),
Replicas: types.Int64Value(1),
Encryption: types.ObjectValueMust(encryptionTypes, map[string]attr.Value{
"keyring_id": types.StringNull(),
"key_id": types.StringNull(),
"key_version": types.StringNull(),
"service_account": types.StringNull(),
}),
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
"class": types.StringNull(),
"size": types.Int64Null(),
}),
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
"acl": types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
}),
"access_scope": types.StringNull(),
"instance_address": types.StringNull(),
"router_address": types.StringNull(),
}),
Encryption: types.ObjectValueMust(
encryptionTypes, map[string]attr.Value{
"keyring_id": types.StringNull(),
"key_id": types.StringNull(),
"key_version": types.StringNull(),
"service_account": types.StringNull(),
},
),
Storage: types.ObjectValueMust(
storageTypes, map[string]attr.Value{
"class": types.StringNull(),
"size": types.Int64Null(),
},
),
Network: types.ObjectValueMust(
networkTypes, map[string]attr.Value{
"acl": types.ListValueMust(
types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
},
),
"access_scope": types.StringNull(),
"instance_address": types.StringNull(),
"router_address": types.StringNull(),
},
),
Version: types.StringNull(),
Region: types.StringValue(testRegion),
},
@ -184,35 +194,31 @@ func TestMapFields(t *testing.T) {
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
client := postgresFlexClientMocked{
returnError: false,
firstItem: 0,
lastItem: 0,
}
err := mapFields(
context.Background(),
client,
tt.input,
&tt.state,
tt.storage,
tt.encryption,
tt.network,
tt.region,
)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(tt.expected, tt.state)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
t.Run(
tt.description, func(t *testing.T) {
err := mapFields(
context.Background(),
tt.input,
&tt.state,
tt.storage,
tt.encryption,
tt.network,
tt.region,
)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
}
})
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(tt.expected, tt.state)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
},
)
}
}
@ -236,9 +242,11 @@ func TestToCreatePayload(t *testing.T) {
&storageModel{},
&encryptionModel{},
&networkModel{
ACL: types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
}),
ACL: types.ListValueMust(
types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
},
),
},
&postgresflex.CreateInstanceRequestPayload{
Acl: &[]string{"0.0.0.0/0"},
@ -293,21 +301,23 @@ func TestToCreatePayload(t *testing.T) {
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
output, err := toCreatePayload(tt.input, tt.inputStorage, tt.inputEncryption, tt.inputNetwork)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(tt.expected, output)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
t.Run(
tt.description, func(t *testing.T) {
output, err := toCreatePayload(tt.input, tt.inputStorage, tt.inputEncryption, tt.inputNetwork)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
}
})
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(tt.expected, output)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
},
)
}
}
@ -629,10 +639,12 @@ func TestNewInstanceResource(t *testing.T) {
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewInstanceResource(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewInstanceResource() = %v, want %v", got, tt.want)
}
})
t.Run(
tt.name, func(t *testing.T) {
if got := NewInstanceResource(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewInstanceResource() = %v, want %v", got, tt.want)
}
},
)
}
}

View file

@ -54,7 +54,8 @@ func configResources(backupSchedule string, region *string) string {
if region != nil {
regionConfig = fmt.Sprintf(`region = %q`, *region)
}
return fmt.Sprintf(`
return fmt.Sprintf(
`
%s
resource "stackit_postgresflex_instance" "instance" {
@ -108,62 +109,125 @@ func configResources(backupSchedule string, region *string) string {
}
func TestAccPostgresFlexFlexResource(t *testing.T) {
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
CheckDestroy: testAccCheckPostgresFlexDestroy,
Steps: []resource.TestStep{
// Creation
{
Config: configResources(instanceResource["backup_schedule"], &testutil.Region),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "project_id", instanceResource["project_id"]),
resource.TestCheckResourceAttrSet("stackit_postgresflex_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "name", instanceResource["name"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "acl.0", instanceResource["acl"]),
resource.TestCheckResourceAttrSet("stackit_postgresflex_instance.instance", "flavor.id"),
resource.TestCheckResourceAttrSet("stackit_postgresflex_instance.instance", "flavor.description"),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "backup_schedule", instanceResource["backup_schedule"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "flavor.cpu", instanceResource["flavor_cpu"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "flavor.ram", instanceResource["flavor_ram"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "replicas", instanceResource["replicas"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "storage.class", instanceResource["storage_class"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "storage.size", instanceResource["storage_size"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "version", instanceResource["version"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "region", testutil.Region),
resource.Test(
t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
CheckDestroy: testAccCheckPostgresFlexDestroy,
Steps: []resource.TestStep{
// Creation
{
Config: configResources(instanceResource["backup_schedule"], &testutil.Region),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"project_id",
instanceResource["project_id"],
),
resource.TestCheckResourceAttrSet(
"stackit_postgresflex_instance.instance",
"instance_id",
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"name",
instanceResource["name"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"acl.#",
"1",
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"acl.0",
instanceResource["acl"],
),
resource.TestCheckResourceAttrSet(
"stackit_postgresflex_instance.instance",
"flavor.id",
),
resource.TestCheckResourceAttrSet(
"stackit_postgresflex_instance.instance",
"flavor.description",
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"backup_schedule",
instanceResource["backup_schedule"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"flavor.cpu",
instanceResource["flavor_cpu"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"flavor.ram",
instanceResource["flavor_ram"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"replicas",
instanceResource["replicas"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"storage.class",
instanceResource["storage_class"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"storage.size",
instanceResource["storage_size"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"version",
instanceResource["version"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"region",
testutil.Region,
),
// User
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_user.user", "project_id",
"stackit_postgresflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_user.user", "instance_id",
"stackit_postgresflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_postgresflex_user.user", "user_id"),
resource.TestCheckResourceAttrSet("stackit_postgresflex_user.user", "password"),
// User
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_user.user", "project_id",
"stackit_postgresflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_user.user", "instance_id",
"stackit_postgresflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_postgresflex_user.user", "user_id"),
resource.TestCheckResourceAttrSet("stackit_postgresflex_user.user", "password"),
// Database
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_database.database", "project_id",
"stackit_postgresflex_instance.instance", "project_id",
// Database
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_database.database", "project_id",
"stackit_postgresflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_database.database", "instance_id",
"stackit_postgresflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_database.database",
"name",
databaseResource["name"],
),
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_database.database", "owner",
"stackit_postgresflex_user.user", "username",
),
),
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_database.database", "instance_id",
"stackit_postgresflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttr("stackit_postgresflex_database.database", "name", databaseResource["name"]),
resource.TestCheckResourceAttrPair(
"stackit_postgresflex_database.database", "owner",
"stackit_postgresflex_user.user", "username",
),
),
},
// data source
{
Config: fmt.Sprintf(`
},
// data source
{
Config: fmt.Sprintf(
`
%s
data "stackit_postgresflex_instance" "instance" {
@ -183,142 +247,279 @@ func TestAccPostgresFlexFlexResource(t *testing.T) {
database_id = stackit_postgresflex_database.database.database_id
}
`,
configResources(instanceResource["backup_schedule"], nil),
),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "project_id", instanceResource["project_id"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "name", instanceResource["name"]),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_instance.instance", "project_id",
"stackit_postgresflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_instance.instance", "instance_id",
"stackit_postgresflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_user.user", "instance_id",
"stackit_postgresflex_user.user", "instance_id",
configResources(instanceResource["backup_schedule"], nil),
),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"project_id",
instanceResource["project_id"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"name",
instanceResource["name"],
),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_instance.instance", "project_id",
"stackit_postgresflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_instance.instance", "instance_id",
"stackit_postgresflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_user.user", "instance_id",
"stackit_postgresflex_user.user", "instance_id",
),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "acl.0", instanceResource["acl"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "backup_schedule", instanceResource["backup_schedule"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "flavor.id", instanceResource["flavor_id"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "flavor.description", instanceResource["flavor_description"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "flavor.cpu", instanceResource["flavor_cpu"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "flavor.ram", instanceResource["flavor_ram"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_instance.instance", "replicas", instanceResource["replicas"]),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"acl.#",
"1",
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"acl.0",
instanceResource["acl"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"backup_schedule",
instanceResource["backup_schedule"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"flavor.id",
instanceResource["flavor_id"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"flavor.description",
instanceResource["flavor_description"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"flavor.cpu",
instanceResource["flavor_cpu"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"flavor.ram",
instanceResource["flavor_ram"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_instance.instance",
"replicas",
instanceResource["replicas"],
),
// User data
resource.TestCheckResourceAttr("data.stackit_postgresflex_user.user", "project_id", userResource["project_id"]),
resource.TestCheckResourceAttrSet("data.stackit_postgresflex_user.user", "user_id"),
resource.TestCheckResourceAttr("data.stackit_postgresflex_user.user", "username", userResource["username"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_user.user", "roles.#", "1"),
resource.TestCheckResourceAttr("data.stackit_postgresflex_user.user", "roles.0", userResource["role"]),
resource.TestCheckResourceAttrSet("data.stackit_postgresflex_user.user", "host"),
resource.TestCheckResourceAttrSet("data.stackit_postgresflex_user.user", "port"),
// User data
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_user.user",
"project_id",
userResource["project_id"],
),
resource.TestCheckResourceAttrSet(
"data.stackit_postgresflex_user.user",
"user_id",
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_user.user",
"username",
userResource["username"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_user.user",
"roles.#",
"1",
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_user.user",
"roles.0",
userResource["role"],
),
resource.TestCheckResourceAttrSet(
"data.stackit_postgresflex_user.user",
"host",
),
resource.TestCheckResourceAttrSet(
"data.stackit_postgresflex_user.user",
"port",
),
// Database data
resource.TestCheckResourceAttr("data.stackit_postgresflex_database.database", "project_id", instanceResource["project_id"]),
resource.TestCheckResourceAttr("data.stackit_postgresflex_database.database", "name", databaseResource["name"]),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_database.database", "instance_id",
"stackit_postgresflex_instance.instance", "instance_id",
// Database data
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_database.database",
"project_id",
instanceResource["project_id"],
),
resource.TestCheckResourceAttr(
"data.stackit_postgresflex_database.database",
"name",
databaseResource["name"],
),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_database.database",
"instance_id",
"stackit_postgresflex_instance.instance",
"instance_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_database.database",
"owner",
"data.stackit_postgresflex_user.user",
"username",
),
),
resource.TestCheckResourceAttrPair(
"data.stackit_postgresflex_database.database", "owner",
"data.stackit_postgresflex_user.user", "username",
),
),
},
// Import
{
ResourceName: "stackit_postgresflex_instance.instance",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_postgresflex_instance.instance"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_postgresflex_instance.instance")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId), nil
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password"},
},
{
ResourceName: "stackit_postgresflex_user.user",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_postgresflex_user.user"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_postgresflex_user.user")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
userId, ok := r.Primary.Attributes["user_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute user_id")
}
// Import
{
ResourceName: "stackit_postgresflex_instance.instance",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_postgresflex_instance.instance"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_postgresflex_instance.instance")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId, userId), nil
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId), nil
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password"},
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password", "uri"},
},
{
ResourceName: "stackit_postgresflex_database.database",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_postgresflex_database.database"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_postgresflex_database.database")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
databaseId, ok := r.Primary.Attributes["database_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute database_id")
}
{
ResourceName: "stackit_postgresflex_user.user",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_postgresflex_user.user"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_postgresflex_user.user")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
userId, ok := r.Primary.Attributes["user_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute user_id")
}
return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId, databaseId), nil
return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId, userId), nil
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password", "uri"},
},
ImportState: true,
ImportStateVerify: true,
{
ResourceName: "stackit_postgresflex_database.database",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_postgresflex_database.database"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_postgresflex_database.database")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
databaseId, ok := r.Primary.Attributes["database_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute database_id")
}
return fmt.Sprintf(
"%s,%s,%s,%s",
testutil.ProjectId,
testutil.Region,
instanceId,
databaseId,
), nil
},
ImportState: true,
ImportStateVerify: true,
},
// Update
{
Config: configResources(instanceResource["backup_schedule_updated"], nil),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"project_id",
instanceResource["project_id"],
),
resource.TestCheckResourceAttrSet(
"stackit_postgresflex_instance.instance",
"instance_id",
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"name",
instanceResource["name"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"acl.#",
"1",
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"acl.0",
instanceResource["acl"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"backup_schedule",
instanceResource["backup_schedule_updated"],
),
resource.TestCheckResourceAttrSet(
"stackit_postgresflex_instance.instance",
"flavor.id",
),
resource.TestCheckResourceAttrSet(
"stackit_postgresflex_instance.instance",
"flavor.description",
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"flavor.cpu",
instanceResource["flavor_cpu"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"flavor.ram",
instanceResource["flavor_ram"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"replicas",
instanceResource["replicas"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"storage.class",
instanceResource["storage_class"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"storage.size",
instanceResource["storage_size"],
),
resource.TestCheckResourceAttr(
"stackit_postgresflex_instance.instance",
"version",
instanceResource["version"],
),
),
},
// Deletion is done by the framework implicitly
},
// Update
{
Config: configResources(instanceResource["backup_schedule_updated"], nil),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "project_id", instanceResource["project_id"]),
resource.TestCheckResourceAttrSet("stackit_postgresflex_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "name", instanceResource["name"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "acl.0", instanceResource["acl"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "backup_schedule", instanceResource["backup_schedule_updated"]),
resource.TestCheckResourceAttrSet("stackit_postgresflex_instance.instance", "flavor.id"),
resource.TestCheckResourceAttrSet("stackit_postgresflex_instance.instance", "flavor.description"),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "flavor.cpu", instanceResource["flavor_cpu"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "flavor.ram", instanceResource["flavor_ram"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "replicas", instanceResource["replicas"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "storage.class", instanceResource["storage_class"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "storage.size", instanceResource["storage_size"]),
resource.TestCheckResourceAttr("stackit_postgresflex_instance.instance", "version", instanceResource["version"]),
),
},
// Deletion is done by the framework implicitly
},
})
)
}
func testAccCheckPostgresFlexDestroy(s *terraform.State) error {
@ -358,15 +559,10 @@ func testAccCheckPostgresFlexDestroy(s *terraform.State) error {
}
if utils.Contains(instancesToDestroy, *items[i].Id) {
// TODO @mhenselin - does force still exist?
// err := client.ForceDeleteInstanceExecute(ctx, testutil.ProjectId, testutil.Region, *items[i].Id)
err := client.DeleteInstanceRequestExecute(ctx, testutil.ProjectId, testutil.Region, *items[i].Id)
if err != nil {
return fmt.Errorf("deleting instance %s during CheckDestroy: %w", *items[i].Id, err)
}
//_, err = wait.DeleteInstanceWaitHandler(ctx, client, testutil.ProjectId, testutil.Region, *items[i].Id).WaitWithContext(ctx)
//if err != nil {
// return fmt.Errorf("deleting instance %s during CheckDestroy: waiting for deletion %w", *items[i].Id, err)
//}
}
}
return nil

View file

@ -89,6 +89,10 @@ func (r *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r
"user_id": "User ID.",
"instance_id": "ID of the PostgresFlex instance.",
"project_id": "STACKIT project ID to which the instance is associated.",
"username": "The name of the user.",
"roles": "The roles assigned to the user.",
"host": "The host address for the user to connect to the instance.",
"port": "The port number for the user to connect to the instance.",
"region": "The resource region. If not defined, the provider region is used.",
"status": "The current status of the user.",
"connection_string": "The connection string for the user to the instance.",
@ -125,17 +129,21 @@ func (r *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r
},
},
"username": schema.StringAttribute{
Computed: true,
Description: descriptions["username"],
Computed: true,
},
"roles": schema.SetAttribute{
Description: descriptions["roles"],
ElementType: types.StringType,
Computed: true,
},
"host": schema.StringAttribute{
Computed: true,
Description: descriptions["host"],
Computed: true,
},
"port": schema.Int64Attribute{
Computed: true,
Description: descriptions["port"],
Computed: true,
},
"region": schema.StringAttribute{
// the region cannot be found automatically, so it has to be passed
@ -143,10 +151,12 @@ func (r *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r
Description: descriptions["region"],
},
"status": schema.StringAttribute{
Computed: true,
Description: descriptions["status"],
Computed: true,
},
"connection_string": schema.StringAttribute{
Computed: true,
Description: descriptions["connection_string"],
Computed: true,
},
},
}

View file

@ -2,13 +2,14 @@ package postgresflexalpha
import (
"context"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha/wait"
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
@ -41,16 +42,15 @@ var (
)
type Model struct {
Id types.String `tfsdk:"id"` // needed by TF
UserId types.Int64 `tfsdk:"user_id"`
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Username types.String `tfsdk:"username"`
Roles types.Set `tfsdk:"roles"`
Password types.String `tfsdk:"password"`
//Host types.String `tfsdk:"host"`
//Port types.Int64 `tfsdk:"port"`
//Uri types.String `tfsdk:"uri"`
Id types.String `tfsdk:"id"` // needed by TF
UserId types.Int64 `tfsdk:"user_id"`
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Username types.String `tfsdk:"username"`
Roles types.Set `tfsdk:"roles"`
Password types.String `tfsdk:"password"`
Host types.String `tfsdk:"host"`
Port types.Int64 `tfsdk:"port"`
Region types.String `tfsdk:"region"`
Status types.String `tfsdk:"status"`
ConnectionString types.String `tfsdk:"connection_string"`
@ -124,8 +124,7 @@ func (r *userResource) Configure(ctx context.Context, req resource.ConfigureRequ
// Schema defines the schema for the resource.
func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
// rolesOptions := []string{"login", "createdb", "createrole"}
rolesOptions := []string{"login", "createdb"}
rolesOptions := []string{"login", "createdb", "createrole"}
descriptions := map[string]string{
"main": "Postgres Flex user resource schema. Must have a `region` specified in the provider configuration.",
@ -133,9 +132,13 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
"user_id": "User ID.",
"instance_id": "ID of the PostgresFlex instance.",
"project_id": "STACKIT project ID to which the instance is associated.",
"username": "The name of the user.",
"roles": "Database access levels for the user. " + utils.FormatPossibleValues(rolesOptions...),
"region": "The resource region. If not defined, the provider region is used.",
"status": "The current status of the user.",
"password": "The password for the user. This is only set upon creation.",
"host": "The host of the Postgres Flex instance.",
"port": "The port of the Postgres Flex instance.",
"connection_string": "The connection string for the user to the instance.",
}
@ -182,6 +185,7 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
},
},
"username": schema.StringAttribute{
Description: descriptions["username"],
Required: true,
PlanModifiers: []planmodifier.String{
// stringplanmodifier.RequiresReplace(),
@ -198,19 +202,18 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
},
},
"password": schema.StringAttribute{
Computed: true,
Sensitive: true,
Description: descriptions["password"],
Computed: true,
Sensitive: true,
},
"host": schema.StringAttribute{
Description: descriptions["host"],
Computed: true,
},
"port": schema.Int64Attribute{
Description: descriptions["port"],
Computed: true,
},
//"host": schema.StringAttribute{
// Computed: true,
//},
//"port": schema.Int64Attribute{
// Computed: true,
//},
//"uri": schema.StringAttribute{
// Computed: true,
// Sensitive: true,
//},
"region": schema.StringAttribute{
Optional: true,
// must be computed to allow for storing the override value from the provider
@ -221,10 +224,12 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
},
},
"status": schema.StringAttribute{
Computed: true,
Description: descriptions["status"],
Computed: true,
},
"connection_string": schema.StringAttribute{
Computed: true,
Description: descriptions["connection_string"],
Computed: true,
},
},
}
@ -244,21 +249,12 @@ func (r *userResource) Create(
}
ctx = core.InitProviderContext(ctx)
ctx = r.setTFLogFields(ctx, &model)
arg := r.getClientArg(&model)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "region", region)
var roles []string
if !model.Roles.IsNull() && !model.Roles.IsUnknown() {
diags = model.Roles.ElementsAs(ctx, &roles, false)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var roles = r.expandRoles(ctx, model.Roles, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}
// Generate API request body from model
@ -270,10 +266,11 @@ func (r *userResource) Create(
// Create new user
userResp, err := r.client.CreateUserRequest(
ctx,
projectId,
region,
instanceId,
arg.projectId,
arg.region,
arg.instanceId,
).CreateUserRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Calling API: %v", err))
return
@ -290,16 +287,22 @@ func (r *userResource) Create(
)
return
}
userId := *userResp.Id
ctx = tflog.SetField(ctx, "user_id", userId)
// Map response body to schema
err = mapFieldsCreate(userResp, toPayloadRoles(&roles), &model, region)
model.UserId = types.Int64PointerValue(userResp.Id)
model.Password = types.StringPointerValue(userResp.Password)
ctx = tflog.SetField(ctx, "user_id", *userResp.Id)
exists, err := r.getUserResource(ctx, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Calling API: %v", err))
return
}
if !exists {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating user",
fmt.Sprintf("Processing API payload: %v", err),
ctx, &resp.Diagnostics, "Error creating user",
fmt.Sprintf("User ID '%v' resource not found after creation", model.UserId.ValueInt64()),
)
return
}
@ -327,40 +330,20 @@ func (r *userResource) Read(
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueInt64()
region := r.providerData.GetRegionWithOverride(model.Region)
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId)
ctx = tflog.SetField(ctx, "region", region)
exists, err := r.getUserResource(ctx, &model)
recordSetResp, err := r.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute()
if err != nil {
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
if ok && oapiErr.StatusCode == http.StatusNotFound {
resp.State.RemoveResource(ctx)
return
}
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Calling API: %v", err))
return
}
ctx = core.LogResponse(ctx)
// Map response body to schema
err = mapFields(recordSetResp, &model, region)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error reading user",
fmt.Sprintf("Processing API payload: %v", err),
)
if !exists {
resp.State.RemoveResource(ctx)
return
}
ctx = core.LogResponse(ctx)
// Set refreshed state
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
@ -384,15 +367,8 @@ func (r *userResource) Update(
}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueInt64()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId)
ctx = tflog.SetField(ctx, "region", region)
ctx = r.setTFLogFields(ctx, &model)
arg := r.getClientArg(&model)
// Retrieve values from state
var stateModel Model
@ -402,13 +378,9 @@ func (r *userResource) Update(
return
}
var roles []string
if !model.Roles.IsNull() && !model.Roles.IsUnknown() {
diags = model.Roles.ElementsAs(ctx, &roles, false)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var roles = r.expandRoles(ctx, model.Roles, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}
// Generate API request body from model
@ -421,10 +393,10 @@ func (r *userResource) Update(
// Update existing instance
err = r.client.UpdateUserRequest(
ctx,
projectId,
region,
instanceId,
userId,
arg.projectId,
arg.region,
arg.instanceId,
arg.userId,
).UpdateUserRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", err.Error())
@ -433,20 +405,17 @@ func (r *userResource) Update(
ctx = core.LogResponse(ctx)
userResp, err := r.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute()
exists, err := r.getUserResource(ctx, &stateModel)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Calling API: %v", err))
return
}
// Map response body to schema
err = mapFields(userResp, &stateModel, region)
if err != nil {
if !exists {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error updating user",
fmt.Sprintf("Processing API payload: %v", err),
ctx, &resp.Diagnostics, "Error updating user",
fmt.Sprintf("User ID '%v' resource not found after update", stateModel.UserId.ValueInt64()),
)
return
}
@ -474,34 +443,32 @@ func (r *userResource) Delete(
}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueInt64()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId)
ctx = tflog.SetField(ctx, "region", region)
ctx = r.setTFLogFields(ctx, &model)
arg := r.getClientArg(&model)
// Delete existing record set
err := r.client.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute()
err := r.client.DeleteUserRequest(ctx, arg.projectId, arg.region, arg.instanceId, arg.userId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err))
}
ctx = core.LogResponse(ctx)
_, err = wait.DeleteUserWaitHandler(ctx, r.client, projectId, region, instanceId, userId).WaitWithContext(ctx)
exists, err := r.getUserResource(ctx, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err))
return
}
if exists {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error deleting user",
fmt.Sprintf("Instance deletion waiting: %v", err),
ctx, &resp.Diagnostics, "Error deleting user",
fmt.Sprintf("User ID '%v' resource still exists after deletion", model.UserId.ValueInt64()),
)
return
}
resp.State.RemoveResource(ctx)
tflog.Info(ctx, "Postgres Flex user deleted")
}
@ -538,57 +505,6 @@ func (r *userResource) ImportState(
tflog.Info(ctx, "postgresflexalpha user state imported")
}
func mapFieldsCreate(
userResp *postgresflex.CreateUserResponse,
rolesArg *[]postgresflex.UserRole,
model *Model,
region string,
) error {
if userResp == nil {
return fmt.Errorf("response is nil")
}
if model == nil {
return fmt.Errorf("model input is nil")
}
user := userResp
if user.Id == nil {
return fmt.Errorf("user id not present")
}
userId := *user.Id
model.Id = utils.BuildInternalTerraformId(
model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(userId, 10),
)
model.UserId = types.Int64Value(userId)
model.Username = types.StringPointerValue(user.Name)
if user.Password == nil {
return fmt.Errorf("user password not present")
}
if rolesArg == nil {
model.Roles = types.SetNull(types.StringType)
} else {
var roles []attr.Value
for _, role := range *rolesArg {
roles = append(roles, types.StringValue(string(role)))
}
rolesSet, diags := types.SetValue(types.StringType, roles)
if diags.HasError() {
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
}
model.Roles = rolesSet
}
model.Password = types.StringPointerValue(user.Password)
model.Region = types.StringValue(region)
model.Status = types.StringPointerValue(user.Status)
//model.Host = types.StringPointerValue()
model.ConnectionString = types.StringPointerValue(user.ConnectionString)
return nil
}
func mapFields(userResp *postgresflex.GetUserResponse, model *Model, region string) error {
if userResp == nil {
return fmt.Errorf("response is nil")
@ -625,14 +541,77 @@ func mapFields(userResp *postgresflex.GetUserResponse, model *Model, region stri
}
model.Roles = rolesSet
}
//model.Host = types.StringPointerValue(user.Host)
//model.Port = types.Int64PointerValue(user.Port)
model.Host = types.StringPointerValue(user.Host)
model.Port = types.Int64PointerValue(user.Port)
model.Region = types.StringValue(region)
model.Status = types.StringPointerValue(user.Status)
model.ConnectionString = types.StringPointerValue(user.ConnectionString)
return nil
}
// getUserResource refreshes the resource state by calling the API and mapping the response to the model.
// Returns true if the resource state was successfully refreshed, false if the resource does not exist.
func (r *userResource) getUserResource(ctx context.Context, model *Model) (bool, error) {
ctx = r.setTFLogFields(ctx, model)
arg := r.getClientArg(model)
// API Call
userResp, err := r.client.GetUserRequest(ctx, arg.projectId, arg.region, arg.instanceId, arg.userId).Execute()
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound {
return false, nil
}
return false, fmt.Errorf("error fetching user resource: %w", err)
}
if err := mapFields(userResp, model, arg.region); err != nil {
return false, fmt.Errorf("error mapping user resource: %w", err)
}
return true, nil
}
type clientArg struct {
projectId string
instanceId string
region string
userId int64
}
// getClientArg constructs client arguments from the model.
func (r *userResource) getClientArg(model *Model) *clientArg {
return &clientArg{
projectId: model.ProjectId.ValueString(),
instanceId: model.InstanceId.ValueString(),
region: r.providerData.GetRegionWithOverride(model.Region),
userId: model.UserId.ValueInt64(),
}
}
// setTFLogFields adds relevant fields to the context for terraform logging purposes.
func (r *userResource) setTFLogFields(ctx context.Context, model *Model) context.Context {
usrCtx := r.getClientArg(model)
ctx = tflog.SetField(ctx, "project_id", usrCtx.projectId)
ctx = tflog.SetField(ctx, "instance_id", usrCtx.instanceId)
ctx = tflog.SetField(ctx, "user_id", usrCtx.userId)
ctx = tflog.SetField(ctx, "region", usrCtx.region)
return ctx
}
func (r *userResource) expandRoles(ctx context.Context, rolesSet types.Set, diags *diag.Diagnostics) []string {
if rolesSet.IsNull() || rolesSet.IsUnknown() {
return nil
}
var roles []string
diags.Append(rolesSet.ElementsAs(ctx, &roles, false)...)
return roles
}
func toCreatePayload(model *Model, roles *[]string) (*postgresflex.CreateUserRequestPayload, error) {
if model == nil {
return nil, fmt.Errorf("nil model")

View file

@ -14,22 +14,16 @@ func TestMapFieldsCreate(t *testing.T) {
const testRegion = "region"
tests := []struct {
description string
input *postgresflexalpha.CreateUserResponse
updateRoles *postgresflexalpha.UpdateUserRequestPayload
input *postgresflexalpha.GetUserResponse
region string
expected Model
isValid bool
}{
{
"default_values",
&postgresflexalpha.CreateUserResponse{
Id: utils.Ptr(int64(1)),
Password: utils.Ptr(""),
&postgresflexalpha.GetUserResponse{
Id: utils.Ptr(int64(1)),
},
&postgresflexalpha.UpdateUserRequestPayload{
Roles: &[]postgresflexalpha.UserRole{},
},
testRegion,
Model{
Id: types.StringValue("pid,region,iid,1"),
@ -37,11 +31,10 @@ func TestMapFieldsCreate(t *testing.T) {
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringNull(),
Roles: types.SetValueMust(types.StringType, []attr.Value{}),
Password: types.StringValue(""),
Roles: types.SetNull(types.StringType),
Password: types.StringNull(),
Host: types.StringNull(),
Port: types.Int64Null(),
Uri: types.StringNull(),
Region: types.StringValue(testRegion),
Status: types.StringNull(),
ConnectionString: types.StringNull(),
@ -50,16 +43,12 @@ func TestMapFieldsCreate(t *testing.T) {
},
{
"simple_values",
&postgresflexalpha.CreateUserResponse{
&postgresflexalpha.GetUserResponse{
Id: utils.Ptr(int64(1)),
Name: utils.Ptr("username"),
Password: utils.Ptr("password"),
ConnectionString: utils.Ptr("connection_string"),
Status: utils.Ptr("status"),
},
&postgresflexalpha.UpdateUserRequestPayload{
Roles: &[]postgresflexalpha.UserRole{},
},
testRegion,
Model{
Id: types.StringValue("pid,region,iid,1"),
@ -67,11 +56,10 @@ func TestMapFieldsCreate(t *testing.T) {
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringValue("username"),
Roles: types.SetValueMust(types.StringType, []attr.Value{}),
Password: types.StringValue("password"),
Roles: types.SetNull(types.StringType),
Password: types.StringNull(),
Host: types.StringNull(),
Port: types.Int64Null(),
Uri: types.StringNull(),
Region: types.StringValue(testRegion),
Status: types.StringValue("status"),
ConnectionString: types.StringValue("connection_string"),
@ -80,16 +68,12 @@ func TestMapFieldsCreate(t *testing.T) {
},
{
"null_fields_and_int_conversions",
&postgresflexalpha.CreateUserResponse{
&postgresflexalpha.GetUserResponse{
Id: utils.Ptr(int64(1)),
Name: nil,
Password: utils.Ptr(""),
ConnectionString: nil,
Status: nil,
},
&postgresflexalpha.UpdateUserRequestPayload{
Roles: &[]postgresflexalpha.UserRole{},
},
testRegion,
Model{
Id: types.StringValue("pid,region,iid,1"),
@ -97,11 +81,10 @@ func TestMapFieldsCreate(t *testing.T) {
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringNull(),
Roles: types.SetValueMust(types.StringType, []attr.Value{}),
Password: types.StringValue(""),
Roles: types.SetNull(types.StringType),
Password: types.StringNull(),
Host: types.StringNull(),
Port: types.Int64Null(),
Uri: types.StringNull(),
Region: types.StringValue(testRegion),
Status: types.StringNull(),
ConnectionString: types.StringNull(),
@ -111,35 +94,20 @@ func TestMapFieldsCreate(t *testing.T) {
{
"nil_response",
nil,
nil,
testRegion,
Model{},
false,
},
{
"nil_response_2",
&postgresflexalpha.CreateUserResponse{},
&postgresflexalpha.UpdateUserRequestPayload{},
&postgresflexalpha.GetUserResponse{},
testRegion,
Model{},
false,
},
{
"no_resource_id",
&postgresflexalpha.CreateUserResponse{},
&postgresflexalpha.UpdateUserRequestPayload{},
testRegion,
Model{},
false,
},
{
"no_password",
&postgresflexalpha.CreateUserResponse{
Id: utils.Ptr(int64(1)),
},
&postgresflexalpha.UpdateUserRequestPayload{
Roles: &[]postgresflexalpha.UserRole{},
},
&postgresflexalpha.GetUserResponse{},
testRegion,
Model{},
false,
@ -152,11 +120,8 @@ func TestMapFieldsCreate(t *testing.T) {
ProjectId: tt.expected.ProjectId,
InstanceId: tt.expected.InstanceId,
}
var roles *[]postgresflexalpha.UserRole
if tt.updateRoles != nil {
roles = tt.updateRoles.Roles
}
err := mapFieldsCreate(tt.input, roles, state, tt.region)
err := mapFields(tt.input, state, tt.region)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
@ -175,7 +140,6 @@ func TestMapFieldsCreate(t *testing.T) {
}
func TestMapFields(t *testing.T) {
t.Skip("Skipping - needs refactoring")
const testRegion = "region"
tests := []struct {
description string
@ -252,7 +216,7 @@ func TestMapFields(t *testing.T) {
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringNull(),
Roles: types.SetValueMust(types.StringType, []attr.Value{}),
Roles: types.SetNull(types.StringType),
Host: types.StringNull(),
Port: types.Int64Value(2123456789),
Region: types.StringValue(testRegion),
@ -424,6 +388,7 @@ func TestToUpdatePayload(t *testing.T) {
"role_2",
},
&postgresflexalpha.UpdateUserRequestPayload{
Name: utils.Ptr("username"),
Roles: &[]postgresflexalpha.UserRole{
"role_1",
"role_2",

View file

@ -16,7 +16,7 @@ type flavorsClient interface {
) (*sqlserverflex.GetFlavorsResponse, error)
}
//func loadFlavorId(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel, storage *storageModel) error {
// func loadFlavorId(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel, storage *storageModel) error {
// if model == nil {
// return fmt.Errorf("nil model")
// }
@ -113,7 +113,10 @@ type flavorsClient interface {
// return nil
//}
func getAllFlavors(ctx context.Context, client flavorsClient, projectId, region string) ([]sqlserverflex.ListFlavors, error) {
func getAllFlavors(ctx context.Context, client flavorsClient, projectId, region string) (
[]sqlserverflex.ListFlavors,
error,
) {
if projectId == "" || region == "" {
return nil, fmt.Errorf("listing sqlserverflex flavors: projectId and region are required")
}
@ -156,7 +159,7 @@ func getAllFlavors(ctx context.Context, client flavorsClient, projectId, region
return flavorList, nil
}
//func getFlavorModelById(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel) error {
// func getFlavorModelById(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel) error {
// if model == nil {
// return fmt.Errorf("nil model")
// }

View file

@ -13,11 +13,15 @@ import (
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
)
type sqlserverflexClient interface {
GetFlavorsRequestExecute(ctx context.Context, projectId, region string, page, size *int64, sort *sqlserverflex.FlavorSort) (*sqlserverflex.GetFlavorsResponse, error)
}
func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, model *Model, storage *storageModel, encryption *encryptionModel, network *networkModel, region string) error {
func mapFields(
ctx context.Context,
resp *sqlserverflex.GetInstanceResponse,
model *Model,
storage *storageModel,
encryption *encryptionModel,
network *networkModel,
region string,
) error {
if resp == nil {
return fmt.Errorf("response input is nil")
}
@ -155,7 +159,12 @@ func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, mod
return nil
}
func toCreatePayload(model *Model, storage *storageModel, encryption *encryptionModel, network *networkModel) (*sqlserverflex.CreateInstanceRequestPayload, error) {
func toCreatePayload(
model *Model,
storage *storageModel,
encryption *encryptionModel,
network *networkModel,
) (*sqlserverflex.CreateInstanceRequestPayload, error) {
if model == nil {
return nil, fmt.Errorf("nil model")
}
@ -201,7 +210,12 @@ func toCreatePayload(model *Model, storage *storageModel, encryption *encryption
}, nil
}
func toUpdatePartiallyPayload(model *Model, storage *storageModel, network *networkModel) (*sqlserverflex.UpdateInstancePartiallyRequestPayload, error) {
//nolint:unused // TODO: remove if not needed later
func toUpdatePartiallyPayload(
model *Model,
storage *storageModel,
network *networkModel,
) (*sqlserverflex.UpdateInstancePartiallyRequestPayload, error) {
if model == nil {
return nil, fmt.Errorf("nil model")
}
@ -242,7 +256,12 @@ func toUpdatePartiallyPayload(model *Model, storage *storageModel, network *netw
}, nil
}
func toUpdatePayload(model *Model, storage *storageModel, network *networkModel) (*sqlserverflex.UpdateInstanceRequestPayload, error) {
// TODO: check func with his args
func toUpdatePayload(
_ *Model,
_ *storageModel,
_ *networkModel,
) (*sqlserverflex.UpdateInstanceRequestPayload, error) {
return &sqlserverflex.UpdateInstanceRequestPayload{
BackupSchedule: nil,
FlavorId: nil,

View file

@ -45,6 +45,7 @@ var (
_ resource.ResourceWithModifyPlan = &instanceResource{}
)
//nolint:unused // TODO: remove if not needed later
var validNodeTypes []string = []string{
"Single",
"Replica",
@ -126,7 +127,11 @@ func (r *instanceResource) Metadata(_ context.Context, req resource.MetadataRequ
}
// Configure adds the provider configured client to the resource.
func (r *instanceResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
func (r *instanceResource) 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 {
@ -143,7 +148,11 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure
// ModifyPlan implements resource.ResourceWithModifyPlan.
// Use the modifier to set the effective region in the current plan.
func (r *instanceResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) ModifyPlan(
ctx context.Context,
req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse,
) { // nolint:gocritic // function signature required by Terraform
var configModel Model
// skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() {
@ -422,7 +431,11 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
}
// Create creates the resource and sets the initial Terraform state.
func (r *instanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) Create(
ctx context.Context,
req resource.CreateRequest,
resp *resource.CreateResponse,
) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -467,11 +480,20 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
// Generate API request body from model
payload, err := toCreatePayload(&model, storage, encryption, network)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Create new instance
createResp, err := r.client.CreateInstanceRequest(ctx, projectId, region).CreateInstanceRequestPayload(*payload).Execute()
createResp, err := r.client.CreateInstanceRequest(
ctx,
projectId,
region,
).CreateInstanceRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Calling API: %v", err))
return
@ -480,31 +502,54 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
ctx = core.LogResponse(ctx)
instanceId := *createResp.Id
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
"id": utils.BuildInternalTerraformId(projectId, region, instanceId),
"instance_id": instanceId,
})
utils.SetAndLogStateFields(
ctx, &resp.Diagnostics, &resp.State, map[string]any{
"id": utils.BuildInternalTerraformId(projectId, region, instanceId),
"instance_id": instanceId,
},
)
if resp.Diagnostics.HasError() {
return
}
// The creation waiter sometimes returns an error from the API: "instance with id xxx has unexpected status Failure"
// which can be avoided by sleeping before wait
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, instanceId, region).SetSleepBeforeWait(30 * time.Second).WaitWithContext(ctx)
waitResp, err := wait.CreateInstanceWaitHandler(
ctx,
r.client,
projectId,
instanceId,
region,
).SetSleepBeforeWait(30 * time.Second).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Instance creation waiting: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Instance creation waiting: %v", err),
)
return
}
if waitResp.FlavorId == nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", "Instance creation waiting: returned flavor id is nil")
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
"Instance creation waiting: returned flavor id is nil",
)
return
}
// Map response body to schema
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// Set state to fully populated data
@ -523,7 +568,11 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
}
// Read refreshes the Terraform state with the latest data.
func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) Read(
ctx context.Context,
req resource.ReadRequest,
resp *resource.ReadResponse,
) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -584,7 +633,12 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
// Map response body to schema
err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error reading instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// Set refreshed state
@ -597,7 +651,11 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
}
// Update updates the resource and sets the updated Terraform state on success.
func (r *instanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) Update(
ctx context.Context,
req resource.UpdateRequest,
resp *resource.UpdateResponse,
) { // nolint:gocritic // function signature required by Terraform
// Retrieve values from plan
var model Model
diags := req.Plan.Get(ctx, &model)
@ -646,12 +704,21 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
// Generate API request body from model
payload, err := toUpdatePayload(&model, storage, network)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Creating API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error updating instance",
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Update existing instance
err = r.client.UpdateInstanceRequest(ctx, projectId, region, instanceId).UpdateInstanceRequestPayload(*payload).Execute()
// err = r.client.UpdateInstancePartiallyRequest(ctx, projectId, region, instanceId).UpdateInstancePartiallyRequestPayload(*payload).Execute()
err = r.client.UpdateInstanceRequest(
ctx,
projectId,
region,
instanceId,
).UpdateInstanceRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error())
return
@ -661,14 +728,24 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
waitResp, err := wait.UpdateInstanceWaitHandler(ctx, r.client, projectId, instanceId, region).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Instance update waiting: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error updating instance",
fmt.Sprintf("Instance update waiting: %v", err),
)
return
}
// Map response body to schema
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error updating instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
diags = resp.State.Set(ctx, model)
@ -680,7 +757,11 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
}
// Delete deletes the resource and removes the Terraform state on success.
func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
func (r *instanceResource) Delete(
ctx context.Context,
req resource.DeleteRequest,
resp *resource.DeleteResponse,
) { // nolint:gocritic // function signature required by Terraform
// Retrieve values from state
var model Model
diags := req.State.Get(ctx, &model)
@ -709,7 +790,12 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques
_, err = wait.DeleteInstanceWaitHandler(ctx, r.client, projectId, instanceId, region).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Instance deletion waiting: %v", err))
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error deleting instance",
fmt.Sprintf("Instance deletion waiting: %v", err),
)
return
}
tflog.Info(ctx, "SQLServer Flex instance deleted")
@ -717,11 +803,16 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques
// ImportState imports a resource into the Terraform state on success.
// The expected format of the resource import identifier is: project_id,instance_id
func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
func (r *instanceResource) ImportState(
ctx context.Context,
req resource.ImportStateRequest,
resp *resource.ImportStateResponse,
) {
idParts := strings.Split(req.ID, core.Separator)
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
core.LogAndAddError(ctx, &resp.Diagnostics,
core.LogAndAddError(
ctx, &resp.Diagnostics,
"Error importing instance",
fmt.Sprintf("Expected import identifier with format: [project_id],[region],[instance_id] Got: %q", req.ID),
)

View file

@ -19,7 +19,7 @@ import (
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/features"
postgresFlexAlphaDatabase "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/database"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/flavor"
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"
sqlserverFlexAlphaFlavor "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavor"
@ -363,7 +363,8 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
setStringField(providerConfig.DefaultRegion, func(v string) { providerData.DefaultRegion = v })
setStringField(
providerConfig.Region, func(v string) { providerData.Region = v }, // nolint:staticcheck // preliminary handling of deprecated attribute
providerConfig.Region,
func(v string) { providerData.Region = v }, // nolint:staticcheck // preliminary handling of deprecated attribute
)
setBoolField(providerConfig.EnableBetaResources, func(v bool) { providerData.EnableBetaResources = v })
@ -490,7 +491,7 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
postgresFlexAlphaFlavor.NewFlavorDataSource,
//postgresFlexAlphaFlavor.NewFlavorListDataSource,
// postgresFlexAlphaFlavor.NewFlavorListDataSource,
postgresFlexAlphaDatabase.NewDatabaseDataSource,
postgresFlexAlphaInstance.NewInstanceDataSource,
postgresFlexAlphaUser.NewUserDataSource,