diff --git a/.ci/cloudbuild.lint.yaml b/.ci/cloudbuild.lint.yaml index 1c6dcf16..2a7ed16f 100644 --- a/.ci/cloudbuild.lint.yaml +++ b/.ci/cloudbuild.lint.yaml @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + steps: - name: "python:3.6-alpine" id: "boilerplate" diff --git a/.ci/cloudbuild.test.yaml b/.ci/cloudbuild.test.yaml new file mode 100644 index 00000000..831a2685 --- /dev/null +++ b/.ci/cloudbuild.test.yaml @@ -0,0 +1,43 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +steps: + - name: python:3.6-alpine + id: prepare + entrypoint: sh + args: + - -c + - | + python -m pip install --user -r tests/requirements.txt && + wget https://releases.hashicorp.com/terraform/${_TERRAFORM_VERSION}/terraform_${_TERRAFORM_VERSION}_linux_amd64.zip && + unzip terraform_${_TERRAFORM_VERSION}_linux_amd64.zip -d /builder/home/.local/bin && + rm terraform_${_TERRAFORM_VERSION}_linux_amd64.zip && + chmod 755 /builder/home/.local/bin/terraform + - name: python:3.6-alpine + id: test + entrypoint: python + args: + - -m + - pytest + - -v + - tests + env: + - PATH=/usr/local/bin:/usr/bin:/bin:/builder/home/.local/bin + +substitutions: + _TERRAFORM_VERSION: 0.12.8 + +tags: + - "ci" + - "test" diff --git a/.gitignore b/.gitignore index 30f62292..c784f263 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ **/.terraform **/terraform.tfstate* **/terraform.tfvars +!tests/**/terraform.tfvars +**/__pycache__ +**/.pytest_cache .idea .vscode +backend.tf backend-config.hcl credentials.json key.json diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..35dff2e8 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/tests/organization-bootstrap/__init__.py b/tests/organization-bootstrap/__init__.py new file mode 100644 index 00000000..35dff2e8 --- /dev/null +++ b/tests/organization-bootstrap/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/tests/organization-bootstrap/environments/__init__.py b/tests/organization-bootstrap/environments/__init__.py new file mode 100644 index 00000000..35dff2e8 --- /dev/null +++ b/tests/organization-bootstrap/environments/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/tests/organization-bootstrap/environments/conftest.py b/tests/organization-bootstrap/environments/conftest.py new file mode 100644 index 00000000..d00369d6 --- /dev/null +++ b/tests/organization-bootstrap/environments/conftest.py @@ -0,0 +1,34 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"Plan fixture." + +import os + +import pytest +import tftest + + +_ABSPATH = os.path.dirname(os.path.abspath(__file__)).split(os.path.sep) +_TFDIR = os.path.sep.join(_ABSPATH[-2:]) + + +# TODO(ludoo): generalize and put in top-level package + +@pytest.fixture(scope='session') +def plan(): + tf = tftest.TerraformTest(_TFDIR, os.path.sep.join(_ABSPATH[:-3]), + os.environ.get('TERRAFORM', 'terraform')) + tf.setup(extra_files=['tests/{}/terraform.tfvars'.format(_TFDIR)]) + return tf.plan_out(parsed=True) diff --git a/tests/organization-bootstrap/environments/terraform.tfvars b/tests/organization-bootstrap/environments/terraform.tfvars new file mode 100644 index 00000000..acc2a16b --- /dev/null +++ b/tests/organization-bootstrap/environments/terraform.tfvars @@ -0,0 +1,7 @@ +billing_account_id = "012345-ABCDEF-012345" +environments = ["dev", "test"] +generate_service_account_keys = true +grant_xpn_roles = true +organization_id = "012345678919" +prefix = "fabric-org-env-3" +root_node = "folders/0123456789" diff --git a/tests/organization-bootstrap/environments/test_outputs.py b/tests/organization-bootstrap/environments/test_outputs.py new file mode 100644 index 00000000..64f3ca39 --- /dev/null +++ b/tests/organization-bootstrap/environments/test_outputs.py @@ -0,0 +1,47 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"Test root module outputs." + + +def test_project_ids(plan): + "Project ids should use prefix and match expected values." + prefix = plan.variables['prefix'] + assert plan.outputs['audit_logs_project'] == prefix + '-audit' + assert plan.outputs['shared_resources_project'] == prefix + '-shared' + assert plan.outputs['terraform_project'] == prefix + '-terraform' + + +def test_bucket_names(plan): + "GCS bucket names should use prefix and location and match expected values." + location = plan.variables['gcs_location'].lower() + prefix = plan.variables['prefix'] + bootstrap_bucket = plan.outputs['bootstrap_tf_gcs_bucket'] + assert bootstrap_bucket.startswith(prefix) + assert bootstrap_bucket.endswith('tf-bootstrap') + assert '-%s-' % location in bootstrap_bucket + + +def test_environment_buckets(plan): + "One GCS bucket should be created for each environment." + buckets = plan.outputs['environment_tf_gcs_buckets'] + for environment in plan.variables['environments']: + assert environment in buckets + assert buckets[environment].endswith(environment) + + +def test_bq_dataset(plan): + "Bigquery dataset should be named after the first environment." + assert plan.outputs['audit_logs_bq_dataset'].endswith( + plan.variables['environments'][0]) diff --git a/tests/organization-bootstrap/environments/test_projects.py b/tests/organization-bootstrap/environments/test_projects.py new file mode 100644 index 00000000..cee01fc1 --- /dev/null +++ b/tests/organization-bootstrap/environments/test_projects.py @@ -0,0 +1,43 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"Test project creation in root module." + + +import pytest + + +@pytest.fixture(scope='module') +def project_modules(plan): + names = ['module.project-%s' % + name for name in ('audit', 'shared-resources', 'tf')] + return dict((name, plan.modules[name]) for name in names) + + +def test_project_resource(plan, project_modules): + "Project resource attributes must match variables." + root_node = plan.variables['root_node'].split('/')[1] + billing_account = plan.variables['billing_account_id'] + for name, mod in project_modules.items(): + resource = mod['%s.google_project.project' % name] + assert resource['values']['folder_id'] == root_node + assert resource['values']['billing_account'] == billing_account + + +def test_project_services(plan, project_modules): + "Project service resource must enable APIs specified in the variable." + services = plan.variables['project_services'] + for name, mod in project_modules.items(): + resource = mod['%s.google_project_services.services[0]' % name] + assert resource['values']['services'] == services diff --git a/tests/organization-bootstrap/environments/test_service_accounts.py b/tests/organization-bootstrap/environments/test_service_accounts.py new file mode 100644 index 00000000..694e145b --- /dev/null +++ b/tests/organization-bootstrap/environments/test_service_accounts.py @@ -0,0 +1,33 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"Test service account creation in root module." + + +import pytest + + +@pytest.fixture(scope='module') +def mod(plan): + return plan.modules['module.service-accounts-tf-environments'] + + +def test_accounts(plan, mod): + "One service account per environment should be created." + environments = plan.variables['environments'] + prefix = plan.variables['prefix'] + resources = [v for k, v in mod.items() if '.google_service_account.' in k] + assert len(resources) == len(environments) + assert sorted([res['values']['account_id'] for res in resources]) == sorted([ + '%s-%s' % (prefix, env) for env in environments]) diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 00000000..210e8fcc --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,3 @@ +pytest>=4.3.1 +pytest-tldr>=0.2.1 +tftest>=0.6.4 \ No newline at end of file