Skip to content

Commit 37b1be9

Browse files
authored
Fix creating and updating hosted deployments with scaling specs (#62)
1 parent 0ba1d5e commit 37b1be9

File tree

7 files changed

+415
-136
lines changed

7 files changed

+415
-136
lines changed

docs/resources/deployment.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -171,24 +171,27 @@ Read-Only:
171171
<a id="nestedatt--scaling_spec"></a>
172172
### Nested Schema for `scaling_spec`
173173

174-
Optional:
174+
Required:
175175

176-
- `hibernation_spec` (Attributes) (see [below for nested schema](#nestedatt--scaling_spec--hibernation_spec))
176+
- `hibernation_spec` (Attributes) Hibernation configuration for the deployment. The deployment will hibernate according to the schedules defined in this configuration. To remove the hibernation configuration, set scaling_spec to null. (see [below for nested schema](#nestedatt--scaling_spec--hibernation_spec))
177177

178178
<a id="nestedatt--scaling_spec--hibernation_spec"></a>
179179
### Nested Schema for `scaling_spec.hibernation_spec`
180180

181181
Optional:
182182

183-
- `override` (Attributes) (see [below for nested schema](#nestedatt--scaling_spec--hibernation_spec--override))
184-
- `schedules` (Attributes Set) (see [below for nested schema](#nestedatt--scaling_spec--hibernation_spec--schedules))
183+
- `override` (Attributes) Hibernation override configuration. Set to null to remove the override. (see [below for nested schema](#nestedatt--scaling_spec--hibernation_spec--override))
184+
- `schedules` (Attributes Set) List of hibernation schedules. Set to null to remove all schedules. (see [below for nested schema](#nestedatt--scaling_spec--hibernation_spec--schedules))
185185

186186
<a id="nestedatt--scaling_spec--hibernation_spec--override"></a>
187187
### Nested Schema for `scaling_spec.hibernation_spec.override`
188188

189-
Optional:
189+
Required:
190190

191191
- `is_hibernating` (Boolean)
192+
193+
Optional:
194+
192195
- `override_until` (String)
193196

194197
Read-Only:

internal/provider/models/deployment.go

+110-76
Original file line numberDiff line numberDiff line change
@@ -244,23 +244,13 @@ func (data *DeploymentResource) ReadFromResponse(
244244
data.SchedulerSize = types.StringPointerValue((*string)(deployment.SchedulerSize))
245245
data.IsDevelopmentMode = types.BoolPointerValue(deployment.IsDevelopmentMode)
246246
data.IsHighAvailability = types.BoolPointerValue(deployment.IsHighAvailability)
247-
248-
// Currently, the scaling status and spec are only available in development mode
249-
// However, there is a bug in the API where the scaling status and spec are returned even if the deployment is not in development mode for updated deployments
250-
// This is a workaround to handle the bug until the API is fixed
251-
// Issue here: https://github.com/astronomer/astro/issues/21073
252-
if deployment.IsDevelopmentMode != nil && *deployment.IsDevelopmentMode {
253-
data.ScalingStatus, diags = ScalingStatusTypesObject(ctx, deployment.ScalingStatus)
254-
if diags.HasError() {
255-
return diags
256-
}
257-
data.ScalingSpec, diags = ScalingSpecTypesObject(ctx, deployment.ScalingSpec)
258-
if diags.HasError() {
259-
return diags
260-
}
261-
} else {
262-
data.ScalingStatus = types.ObjectNull(schemas.ScalingStatusAttributeTypes())
263-
data.ScalingSpec = types.ObjectNull(schemas.ScalingSpecAttributeTypes())
247+
data.ScalingStatus, diags = ScalingStatusTypesObject(ctx, deployment.ScalingStatus)
248+
if diags.HasError() {
249+
return diags
250+
}
251+
data.ScalingSpec, diags = ScalingSpecTypesObject(ctx, deployment.ScalingSpec)
252+
if diags.HasError() {
253+
return diags
264254
}
265255

266256
return nil
@@ -351,23 +341,13 @@ func (data *DeploymentDataSource) ReadFromResponse(
351341
data.SchedulerSize = types.StringPointerValue((*string)(deployment.SchedulerSize))
352342
data.IsDevelopmentMode = types.BoolPointerValue(deployment.IsDevelopmentMode)
353343
data.IsHighAvailability = types.BoolPointerValue(deployment.IsHighAvailability)
354-
355-
// Currently, the scaling status and spec are only available in development mode
356-
// However, there is a bug in the API where the scaling status and spec are returned even if the deployment is not in development mode for updated deployments
357-
// This is a workaround to handle the bug until the API is fixed
358-
// Issue here: https://github.com/astronomer/astro/issues/21073
359-
if deployment.IsDevelopmentMode != nil && *deployment.IsDevelopmentMode {
360-
data.ScalingStatus, diags = ScalingStatusTypesObject(ctx, deployment.ScalingStatus)
361-
if diags.HasError() {
362-
return diags
363-
}
364-
data.ScalingSpec, diags = ScalingSpecTypesObject(ctx, deployment.ScalingSpec)
365-
if diags.HasError() {
366-
return diags
367-
}
368-
} else {
369-
data.ScalingStatus = types.ObjectNull(schemas.ScalingStatusAttributeTypes())
370-
data.ScalingSpec = types.ObjectNull(schemas.ScalingSpecAttributeTypes())
344+
data.ScalingStatus, diags = ScalingStatusTypesObject(ctx, deployment.ScalingStatus)
345+
if diags.HasError() {
346+
return diags
347+
}
348+
data.ScalingSpec, diags = ScalingSpecTypesObject(ctx, deployment.ScalingSpec)
349+
if diags.HasError() {
350+
return diags
371351
}
372352

373353
return nil
@@ -459,11 +439,11 @@ func WorkerQueueDataSourceTypesObject(
459439
}
460440

461441
type DeploymentScalingSpec struct {
462-
HibernationSpec HibernationSpec `tfsdk:"hibernation_spec"`
442+
HibernationSpec types.Object `tfsdk:"hibernation_spec"`
463443
}
464444

465445
type DeploymentStatus struct {
466-
HibernationStatus HibernationStatus `tfsdk:"hibernation_status"`
446+
HibernationStatus types.Object `tfsdk:"hibernation_status"`
467447
}
468448

469449
type HibernationStatus struct {
@@ -474,8 +454,8 @@ type HibernationStatus struct {
474454
}
475455

476456
type HibernationSpec struct {
477-
Override HibernationSpecOverride `tfsdk:"override"`
478-
Schedules []HibernationSchedule `tfsdk:"schedules"`
457+
Override types.Object `tfsdk:"override"`
458+
Schedules types.Set `tfsdk:"schedules"`
479459
}
480460

481461
type HibernationSpecOverride struct {
@@ -491,54 +471,108 @@ type HibernationSchedule struct {
491471
WakeAtCron types.String `tfsdk:"wake_at_cron"`
492472
}
493473

474+
func HibernationStatusTypesObject(
475+
ctx context.Context,
476+
hibernationStatus *platform.DeploymentHibernationStatus,
477+
) (types.Object, diag.Diagnostics) {
478+
if hibernationStatus == nil {
479+
return types.ObjectNull(schemas.HibernationStatusAttributeTypes()), nil
480+
}
481+
482+
obj := HibernationStatus{
483+
IsHibernating: types.BoolValue(hibernationStatus.IsHibernating),
484+
NextEventType: types.StringPointerValue((*string)(hibernationStatus.NextEventType)),
485+
NextEventAt: types.StringPointerValue(hibernationStatus.NextEventAt),
486+
Reason: types.StringPointerValue(hibernationStatus.Reason),
487+
}
488+
return types.ObjectValueFrom(ctx, schemas.HibernationStatusAttributeTypes(), obj)
489+
}
490+
491+
func HibernationOverrideTypesObject(
492+
ctx context.Context,
493+
hibernationOverride *platform.DeploymentHibernationOverride,
494+
) (types.Object, diag.Diagnostics) {
495+
if hibernationOverride == nil {
496+
return types.ObjectNull(schemas.HibernationOverrideAttributeTypes()), nil
497+
}
498+
obj := HibernationSpecOverride{
499+
IsHibernating: types.BoolPointerValue(hibernationOverride.IsHibernating),
500+
IsActive: types.BoolPointerValue(hibernationOverride.IsActive),
501+
}
502+
if hibernationOverride.OverrideUntil != nil {
503+
obj.OverrideUntil = types.StringValue(hibernationOverride.OverrideUntil.Format(time.RFC3339))
504+
}
505+
return types.ObjectValueFrom(ctx, schemas.HibernationOverrideAttributeTypes(), obj)
506+
}
507+
508+
func HibernationScheduleTypesObject(
509+
ctx context.Context,
510+
schedule platform.DeploymentHibernationSchedule,
511+
) (types.Object, diag.Diagnostics) {
512+
obj := HibernationSchedule{
513+
Description: types.StringPointerValue(schedule.Description),
514+
HibernateAtCron: types.StringValue(schedule.HibernateAtCron),
515+
IsEnabled: types.BoolValue(schedule.IsEnabled),
516+
WakeAtCron: types.StringValue(schedule.WakeAtCron),
517+
}
518+
return types.ObjectValueFrom(ctx, schemas.HibernationScheduleAttributeTypes(), obj)
519+
}
520+
521+
func HibernationSpecTypesObject(
522+
ctx context.Context,
523+
hibernationSpec *platform.DeploymentHibernationSpec,
524+
) (types.Object, diag.Diagnostics) {
525+
if hibernationSpec == nil || (hibernationSpec.Override == nil && hibernationSpec.Schedules == nil) {
526+
return types.ObjectNull(schemas.HibernationSpecAttributeTypes()), nil
527+
}
528+
529+
override, diags := HibernationOverrideTypesObject(ctx, hibernationSpec.Override)
530+
if diags.HasError() {
531+
return types.ObjectNull(schemas.HibernationSpecAttributeTypes()), diags
532+
}
533+
schedules, diags := utils.ObjectSet(ctx, hibernationSpec.Schedules, schemas.HibernationScheduleAttributeTypes(), HibernationScheduleTypesObject)
534+
if diags.HasError() {
535+
return types.ObjectNull(schemas.HibernationSpecAttributeTypes()), diags
536+
}
537+
obj := HibernationSpec{
538+
Override: override,
539+
Schedules: schedules,
540+
}
541+
return types.ObjectValueFrom(ctx, schemas.HibernationSpecAttributeTypes(), obj)
542+
}
543+
494544
func ScalingStatusTypesObject(
495545
ctx context.Context,
496546
scalingStatus *platform.DeploymentScalingStatus,
497547
) (types.Object, diag.Diagnostics) {
498-
if scalingStatus != nil && scalingStatus.HibernationStatus != nil {
499-
obj := DeploymentStatus{
500-
HibernationStatus: HibernationStatus{
501-
IsHibernating: types.BoolValue(scalingStatus.HibernationStatus.IsHibernating),
502-
NextEventType: types.StringPointerValue((*string)(scalingStatus.HibernationStatus.NextEventType)),
503-
NextEventAt: types.StringPointerValue(scalingStatus.HibernationStatus.NextEventAt),
504-
Reason: types.StringPointerValue(scalingStatus.HibernationStatus.Reason),
505-
},
506-
}
507-
return types.ObjectValueFrom(ctx, schemas.ScalingStatusAttributeTypes(), obj)
548+
if scalingStatus == nil {
549+
return types.ObjectNull(schemas.ScalingStatusAttributeTypes()), nil
550+
}
551+
552+
hibernationStatus, diags := HibernationStatusTypesObject(ctx, scalingStatus.HibernationStatus)
553+
if diags.HasError() {
554+
return types.ObjectNull(schemas.ScalingStatusAttributeTypes()), diags
508555
}
509-
return types.ObjectNull(schemas.ScalingStatusAttributeTypes()), nil
556+
obj := DeploymentStatus{
557+
HibernationStatus: hibernationStatus,
558+
}
559+
return types.ObjectValueFrom(ctx, schemas.ScalingStatusAttributeTypes(), obj)
510560
}
511561

512562
func ScalingSpecTypesObject(
513563
ctx context.Context,
514564
scalingSpec *platform.DeploymentScalingSpec,
515565
) (types.Object, diag.Diagnostics) {
516-
if scalingSpec != nil && scalingSpec.HibernationSpec != nil && (scalingSpec.HibernationSpec.Override != nil || scalingSpec.HibernationSpec.Schedules != nil) {
517-
obj := DeploymentScalingSpec{
518-
HibernationSpec: HibernationSpec{},
519-
}
520-
if scalingSpec.HibernationSpec.Override != nil {
521-
obj.HibernationSpec.Override = HibernationSpecOverride{
522-
IsHibernating: types.BoolPointerValue(scalingSpec.HibernationSpec.Override.IsHibernating),
523-
IsActive: types.BoolPointerValue(scalingSpec.HibernationSpec.Override.IsActive),
524-
}
525-
if scalingSpec.HibernationSpec.Override.OverrideUntil != nil {
526-
obj.HibernationSpec.Override.OverrideUntil = types.StringValue(scalingSpec.HibernationSpec.Override.OverrideUntil.Format(time.RFC3339))
527-
}
528-
}
529-
if scalingSpec.HibernationSpec.Schedules != nil {
530-
schedules := make([]HibernationSchedule, 0, len(*scalingSpec.HibernationSpec.Schedules))
531-
for _, schedule := range *scalingSpec.HibernationSpec.Schedules {
532-
schedules = append(schedules, HibernationSchedule{
533-
Description: types.StringPointerValue(schedule.Description),
534-
HibernateAtCron: types.StringValue(schedule.HibernateAtCron),
535-
IsEnabled: types.BoolValue(schedule.IsEnabled),
536-
WakeAtCron: types.StringValue(schedule.WakeAtCron),
537-
})
538-
}
539-
obj.HibernationSpec.Schedules = schedules
540-
}
541-
return types.ObjectValueFrom(ctx, schemas.ScalingSpecAttributeTypes(), obj)
566+
if scalingSpec == nil {
567+
return types.ObjectNull(schemas.ScalingSpecAttributeTypes()), nil
568+
}
569+
570+
hibernationSpec, diags := HibernationSpecTypesObject(ctx, scalingSpec.HibernationSpec)
571+
if diags.HasError() {
572+
return types.ObjectNull(schemas.ScalingSpecAttributeTypes()), diags
573+
}
574+
obj := DeploymentScalingSpec{
575+
HibernationSpec: hibernationSpec,
542576
}
543-
return types.ObjectNull(schemas.ScalingSpecAttributeTypes()), nil
577+
return types.ObjectValueFrom(ctx, schemas.ScalingSpecAttributeTypes(), obj)
544578
}

internal/provider/resources/resource_cluster.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ func (r *ClusterResource) Create(
8989
return
9090
}
9191

92-
var diags diag.Diagnostics
9392
var createClusterRequest platform.CreateClusterRequest
9493

9594
switch platform.ClusterCloudProvider(data.CloudProvider.ValueString()) {
@@ -221,7 +220,6 @@ func (r *ClusterResource) Create(
221220
Refresh: r.resourceRefreshFunc(ctx, cluster.JSON200.Id),
222221
Timeout: 3 * time.Hour,
223222
MinTimeout: 1 * time.Minute,
224-
Delay: 5 * time.Minute,
225223
}
226224

227225
// readyCluster is the final state of the cluster after it has reached a target status
@@ -375,7 +373,6 @@ func (r *ClusterResource) Update(
375373
Refresh: r.resourceRefreshFunc(ctx, cluster.JSON200.Id),
376374
Timeout: 3 * time.Hour,
377375
MinTimeout: 1 * time.Minute,
378-
Delay: 5 * time.Minute,
379376
}
380377

381378
// readyCluster is the final state of the cluster after it has reached a target status
@@ -447,7 +444,6 @@ func (r *ClusterResource) Delete(
447444
Refresh: r.resourceRefreshFunc(ctx, data.Id.ValueString()),
448445
Timeout: 1 * time.Hour,
449446
MinTimeout: 30 * time.Second,
450-
Delay: 1 * time.Minute,
451447
}
452448

453449
_, err = stateConf.WaitForStateContext(ctx)
@@ -493,7 +489,7 @@ func (r *ClusterResource) ValidateConfig(
493489
}
494490

495491
func validateAwsConfig(ctx context.Context, data *models.ClusterResource) diag.Diagnostics {
496-
var diags diag.Diagnostics
492+
diags := make(diag.Diagnostics, 0)
497493

498494
// Unallowed values
499495
if !data.TenantId.IsNull() {
@@ -524,7 +520,7 @@ func validateAwsConfig(ctx context.Context, data *models.ClusterResource) diag.D
524520
}
525521

526522
func validateAzureConfig(ctx context.Context, data *models.ClusterResource) diag.Diagnostics {
527-
var diags diag.Diagnostics
523+
diags := make(diag.Diagnostics, 0)
528524

529525
// Unallowed values
530526
if !data.ServicePeeringRange.IsNull() {
@@ -549,7 +545,7 @@ func validateAzureConfig(ctx context.Context, data *models.ClusterResource) diag
549545
}
550546

551547
func validateGcpConfig(ctx context.Context, data *models.ClusterResource) diag.Diagnostics {
552-
var diags diag.Diagnostics
548+
diags := make(diag.Diagnostics, 0)
553549

554550
// required values
555551
if data.ServicePeeringRange.IsNull() {
@@ -600,6 +596,18 @@ func (r *ClusterResource) resourceRefreshFunc(ctx context.Context, clusterId str
600596
if diagnostic != nil {
601597
return nil, "", fmt.Errorf("error getting cluster %s", diagnostic.Detail())
602598
}
603-
return cluster.JSON200, string(cluster.JSON200.Status), nil
599+
if cluster != nil && cluster.JSON200 != nil {
600+
switch cluster.JSON200.Status {
601+
case platform.ClusterStatusCREATED:
602+
return cluster.JSON200, string(cluster.JSON200.Status), nil
603+
case platform.ClusterStatusUPDATEFAILED, platform.ClusterStatusCREATEFAILED:
604+
return cluster.JSON200, string(cluster.JSON200.Status), fmt.Errorf("cluster mutation failed for cluster '%v'", cluster.JSON200.Id)
605+
case platform.ClusterStatusCREATING, platform.ClusterStatusUPDATING:
606+
return cluster.JSON200, string(cluster.JSON200.Status), nil
607+
default:
608+
return cluster.JSON200, string(cluster.JSON200.Status), fmt.Errorf("unexpected cluster status '%v' for cluster '%v'", cluster.JSON200.Status, cluster.JSON200.Id)
609+
}
610+
}
611+
return nil, "", fmt.Errorf("error getting cluster %s", clusterId)
604612
}
605613
}

internal/provider/resources/resource_cluster_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const SKIP_CLUSTER_RESOURCE_TESTS = "SKIP_CLUSTER_RESOURCE_TESTS"
2626
const SKIP_CLUSTER_RESOURCE_TESTS_REASON = "Skipping dedicated cluster (and dedicated deployment) resource tests. To run these tests, unset the SKIP_CLUSTER_RESOURCE_TESTS environment variable."
2727

2828
func TestAcc_ResourceClusterAwsWithDedicatedDeployments(t *testing.T) {
29+
t.Skip("AWS cluster creation is currently not working on dev")
2930
if os.Getenv(SKIP_CLUSTER_RESOURCE_TESTS) == "True" {
3031
t.Skip(SKIP_CLUSTER_RESOURCE_TESTS_REASON)
3132
}

0 commit comments

Comments
 (0)