Skip to content

Commit

Permalink
Merge pull request #1864 from GoogleCloudPlatform/wiktorn-gcs-examples
Browse files Browse the repository at this point in the history
End to end tests for GCS
  • Loading branch information
wiktorn authored Nov 16, 2023
2 parents 03bf0b1 + ad14b31 commit 4343a57
Show file tree
Hide file tree
Showing 17 changed files with 121 additions and 66 deletions.
18 changes: 9 additions & 9 deletions fast/stages/2-security/README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions fast/stages/2-security/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ variable "kms_keys" {
iam = optional(map(list(string)), {})
iam_bindings = optional(map(object({
members = list(string)
role = string
condition = optional(object({
expression = string
title = string
Expand Down
89 changes: 65 additions & 24 deletions modules/gcs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,64 @@
```hcl
module "bucket" {
source = "./fabric/modules/gcs"
project_id = "myproject"
prefix = "test"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
versioning = true
labels = {
cost-center = "devops"
}
}
# tftest modules=1 resources=1 inventory=simple.yaml
# tftest modules=1 resources=1 inventory=simple.yaml e2e
```

### Example with Cloud KMS

```hcl
module "project" {
source = "./fabric/modules/project"
name = var.project_id
project_create = false
}
module "kms" {
source = "./fabric/modules/kms"
project_id = var.project_id
keyring = {
location = "europe" # location of the KMS must match location of the bucket
name = "test"
}
keys = {
bucket_key = {
iam_bindings = {
bucket_key_iam = {
members = ["serviceAccount:${module.project.service_accounts.robots.storage}"]
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
}
}
}
}
}
module "bucket" {
source = "./fabric/modules/gcs"
project_id = "myproject"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
encryption_key = "my-encryption-key"
encryption_key = module.kms.keys.bucket_key.id
location = "EU"
}
# tftest modules=1 resources=1 inventory=cmek.yaml
# tftest skip e2e
```

### Example with retention policy and logging

```hcl
module "bucket" {
source = "./fabric/modules/gcs"
project_id = "myproject"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
retention_policy = {
retention_period = 100
Expand All @@ -52,7 +81,8 @@ module "bucket" {
```hcl
module "bucket" {
source = "./fabric/modules/gcs"
project_id = "myproject"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
lifecycle_rules = {
lr-0 = {
Expand All @@ -66,69 +96,79 @@ module "bucket" {
}
}
}
# tftest modules=1 resources=1 inventory=lifecycle.yaml
# tftest modules=1 resources=1 inventory=lifecycle.yaml e2e
```

### Minimal example with GCS notifications

```hcl
module "project" {
source = "./fabric/modules/project"
name = var.project_id
project_create = false
}
module "bucket-gcs-notification" {
source = "./fabric/modules/gcs"
project_id = "myproject"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
notification_config = {
enabled = true
payload_format = "JSON_API_V1"
sa_email = "service-<project-number>@gs-project-accounts.iam.gserviceaccount.com" # GCS SA email must be passed or fetched from projects module.
sa_email = module.project.service_accounts.robots.storage
topic_name = "gcs-notification-topic"
event_types = ["OBJECT_FINALIZE"]
custom_attributes = {}
}
}
# tftest modules=1 resources=4 inventory=notification.yaml
# tftest skip e2e
```

### Example with object upload

```hcl
module "bucket" {
source = "./fabric/modules/gcs"
project_id = "myproject"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
objects_to_upload = {
sample-data = {
name = "example-file.csv"
source = "data/example-file.csv"
source = "assets/example-file.csv"
content_type = "text/csv"
}
}
}
# tftest modules=1 resources=2 inventory=object-upload.yaml
# tftest modules=1 resources=2 inventory=object-upload.yaml e2e
```

### Examples of IAM

```hcl
module "bucket" {
source = "./fabric/modules/gcs"
project_id = "myproject"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
iam = {
"roles/storage.admin" = ["group:[email protected]"]
"roles/storage.admin" = ["group:${var.group_email}"]
}
}
# tftest modules=1 resources=2 inventory=iam-authoritative.yaml
# tftest modules=1 resources=2 inventory=iam-authoritative.yaml e2e
```

```hcl
module "bucket" {
source = "./fabric/modules/gcs"
project_id = "myproject"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
iam_bindings = {
storage-admin-with-delegated_roles = {
role = "roles/storage.admin"
members = ["group:[email protected]"]
members = ["group:${var.group_email}"]
condition = {
title = "delegated-role-grants"
expression = format(
Expand All @@ -144,18 +184,19 @@ module "bucket" {
}
}
}
# tftest modules=1 resources=2 inventory=iam-bindings.yaml
# tftest modules=1 resources=2 inventory=iam-bindings.yaml e2e
```

```hcl
module "bucket" {
source = "./fabric/modules/gcs"
project_id = "myproject"
project_id = var.project_id
prefix = var.prefix
name = "my-bucket"
iam_bindings_additive = {
storage-admin-with-delegated_roles = {
role = "roles/storage.admin"
member = "group:[email protected]"
member = "group:${var.group_email}"
condition = {
title = "delegated-role-grants"
expression = format(
Expand All @@ -171,7 +212,7 @@ module "bucket" {
}
}
}
# tftest modules=1 resources=2 inventory=iam-bindings-additive.yaml
# tftest modules=1 resources=2 inventory=iam-bindings-additive.yaml e2e
```
<!-- BEGIN TFDOC -->
## Variables
Expand Down
6 changes: 3 additions & 3 deletions modules/kms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ module "kms" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [keyring](variables.tf#L64) | Keyring attributes. | <code title="object&#40;&#123;&#10; location &#61; string&#10; name &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> || |
| [project_id](variables.tf#L113) | Project id where the keyring will be created. | <code>string</code> || |
| [project_id](variables.tf#L114) | Project id where the keyring will be created. | <code>string</code> || |
| [iam](variables.tf#L17) | Keyring IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L39) | Keyring individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [import_job](variables.tf#L54) | Keyring import job attributes. | <code title="object&#40;&#123;&#10; id &#61; string&#10; import_method &#61; string&#10; protection_level &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [keyring_create](variables.tf#L72) | Set to false to manage keys and IAM bindings in an existing keyring. | <code>bool</code> | | <code>true</code> |
| [keys](variables.tf#L78) | Key names and base attributes. Set attributes to null if not needed. | <code title="map&#40;object&#40;&#123;&#10; rotation_period &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; purpose &#61; optional&#40;string, &#34;ENCRYPT_DECRYPT&#34;&#41;&#10; skip_initial_version_creation &#61; optional&#40;bool, false&#41;&#10; version_template &#61; optional&#40;object&#40;&#123;&#10; algorithm &#61; string&#10; protection_level &#61; optional&#40;string, &#34;SOFTWARE&#34;&#41;&#10; &#125;&#41;&#41;&#10;&#10;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings &#61; optional&#40;map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L118) | Tag bindings for this keyring, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [keys](variables.tf#L78) | Key names and base attributes. Set attributes to null if not needed. | <code title="map&#40;object&#40;&#123;&#10; rotation_period &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; purpose &#61; optional&#40;string, &#34;ENCRYPT_DECRYPT&#34;&#41;&#10; skip_initial_version_creation &#61; optional&#40;bool, false&#41;&#10; version_template &#61; optional&#40;object&#40;&#123;&#10; algorithm &#61; string&#10; protection_level &#61; optional&#40;string, &#34;SOFTWARE&#34;&#41;&#10; &#125;&#41;&#41;&#10;&#10;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings &#61; optional&#40;map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L119) | Tag bindings for this keyring, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |

## Outputs

Expand Down
1 change: 1 addition & 0 deletions modules/kms/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ variable "keys" {
iam = optional(map(list(string)), {})
iam_bindings = optional(map(object({
members = list(string)
role = string
condition = optional(object({
expression = string
title = string
Expand Down
5 changes: 2 additions & 3 deletions tests/examples/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def get_tftest_directive(s):


def pytest_generate_tests(metafunc, test_group='example',
filter_tests=lambda x: True):
filter_tests=lambda x: 'skip' not in x):
"""Find all README.md files and collect code examples tagged for testing."""
if test_group in metafunc.fixturenames:
readmes = FABRIC_ROOT.glob('**/README.md')
Expand Down Expand Up @@ -70,8 +70,7 @@ def pytest_generate_tests(metafunc, test_group='example',
index += 1
code = child.children[0].children
tftest_tag = get_tftest_directive(code)
if tftest_tag and ('skip' in tftest_tag or
not filter_tests(tftest_tag)):
if tftest_tag and not filter_tests(tftest_tag):
continue
if child.lang == 'hcl':
path = module.relative_to(FABRIC_ROOT)
Expand Down
12 changes: 12 additions & 0 deletions tests/examples_e2e/setup_module/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

locals {
prefix = "${var.prefix}-${var.timestamp}${var.suffix}"
jit_services = [
"storage.googleapis.com", # no permissions granted by default
]
services = [
# trimmed down list of services, to be extended as needed
"apigee.googleapis.com",
Expand Down Expand Up @@ -93,6 +96,15 @@ resource "google_kms_crypto_key" "key" {
rotation_period = "100000s"
}

resource "google_project_service_identity" "jit_si" {
for_each = toset(local.jit_services)
provider = google-beta
project = google_project.project.project_id
service = each.value
depends_on = [google_project_service.project_service]
}


resource "local_file" "terraform_tfvars" {
filename = "e2e_tests.tfvars"
content = templatefile("e2e_tests.tfvars.tftpl", {
Expand Down
1 change: 1 addition & 0 deletions tests/modules/gcs/assets/example-file.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
example,file
8 changes: 4 additions & 4 deletions tests/modules/gcs/examples/cmek.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

values:
module.bucket.google_storage_bucket.bucket:
encryption:
- default_kms_key_name: my-encryption-key
name: my-bucket
project: myproject
# encryption: __missing__
# - default_kms_key_name:
name: test-my-bucket
project: project-id

counts:
google_storage_bucket: 1
8 changes: 4 additions & 4 deletions tests/modules/gcs/examples/iam-authoritative.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ values:
lifecycle_rule: []
location: EU
logging: []
name: my-bucket
project: myproject
name: test-my-bucket
project: project-id
requester_pays: null
retention_policy: []
storage_class: MULTI_REGIONAL
Expand All @@ -36,10 +36,10 @@ values:
autoclass:
- enabled: false
module.bucket.google_storage_bucket_iam_binding.authoritative["roles/storage.admin"]:
bucket: my-bucket
bucket: test-my-bucket
condition: []
members:
- group:storage@example.com
- group:organization-admins@example.org
role: roles/storage.admin

counts:
Expand Down
8 changes: 4 additions & 4 deletions tests/modules/gcs/examples/iam-bindings-additive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ values:
lifecycle_rule: []
location: EU
logging: []
name: my-bucket
project: myproject
name: test-my-bucket
project: project-id
requester_pays: null
retention_policy: []
storage_class: MULTI_REGIONAL
Expand All @@ -36,12 +36,12 @@ values:
autoclass:
- enabled: false
module.bucket.google_storage_bucket_iam_member.bindings["storage-admin-with-delegated_roles"]:
bucket: my-bucket
bucket: test-my-bucket
condition:
- description: null
expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/storage.objectAdmin','roles/storage.objectViewer'])
title: delegated-role-grants
member: group:storage@example.com
member: group:organization-admins@example.org
role: roles/storage.admin

counts:
Expand Down
8 changes: 4 additions & 4 deletions tests/modules/gcs/examples/iam-bindings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ values:
lifecycle_rule: []
location: EU
logging: []
name: my-bucket
project: myproject
name: test-my-bucket
project: project-id
requester_pays: null
retention_policy: []
storage_class: MULTI_REGIONAL
Expand All @@ -36,13 +36,13 @@ values:
autoclass:
- enabled: false
module.bucket.google_storage_bucket_iam_binding.bindings["storage-admin-with-delegated_roles"]:
bucket: my-bucket
bucket: test-my-bucket
condition:
- description: null
expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/storage.objectAdmin','roles/storage.objectViewer'])
title: delegated-role-grants
members:
- group:storage@example.com
- group:organization-admins@example.org
role: roles/storage.admin

counts:
Expand Down
4 changes: 2 additions & 2 deletions tests/modules/gcs/examples/lifecycle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ values:
matches_suffix: []
noncurrent_time_before: ''
num_newer_versions: null
name: my-bucket
project: myproject
name: test-my-bucket
project: project-id

counts:
google_storage_bucket: 1
Expand Down
Loading

0 comments on commit 4343a57

Please sign in to comment.