Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 29 additions & 12 deletions dm/templates/gcs_bucket/gcs_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# limitations under the License.
""" This template creates a Google Cloud Storage bucket. """

from hashlib import sha1


def generate_config(context):
""" Entry point for the deployment resources. """
Expand Down Expand Up @@ -67,21 +69,36 @@ def generate_config(context):
resources.append(bucket)

# If IAM policy bindings are defined, apply these bindings.
# https://cloud.google.com/storage/docs/json_api/v1/buckets/setIamPolicy
storage_provider_type = 'gcp-types/storage-v1:storage.buckets.setIamPolicy'
storage_provider_type = 'gcp-types/storage-v1:virtual.buckets.iamMemberBinding'
bindings = properties.get('bindings', [])

if 'dependsOn' in properties:
dependson = { 'metadata': { 'dependsOn': properties['dependsOn'] } }
dependson_root = properties['dependsOn']
else:
dependson = {}
dependson_root = []

if bindings:
iam_policy = {
'name': '{}-iampolicy'.format(context.env['name']),
'action': (storage_provider_type),
'properties':
{
'bucket': '$(ref.{}.name)'.format(context.env['name']),
'project': project_id,
'bindings': bindings
for role in bindings:
for member in role['members']:
suffix = sha1('{}-{}'.format(role['role'], member)).hexdigest()[:10]
policy_get_name = '{}-{}'.format(context.env['name'], suffix)
policy_name = '{}-iampolicy'.format(policy_get_name)
iam_policy_resource = {
'name': policy_name,
# TODO - Virtual type documentation needed
'type': (storage_provider_type),
'properties':
{
'bucket': '$(ref.{}.name)'.format(context.env['name']),
'role': role['role'],
'member': member,
}
}
}
resources.append(iam_policy)
iam_policy_resource.update(dependson)
resources.append(iam_policy_resource)
dependson = { 'metadata': { 'dependsOn': [policy_name] + dependson_root } }

if properties.get('billing', {}).get('requesterPays'):
for resource in resources:
Expand Down
4 changes: 2 additions & 2 deletions dm/templates/gcs_bucket/gcs_bucket.py.schema
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ info:
APIs endpoints used by this template:
- gcp-types/storage-v1:buckets =>
https://cloud.google.com/storage/docs/json_api/v1/buckets
- gcp-types/storage-v1:storage.buckets.setIamPolicy =>
https://cloud.google.com/storage/docs/json_api/v1/buckets/setIamPolicy
- gcp-types/storage-v1:virtual.buckets.iamMemberBinding =>
TODO - Virtual type documentation needed

imports:
- path: gcs_bucket.py
Expand Down
96 changes: 96 additions & 0 deletions dm/templates/gcs_bucket/tests/integration/gcs_35_iam_bindings.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bats

source tests/helpers.bash

TEST_NAME=$(basename "${BATS_TEST_FILENAME}" | cut -d '.' -f 1)

export TEST_SERVICE_ACCOUNT="test-gcs-iam-sa-${RAND}"

########## HELPER FUNCTIONS ##########


# Create a random 10-char string and save it in a file.
RANDOM_FILE="/tmp/${CLOUD_FOUNDATION_ORGANIZATION_ID}-${TEST_NAME}.txt"
if [[ ! -e "${RANDOM_FILE}" ]]; then
RAND=$(head /dev/urandom | LC_ALL=C tr -dc a-z0-9 | head -c 10)
echo ${RAND} > "${RANDOM_FILE}"
fi

# Set variables based on the random string saved in the file.
# envsubst requires all variables used in the example/config to be exported.
if [[ -e "${RANDOM_FILE}" ]]; then
export RAND=$(cat "${RANDOM_FILE}")
DEPLOYMENT_NAME="${CLOUD_FOUNDATION_PROJECT_ID}-${TEST_NAME}-${RAND}"
# Replace underscores in the deployment name with dashes.
DEPLOYMENT_NAME=${DEPLOYMENT_NAME//_/-}
CONFIG=".${DEPLOYMENT_NAME}.yaml"
# Test specific variables:
export BUCKET_NAME="test-bucket-${RAND}"
export ROLE="roles/storage.objectViewer"
fi

########## HELPER FUNCTIONS ##########

function create_config() {
echo "Creating ${CONFIG}"
envsubst < ${BATS_TEST_DIRNAME}/${TEST_NAME}.yaml > "${CONFIG}"
}

function delete_config() {
echo "Deleting ${CONFIG}"
rm -f "${CONFIG}"
}

function setup() {
# Global setup; executed once per test file.
if [ ${BATS_TEST_NUMBER} -eq 1 ]; then
create_config
fi

# Per-test setup steps.
}

function teardown() {
# Global teardown; executed once per test file.
if [[ "$BATS_TEST_NUMBER" -eq "${#BATS_TEST_NAMES[@]}" ]]; then
delete_config
rm -f "${RANDOM_FILE}"
fi

# Per-test teardown steps.
}

########## TESTS ##########

@test "Creating deployment ${DEPLOYMENT_NAME} from ${CONFIG}" {
gcloud deployment-manager deployments create "${DEPLOYMENT_NAME}" \
--config "${CONFIG}" --project "${CLOUD_FOUNDATION_PROJECT_ID}"
[[ "$status" -eq 0 ]]
}

@test "Verify if Storage Bucket ${BUCKET_NAME} is created " {
res=$(gsutil ls | grep "${BUCKET_NAME}")
[[ "$status" -eq 0 ]]
[[ "$res" =~ "gs://${BUCKET_NAME}/" ]]
}

@test "Verify if SAs have role ${ROLE}" {
role=$(gsutil iam get "gs://${BUCKET_NAME}/" | grep role)
[[ "$status" -eq 0 ]]
[[ "$role" =~ "${ROLE}" ]]
}

@test "Verify if SAs are the members of this bucket" {
run gsutil iam get "gs://${BUCKET_NAME}/" | grep serviceAccount:${TEST_SERVICE_ACCOUNT}-.*@${CLOUD_FOUNDATION_PROJECT_ID}.iam.gserviceaccount.com
[[ "$status" -eq 0 ]]
}

@test "Deleting deployment ${DEPLOYMENT_NAME}" {
gcloud deployment-manager deployments delete "${DEPLOYMENT_NAME}" \
--project "${CLOUD_FOUNDATION_PROJECT_ID}" -q
[[ "$status" -eq 0 ]]

run gsutil ls
[[ "$status" -eq 0 ]]
[[ ! "$output" =~ "gs://${BUCKET_NAME}/" ]]
}
Loading