Skip to content

Commit

Permalink
Add EncryptionConfig to Cloud Composer's EnvironmentConfig (#4310)
Browse files Browse the repository at this point in the history
* Adds a new section (EncryptionConfig) to Cloud Composer's EnvironmentConfig used for environment creation

* Add missing part of the config.

* Fixes to how the kms key is passed

* Fix testing call

* Fix nesting of ForceNew in encryption_config

Co-authored-by: Aleksandra Jarmolińska <[email protected]>
  • Loading branch information
dzarmola and Aleksandra Jarmolińska authored Feb 17, 2021
1 parent 08d4b6d commit 1c35689
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ var (
<% unless version == "ga" -%>
"config.0.database_config",
"config.0.web_server_config",
"config.0.encryption_config",
<% end -%>
}

Expand Down Expand Up @@ -433,6 +434,24 @@ func resourceComposerEnvironment() *schema.Resource {
},
},
},
"encryption_config": {
Type: schema.TypeList,
Optional: true,
Computed: true,
AtLeastOneOf: composerConfigKeys,
MaxItems: 1,
Description: `The encryption options for the Composer environment and its dependencies.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"kms_key_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `Optional. Customer-managed Encryption Key available through Google's Key Management Service. Cannot be updated.`,
},
},
},
},
<% end -%>
"airflow_uri": {
Type: schema.TypeString,
Expand Down Expand Up @@ -826,6 +845,7 @@ func flattenComposerEnvironmentConfig(envCfg *composer.EnvironmentConfig) interf
<% unless version == "ga" -%>
transformed["database_config"] = flattenComposerEnvironmentConfigDatabaseConfig(envCfg.DatabaseConfig)
transformed["web_server_config"] = flattenComposerEnvironmentConfigWebServerConfig(envCfg.WebServerConfig)
transformed["encryption_config"] = flattenComposerEnvironmentConfigEncryptionConfig(envCfg.EncryptionConfig)
<% end -%>

return []interface{}{transformed}
Expand Down Expand Up @@ -876,6 +896,17 @@ func flattenComposerEnvironmentConfigWebServerConfig(webServerCfg *composer.WebS

return []interface{}{transformed}
}

func flattenComposerEnvironmentConfigEncryptionConfig(encryptionCfg *composer.EncryptionConfig) interface{} {
if encryptionCfg == nil {
return nil
}

transformed := make(map[string]interface{})
transformed["kms_key_name"] = encryptionCfg.KmsKeyName

return []interface{}{transformed}
}
<% end -%>

func flattenComposerEnvironmentConfigPrivateEnvironmentConfig(envCfg *composer.PrivateEnvironmentConfig) interface{} {
Expand Down Expand Up @@ -1006,6 +1037,12 @@ func expandComposerEnvironmentConfig(v interface{}, d *schema.ResourceData, conf
}
transformed.WebServerConfig = transformedWebServerConfig

transformedEncryptionConfig, err := expandComposerEnvironmentConfigEncryptionConfig(original["encryption_config"], d, config)
if err != nil {
return nil, err
}
transformed.EncryptionConfig = transformedEncryptionConfig

<% end -%>
return transformed, nil
}
Expand Down Expand Up @@ -1080,6 +1117,23 @@ func expandComposerEnvironmentConfigWebServerConfig(v interface{}, d *schema.Res

<% end -%>

<% unless version == "ga" -%>
func expandComposerEnvironmentConfigEncryptionConfig(v interface{}, d *schema.ResourceData, config *Config) (*composer.EncryptionConfig, error) {
l := v.([]interface{})
if len(l) == 0 || l[0] == nil {
return nil, nil
}
raw := l[0]
original := raw.(map[string]interface{})

transformed := &composer.EncryptionConfig{}
transformed.KmsKeyName = original["kms_key_name"].(string)

return transformed, nil
}

<% end -%>

func expandComposerEnvironmentConfigPrivateEnvironmentConfig(v interface{}, d *schema.ResourceData, config *Config) (*composer.PrivateEnvironmentConfig, error) {
l := v.([]interface{})
if len(l) == 0 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,41 @@ func TestAccComposerEnvironment_withWebServerConfig(t *testing.T) {
},
})
}

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

kms := BootstrapKMSKeyInLocation(t, "us-central1")
pid := getTestProjectFromEnv()
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_encryptionCfg(pid, envName, kms.CryptoKey.Name, 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_encryptionCfg(pid, envName, kms.CryptoKey.Name, network, subnetwork),
Check: testAccCheckClearComposerEnvironmentFirewalls(t, network),
},
},
})
}
<% end -%>
// Checks behavior of node config, including dependencies on Compute resources.
func TestAccComposerEnvironment_withNodeConfig(t *testing.T) {
Expand Down Expand Up @@ -735,6 +770,72 @@ resource "google_compute_subnetwork" "test" {
`, name, network, subnetwork)
}

func testAccComposerEnvironment_encryptionCfg(pid, name, kmsKey, network, subnetwork string) string {
return fmt.Sprintf(`
data "google_project" "project" {
project_id = "%s"
}
resource "google_project_iam_member" "kms-project-binding1" {
project = data.google_project.project.project_id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@cloudcomposer-accounts.iam.gserviceaccount.com"
}
resource "google_project_iam_member" "kms-project-binding2" {
project = data.google_project.project.project_id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@compute-system.iam.gserviceaccount.com"
}
resource "google_project_iam_member" "kms-project-binding3" {
project = data.google_project.project.project_id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com"
}
resource "google_project_iam_member" "kms-project-binding4" {
project = data.google_project.project.project_id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com"
}
resource "google_project_iam_member" "kms-project-binding5" {
project = data.google_project.project.project_id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-pubsub.iam.gserviceaccount.com"
}
resource "google_kms_crypto_key_iam_member" "iam" {
crypto_key_id = "%s"
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@gs-project-accounts.iam.gserviceaccount.com"
}
resource "google_composer_environment" "test" {
depends_on = [google_project_iam_member.kms-project-binding1, google_project_iam_member.kms-project-binding2,
google_project_iam_member.kms-project-binding3, google_project_iam_member.kms-project-binding4,
google_project_iam_member.kms-project-binding5, google_kms_crypto_key_iam_member.iam]
name = "%s"
region = "us-central1"
config {
node_config {
network = google_compute_network.test.self_link
subnetwork = google_compute_subnetwork.test.self_link
zone = "us-central1-a"
}
encryption_config {
kms_key_name = "%s"
}
}
}
// use a separate network to avoid conflicts with other tests running in parallel
// that use the default network/subnet
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
}
`, pid, kmsKey, name, kmsKey, network, subnetwork)
}
<% end -%>
func testAccComposerEnvironment_update(name, network, subnetwork string) string {
return fmt.Sprintf(`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ The `config` block supports:
(Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html))
The configuration settings for the Airflow web server App Engine instance.

* `encryption_config` -
(Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html))
The encryption options for the Cloud Composer environment and its dependencies.

The `node_config` block supports:

* `zone` -
Expand Down Expand Up @@ -390,6 +394,13 @@ The `web_server_config` block supports:
Value custom is returned only in response, if Airflow web server parameters were
manually changed to a non-standard values.

The `encryption_config` block supports:

* `kms_key_name` -
(Required)
Customer-managed Encryption Key available through Google's Key Management Service. It must
be the fully qualified resource name,
i.e. projects/project-id/locations/location/keyRings/keyring/cryptoKeys/key. Cannot be updated.


## Attributes Reference
Expand Down

0 comments on commit 1c35689

Please sign in to comment.