Skip to content

Commit

Permalink
Cloud composer maintenance window GA (#5471) (#11170)
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Feb 24, 2022
1 parent c6220ca commit 5e61d2b
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .changelog/5471.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
composer: added support for Cloud Composer maintenance window in GA
```
88 changes: 86 additions & 2 deletions google/resource_composer_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var (
"config.0.database_config",
"config.0.web_server_config",
"config.0.encryption_config",
"config.0.maintenance_window",
"config.0.workloads_config",
"config.0.environment_size",
}
Expand Down Expand Up @@ -310,7 +311,7 @@ func resourceComposerEnvironment() *schema.Resource {
AtLeastOneOf: composerSoftwareConfigKeys,
Elem: &schema.Schema{Type: schema.TypeString},
ValidateFunc: validateComposerEnvironmentEnvVariables,
Description: `Additional environment variables to provide to the Apache Airflow schedulerf, worker, and webserver processes. Environment variable names must match the regular expression [a-zA-Z_][a-zA-Z0-9_]*. They cannot specify Apache Airflow software configuration overrides (they cannot match the regular expression AIRFLOW__[A-Z0-9_]+__[A-Z0-9_]+), and they cannot match any of the following reserved names: AIRFLOW_HOME C_FORCE_ROOT CONTAINER_NAME DAGS_FOLDER GCP_PROJECT GCS_BUCKET GKE_CLUSTER_NAME SQL_DATABASE SQL_INSTANCE SQL_PASSWORD SQL_PROJECT SQL_REGION SQL_USER.`,
Description: `Additional environment variables to provide to the Apache Airflow scheduler, worker, and webserver processes. Environment variable names must match the regular expression [a-zA-Z_][a-zA-Z0-9_]*. They cannot specify Apache Airflow software configuration overrides (they cannot match the regular expression AIRFLOW__[A-Z0-9_]+__[A-Z0-9_]+), and they cannot match any of the following reserved names: AIRFLOW_HOME C_FORCE_ROOT CONTAINER_NAME DAGS_FOLDER GCP_PROJECT GCS_BUCKET GKE_CLUSTER_NAME SQL_DATABASE SQL_INSTANCE SQL_PASSWORD SQL_PROJECT SQL_REGION SQL_USER.`,
},
"image_version": {
Type: schema.TypeString,
Expand Down Expand Up @@ -463,7 +464,36 @@ func resourceComposerEnvironment() *schema.Resource {
},
},
},

"maintenance_window": {
Type: schema.TypeList,
Optional: true,
Computed: true,
AtLeastOneOf: composerConfigKeys,
MaxItems: 1,
Description: `The configuration for Cloud Composer maintenance window.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"start_time": {
Type: schema.TypeString,
Required: true,
ForceNew: false,
Description: `Start time of the first recurrence of the maintenance window.`,
},
"end_time": {
Type: schema.TypeString,
Required: true,
ForceNew: false,
Description: `Maintenance window end time. It is used only to calculate the duration of the maintenance window. The value for end-time must be in the future, relative to 'start_time'.`,
},
"recurrence": {
Type: schema.TypeString,
Required: true,
ForceNew: false,
Description: `Maintenance window recurrence. Format is a subset of RFC-5545 (https://tools.ietf.org/html/rfc5545) 'RRULE'. The only allowed values for 'FREQ' field are 'FREQ=DAILY' and 'FREQ=WEEKLY;BYDAY=...'. Example values: 'FREQ=WEEKLY;BYDAY=TU,WE', 'FREQ=DAILY'.`,
},
},
},
},
"workloads_config": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -869,6 +899,17 @@ func resourceComposerEnvironmentUpdate(d *schema.ResourceData, meta interface{})
}
}

if d.HasChange("config.0.maintenance_window") {
patchObj := &composer.Environment{Config: &composer.EnvironmentConfig{}}
if config != nil {
patchObj.Config.MaintenanceWindow = config.MaintenanceWindow
}
err = resourceComposerEnvironmentPatchField("config.maintenanceWindow", userAgent, patchObj, d, tfConfig)
if err != nil {
return err
}
}

if d.HasChange("config.0.workloads_config") {
patchObj := &composer.Environment{Config: &composer.EnvironmentConfig{}}
if config != nil {
Expand Down Expand Up @@ -1011,6 +1052,7 @@ func flattenComposerEnvironmentConfig(envCfg *composer.EnvironmentConfig) interf
transformed["database_config"] = flattenComposerEnvironmentConfigDatabaseConfig(envCfg.DatabaseConfig)
transformed["web_server_config"] = flattenComposerEnvironmentConfigWebServerConfig(envCfg.WebServerConfig)
transformed["encryption_config"] = flattenComposerEnvironmentConfigEncryptionConfig(envCfg.EncryptionConfig)
transformed["maintenance_window"] = flattenComposerEnvironmentConfigMaintenanceWindow(envCfg.MaintenanceWindow)
transformed["workloads_config"] = flattenComposerEnvironmentConfigWorkloadsConfig(envCfg.WorkloadsConfig)
transformed["environment_size"] = envCfg.EnvironmentSize
return []interface{}{transformed}
Expand Down Expand Up @@ -1070,6 +1112,19 @@ func flattenComposerEnvironmentConfigEncryptionConfig(encryptionCfg *composer.En
return []interface{}{transformed}
}

func flattenComposerEnvironmentConfigMaintenanceWindow(maintenanceWindow *composer.MaintenanceWindow) interface{} {
if maintenanceWindow == nil {
return nil
}

transformed := make(map[string]interface{})
transformed["start_time"] = maintenanceWindow.StartTime
transformed["end_time"] = maintenanceWindow.EndTime
transformed["recurrence"] = maintenanceWindow.Recurrence

return []interface{}{transformed}
}

func flattenComposerEnvironmentConfigWorkloadsConfig(workloadsConfig *composer.WorkloadsConfig) interface{} {
if workloadsConfig == nil {
return nil
Expand Down Expand Up @@ -1250,6 +1305,11 @@ func expandComposerEnvironmentConfig(v interface{}, d *schema.ResourceData, conf
}
transformed.EncryptionConfig = transformedEncryptionConfig

transformedMaintenanceWindow, err := expandComposerEnvironmentConfigMaintenanceWindow(original["maintenance_window"], d, config)
if err != nil {
return nil, err
}
transformed.MaintenanceWindow = transformedMaintenanceWindow
transformedWorkloadsConfig, err := expandComposerEnvironmentConfigWorkloadsConfig(original["workloads_config"], d, config)
if err != nil {
return nil, err
Expand Down Expand Up @@ -1342,6 +1402,30 @@ func expandComposerEnvironmentConfigEncryptionConfig(v interface{}, d *schema.Re
return transformed, nil
}

func expandComposerEnvironmentConfigMaintenanceWindow(v interface{}, d *schema.ResourceData, config *Config) (*composer.MaintenanceWindow, error) {
l := v.([]interface{})
if len(l) == 0 {
return nil, nil
}
raw := l[0]
original := raw.(map[string]interface{})
transformed := &composer.MaintenanceWindow{}

if v, ok := original["start_time"]; ok {
transformed.StartTime = v.(string)
}

if v, ok := original["end_time"]; ok {
transformed.EndTime = v.(string)
}

if v, ok := original["recurrence"]; ok {
transformed.Recurrence = v.(string)
}

return transformed, nil
}

func expandComposerEnvironmentConfigWorkloadsConfig(v interface{}, d *schema.ResourceData, config *Config) (*composer.WorkloadsConfig, error) {
l := v.([]interface{})
if len(l) == 0 {
Expand Down
127 changes: 127 additions & 0 deletions google/resource_composer_environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,75 @@ func TestAccComposerEnvironment_withEncryptionConfig(t *testing.T) {
})
}

func TestAccComposerEnvironment_withMaintenanceWindow(t *testing.T) {
t.Parallel()

envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, randInt(t))
network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, randInt(t))
subnetwork := network + "-1"

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccComposerEnvironmentDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccComposerEnvironment_maintenanceWindow(envName, network, subnetwork),
},
{
ResourceName: "google_composer_environment.test",
ImportState: true,
ImportStateVerify: true,
},
// This is a terrible clean-up step in order to get destroy to succeed,
// due to dangling firewall rules left by the Composer Environment blocking network deletion.
// TODO(dzarmola): Remove this check if firewall rules bug gets fixed by Composer.
{
PlanOnly: true,
ExpectNonEmptyPlan: false,
Config: testAccComposerEnvironment_maintenanceWindow(envName, network, subnetwork),
Check: testAccCheckClearComposerEnvironmentFirewalls(t, network),
},
},
})
}

func TestAccComposerEnvironment_maintenanceWindowUpdate(t *testing.T) {
t.Parallel()

envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, randInt(t))
network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, randInt(t))
subnetwork := network + "-1"

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccComposerEnvironmentDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccComposerEnvironment_maintenanceWindow(envName, network, subnetwork),
},
{
Config: testAccComposerEnvironment_maintenanceWindowUpdate(envName, network, subnetwork),
},
{
ResourceName: "google_composer_environment.test",
ImportState: true,
ImportStateVerify: true,
},
// This is a terrible clean-up step in order to get destroy to succeed,
// due to dangling firewall rules left by the Composer Environment blocking network deletion.
// TODO: Remove this check if firewall rules bug gets fixed by Composer.
{
PlanOnly: true,
ExpectNonEmptyPlan: false,
Config: testAccComposerEnvironment_maintenanceWindowUpdate(envName, network, subnetwork),
Check: testAccCheckClearComposerEnvironmentFirewalls(t, network),
},
},
})
}

func TestAccComposerEnvironment_ComposerV2(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -934,6 +1003,64 @@ resource "google_compute_subnetwork" "test" {
`, pid, kmsKey, name, kmsKey, network, subnetwork)
}

func testAccComposerEnvironment_maintenanceWindow(envName, network, subnetwork string) string {
return fmt.Sprintf(`
resource "google_composer_environment" "test" {
name = "%s"
region = "us-central1"
config {
maintenance_window {
start_time = "2019-08-01T01:00:00Z"
end_time = "2019-08-01T07:00:00Z"
recurrence = "FREQ=WEEKLY;BYDAY=TU,WE"
}
}
}
resource "google_compute_network" "test" {
name = "%s"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "test" {
name = "%s"
ip_cidr_range = "10.2.0.0/16"
region = "us-central1"
network = google_compute_network.test.self_link
}
`, envName, network, subnetwork)
}

func testAccComposerEnvironment_maintenanceWindowUpdate(envName, network, subnetwork string) string {
return fmt.Sprintf(`
resource "google_composer_environment" "test" {
name = "%s"
region = "us-central1"
config {
maintenance_window {
start_time = "2019-08-01T01:00:00Z"
end_time = "2019-08-01T07:00:00Z"
recurrence = "FREQ=DAILY"
}
}
}
resource "google_compute_network" "test" {
name = "%s"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "test" {
name = "%s"
ip_cidr_range = "10.2.0.0/16"
region = "us-central1"
network = google_compute_network.test.self_link
}
`, envName, network, subnetwork)
}

func testAccComposerEnvironment_composerV2(envName, network, subnetwork string) string {
return fmt.Sprintf(`
data "google_composer_image_versions" "all" {
Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/composer_environment.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ The `config` block supports:
below.

* `maintenance_window` -
(Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html))
(Optional)
The configuration settings for Cloud Composer maintenance windows.

* `workloads_config` -
Expand Down

0 comments on commit 5e61d2b

Please sign in to comment.