Merge pull request #1029 from GoogleCloudPlatform/jccb/tests-revamp
Testing framework revamp
This commit is contained in:
92
tests/collectors.py
Normal file
92
tests/collectors.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
"""Pytest plugin to discover tests specified in YAML files.
|
||||
|
||||
This plugin uses the pytest_collect_file hook to collect all files
|
||||
matching tftest*.yaml and runs plan_validate for each test found.
|
||||
See FabricTestFile for details on the file structure.
|
||||
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from .fixtures import plan_summary, plan_validator
|
||||
|
||||
|
||||
class FabricTestFile(pytest.File):
|
||||
|
||||
def collect(self):
|
||||
"""Read yaml test spec and yield test items for each test definition.
|
||||
|
||||
The test spec should contain a `module` key with the path of the
|
||||
terraform module to test, relative to the root of the repository
|
||||
|
||||
Tests are defined within the top-level `tests` key, and should
|
||||
have the following structure:
|
||||
|
||||
test-name:
|
||||
tfvars:
|
||||
- tfvars1.tfvars
|
||||
- tfvars2.tfvars
|
||||
inventory:
|
||||
- inventory1.yaml
|
||||
- inventory2.yaml
|
||||
|
||||
All paths specifications are relative to the location of the test
|
||||
spec. The inventory key is optional, if omitted, the inventory
|
||||
will be taken from the file test-name.yaml
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
raw = yaml.safe_load(self.path.open())
|
||||
module = raw.pop('module')
|
||||
except (IOError, OSError, yaml.YAMLError) as e:
|
||||
raise Exception(f'cannot read test spec {self.path}: {e}')
|
||||
except KeyError as e:
|
||||
raise Exception(f'`module` key not found in {self.path}: {e}')
|
||||
common = raw.pop('common_tfvars', [])
|
||||
for test_name, spec in raw.get('tests', {}).items():
|
||||
spec = {} if spec is None else spec
|
||||
inventories = spec.get('inventory', [f'{test_name}.yaml'])
|
||||
tfvars = common + [f'{test_name}.tfvars'] + spec.get('tfvars', [])
|
||||
for i in inventories:
|
||||
name = test_name
|
||||
if isinstance(inventories, list) and len(inventories) > 1:
|
||||
name = f'{test_name}[{i}]'
|
||||
yield FabricTestItem.from_parent(self, name=name, module=module,
|
||||
inventory=[i], tfvars=tfvars)
|
||||
|
||||
|
||||
class FabricTestItem(pytest.Item):
|
||||
|
||||
def __init__(self, name, parent, module, inventory, tfvars):
|
||||
super().__init__(name, parent)
|
||||
self.module = module
|
||||
self.inventory = inventory
|
||||
self.tfvars = tfvars
|
||||
|
||||
def runtest(self):
|
||||
s = plan_validator(self.module, self.inventory, self.parent.path.parent,
|
||||
self.tfvars)
|
||||
|
||||
def reportinfo(self):
|
||||
return self.path, None, self.name
|
||||
|
||||
|
||||
def pytest_collect_file(parent, file_path):
|
||||
'Collect tftest*.yaml files and run plan_validator from them.'
|
||||
if file_path.suffix == '.yaml' and file_path.name.startswith('tftest'):
|
||||
return FabricTestFile.from_parent(parent, path=file_path)
|
||||
@@ -11,144 +11,12 @@
|
||||
# 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.
|
||||
"Shared fixtures"
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
'Pytest configuration.'
|
||||
|
||||
import pytest
|
||||
import tftest
|
||||
|
||||
BASEDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def _plan_runner():
|
||||
"Returns a function to run Terraform plan on a fixture."
|
||||
|
||||
def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
|
||||
targets=None, refresh=True, tmpdir=True, **tf_vars):
|
||||
"Runs Terraform plan and returns parsed output."
|
||||
if fixture_path is None:
|
||||
# find out the fixture directory from the caller's directory
|
||||
caller = inspect.stack()[2]
|
||||
fixture_path = os.path.join(os.path.dirname(caller.filename), "fixture")
|
||||
|
||||
fixture_parent = os.path.dirname(fixture_path)
|
||||
fixture_prefix = os.path.basename(fixture_path) + "_"
|
||||
with tempfile.TemporaryDirectory(prefix=fixture_prefix,
|
||||
dir=fixture_parent) as tmp_path:
|
||||
# copy fixture to a temporary directory so we can execute
|
||||
# multiple tests in parallel
|
||||
if tmpdir:
|
||||
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
|
||||
tf = tftest.TerraformTest(tmp_path if tmpdir else fixture_path, BASEDIR,
|
||||
os.environ.get('TERRAFORM', 'terraform'))
|
||||
tf.setup(extra_files=extra_files, upgrade=True)
|
||||
plan = tf.plan(output=True, refresh=refresh, tf_var_file=tf_var_file,
|
||||
tf_vars=tf_vars, targets=targets)
|
||||
return plan
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def plan_runner(_plan_runner):
|
||||
"Returns a function to run Terraform plan on a module fixture."
|
||||
|
||||
def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
|
||||
targets=None, **tf_vars):
|
||||
"Runs Terraform plan and returns plan and module resources."
|
||||
plan = _plan_runner(fixture_path, extra_files=extra_files,
|
||||
tf_var_file=tf_var_file, targets=targets, **tf_vars)
|
||||
# skip the fixture
|
||||
root_module = plan.root_module['child_modules'][0]
|
||||
return plan, root_module['resources']
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def e2e_plan_runner(_plan_runner):
|
||||
"Returns a function to run Terraform plan on an end-to-end fixture."
|
||||
|
||||
def run_plan(fixture_path=None, tf_var_file=None, targets=None,
|
||||
refresh=True, include_bare_resources=False, **tf_vars):
|
||||
"Runs Terraform plan on an end-to-end module using defaults, returns data."
|
||||
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
|
||||
refresh=refresh, **tf_vars)
|
||||
# skip the fixture
|
||||
root_module = plan.root_module['child_modules'][0]
|
||||
modules = dict((mod['address'], mod['resources'])
|
||||
for mod in root_module['child_modules'])
|
||||
resources = [r for m in modules.values() for r in m]
|
||||
if include_bare_resources:
|
||||
bare_resources = root_module['resources']
|
||||
resources.extend(bare_resources)
|
||||
return modules, resources
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def recursive_e2e_plan_runner(_plan_runner):
|
||||
"""Plan runner for end-to-end root module, returns total number of
|
||||
(nested) modules and resources"""
|
||||
|
||||
def walk_plan(node, modules, resources):
|
||||
# TODO(jccb): this would be better with node.get() but
|
||||
# TerraformPlanOutput objects don't have it
|
||||
new_modules = node.get('child_modules', [])
|
||||
resources += node.get('resources', [])
|
||||
modules += new_modules
|
||||
for module in new_modules:
|
||||
walk_plan(module, modules, resources)
|
||||
|
||||
def run_plan(fixture_path=None, tf_var_file=None, targets=None, refresh=True,
|
||||
include_bare_resources=False, compute_sums=True, tmpdir=True,
|
||||
**tf_vars):
|
||||
"Runs Terraform plan on a root module using defaults, returns data."
|
||||
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
|
||||
refresh=refresh, tmpdir=tmpdir, **tf_vars)
|
||||
modules = []
|
||||
resources = []
|
||||
walk_plan(plan.root_module, modules, resources)
|
||||
return len(modules), len(resources)
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def apply_runner():
|
||||
"Returns a function to run Terraform apply on a fixture."
|
||||
|
||||
def run_apply(fixture_path=None, **tf_vars):
|
||||
"Runs Terraform plan and returns parsed output."
|
||||
if fixture_path is None:
|
||||
# find out the fixture directory from the caller's directory
|
||||
caller = inspect.stack()[1]
|
||||
fixture_path = os.path.join(os.path.dirname(caller.filename), "fixture")
|
||||
|
||||
fixture_parent = os.path.dirname(fixture_path)
|
||||
fixture_prefix = os.path.basename(fixture_path) + "_"
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix=fixture_prefix,
|
||||
dir=fixture_parent) as tmp_path:
|
||||
# copy fixture to a temporary directory so we can execute
|
||||
# multiple tests in parallel
|
||||
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
|
||||
tf = tftest.TerraformTest(tmp_path, BASEDIR,
|
||||
os.environ.get('TERRAFORM', 'terraform'))
|
||||
tf.setup(upgrade=True)
|
||||
apply = tf.apply(tf_vars=tf_vars)
|
||||
output = tf.output(json_format=True)
|
||||
return apply, output
|
||||
|
||||
return run_apply
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def basedir():
|
||||
return BASEDIR
|
||||
pytest_plugins = (
|
||||
'tests.fixtures',
|
||||
'tests.legacy_fixtures',
|
||||
'tests.collectors',
|
||||
)
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
module "stage" {
|
||||
source = "../../../../../fast/stages/00-bootstrap"
|
||||
prefix = "fast"
|
||||
organization = {
|
||||
domain = "fast.example.com"
|
||||
id = 123456789012
|
||||
customer_id = "C00000000"
|
||||
}
|
||||
billing_account = {
|
||||
id = "000000-111111-222222"
|
||||
organization_id = 123456789012
|
||||
}
|
||||
}
|
||||
11
tests/fast/stages/s00_bootstrap/simple.tfvars
Normal file
11
tests/fast/stages/s00_bootstrap/simple.tfvars
Normal file
@@ -0,0 +1,11 @@
|
||||
organization = {
|
||||
domain = "fast.example.com"
|
||||
id = 123456789012
|
||||
customer_id = "C00000000"
|
||||
}
|
||||
billing_account = {
|
||||
id = "000000-111111-222222"
|
||||
organization_id = 123456789012
|
||||
}
|
||||
prefix = "fast"
|
||||
outputs_location = "/fast-config"
|
||||
49
tests/fast/stages/s00_bootstrap/simple.yaml
Normal file
49
tests/fast/stages/s00_bootstrap/simple.yaml
Normal file
@@ -0,0 +1,49 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
counts:
|
||||
google_bigquery_dataset: 2
|
||||
google_bigquery_dataset_iam_member: 2
|
||||
google_bigquery_default_service_account: 3
|
||||
google_logging_organization_sink: 2
|
||||
google_organization_iam_binding: 19
|
||||
google_organization_iam_custom_role: 2
|
||||
google_organization_iam_member: 16
|
||||
google_project: 3
|
||||
google_project_iam_binding: 9
|
||||
google_project_iam_member: 1
|
||||
google_project_service: 29
|
||||
google_project_service_identity: 2
|
||||
google_service_account: 3
|
||||
google_service_account_iam_binding: 3
|
||||
google_storage_bucket: 4
|
||||
google_storage_bucket_iam_binding: 2
|
||||
google_storage_bucket_iam_member: 3
|
||||
google_storage_bucket_object: 5
|
||||
google_storage_project_service_account: 3
|
||||
local_file: 5
|
||||
|
||||
outputs:
|
||||
custom_roles:
|
||||
organization_iam_admin: organizations/123456789012/roles/organizationIamAdmin
|
||||
service_project_network_admin: organizations/123456789012/roles/serviceProjectNetworkAdmin
|
||||
outputs_bucket: fast-prod-iac-core-outputs-0
|
||||
project_ids:
|
||||
automation: fast-prod-iac-core-0
|
||||
billing-export: fast-prod-billing-exp-0
|
||||
log-export: fast-prod-audit-logs-0
|
||||
service_accounts:
|
||||
bootstrap: fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com
|
||||
cicd: fast-prod-cicd-0@fast-prod-iac-core-0.iam.gserviceaccount.com
|
||||
resman: fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com
|
||||
33
tests/fast/stages/s00_bootstrap/simple_projects.yaml
Normal file
33
tests/fast/stages/s00_bootstrap/simple_projects.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
module.automation-project.google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 000000-111111-222222
|
||||
name: fast-prod-iac-core-0
|
||||
org_id: '123456789012'
|
||||
project_id: fast-prod-iac-core-0
|
||||
module.billing-export-project[0].google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 000000-111111-222222
|
||||
name: fast-prod-billing-exp-0
|
||||
org_id: '123456789012'
|
||||
project_id: fast-prod-billing-exp-0
|
||||
module.log-export-project.google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 000000-111111-222222
|
||||
name: fast-prod-audit-logs-0
|
||||
org_id: '123456789012'
|
||||
project_id: fast-prod-audit-logs-0
|
||||
27
tests/fast/stages/s00_bootstrap/simple_sas.yaml
Normal file
27
tests/fast/stages/s00_bootstrap/simple_sas.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
module.automation-tf-bootstrap-sa.google_service_account.service_account[0]:
|
||||
account_id: fast-prod-bootstrap-0
|
||||
display_name: Terraform organization bootstrap service account.
|
||||
project: fast-prod-iac-core-0
|
||||
module.automation-tf-cicd-provisioning-sa.google_service_account.service_account[0]:
|
||||
account_id: fast-prod-cicd-0
|
||||
display_name: Terraform stage 1 CICD service account.
|
||||
project: fast-prod-iac-core-0
|
||||
module.automation-tf-resman-sa.google_service_account.service_account[0]:
|
||||
account_id: fast-prod-resman-0
|
||||
display_name: Terraform stage 1 resman service account.
|
||||
project: fast-prod-iac-core-0
|
||||
@@ -1,33 +0,0 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
# _RESOURCE_COUNT = {
|
||||
# 'module.organization': 28,
|
||||
# 'module.automation-project': 23,
|
||||
# 'module.automation-tf-bootstrap-gcs': 1,
|
||||
# 'module.automation-tf-bootstrap-sa': 1,
|
||||
# 'module.automation-tf-resman-gcs': 2,
|
||||
# 'module.automation-tf-resman-sa': 1,
|
||||
# 'module.billing-export-dataset': 1,
|
||||
# 'module.billing-export-project': 7,
|
||||
# 'module.log-export-dataset': 1,
|
||||
# 'module.log-export-project': 7,
|
||||
# }
|
||||
|
||||
|
||||
def test_counts(recursive_e2e_plan_runner):
|
||||
"Test stage."
|
||||
# TODO: to re-enable per-module resource count check print _, then test
|
||||
num_modules, num_resources = recursive_e2e_plan_runner()
|
||||
assert num_modules > 0 and num_resources > 0
|
||||
12
tests/fast/stages/s00_bootstrap/tftest.yaml
Normal file
12
tests/fast/stages/s00_bootstrap/tftest.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
# skip boilerplate check
|
||||
|
||||
module: fast/stages/00-bootstrap
|
||||
|
||||
tests:
|
||||
simple:
|
||||
tfvars:
|
||||
- simple.tfvars
|
||||
inventory:
|
||||
- simple.yaml
|
||||
- simple_projects.yaml
|
||||
- simple_sas.yaml
|
||||
235
tests/fixtures.py
Normal file
235
tests/fixtures.py
Normal file
@@ -0,0 +1,235 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
"""Common fixtures."""
|
||||
|
||||
import collections
|
||||
import contextlib
|
||||
import itertools
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import tftest
|
||||
import yaml
|
||||
|
||||
PlanSummary = collections.namedtuple('PlanSummary', 'values counts outputs')
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _prepare_root_module(path):
|
||||
"""Context manager to prepare a terraform module to be tested.
|
||||
|
||||
If the TFTEST_COPY environment variable is set, `path` is copied to
|
||||
a temporary directory and a few terraform files (e.g.
|
||||
terraform.tfvars) are delete to ensure a clean test environment.
|
||||
Otherwise, `path` is simply returned untouched.
|
||||
"""
|
||||
if os.environ.get('TFTEST_COPY'):
|
||||
# if the TFTEST_COPY is set, create temp dir and copy the root
|
||||
# module there
|
||||
with tempfile.TemporaryDirectory(dir=path.parent) as tmp_path:
|
||||
tmp_path = Path(tmp_path)
|
||||
|
||||
# if we're copying the module, we might as well ignore files and
|
||||
# directories that are automatically read by terraform. Useful
|
||||
# to avoid surprises if, for example, you have an active fast
|
||||
# deployment with links to configs)
|
||||
ignore_patterns = shutil.ignore_patterns('*.auto.tfvars',
|
||||
'*.auto.tfvars.json',
|
||||
'terraform.tfstate*',
|
||||
'terraform.tfvars', '.terraform')
|
||||
|
||||
shutil.copytree(path, tmp_path, dirs_exist_ok=True,
|
||||
ignore=ignore_patterns)
|
||||
|
||||
yield tmp_path
|
||||
else:
|
||||
# if TFTEST_COPY is not set, just return the same path
|
||||
yield path
|
||||
|
||||
|
||||
def plan_summary(module_path, basedir, tf_var_files=None, **tf_vars):
|
||||
"""
|
||||
Run a Terraform plan on the module located at `module_path`.
|
||||
|
||||
- module_path: terraform root module to run. Can be an absolute
|
||||
path or relative to the root of the repository
|
||||
|
||||
- basedir: directory root to use for relative paths in
|
||||
tf_var_files.
|
||||
|
||||
- tf_var_files: set of terraform variable files (tfvars) to pass
|
||||
in to terraform
|
||||
|
||||
Returns a PlanSummary object containing 3 attributes:
|
||||
- values: dictionary where the keys are terraform plan addresses
|
||||
and values are the JSON representation (converted to python
|
||||
types) of the attribute values of the resource.
|
||||
|
||||
- counts: dictionary where the keys are the terraform resource
|
||||
types and the values are the number of times that type appears
|
||||
in the plan
|
||||
|
||||
- outputs: dictionary of the modules outputs that can be
|
||||
determined at plan type.
|
||||
|
||||
Consult [1] for mode details on the structure of values and outputs
|
||||
|
||||
[1] https://developer.hashicorp.com/terraform/internals/json-format
|
||||
"""
|
||||
# make the module_path relative to the root of the repo while still
|
||||
# supporting absolute paths
|
||||
module_path = Path(__file__).parents[1] / module_path
|
||||
with _prepare_root_module(module_path) as test_path:
|
||||
binary = os.environ.get('TERRAFORM', 'terraform')
|
||||
tf = tftest.TerraformTest(test_path, binary=binary)
|
||||
tf.setup(upgrade=True)
|
||||
tf_var_files = [(basedir / x).resolve() for x in tf_var_files or []]
|
||||
plan = tf.plan(output=True, tf_var_file=tf_var_files, tf_vars=tf_vars)
|
||||
|
||||
# compute resource type counts and address->values map
|
||||
values = {}
|
||||
counts = collections.defaultdict(int)
|
||||
q = collections.deque([plan.root_module])
|
||||
while q:
|
||||
e = q.popleft()
|
||||
|
||||
if 'type' in e:
|
||||
counts[e['type']] += 1
|
||||
if 'values' in e:
|
||||
values[e['address']] = e['values']
|
||||
|
||||
for x in e.get('resources', []):
|
||||
q.append(x)
|
||||
for x in e.get('child_modules', []):
|
||||
q.append(x)
|
||||
|
||||
# extract planned outputs
|
||||
outputs = plan.get('planned_values', {}).get('outputs', {})
|
||||
|
||||
return PlanSummary(values, dict(counts), outputs)
|
||||
|
||||
|
||||
@pytest.fixture(name='plan_summary')
|
||||
def plan_summary_fixture(request):
|
||||
"""Return a function to generate a PlanSummary.
|
||||
|
||||
In the returned function `basedir` becomes optional and it defaults
|
||||
to the directory of the calling test
|
||||
"""
|
||||
|
||||
def inner(module_path, basedir=None, tf_var_files=None, **tf_vars):
|
||||
if basedir is None:
|
||||
basedir = Path(request.fspath).parent
|
||||
return plan_summary(module_path=module_path, basedir=basedir,
|
||||
tf_var_files=tf_var_files, **tf_vars)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def plan_validator(module_path, inventory_paths, basedir, tf_var_files=None,
|
||||
**tf_vars):
|
||||
summary = plan_summary(module_path=module_path, tf_var_files=tf_var_files,
|
||||
basedir=basedir, **tf_vars)
|
||||
|
||||
# allow single single string for inventory_paths
|
||||
if not isinstance(inventory_paths, list):
|
||||
inventory_paths = [inventory_paths]
|
||||
|
||||
for path in inventory_paths:
|
||||
# allow tfvars and inventory to be relative to the caller
|
||||
path = basedir / path
|
||||
try:
|
||||
inventory = yaml.safe_load(path.read_text())
|
||||
except (IOError, OSError, yaml.YAMLError) as e:
|
||||
raise Exception(f'cannot read test inventory {path}: {e}')
|
||||
|
||||
# don't fail if the inventory is empty
|
||||
inventory = inventory or {}
|
||||
|
||||
# If you add additional asserts to this function:
|
||||
# - put the values coming from the plan on the left side of
|
||||
# any comparison operators
|
||||
# - put the values coming from user's inventory the right
|
||||
# side of any comparison operators.
|
||||
# - include a descriptive error message to the assert
|
||||
|
||||
# for values:
|
||||
# - verify each address in the user's inventory exists in the plan
|
||||
# - for those address that exist on both the user's inventory and
|
||||
# the plan output, ensure the set of keys on the inventory are a
|
||||
# subset of the keys in the plan, and compare their values by
|
||||
# equality
|
||||
if 'values' in inventory:
|
||||
expected_values = inventory['values']
|
||||
for address, expected_value in expected_values.items():
|
||||
assert address in summary.values, \
|
||||
f'{address} is not a valid address in the plan'
|
||||
for k, v in expected_value.items():
|
||||
assert k in summary.values[address], \
|
||||
f'{k} not found at {address}'
|
||||
plan_value = summary.values[address][k]
|
||||
assert plan_value == v, \
|
||||
f'{k} at {address} failed. Got `{plan_value}`, expected `{v}`'
|
||||
|
||||
if 'counts' in inventory:
|
||||
expected_counts = inventory['counts']
|
||||
for type_, expected_count in expected_counts.items():
|
||||
assert type_ in summary.counts, \
|
||||
f'module does not create any resources of type `{type_}`'
|
||||
plan_count = summary.counts[type_]
|
||||
assert plan_count == expected_count, \
|
||||
f'count of {type_} resources failed. Got {plan_count}, expected {expected_count}'
|
||||
|
||||
if 'outputs' in inventory:
|
||||
expected_outputs = inventory['outputs']
|
||||
for output_name, expected_output in expected_outputs.items():
|
||||
assert output_name in summary.outputs, \
|
||||
f'module does not output `{output_name}`'
|
||||
output = summary.outputs[output_name]
|
||||
# assert 'value' in output, \
|
||||
# f'output `{output_name}` does not have a value (is it sensitive or dynamic?)'
|
||||
plan_output = output.get('value', '__missing__')
|
||||
assert plan_output == expected_output, \
|
||||
f'output {output_name} failed. Got `{plan_output}`, expected `{expected_output}`'
|
||||
|
||||
return summary
|
||||
|
||||
|
||||
@pytest.fixture(name='plan_validator')
|
||||
def plan_validator_fixture(request):
|
||||
"""Return a function to build a PlanSummary and compare it to a YAML inventory.
|
||||
|
||||
In the returned function `basedir` becomes optional and it defaults
|
||||
to the directory of the calling test'
|
||||
|
||||
"""
|
||||
|
||||
def inner(module_path, inventory_paths, basedir=None, tf_var_files=None,
|
||||
**tf_vars):
|
||||
if basedir is None:
|
||||
basedir = Path(request.fspath).parent
|
||||
return plan_validator(module_path=module_path,
|
||||
inventory_paths=inventory_paths, basedir=basedir,
|
||||
tf_var_files=tf_var_paths, **tf_vars)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
# @pytest.fixture
|
||||
# def repo_root():
|
||||
# 'Return a pathlib.Path to the root of the repository'
|
||||
# return Path(__file__).parents[1]
|
||||
153
tests/legacy_fixtures.py
Normal file
153
tests/legacy_fixtures.py
Normal file
@@ -0,0 +1,153 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
"""Legacy pytest fixtures.
|
||||
|
||||
The fixtures contained in this file will eventually go away. Consider
|
||||
using one of the fixtures in fixtures.py
|
||||
"""
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
import tftest
|
||||
|
||||
BASEDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def _plan_runner():
|
||||
'Return a function to run Terraform plan on a fixture.'
|
||||
|
||||
def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
|
||||
targets=None, refresh=True, tmpdir=True, **tf_vars):
|
||||
'Run Terraform plan and returns parsed output.'
|
||||
if fixture_path is None:
|
||||
# find out the fixture directory from the caller's directory
|
||||
caller = inspect.stack()[2]
|
||||
fixture_path = os.path.join(os.path.dirname(caller.filename), 'fixture')
|
||||
|
||||
fixture_parent = os.path.dirname(fixture_path)
|
||||
fixture_prefix = os.path.basename(fixture_path) + '_'
|
||||
with tempfile.TemporaryDirectory(prefix=fixture_prefix,
|
||||
dir=fixture_parent) as tmp_path:
|
||||
# copy fixture to a temporary directory so we can execute
|
||||
# multiple tests in parallel
|
||||
if tmpdir:
|
||||
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
|
||||
tf = tftest.TerraformTest(tmp_path if tmpdir else fixture_path, BASEDIR,
|
||||
os.environ.get('TERRAFORM', 'terraform'))
|
||||
tf.setup(extra_files=extra_files, upgrade=True)
|
||||
plan = tf.plan(output=True, refresh=refresh, tf_var_file=tf_var_file,
|
||||
tf_vars=tf_vars, targets=targets)
|
||||
return plan
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def plan_runner(_plan_runner):
|
||||
'Return a function to run Terraform plan on a module fixture.'
|
||||
|
||||
def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
|
||||
targets=None, **tf_vars):
|
||||
'Run Terraform plan and returns plan and module resources.'
|
||||
plan = _plan_runner(fixture_path, extra_files=extra_files,
|
||||
tf_var_file=tf_var_file, targets=targets, **tf_vars)
|
||||
# skip the fixture
|
||||
root_module = plan.root_module['child_modules'][0]
|
||||
return plan, root_module['resources']
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def e2e_plan_runner(_plan_runner):
|
||||
'Return a function to run Terraform plan on an end-to-end fixture.'
|
||||
|
||||
def run_plan(fixture_path=None, tf_var_file=None, targets=None, refresh=True,
|
||||
include_bare_resources=False, **tf_vars):
|
||||
'Run Terraform plan on an end-to-end module using defaults, returns data.'
|
||||
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
|
||||
refresh=refresh, **tf_vars)
|
||||
# skip the fixture
|
||||
root_module = plan.root_module['child_modules'][0]
|
||||
modules = dict((mod['address'], mod['resources'])
|
||||
for mod in root_module['child_modules'])
|
||||
resources = [r for m in modules.values() for r in m]
|
||||
if include_bare_resources:
|
||||
bare_resources = root_module['resources']
|
||||
resources.extend(bare_resources)
|
||||
return modules, resources
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def recursive_e2e_plan_runner(_plan_runner):
|
||||
"""
|
||||
Plan runner for end-to-end root module, returns total number of
|
||||
(nested) modules and resources
|
||||
"""
|
||||
|
||||
def walk_plan(node, modules, resources):
|
||||
new_modules = node.get('child_modules', [])
|
||||
resources += node.get('resources', [])
|
||||
modules += new_modules
|
||||
for module in new_modules:
|
||||
walk_plan(module, modules, resources)
|
||||
|
||||
def run_plan(fixture_path=None, tf_var_file=None, targets=None, refresh=True,
|
||||
include_bare_resources=False, compute_sums=True, tmpdir=True,
|
||||
**tf_vars):
|
||||
'Run Terraform plan on a root module using defaults, returns data.'
|
||||
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
|
||||
refresh=refresh, tmpdir=tmpdir, **tf_vars)
|
||||
modules = []
|
||||
resources = []
|
||||
walk_plan(plan.root_module, modules, resources)
|
||||
return len(modules), len(resources)
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def apply_runner():
|
||||
'Return a function to run Terraform apply on a fixture.'
|
||||
|
||||
def run_apply(fixture_path=None, **tf_vars):
|
||||
'Run Terraform plan and returns parsed output.'
|
||||
if fixture_path is None:
|
||||
# find out the fixture directory from the caller's directory
|
||||
caller = inspect.stack()[1]
|
||||
fixture_path = os.path.join(os.path.dirname(caller.filename), 'fixture')
|
||||
|
||||
fixture_parent = os.path.dirname(fixture_path)
|
||||
fixture_prefix = os.path.basename(fixture_path) + '_'
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix=fixture_prefix,
|
||||
dir=fixture_parent) as tmp_path:
|
||||
# copy fixture to a temporary directory so we can execute
|
||||
# multiple tests in parallel
|
||||
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
|
||||
tf = tftest.TerraformTest(tmp_path, BASEDIR,
|
||||
os.environ.get('TERRAFORM', 'terraform'))
|
||||
tf.setup(upgrade=True)
|
||||
apply = tf.apply(tf_vars=tf_vars)
|
||||
output = tf.output(json_format=True)
|
||||
return apply, output
|
||||
|
||||
return run_apply
|
||||
@@ -20,16 +20,11 @@ import pytest
|
||||
import yaml
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
for item in items:
|
||||
item.add_marker(pytest.mark.xdist_group(name=item.path.parent.name))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def tfvars_to_yaml():
|
||||
@pytest.fixture()
|
||||
def tfvars_to_yaml(request):
|
||||
|
||||
def converter(source, dest, from_var, to_var=None):
|
||||
p_fixture = pathlib.Path(inspect.stack()[1].filename).parent / 'fixture'
|
||||
p_fixture = pathlib.Path(request.path).parent
|
||||
p_source = p_fixture / source
|
||||
if not p_source.exists():
|
||||
raise ValueError(f"tfvars '{source}' not found")
|
||||
|
||||
@@ -31,13 +31,14 @@ def test_policy_list(plan_runner):
|
||||
|
||||
def test_factory_policy_boolean(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
dest = tmp_path / 'policies.yaml'
|
||||
tfvars_to_yaml('test.orgpolicies-boolean.tfvars', dest, 'org_policies')
|
||||
tfvars_to_yaml('fixture/test.orgpolicies-boolean.tfvars', dest,
|
||||
'org_policies')
|
||||
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
|
||||
validate_policy_boolean(resources)
|
||||
|
||||
|
||||
def test_factory_policy_list(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
dest = tmp_path / 'policies.yaml'
|
||||
tfvars_to_yaml('test.orgpolicies-list.tfvars', dest, 'org_policies')
|
||||
tfvars_to_yaml('fixture/test.orgpolicies-list.tfvars', dest, 'org_policies')
|
||||
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
|
||||
validate_policy_list(resources)
|
||||
|
||||
2
tests/modules/net_vpc/common.tfvars
Normal file
2
tests/modules/net_vpc/common.tfvars
Normal file
@@ -0,0 +1,2 @@
|
||||
project_id = "test-project"
|
||||
name = "test"
|
||||
1
tests/modules/net_vpc/factory.tfvars
Normal file
1
tests/modules/net_vpc/factory.tfvars
Normal file
@@ -0,0 +1 @@
|
||||
data_folder = "../../tests/modules/net_vpc/data"
|
||||
44
tests/modules/net_vpc/factory.yaml
Normal file
44
tests/modules/net_vpc/factory.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_subnetwork.subnetwork["europe-west1/factory-subnet"]:
|
||||
description: 'Sample description'
|
||||
ip_cidr_range: '10.128.0.0/24'
|
||||
ipv6_access_type: null
|
||||
log_config: []
|
||||
name: 'factory-subnet'
|
||||
private_ip_google_access: false
|
||||
project: 'test-project'
|
||||
region: 'europe-west1'
|
||||
role: null
|
||||
secondary_ip_range:
|
||||
- ip_cidr_range: '192.168.128.0/24'
|
||||
range_name: 'secondary-range-a'
|
||||
google_compute_subnetwork.subnetwork["europe-west4/factory-subnet2"]:
|
||||
description: 'Sample description'
|
||||
ip_cidr_range: '10.129.0.0/24'
|
||||
log_config: []
|
||||
name: 'factory-subnet2'
|
||||
private_ip_google_access: true
|
||||
project: 'test-project'
|
||||
region: 'europe-west4'
|
||||
role: null
|
||||
secondary_ip_range: []
|
||||
|
||||
# FIXME: should we have some bindings here?
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
google_compute_subnetwork: 2
|
||||
5
tests/modules/net_vpc/peering.tfvars
Normal file
5
tests/modules/net_vpc/peering.tfvars
Normal file
@@ -0,0 +1,5 @@
|
||||
peering_config = {
|
||||
peer_vpc_self_link = "projects/my-project/global/networks/peer"
|
||||
export_routes = true
|
||||
import_routes = null
|
||||
}
|
||||
47
tests/modules/net_vpc/peering.yaml
Normal file
47
tests/modules/net_vpc/peering.yaml
Normal file
@@ -0,0 +1,47 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering.local[0]:
|
||||
export_custom_routes: true
|
||||
import_custom_routes: false
|
||||
name: test-peer
|
||||
peer_network: projects/my-project/global/networks/peer
|
||||
google_compute_network_peering.remote[0]:
|
||||
export_custom_routes: false
|
||||
import_custom_routes: true
|
||||
name: peer-test
|
||||
network: projects/my-project/global/networks/peer
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering: 2
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
project_id: test-project
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
||||
7
tests/modules/net_vpc/psa_routes_export.tfvars
Normal file
7
tests/modules/net_vpc/psa_routes_export.tfvars
Normal file
@@ -0,0 +1,7 @@
|
||||
psa_config = {
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
}
|
||||
export_routes = true
|
||||
import_routes = false
|
||||
}
|
||||
60
tests/modules/net_vpc/psa_routes_export.yaml
Normal file
60
tests/modules/net_vpc/psa_routes_export.yaml
Normal file
@@ -0,0 +1,60 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_global_address.psa_ranges["bar"]:
|
||||
address: 172.16.100.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: bar
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
enable_ula_internal_ipv6: null
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering_routes_config.psa_routes["1"]:
|
||||
export_custom_routes: true
|
||||
import_custom_routes: false
|
||||
project: test-project
|
||||
google_service_networking_connection.psa_connection["1"]:
|
||||
reserved_peering_ranges:
|
||||
- bar
|
||||
service: servicenetworking.googleapis.com
|
||||
|
||||
counts:
|
||||
google_compute_global_address: 1
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering_routes_config: 1
|
||||
google_service_networking_connection: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
name: __missing__
|
||||
network: __missing__
|
||||
project_id: test-project
|
||||
self_link: __missing__
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
||||
7
tests/modules/net_vpc/psa_routes_import.tfvars
Normal file
7
tests/modules/net_vpc/psa_routes_import.tfvars
Normal file
@@ -0,0 +1,7 @@
|
||||
psa_config = {
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
}
|
||||
export_routes = false
|
||||
import_routes = true
|
||||
}
|
||||
60
tests/modules/net_vpc/psa_routes_import.yaml
Normal file
60
tests/modules/net_vpc/psa_routes_import.yaml
Normal file
@@ -0,0 +1,60 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_global_address.psa_ranges["bar"]:
|
||||
address: 172.16.100.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: bar
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
enable_ula_internal_ipv6: null
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering_routes_config.psa_routes["1"]:
|
||||
export_custom_routes: false
|
||||
import_custom_routes: true
|
||||
project: test-project
|
||||
google_service_networking_connection.psa_connection["1"]:
|
||||
reserved_peering_ranges:
|
||||
- bar
|
||||
service: servicenetworking.googleapis.com
|
||||
|
||||
counts:
|
||||
google_compute_global_address: 1
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering_routes_config: 1
|
||||
google_service_networking_connection: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
name: __missing__
|
||||
network: __missing__
|
||||
project_id: test-project
|
||||
self_link: __missing__
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
||||
7
tests/modules/net_vpc/psa_routes_import_export.tfvars
Normal file
7
tests/modules/net_vpc/psa_routes_import_export.tfvars
Normal file
@@ -0,0 +1,7 @@
|
||||
psa_config = {
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
}
|
||||
export_routes = true
|
||||
import_routes = true
|
||||
}
|
||||
60
tests/modules/net_vpc/psa_routes_import_export.yaml
Normal file
60
tests/modules/net_vpc/psa_routes_import_export.yaml
Normal file
@@ -0,0 +1,60 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_global_address.psa_ranges["bar"]:
|
||||
address: 172.16.100.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: bar
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
enable_ula_internal_ipv6: null
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering_routes_config.psa_routes["1"]:
|
||||
export_custom_routes: true
|
||||
import_custom_routes: true
|
||||
project: test-project
|
||||
google_service_networking_connection.psa_connection["1"]:
|
||||
reserved_peering_ranges:
|
||||
- bar
|
||||
service: servicenetworking.googleapis.com
|
||||
|
||||
counts:
|
||||
google_compute_global_address: 1
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering_routes_config: 1
|
||||
google_service_networking_connection: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
name: __missing__
|
||||
network: __missing__
|
||||
project_id: test-project
|
||||
self_link: __missing__
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
||||
7
tests/modules/net_vpc/psa_simple.tfvars
Normal file
7
tests/modules/net_vpc/psa_simple.tfvars
Normal file
@@ -0,0 +1,7 @@
|
||||
psa_config = {
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
foo = "172.16.101.0/24"
|
||||
}
|
||||
routes = null
|
||||
}
|
||||
70
tests/modules/net_vpc/psa_simple.yaml
Normal file
70
tests/modules/net_vpc/psa_simple.yaml
Normal file
@@ -0,0 +1,70 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_global_address.psa_ranges["bar"]:
|
||||
address: 172.16.100.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: bar
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_global_address.psa_ranges["foo"]:
|
||||
address: 172.16.101.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: foo
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
enable_ula_internal_ipv6: null
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering_routes_config.psa_routes["1"]:
|
||||
export_custom_routes: false
|
||||
import_custom_routes: false
|
||||
project: test-project
|
||||
google_service_networking_connection.psa_connection["1"]:
|
||||
reserved_peering_ranges:
|
||||
- bar
|
||||
- foo
|
||||
service: servicenetworking.googleapis.com
|
||||
|
||||
counts:
|
||||
google_compute_global_address: 2
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering_routes_config: 1
|
||||
google_service_networking_connection: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
name: __missing__
|
||||
network: __missing__
|
||||
project_id: test-project
|
||||
self_link: __missing__
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
||||
2
tests/modules/net_vpc/shared_vpc.tfvars
Normal file
2
tests/modules/net_vpc/shared_vpc.tfvars
Normal file
@@ -0,0 +1,2 @@
|
||||
shared_vpc_host = true
|
||||
shared_vpc_service_projects = ["tf-a", "tf-b"]
|
||||
46
tests/modules/net_vpc/shared_vpc.yaml
Normal file
46
tests/modules/net_vpc/shared_vpc.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_shared_vpc_host_project.shared_vpc_host[0]:
|
||||
project: test-project
|
||||
google_compute_shared_vpc_service_project.service_projects["tf-a"]:
|
||||
host_project: test-project
|
||||
service_project: tf-a
|
||||
google_compute_shared_vpc_service_project.service_projects["tf-b"]:
|
||||
host_project: test-project
|
||||
service_project: tf-b
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
google_compute_shared_vpc_host_project: 1
|
||||
google_compute_shared_vpc_service_project: 2
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
project_id: test-project
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
||||
1
tests/modules/net_vpc/simple.tfvars
Normal file
1
tests/modules/net_vpc/simple.tfvars
Normal file
@@ -0,0 +1 @@
|
||||
# skip boilerplate check
|
||||
36
tests/modules/net_vpc/simple.yaml
Normal file
36
tests/modules/net_vpc/simple.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
project_id: test-project
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
||||
44
tests/modules/net_vpc/subnets.tfvars
Normal file
44
tests/modules/net_vpc/subnets.tfvars
Normal file
@@ -0,0 +1,44 @@
|
||||
subnet_iam = {
|
||||
"europe-west1/a" = {
|
||||
"roles/compute.networkUser" = [
|
||||
"user:a@example.com", "group:g-a@example.com"
|
||||
]
|
||||
}
|
||||
"europe-west1/c" = {
|
||||
"roles/compute.networkUser" = [
|
||||
"user:c@example.com", "group:g-c@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
subnets = [
|
||||
{
|
||||
name = "a"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.0.0/24"
|
||||
},
|
||||
{
|
||||
name = "b"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.1.0/24",
|
||||
description = "Subnet b"
|
||||
enable_private_access = false
|
||||
},
|
||||
{
|
||||
name = "c"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.2.0/24"
|
||||
secondary_ip_ranges = {
|
||||
a = "192.168.0.0/24"
|
||||
b = "192.168.1.0/24"
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "d"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.3.0/24"
|
||||
flow_logs_config = {
|
||||
flow_sampling = 0.5
|
||||
aggregation_interval = "INTERVAL_10_MIN"
|
||||
}
|
||||
}
|
||||
]
|
||||
120
tests/modules/net_vpc/subnets.yaml
Normal file
120
tests/modules/net_vpc/subnets.yaml
Normal file
@@ -0,0 +1,120 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_subnetwork.subnetwork["europe-west1/a"]:
|
||||
description: Terraform-managed.
|
||||
ip_cidr_range: 10.0.0.0/24
|
||||
log_config: []
|
||||
name: a
|
||||
private_ip_google_access: true
|
||||
project: test-project
|
||||
region: europe-west1
|
||||
role: null
|
||||
secondary_ip_range: []
|
||||
google_compute_subnetwork.subnetwork["europe-west1/b"]:
|
||||
description: Subnet b
|
||||
ip_cidr_range: 10.0.1.0/24
|
||||
log_config: []
|
||||
name: b
|
||||
private_ip_google_access: false
|
||||
project: test-project
|
||||
region: europe-west1
|
||||
role: null
|
||||
secondary_ip_range: []
|
||||
google_compute_subnetwork.subnetwork["europe-west1/c"]:
|
||||
description: Terraform-managed.
|
||||
ip_cidr_range: 10.0.2.0/24
|
||||
ipv6_access_type: null
|
||||
log_config: []
|
||||
name: c
|
||||
private_ip_google_access: true
|
||||
project: test-project
|
||||
region: europe-west1
|
||||
role: null
|
||||
secondary_ip_range:
|
||||
- ip_cidr_range: 192.168.0.0/24
|
||||
range_name: a
|
||||
- ip_cidr_range: 192.168.1.0/24
|
||||
range_name: b
|
||||
google_compute_subnetwork.subnetwork["europe-west1/d"]:
|
||||
description: Terraform-managed.
|
||||
ip_cidr_range: 10.0.3.0/24
|
||||
log_config:
|
||||
- aggregation_interval: INTERVAL_10_MIN
|
||||
filter_expr: 'true'
|
||||
flow_sampling: 0.5
|
||||
metadata: INCLUDE_ALL_METADATA
|
||||
metadata_fields: null
|
||||
name: d
|
||||
private_ip_google_access: true
|
||||
project: test-project
|
||||
region: europe-west1
|
||||
role: null
|
||||
secondary_ip_range: []
|
||||
google_compute_subnetwork_iam_binding.binding["europe-west1/a.roles/compute.networkUser"]:
|
||||
condition: []
|
||||
members:
|
||||
- group:g-a@example.com
|
||||
- user:a@example.com
|
||||
project: test-project
|
||||
region: europe-west1
|
||||
role: roles/compute.networkUser
|
||||
subnetwork: a
|
||||
google_compute_subnetwork_iam_binding.binding["europe-west1/c.roles/compute.networkUser"]:
|
||||
condition: []
|
||||
members:
|
||||
- group:g-c@example.com
|
||||
- user:c@example.com
|
||||
project: test-project
|
||||
region: europe-west1
|
||||
role: roles/compute.networkUser
|
||||
subnetwork: c
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
google_compute_subnetwork: 4
|
||||
google_compute_subnetwork_iam_binding: 2
|
||||
|
||||
outputs:
|
||||
bindings: __missing__
|
||||
project_id: test-project
|
||||
subnet_ips:
|
||||
europe-west1/a: 10.0.0.0/24
|
||||
europe-west1/b: 10.0.1.0/24
|
||||
europe-west1/c: 10.0.2.0/24
|
||||
europe-west1/d: 10.0.3.0/24
|
||||
subnet_regions:
|
||||
europe-west1/a: europe-west1
|
||||
europe-west1/b: europe-west1
|
||||
europe-west1/c: europe-west1
|
||||
europe-west1/d: europe-west1
|
||||
subnet_secondary_ranges:
|
||||
europe-west1/a: {}
|
||||
europe-west1/b: {}
|
||||
europe-west1/c:
|
||||
a: 192.168.0.0/24
|
||||
b: 192.168.1.0/24
|
||||
europe-west1/d: {}
|
||||
subnet_self_links: __missing__
|
||||
subnets: __missing__
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
||||
@@ -1,81 +0,0 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
_VAR_PEER_VPC_CONFIG = '''{
|
||||
peer_vpc_self_link="projects/my-project/global/networks/peer",
|
||||
export_routes=true, import_routes=null
|
||||
}'''
|
||||
_VAR_ROUTES_TEMPLATE = '''{
|
||||
next-hop = {
|
||||
dest_range="192.168.128.0/24", tags=null,
|
||||
next_hop_type="%s", next_hop="%s"},
|
||||
gateway = {
|
||||
dest_range="0.0.0.0/0", priority=100, tags=["tag-a"],
|
||||
next_hop_type="gateway",
|
||||
next_hop="global/gateways/default-internet-gateway"}
|
||||
}'''
|
||||
_VAR_ROUTES_NEXT_HOPS = {
|
||||
'gateway': 'global/gateways/default-internet-gateway',
|
||||
'instance': 'zones/europe-west1-b/test',
|
||||
'ip': '192.168.0.128',
|
||||
'ilb': 'regions/europe-west1/forwardingRules/test',
|
||||
'vpn_tunnel': 'regions/europe-west1/vpnTunnels/foo'
|
||||
}
|
||||
|
||||
|
||||
def test_vpc_simple(plan_runner):
|
||||
"Test vpc with no extra options."
|
||||
_, resources = plan_runner()
|
||||
assert len(resources) == 1
|
||||
assert [r['type'] for r in resources] == ['google_compute_network']
|
||||
assert [r['values']['name'] for r in resources] == ['test']
|
||||
assert [r['values']['project'] for r in resources] == ['test-project']
|
||||
|
||||
|
||||
def test_vpc_shared(plan_runner):
|
||||
"Test shared vpc variables."
|
||||
_, resources = plan_runner(shared_vpc_host='true',
|
||||
shared_vpc_service_projects='["tf-a", "tf-b"]')
|
||||
assert len(resources) == 4
|
||||
assert set(r['type'] for r in resources) == set([
|
||||
'google_compute_network', 'google_compute_shared_vpc_host_project',
|
||||
'google_compute_shared_vpc_service_project'
|
||||
])
|
||||
|
||||
|
||||
def test_vpc_peering(plan_runner):
|
||||
"Test vpc peering variables."
|
||||
_, resources = plan_runner(peering_config=_VAR_PEER_VPC_CONFIG)
|
||||
assert len(resources) == 3
|
||||
assert set(r['type'] for r in resources) == set(
|
||||
['google_compute_network', 'google_compute_network_peering'])
|
||||
peerings = [
|
||||
r['values']
|
||||
for r in resources
|
||||
if r['type'] == 'google_compute_network_peering'
|
||||
]
|
||||
assert [p['name'] for p in peerings] == ['test-peer', 'peer-test']
|
||||
assert [p['export_custom_routes'] for p in peerings] == [True, False]
|
||||
assert [p['import_custom_routes'] for p in peerings] == [False, True]
|
||||
|
||||
|
||||
def test_vpc_routes(plan_runner):
|
||||
"Test vpc routes."
|
||||
for next_hop_type, next_hop in _VAR_ROUTES_NEXT_HOPS.items():
|
||||
_var_routes = _VAR_ROUTES_TEMPLATE % (next_hop_type, next_hop)
|
||||
_, resources = plan_runner(routes=_var_routes)
|
||||
assert len(resources) == 3
|
||||
resource = [r for r in resources if r['values']['name'] == 'test-next-hop'
|
||||
][0]
|
||||
assert resource['values']['next_hop_%s' % next_hop_type]
|
||||
@@ -1,79 +0,0 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
|
||||
def test_single_range(plan_runner):
|
||||
"Test single PSA range."
|
||||
psa_config = '''{
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
foo = "172.16.101.0/24"
|
||||
},
|
||||
routes = null
|
||||
}'''
|
||||
_, resources = plan_runner(psa_config=psa_config)
|
||||
assert len(resources) == 5
|
||||
for r in resources:
|
||||
if r['type'] == 'google_compute_network_peering_routes_config':
|
||||
assert not r['values']['export_custom_routes']
|
||||
assert not r['values']['import_custom_routes']
|
||||
|
||||
|
||||
def test_routes_export(plan_runner):
|
||||
"Test routes export."
|
||||
psa_config = '''{
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
}
|
||||
export_routes = true
|
||||
import_routes = false
|
||||
}'''
|
||||
_, resources = plan_runner(psa_config=psa_config)
|
||||
assert len(resources) == 4
|
||||
for r in resources:
|
||||
if r['type'] == 'google_compute_network_peering_routes_config':
|
||||
assert r['values']['export_custom_routes']
|
||||
assert not r['values']['import_custom_routes']
|
||||
|
||||
|
||||
def test_routes_import(plan_runner):
|
||||
"Test routes import."
|
||||
psa_config = '''{
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
},
|
||||
export_routes = false
|
||||
import_routes = true
|
||||
}'''
|
||||
_, resources = plan_runner(psa_config=psa_config)
|
||||
for r in resources:
|
||||
if r['type'] == 'google_compute_network_peering_routes_config':
|
||||
assert not r['values']['export_custom_routes']
|
||||
assert r['values']['import_custom_routes']
|
||||
|
||||
|
||||
def test_routes_export_import(plan_runner):
|
||||
"Test routes export and import."
|
||||
psa_config = '''{
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
},
|
||||
export_routes = true
|
||||
import_routes = true
|
||||
}'''
|
||||
_, resources = plan_runner(psa_config=psa_config)
|
||||
for r in resources:
|
||||
if r['type'] == 'google_compute_network_peering_routes_config':
|
||||
assert r['values']['export_custom_routes']
|
||||
assert r['values']['import_custom_routes']
|
||||
@@ -1,71 +0,0 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
DATA_FOLDER = "data"
|
||||
|
||||
|
||||
def test_subnet_factory(plan_runner):
|
||||
"Test subnet factory."
|
||||
_, resources = plan_runner(data_folder=DATA_FOLDER)
|
||||
assert len(resources) == 3
|
||||
subnets = [
|
||||
r['values'] for r in resources if r['type'] == 'google_compute_subnetwork'
|
||||
]
|
||||
assert {s['name'] for s in subnets} == {'factory-subnet', 'factory-subnet2'}
|
||||
assert {len(s['secondary_ip_range']) for s in subnets} == {0, 1}
|
||||
assert {s['private_ip_google_access'] for s in subnets} == {True, False}
|
||||
|
||||
|
||||
def test_subnets(plan_runner):
|
||||
"Test subnets variable."
|
||||
_, resources = plan_runner(tf_var_file='test.subnets.tfvars')
|
||||
assert len(resources) == 7
|
||||
subnets = [
|
||||
r['values'] for r in resources if r['type'] == 'google_compute_subnetwork'
|
||||
]
|
||||
assert {s['name'] for s in subnets} == {'a', 'b', 'c', 'd'}
|
||||
assert {len(s['secondary_ip_range']) for s in subnets} == {0, 0, 2, 0}
|
||||
log_config = {s['name']: s['log_config'] for s in subnets if s['log_config']}
|
||||
assert log_config == {
|
||||
'd': [{
|
||||
'aggregation_interval': 'INTERVAL_10_MIN',
|
||||
'filter_expr': 'true',
|
||||
'flow_sampling': 0.5,
|
||||
'metadata': 'INCLUDE_ALL_METADATA',
|
||||
'metadata_fields': None
|
||||
}]
|
||||
}
|
||||
bindings = {
|
||||
r['index']: r['values']
|
||||
for r in resources
|
||||
if r['type'] == 'google_compute_subnetwork_iam_binding'
|
||||
}
|
||||
assert bindings == {
|
||||
'europe-west1/a.roles/compute.networkUser': {
|
||||
'condition': [],
|
||||
'members': ['group:g-a@example.com', 'user:a@example.com'],
|
||||
'project': 'test-project',
|
||||
'region': 'europe-west1',
|
||||
'role': 'roles/compute.networkUser',
|
||||
'subnetwork': 'a'
|
||||
},
|
||||
'europe-west1/c.roles/compute.networkUser': {
|
||||
'condition': [],
|
||||
'members': ['group:g-c@example.com', 'user:c@example.com'],
|
||||
'project': 'test-project',
|
||||
'region': 'europe-west1',
|
||||
'role': 'roles/compute.networkUser',
|
||||
'subnetwork': 'c'
|
||||
},
|
||||
}
|
||||
47
tests/modules/net_vpc/test_routes.py
Normal file
47
tests/modules/net_vpc/test_routes.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
import pytest
|
||||
|
||||
_route_parameters = [('gateway', 'global/gateways/default-internet-gateway'),
|
||||
('instance', 'zones/europe-west1-b/test'),
|
||||
('ip', '192.168.0.128'),
|
||||
('ilb', 'regions/europe-west1/forwardingRules/test'),
|
||||
('vpn_tunnel', 'regions/europe-west1/vpnTunnels/foo')]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('next_hop_type,next_hop', _route_parameters)
|
||||
def test_vpc_routes(plan_summary, next_hop_type, next_hop):
|
||||
'Test vpc routes.'
|
||||
|
||||
var_routes = '''{
|
||||
next-hop = {
|
||||
dest_range = "192.168.128.0/24"
|
||||
tags = null
|
||||
next_hop_type = "%s"
|
||||
next_hop = "%s"
|
||||
}
|
||||
gateway = {
|
||||
dest_range = "0.0.0.0/0",
|
||||
priority = 100
|
||||
tags = ["tag-a"]
|
||||
next_hop_type = "gateway",
|
||||
next_hop = "global/gateways/default-internet-gateway"
|
||||
}
|
||||
}''' % (next_hop_type, next_hop)
|
||||
summary = plan_summary('modules/net-vpc', tf_var_files=['common.tfvars'],
|
||||
routes=var_routes)
|
||||
assert len(summary.values) == 3
|
||||
route = summary.values[f'google_compute_route.{next_hop_type}["next-hop"]']
|
||||
assert route[f'next_hop_{next_hop_type}'] == next_hop
|
||||
@@ -12,10 +12,17 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pytest
|
||||
module: modules/net-vpc
|
||||
common_tfvars:
|
||||
- common.tfvars
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
for item in items:
|
||||
item.add_marker(
|
||||
pytest.mark.xdist_group(name='/'.join(item.path.parent.parts[-2:])))
|
||||
tests:
|
||||
simple:
|
||||
subnets:
|
||||
peering:
|
||||
shared_vpc:
|
||||
factory:
|
||||
psa_simple:
|
||||
psa_routes_export:
|
||||
psa_routes_import:
|
||||
psa_routes_import_export:
|
||||
6
tests/modules/organization/audit_config.tfvars
Normal file
6
tests/modules/organization/audit_config.tfvars
Normal file
@@ -0,0 +1,6 @@
|
||||
iam_audit_config = {
|
||||
allServices = {
|
||||
DATA_READ = [],
|
||||
DATA_WRITE = ["user:me@example.org"]
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,5 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
for item in items:
|
||||
item.add_marker(pytest.mark.xdist_group(name=item.path.parent.name))
|
||||
counts:
|
||||
google_organization_iam_audit_config: 1
|
||||
1
tests/modules/organization/common.tfvars
Normal file
1
tests/modules/organization/common.tfvars
Normal file
@@ -0,0 +1 @@
|
||||
organization_id = "organizations/1234567890"
|
||||
45
tests/modules/organization/firewall_policies.tfvars
Normal file
45
tests/modules/organization/firewall_policies.tfvars
Normal file
@@ -0,0 +1,45 @@
|
||||
firewall_policies = {
|
||||
policy1 = {
|
||||
allow-ingress = {
|
||||
description = ""
|
||||
direction = "INGRESS"
|
||||
action = "allow"
|
||||
priority = 100
|
||||
ranges = ["10.0.0.0/8"]
|
||||
ports = {
|
||||
tcp = ["22"]
|
||||
}
|
||||
target_service_accounts = null
|
||||
target_resources = null
|
||||
logging = false
|
||||
}
|
||||
deny-egress = {
|
||||
description = ""
|
||||
direction = "EGRESS"
|
||||
action = "deny"
|
||||
priority = 200
|
||||
ranges = ["192.168.0.0/24"]
|
||||
ports = {
|
||||
tcp = ["443"]
|
||||
}
|
||||
target_service_accounts = null
|
||||
target_resources = null
|
||||
logging = false
|
||||
}
|
||||
}
|
||||
policy2 = {
|
||||
allow-ingress = {
|
||||
description = ""
|
||||
direction = "INGRESS"
|
||||
action = "allow"
|
||||
priority = 100
|
||||
ranges = ["10.0.0.0/8"]
|
||||
ports = {
|
||||
tcp = ["22"]
|
||||
}
|
||||
target_service_accounts = null
|
||||
target_resources = null
|
||||
logging = false
|
||||
}
|
||||
}
|
||||
}
|
||||
73
tests/modules/organization/firewall_policies.yaml
Normal file
73
tests/modules/organization/firewall_policies.yaml
Normal file
@@ -0,0 +1,73 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_firewall_policy.policy["policy1"]:
|
||||
parent: organizations/1234567890
|
||||
short_name: policy1
|
||||
google_compute_firewall_policy.policy["policy2"]:
|
||||
parent: organizations/1234567890
|
||||
short_name: policy2
|
||||
google_compute_firewall_policy_rule.rule["policy1-allow-ingress"]:
|
||||
action: allow
|
||||
direction: INGRESS
|
||||
disabled: null
|
||||
enable_logging: false
|
||||
match:
|
||||
- dest_ip_ranges: null
|
||||
layer4_configs:
|
||||
- ip_protocol: tcp
|
||||
ports:
|
||||
- '22'
|
||||
src_ip_ranges:
|
||||
- 10.0.0.0/8
|
||||
priority: 100
|
||||
target_resources: null
|
||||
target_service_accounts: null
|
||||
google_compute_firewall_policy_rule.rule["policy1-deny-egress"]:
|
||||
action: deny
|
||||
direction: EGRESS
|
||||
disabled: null
|
||||
enable_logging: false
|
||||
match:
|
||||
- dest_ip_ranges:
|
||||
- 192.168.0.0/24
|
||||
layer4_configs:
|
||||
- ip_protocol: tcp
|
||||
ports:
|
||||
- '443'
|
||||
src_ip_ranges: null
|
||||
priority: 200
|
||||
target_resources: null
|
||||
target_service_accounts: null
|
||||
google_compute_firewall_policy_rule.rule["policy2-allow-ingress"]:
|
||||
action: allow
|
||||
direction: INGRESS
|
||||
disabled: null
|
||||
enable_logging: false
|
||||
match:
|
||||
- dest_ip_ranges: null
|
||||
layer4_configs:
|
||||
- ip_protocol: tcp
|
||||
ports:
|
||||
- '22'
|
||||
src_ip_ranges:
|
||||
- 10.0.0.0/8
|
||||
priority: 100
|
||||
target_resources: null
|
||||
target_service_accounts: null
|
||||
|
||||
counts:
|
||||
google_compute_firewall_policy: 2
|
||||
google_compute_firewall_policy_rule: 3
|
||||
@@ -0,0 +1,5 @@
|
||||
firewall_policy_factory = {
|
||||
cidr_file = "../../tests/modules/organization/data/firewall-cidrs.yaml"
|
||||
policy_name = "factory-1"
|
||||
rules_file = "../../tests/modules/organization/data/firewall-rules.yaml"
|
||||
}
|
||||
61
tests/modules/organization/firewall_policies_factory.yaml
Normal file
61
tests/modules/organization/firewall_policies_factory.yaml
Normal file
@@ -0,0 +1,61 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_firewall_policy.policy["factory-1"]:
|
||||
description: null
|
||||
parent: organizations/1234567890
|
||||
short_name: factory-1
|
||||
timeouts: null
|
||||
google_compute_firewall_policy_rule.rule["factory-1-allow-admins"]:
|
||||
action: allow
|
||||
description: Access from the admin subnet to all subnets
|
||||
direction: INGRESS
|
||||
disabled: null
|
||||
enable_logging: null
|
||||
match:
|
||||
- dest_ip_ranges: null
|
||||
layer4_configs:
|
||||
- ip_protocol: all
|
||||
ports: []
|
||||
src_ip_ranges:
|
||||
- 10.0.0.0/8
|
||||
- 172.168.0.0/12
|
||||
- 192.168.0.0/16
|
||||
priority: 1000
|
||||
target_resources: null
|
||||
target_service_accounts: null
|
||||
timeouts: null
|
||||
google_compute_firewall_policy_rule.rule["factory-1-allow-ssh-from-iap"]:
|
||||
action: allow
|
||||
description: Enable SSH from IAP
|
||||
direction: INGRESS
|
||||
disabled: null
|
||||
enable_logging: null
|
||||
match:
|
||||
- dest_ip_ranges: null
|
||||
layer4_configs:
|
||||
- ip_protocol: tcp
|
||||
ports:
|
||||
- '22'
|
||||
src_ip_ranges:
|
||||
- 35.235.240.0/20
|
||||
priority: 1002
|
||||
target_resources: null
|
||||
target_service_accounts: null
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_compute_firewall_policy: 1
|
||||
google_compute_firewall_policy_rule: 2
|
||||
@@ -0,0 +1 @@
|
||||
# skip boilerplate check
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_compute_firewall_policy.policy["factory-1"]: {}
|
||||
google_compute_firewall_policy.policy["policy1"]: {}
|
||||
google_compute_firewall_policy.policy["policy2"]: {}
|
||||
google_compute_firewall_policy_rule.rule["factory-1-allow-admins"]: {}
|
||||
google_compute_firewall_policy_rule.rule["factory-1-allow-ssh-from-iap"]: {}
|
||||
google_compute_firewall_policy_rule.rule["policy1-allow-ingress"]: {}
|
||||
google_compute_firewall_policy_rule.rule["policy1-deny-egress"]: {}
|
||||
google_compute_firewall_policy_rule.rule["policy2-allow-ingress"]: {}
|
||||
|
||||
counts:
|
||||
google_compute_firewall_policy: 3
|
||||
google_compute_firewall_policy_rule: 5
|
||||
@@ -1,5 +0,0 @@
|
||||
network_tags = {
|
||||
net_environment = {
|
||||
network = "foobar"
|
||||
}
|
||||
}
|
||||
18
tests/modules/organization/iam.tfvars
Normal file
18
tests/modules/organization/iam.tfvars
Normal file
@@ -0,0 +1,18 @@
|
||||
group_iam = {
|
||||
"owners@example.org" = [
|
||||
"roles/owner",
|
||||
"roles/resourcemanager.folderAdmin"
|
||||
],
|
||||
"viewers@example.org" = [
|
||||
"roles/viewer"
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
"roles/owner" = [
|
||||
"user:one@example.org",
|
||||
"user:two@example.org"
|
||||
],
|
||||
"roles/browser" = [
|
||||
"domain:example.org"
|
||||
]
|
||||
}
|
||||
44
tests/modules/organization/iam.yaml
Normal file
44
tests/modules/organization/iam.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_organization_iam_binding.authoritative["roles/browser"]:
|
||||
condition: []
|
||||
members:
|
||||
- domain:example.org
|
||||
org_id: '1234567890'
|
||||
role: roles/browser
|
||||
google_organization_iam_binding.authoritative["roles/owner"]:
|
||||
condition: []
|
||||
members:
|
||||
- group:owners@example.org
|
||||
- user:one@example.org
|
||||
- user:two@example.org
|
||||
org_id: '1234567890'
|
||||
role: roles/owner
|
||||
google_organization_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]:
|
||||
condition: []
|
||||
members:
|
||||
- group:owners@example.org
|
||||
org_id: '1234567890'
|
||||
role: roles/resourcemanager.folderAdmin
|
||||
google_organization_iam_binding.authoritative["roles/viewer"]:
|
||||
condition: []
|
||||
members:
|
||||
- group:viewers@example.org
|
||||
org_id: '1234567890'
|
||||
role: roles/viewer
|
||||
|
||||
counts:
|
||||
google_organization_iam_binding: 4
|
||||
4
tests/modules/organization/iam_additive.tfvars
Normal file
4
tests/modules/organization/iam_additive.tfvars
Normal file
@@ -0,0 +1,4 @@
|
||||
iam = {
|
||||
"user:one@example.org" = ["roles/owner"],
|
||||
"user:two@example.org" = ["roles/owner", "roles/editor"]
|
||||
}
|
||||
31
tests/modules/organization/iam_additive.yaml
Normal file
31
tests/modules/organization/iam_additive.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_organization_iam_binding.authoritative["user:one@example.org"]:
|
||||
condition: []
|
||||
members:
|
||||
- roles/owner
|
||||
org_id: '1234567890'
|
||||
role: user:one@example.org
|
||||
google_organization_iam_binding.authoritative["user:two@example.org"]:
|
||||
condition: []
|
||||
members:
|
||||
- roles/editor
|
||||
- roles/owner
|
||||
org_id: '1234567890'
|
||||
role: user:two@example.org
|
||||
|
||||
counts:
|
||||
google_organization_iam_binding: 2
|
||||
29
tests/modules/organization/logging.tfvars
Normal file
29
tests/modules/organization/logging.tfvars
Normal file
@@ -0,0 +1,29 @@
|
||||
logging_sinks = {
|
||||
warning = {
|
||||
destination = "mybucket"
|
||||
type = "storage"
|
||||
filter = "severity=WARNING"
|
||||
}
|
||||
info = {
|
||||
destination = "projects/myproject/datasets/mydataset"
|
||||
type = "bigquery"
|
||||
filter = "severity=INFO"
|
||||
disabled = true
|
||||
}
|
||||
notice = {
|
||||
destination = "projects/myproject/topics/mytopic"
|
||||
type = "pubsub"
|
||||
filter = "severity=NOTICE"
|
||||
include_children = false
|
||||
}
|
||||
debug = {
|
||||
destination = "projects/myproject/locations/global/buckets/mybucket"
|
||||
type = "logging"
|
||||
filter = "severity=DEBUG"
|
||||
include_children = false
|
||||
exclusions = {
|
||||
no-compute = "logName:compute"
|
||||
no-container = "logName:container"
|
||||
}
|
||||
}
|
||||
}
|
||||
86
tests/modules/organization/logging.yaml
Normal file
86
tests/modules/organization/logging.yaml
Normal file
@@ -0,0 +1,86 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_bigquery_dataset_iam_member.bq-sinks-binding["info"]:
|
||||
condition: []
|
||||
dataset_id: mydataset
|
||||
project: myproject
|
||||
role: roles/bigquery.dataEditor
|
||||
google_logging_organization_sink.sink["debug"]:
|
||||
description: debug (Terraform-managed).
|
||||
destination: logging.googleapis.com/projects/myproject/locations/global/buckets/mybucket
|
||||
disabled: false
|
||||
exclusions:
|
||||
- description: null
|
||||
disabled: false
|
||||
filter: logName:compute
|
||||
name: no-compute
|
||||
- description: null
|
||||
disabled: false
|
||||
filter: logName:container
|
||||
name: no-container
|
||||
filter: severity=DEBUG
|
||||
include_children: false
|
||||
name: debug
|
||||
org_id: '1234567890'
|
||||
google_logging_organization_sink.sink["info"]:
|
||||
description: info (Terraform-managed).
|
||||
destination: bigquery.googleapis.com/projects/myproject/datasets/mydataset
|
||||
disabled: true
|
||||
exclusions: []
|
||||
filter: severity=INFO
|
||||
include_children: true
|
||||
name: info
|
||||
org_id: '1234567890'
|
||||
google_logging_organization_sink.sink["notice"]:
|
||||
description: notice (Terraform-managed).
|
||||
destination: pubsub.googleapis.com/projects/myproject/topics/mytopic
|
||||
disabled: false
|
||||
exclusions: []
|
||||
filter: severity=NOTICE
|
||||
include_children: false
|
||||
name: notice
|
||||
org_id: '1234567890'
|
||||
google_logging_organization_sink.sink["warning"]:
|
||||
description: warning (Terraform-managed).
|
||||
destination: storage.googleapis.com/mybucket
|
||||
disabled: false
|
||||
exclusions: []
|
||||
filter: severity=WARNING
|
||||
include_children: true
|
||||
name: warning
|
||||
org_id: '1234567890'
|
||||
google_project_iam_member.bucket-sinks-binding["debug"]:
|
||||
condition:
|
||||
- expression: resource.name.endsWith('projects/myproject/locations/global/buckets/mybucket')
|
||||
title: debug bucket writer
|
||||
project: myproject
|
||||
role: roles/logging.bucketWriter
|
||||
google_pubsub_topic_iam_member.pubsub-sinks-binding["notice"]:
|
||||
condition: []
|
||||
project: myproject
|
||||
role: roles/pubsub.publisher
|
||||
topic: mytopic
|
||||
google_storage_bucket_iam_member.storage-sinks-binding["warning"]:
|
||||
bucket: mybucket
|
||||
condition: []
|
||||
role: roles/storage.objectCreator
|
||||
|
||||
counts:
|
||||
google_bigquery_dataset_iam_member: 1
|
||||
google_logging_organization_sink: 4
|
||||
google_project_iam_member: 1
|
||||
google_pubsub_topic_iam_member: 1
|
||||
google_storage_bucket_iam_member: 1
|
||||
4
tests/modules/organization/logging_exclusions.tfvars
Normal file
4
tests/modules/organization/logging_exclusions.tfvars
Normal file
@@ -0,0 +1,4 @@
|
||||
logging_exclusions = {
|
||||
exclusion1 = "resource.type=gce_instance"
|
||||
exclusion2 = "severity=NOTICE"
|
||||
}
|
||||
30
tests/modules/organization/logging_exclusions.yaml
Normal file
30
tests/modules/organization/logging_exclusions.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_logging_organization_exclusion.logging-exclusion["exclusion1"]:
|
||||
description: exclusion1 (Terraform-managed).
|
||||
disabled: null
|
||||
filter: resource.type=gce_instance
|
||||
name: exclusion1
|
||||
org_id: '1234567890'
|
||||
google_logging_organization_exclusion.logging-exclusion["exclusion2"]:
|
||||
description: exclusion2 (Terraform-managed).
|
||||
disabled: null
|
||||
filter: severity=NOTICE
|
||||
name: exclusion2
|
||||
org_id: '1234567890'
|
||||
|
||||
counts:
|
||||
google_logging_organization_exclusion: 2
|
||||
53
tests/modules/organization/org_policies_boolean.yaml
Normal file
53
tests/modules/organization/org_policies_boolean.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]:
|
||||
name: organizations/1234567890/policies/iam.disableServiceAccountKeyCreation
|
||||
parent: organizations/1234567890
|
||||
spec:
|
||||
- inherit_from_parent: null
|
||||
reset: null
|
||||
rules:
|
||||
- allow_all: null
|
||||
condition: []
|
||||
deny_all: null
|
||||
enforce: 'TRUE'
|
||||
values: []
|
||||
timeouts: null
|
||||
google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]:
|
||||
name: organizations/1234567890/policies/iam.disableServiceAccountKeyUpload
|
||||
parent: organizations/1234567890
|
||||
spec:
|
||||
- inherit_from_parent: null
|
||||
reset: null
|
||||
rules:
|
||||
- allow_all: null
|
||||
condition: []
|
||||
deny_all: null
|
||||
enforce: 'FALSE'
|
||||
values: []
|
||||
- allow_all: null
|
||||
condition:
|
||||
- description: test condition
|
||||
expression: resource.matchTagId(aa, bb)
|
||||
location: xxx
|
||||
title: condition
|
||||
deny_all: null
|
||||
enforce: 'TRUE'
|
||||
values: []
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_org_policy_policy: 2
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_org_policy_custom_constraint.constraint["custom.dataprocNoMoreThan10Workers"]:
|
||||
action_type: DENY
|
||||
condition: resource.config.workerConfig.numInstances + resource.config.secondaryWorkerConfig.numInstances > 10
|
||||
method_types:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
name: custom.dataprocNoMoreThan10Workers
|
||||
parent: organizations/1234567890
|
||||
resource_types:
|
||||
- dataproc.googleapis.com/Cluster
|
||||
google_org_policy_custom_constraint.constraint["custom.gkeEnableAutoUpgrade"]:
|
||||
action_type: ALLOW
|
||||
condition: resource.management.autoUpgrade == true
|
||||
method_types:
|
||||
- CREATE
|
||||
name: custom.gkeEnableAutoUpgrade
|
||||
parent: organizations/1234567890
|
||||
resource_types:
|
||||
- container.googleapis.com/NodePool
|
||||
|
||||
counts:
|
||||
google_org_policy_custom_constraint: 2
|
||||
@@ -3,6 +3,7 @@ org_policies = {
|
||||
deny = { all = true }
|
||||
}
|
||||
"iam.allowedPolicyMemberDomains" = {
|
||||
inherit_from_parent = true
|
||||
allow = {
|
||||
values = ["C0xxxxxxx", "C0yyyyyyy"]
|
||||
}
|
||||
85
tests/modules/organization/org_policies_list.yaml
Normal file
85
tests/modules/organization/org_policies_list.yaml
Normal file
@@ -0,0 +1,85 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_org_policy_policy.default["compute.restrictLoadBalancerCreationForTypes"]:
|
||||
name: organizations/1234567890/policies/compute.restrictLoadBalancerCreationForTypes
|
||||
parent: organizations/1234567890
|
||||
spec:
|
||||
- inherit_from_parent: null
|
||||
reset: null
|
||||
rules:
|
||||
- allow_all: null
|
||||
condition: []
|
||||
deny_all: null
|
||||
enforce: null
|
||||
values:
|
||||
- allowed_values: null
|
||||
denied_values:
|
||||
- in:EXTERNAL
|
||||
- allow_all: null
|
||||
condition:
|
||||
- description: test condition
|
||||
expression: resource.matchTagId(aa, bb)
|
||||
location: xxx
|
||||
title: condition
|
||||
deny_all: null
|
||||
enforce: null
|
||||
values:
|
||||
- allowed_values:
|
||||
- EXTERNAL_1
|
||||
denied_values: null
|
||||
- allow_all: 'TRUE'
|
||||
condition:
|
||||
- description: test condition2
|
||||
expression: resource.matchTagId(cc, dd)
|
||||
location: xxx
|
||||
title: condition2
|
||||
deny_all: null
|
||||
enforce: null
|
||||
values: []
|
||||
timeouts: null
|
||||
google_org_policy_policy.default["compute.vmExternalIpAccess"]:
|
||||
name: organizations/1234567890/policies/compute.vmExternalIpAccess
|
||||
parent: organizations/1234567890
|
||||
spec:
|
||||
- inherit_from_parent: null
|
||||
reset: null
|
||||
rules:
|
||||
- allow_all: null
|
||||
condition: []
|
||||
deny_all: 'TRUE'
|
||||
enforce: null
|
||||
values: []
|
||||
timeouts: null
|
||||
google_org_policy_policy.default["iam.allowedPolicyMemberDomains"]:
|
||||
name: organizations/1234567890/policies/iam.allowedPolicyMemberDomains
|
||||
parent: organizations/1234567890
|
||||
spec:
|
||||
- inherit_from_parent: true
|
||||
reset: null
|
||||
rules:
|
||||
- allow_all: null
|
||||
condition: []
|
||||
deny_all: null
|
||||
enforce: null
|
||||
values:
|
||||
- allowed_values:
|
||||
- C0xxxxxxx
|
||||
- C0yyyyyyy
|
||||
denied_values: null
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_org_policy_policy: 3
|
||||
@@ -1,3 +1,8 @@
|
||||
network_tags = {
|
||||
net_environment = {
|
||||
network = "foobar"
|
||||
}
|
||||
}
|
||||
tags = {
|
||||
foo = {}
|
||||
bar = {
|
||||
76
tests/modules/organization/tags.yaml
Normal file
76
tests/modules/organization/tags.yaml
Normal file
@@ -0,0 +1,76 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
values:
|
||||
google_tags_tag_key.default["bar"]:
|
||||
description: Managed by the Terraform organization module.
|
||||
parent: organizations/1234567890
|
||||
purpose: null
|
||||
purpose_data: null
|
||||
short_name: bar
|
||||
google_tags_tag_key.default["foo"]:
|
||||
description: Managed by the Terraform organization module.
|
||||
parent: organizations/1234567890
|
||||
purpose: null
|
||||
purpose_data: null
|
||||
short_name: foo
|
||||
google_tags_tag_key.default["foobar"]:
|
||||
description: Foobar tag.
|
||||
parent: organizations/1234567890
|
||||
purpose: null
|
||||
purpose_data: null
|
||||
short_name: foobar
|
||||
google_tags_tag_key.default["net_environment"]:
|
||||
description: Managed by the Terraform organization module.
|
||||
parent: organizations/1234567890
|
||||
purpose: GCE_FIREWALL
|
||||
purpose_data:
|
||||
network: foobar
|
||||
short_name: net_environment
|
||||
google_tags_tag_key_iam_binding.default["foobar:roles/resourcemanager.tagAdmin"]:
|
||||
condition: []
|
||||
members:
|
||||
- user:user1@example.com
|
||||
- user:user2@example.com
|
||||
role: roles/resourcemanager.tagAdmin
|
||||
google_tags_tag_value.default["foobar/one"]:
|
||||
description: Managed by the Terraform organization module.
|
||||
short_name: one
|
||||
google_tags_tag_value.default["foobar/three"]:
|
||||
description: Foobar 3.
|
||||
short_name: three
|
||||
google_tags_tag_value.default["foobar/two"]:
|
||||
description: Foobar 2.
|
||||
short_name: two
|
||||
google_tags_tag_value_iam_binding.default["foobar/three:roles/resourcemanager.tagAdmin"]:
|
||||
condition: []
|
||||
members:
|
||||
- user:user4@example.com
|
||||
role: roles/resourcemanager.tagAdmin
|
||||
google_tags_tag_value_iam_binding.default["foobar/three:roles/resourcemanager.tagViewer"]:
|
||||
condition: []
|
||||
members:
|
||||
- user:user3@example.com
|
||||
role: roles/resourcemanager.tagViewer
|
||||
google_tags_tag_value_iam_binding.default["foobar/two:roles/resourcemanager.tagViewer"]:
|
||||
condition: []
|
||||
members:
|
||||
- user:user3@example.com
|
||||
role: roles/resourcemanager.tagViewer
|
||||
|
||||
counts:
|
||||
google_tags_tag_key: 4
|
||||
google_tags_tag_key_iam_binding: 1
|
||||
google_tags_tag_value: 3
|
||||
google_tags_tag_value_iam_binding: 3
|
||||
@@ -1,62 +0,0 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
|
||||
def test_audit_config(plan_runner):
|
||||
"Test audit config."
|
||||
iam_audit_config = '{allServices={DATA_READ=[], DATA_WRITE=["user:me@example.org"]}}'
|
||||
_, resources = plan_runner(iam_audit_config=iam_audit_config)
|
||||
assert len(resources) == 1
|
||||
log_types = set(
|
||||
r['log_type'] for r in resources[0]['values']['audit_log_config'])
|
||||
assert log_types == set(['DATA_READ', 'DATA_WRITE'])
|
||||
|
||||
|
||||
def test_iam(plan_runner):
|
||||
"Test IAM."
|
||||
group_iam = (
|
||||
'{'
|
||||
'"owners@example.org" = ["roles/owner", "roles/resourcemanager.folderAdmin"],'
|
||||
'"viewers@example.org" = ["roles/viewer"]'
|
||||
'}')
|
||||
iam = ('{'
|
||||
'"roles/owner" = ["user:one@example.org", "user:two@example.org"],'
|
||||
'"roles/browser" = ["domain:example.org"]'
|
||||
'}')
|
||||
_, resources = plan_runner(group_iam=group_iam, iam=iam)
|
||||
roles = sorted([(r['values']['role'], sorted(r['values']['members']))
|
||||
for r in resources
|
||||
if r['type'] == 'google_organization_iam_binding'])
|
||||
assert roles == [
|
||||
('roles/browser', ['domain:example.org']),
|
||||
('roles/owner', [
|
||||
'group:owners@example.org', 'user:one@example.org',
|
||||
'user:two@example.org'
|
||||
]),
|
||||
('roles/resourcemanager.folderAdmin', ['group:owners@example.org']),
|
||||
('roles/viewer', ['group:viewers@example.org']),
|
||||
]
|
||||
|
||||
|
||||
def test_iam_additive_members(plan_runner):
|
||||
"Test IAM additive members."
|
||||
iam = ('{"user:one@example.org" = ["roles/owner"],'
|
||||
'"user:two@example.org" = ["roles/owner", "roles/editor"]}')
|
||||
_, resources = plan_runner(iam_additive_members=iam)
|
||||
roles = set((r['values']['role'], r['values']['member'])
|
||||
for r in resources
|
||||
if r['type'] == 'google_organization_iam_member')
|
||||
assert roles == set([('roles/owner', 'user:one@example.org'),
|
||||
('roles/owner', 'user:two@example.org'),
|
||||
('roles/editor', 'user:two@example.org')])
|
||||
@@ -1,130 +0,0 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
_FACTORY = '''
|
||||
{
|
||||
cidr_file = "data/firewall-cidrs.yaml"
|
||||
policy_name = "factory-1"
|
||||
rules_file = "data/firewall-rules.yaml"
|
||||
}
|
||||
'''
|
||||
_POLICIES = '''
|
||||
{
|
||||
policy1 = {
|
||||
allow-ingress = {
|
||||
description = ""
|
||||
direction = "INGRESS"
|
||||
action = "allow"
|
||||
priority = 100
|
||||
ranges = ["10.0.0.0/8"]
|
||||
ports = {
|
||||
tcp = ["22"]
|
||||
}
|
||||
target_service_accounts = null
|
||||
target_resources = null
|
||||
logging = false
|
||||
}
|
||||
deny-egress = {
|
||||
description = ""
|
||||
direction = "EGRESS"
|
||||
action = "deny"
|
||||
priority = 200
|
||||
ranges = ["192.168.0.0/24"]
|
||||
ports = {
|
||||
tcp = ["443"]
|
||||
}
|
||||
target_service_accounts = null
|
||||
target_resources = null
|
||||
logging = false
|
||||
}
|
||||
}
|
||||
policy2 = {
|
||||
allow-ingress = {
|
||||
description = ""
|
||||
direction = "INGRESS"
|
||||
action = "allow"
|
||||
priority = 100
|
||||
ranges = ["10.0.0.0/8"]
|
||||
ports = {
|
||||
tcp = ["22"]
|
||||
}
|
||||
target_service_accounts = null
|
||||
target_resources = null
|
||||
logging = false
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
def test_custom(plan_runner):
|
||||
'Test custom firewall policies.'
|
||||
_, resources = plan_runner(firewall_policies=_POLICIES)
|
||||
assert len(resources) == 5
|
||||
policies = [r for r in resources
|
||||
if r['type'] == 'google_compute_firewall_policy']
|
||||
rules = [r for r in resources
|
||||
if r['type'] == 'google_compute_firewall_policy_rule']
|
||||
assert set(r['index'] for r in policies) == set([
|
||||
'policy1', 'policy2'
|
||||
])
|
||||
assert set(r['index'] for r in rules) == set([
|
||||
'policy1-deny-egress', 'policy2-allow-ingress', 'policy1-allow-ingress'
|
||||
])
|
||||
|
||||
|
||||
def test_factory(plan_runner):
|
||||
'Test firewall policy factory.'
|
||||
_, resources = plan_runner(firewall_policy_factory=_FACTORY)
|
||||
assert len(resources) == 3
|
||||
policies = [r for r in resources
|
||||
if r['type'] == 'google_compute_firewall_policy']
|
||||
rules = [r for r in resources
|
||||
if r['type'] == 'google_compute_firewall_policy_rule']
|
||||
assert set(r['index'] for r in policies) == set([
|
||||
'factory-1'
|
||||
])
|
||||
assert set(r['index'] for r in rules) == set([
|
||||
'factory-1-allow-admins', 'factory-1-allow-ssh-from-iap'
|
||||
])
|
||||
|
||||
|
||||
def test_factory_name(plan_runner):
|
||||
'Test firewall policy factory default name.'
|
||||
factory = _FACTORY.replace('"factory-1"', 'null')
|
||||
_, resources = plan_runner(firewall_policy_factory=factory)
|
||||
assert len(resources) == 3
|
||||
policies = [r for r in resources
|
||||
if r['type'] == 'google_compute_firewall_policy']
|
||||
assert set(r['index'] for r in policies) == set([
|
||||
'factory'
|
||||
])
|
||||
|
||||
|
||||
def test_combined(plan_runner):
|
||||
'Test combined rules.'
|
||||
_, resources = plan_runner(firewall_policies=_POLICIES,
|
||||
firewall_policy_factory=_FACTORY)
|
||||
assert len(resources) == 8
|
||||
policies = [r for r in resources
|
||||
if r['type'] == 'google_compute_firewall_policy']
|
||||
rules = [r for r in resources
|
||||
if r['type'] == 'google_compute_firewall_policy_rule']
|
||||
assert set(r['index'] for r in policies) == set([
|
||||
'factory-1', 'policy1', 'policy2'
|
||||
])
|
||||
assert set(r['index'] for r in rules) == set([
|
||||
'factory-1-allow-admins', 'factory-1-allow-ssh-from-iap',
|
||||
'policy1-deny-egress', 'policy2-allow-ingress', 'policy1-allow-ingress'
|
||||
])
|
||||
@@ -1,126 +0,0 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
from collections import Counter
|
||||
|
||||
|
||||
def test_sinks(plan_runner):
|
||||
"Test folder-level sinks."
|
||||
tfvars = 'test.logging-sinks.tfvars'
|
||||
_, resources = plan_runner(tf_var_file=tfvars)
|
||||
assert len(resources) == 8
|
||||
|
||||
resource_types = Counter([r["type"] for r in resources])
|
||||
assert resource_types == {
|
||||
"google_logging_organization_sink": 4,
|
||||
"google_bigquery_dataset_iam_member": 1,
|
||||
"google_project_iam_member": 1,
|
||||
"google_pubsub_topic_iam_member": 1,
|
||||
"google_storage_bucket_iam_member": 1,
|
||||
}
|
||||
|
||||
sinks = [
|
||||
r for r in resources if r["type"] == "google_logging_organization_sink"
|
||||
]
|
||||
assert sorted([r["index"] for r in sinks]) == [
|
||||
"debug",
|
||||
"info",
|
||||
"notice",
|
||||
"warning",
|
||||
]
|
||||
values = [(
|
||||
r["index"],
|
||||
r["values"]["filter"],
|
||||
r["values"]["destination"],
|
||||
r["values"]["include_children"],
|
||||
) for r in sinks]
|
||||
assert sorted(values) == [
|
||||
(
|
||||
"debug",
|
||||
"severity=DEBUG",
|
||||
"logging.googleapis.com/projects/myproject/locations/global/buckets/mybucket",
|
||||
False,
|
||||
),
|
||||
(
|
||||
"info",
|
||||
"severity=INFO",
|
||||
"bigquery.googleapis.com/projects/myproject/datasets/mydataset",
|
||||
True,
|
||||
),
|
||||
(
|
||||
"notice",
|
||||
"severity=NOTICE",
|
||||
"pubsub.googleapis.com/projects/myproject/topics/mytopic",
|
||||
False,
|
||||
),
|
||||
("warning", "severity=WARNING", "storage.googleapis.com/mybucket", True),
|
||||
]
|
||||
|
||||
bindings = [r for r in resources if "member" in r["type"]]
|
||||
values = [(r["index"], r["type"], r["values"]["role"]) for r in bindings]
|
||||
assert sorted(values) == [
|
||||
("debug", "google_project_iam_member", "roles/logging.bucketWriter"),
|
||||
("info", "google_bigquery_dataset_iam_member",
|
||||
"roles/bigquery.dataEditor"),
|
||||
("notice", "google_pubsub_topic_iam_member", "roles/pubsub.publisher"),
|
||||
("warning", "google_storage_bucket_iam_member",
|
||||
"roles/storage.objectCreator"),
|
||||
]
|
||||
|
||||
exclusions = [(r["index"], r["values"]["exclusions"]) for r in sinks]
|
||||
assert sorted(exclusions) == [
|
||||
(
|
||||
"debug",
|
||||
[
|
||||
{
|
||||
"description": None,
|
||||
"disabled": False,
|
||||
"filter": "logName:compute",
|
||||
"name": "no-compute",
|
||||
},
|
||||
{
|
||||
"description": None,
|
||||
"disabled": False,
|
||||
"filter": "logName:container",
|
||||
"name": "no-container",
|
||||
},
|
||||
],
|
||||
),
|
||||
("info", []),
|
||||
("notice", []),
|
||||
("warning", []),
|
||||
]
|
||||
|
||||
|
||||
def test_exclusions(plan_runner):
|
||||
"Test folder-level logging exclusions."
|
||||
logging_exclusions = ("{"
|
||||
'exclusion1 = "resource.type=gce_instance", '
|
||||
'exclusion2 = "severity=NOTICE", '
|
||||
"}")
|
||||
_, resources = plan_runner(logging_exclusions=logging_exclusions)
|
||||
assert len(resources) == 2
|
||||
exclusions = [
|
||||
r for r in resources
|
||||
if r["type"] == "google_logging_organization_exclusion"
|
||||
]
|
||||
assert sorted([r["index"] for r in exclusions]) == [
|
||||
"exclusion1",
|
||||
"exclusion2",
|
||||
]
|
||||
values = [(r["index"], r["values"]["filter"]) for r in exclusions]
|
||||
assert sorted(values) == [
|
||||
("exclusion1", "resource.type=gce_instance"),
|
||||
("exclusion2", "severity=NOTICE"),
|
||||
]
|
||||
@@ -14,46 +14,32 @@
|
||||
|
||||
import pathlib
|
||||
|
||||
from .validate_policies import validate_policy_boolean, validate_policy_list, validate_policy_custom_constraints
|
||||
import pytest
|
||||
|
||||
_params = ['boolean', 'list']
|
||||
|
||||
|
||||
def test_policy_boolean(plan_runner):
|
||||
"Test boolean org policy."
|
||||
tfvars = 'test.orgpolicies-boolean.tfvars'
|
||||
_, resources = plan_runner(tf_var_file=tfvars)
|
||||
validate_policy_boolean(resources)
|
||||
|
||||
|
||||
def test_policy_list(plan_runner):
|
||||
"Test list org policy."
|
||||
tfvars = 'test.orgpolicies-list.tfvars'
|
||||
_, resources = plan_runner(tf_var_file=tfvars)
|
||||
validate_policy_list(resources)
|
||||
|
||||
|
||||
def test_policy_custom_constraints(plan_runner):
|
||||
"Test org policy custom constraints."
|
||||
tfvars = 'test.orgpolicy-custom-constraints.tfvars'
|
||||
_, resources = plan_runner(tf_var_file=tfvars)
|
||||
validate_policy_custom_constraints(resources)
|
||||
|
||||
|
||||
def test_factory_policy_boolean(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
@pytest.mark.parametrize('policy_type', _params)
|
||||
def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type):
|
||||
dest = tmp_path / 'policies.yaml'
|
||||
tfvars_to_yaml('test.orgpolicies-boolean.tfvars', dest, 'org_policies')
|
||||
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
|
||||
validate_policy_boolean(resources)
|
||||
tfvars_to_yaml(f'org_policies_{policy_type}.tfvars', dest, 'org_policies')
|
||||
tfvars_plan = plan_summary(
|
||||
'modules/organization',
|
||||
tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars'])
|
||||
yaml_plan = plan_summary('modules/organization',
|
||||
tf_var_files=['common.tfvars'],
|
||||
org_policies_data_path=f'{tmp_path}')
|
||||
assert tfvars_plan.values == yaml_plan.values
|
||||
|
||||
|
||||
def test_factory_policy_list(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
dest = tmp_path / 'policies.yaml'
|
||||
tfvars_to_yaml('test.orgpolicies-list.tfvars', dest, 'org_policies')
|
||||
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
|
||||
validate_policy_list(resources)
|
||||
|
||||
|
||||
def test_factory_policy_custom_constraints(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
def test_custom_constraint_factory(plan_summary, tfvars_to_yaml, tmp_path):
|
||||
dest = tmp_path / 'constraints.yaml'
|
||||
tfvars_to_yaml('test.orgpolicy-custom-constraints.tfvars', dest, 'org_policy_custom_constraints')
|
||||
_, resources = plan_runner(org_policy_custom_constraints_data_path=f'"{tmp_path}"')
|
||||
validate_policy_custom_constraints(resources)
|
||||
tfvars_to_yaml(f'org_policies_custom_constraints.tfvars', dest,
|
||||
'org_policy_custom_constraints')
|
||||
tfvars_plan = plan_summary(
|
||||
'modules/organization',
|
||||
tf_var_files=['common.tfvars', f'org_policies_custom_constraints.tfvars'])
|
||||
yaml_plan = plan_summary(
|
||||
'modules/organization', tf_var_files=['common.tfvars'],
|
||||
org_policy_custom_constraints_data_path=f'{tmp_path}')
|
||||
assert tfvars_plan.values == yaml_plan.values
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
|
||||
def test_resource_tags(plan_runner):
|
||||
'Test resource tags.'
|
||||
_, resources = plan_runner(tf_var_file='test.resource_tags.tfvars')
|
||||
assert len(resources) == 10
|
||||
resource_values = {}
|
||||
for r in resources:
|
||||
resource_values.setdefault(r['type'], []).append(r['values'])
|
||||
assert len(resource_values['google_tags_tag_key']) == 3
|
||||
assert len(resource_values['google_tags_tag_value']) == 3
|
||||
result = [
|
||||
r['role'] for r in resource_values['google_tags_tag_value_iam_binding']
|
||||
]
|
||||
expected = [
|
||||
'roles/resourcemanager.tagAdmin',
|
||||
'roles/resourcemanager.tagViewer',
|
||||
'roles/resourcemanager.tagViewer'
|
||||
]
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_network_tags(plan_runner):
|
||||
'Test network tags.'
|
||||
_, resources = plan_runner(tf_var_file='test.network_tags.tfvars')
|
||||
assert len(resources) == 1
|
||||
resource_values = {}
|
||||
for r in resources:
|
||||
resource_values.setdefault(r['type'], []).append(r['values'])
|
||||
google_tags_tag_key = resource_values['google_tags_tag_key'][0]
|
||||
assert google_tags_tag_key['purpose'] == "GCE_FIREWALL"
|
||||
assert google_tags_tag_key['purpose_data']['network'] == "foobar"
|
||||
|
||||
|
||||
def test_bindings(plan_runner):
|
||||
'Test tag bindings.'
|
||||
tag_bindings = '{foo = "tagValues/123456789012"}'
|
||||
_, resources = plan_runner(tag_bindings=tag_bindings)
|
||||
assert len(resources) == 1
|
||||
35
tests/modules/organization/tftest.yaml
Normal file
35
tests/modules/organization/tftest.yaml
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright 2022 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
module: modules/organization
|
||||
|
||||
common_tfvars:
|
||||
- common.tfvars
|
||||
|
||||
tests:
|
||||
audit_config:
|
||||
iam:
|
||||
iam_additive:
|
||||
logging:
|
||||
logging_exclusions:
|
||||
org_policies_list:
|
||||
org_policies_boolean:
|
||||
org_policies_custom_constraints:
|
||||
tags:
|
||||
firewall_policies:
|
||||
firewall_policies_factory:
|
||||
firewall_policies_factory_combined:
|
||||
tfvars:
|
||||
- firewall_policies.tfvars
|
||||
- firewall_policies_factory.tfvars
|
||||
@@ -31,13 +31,14 @@ def test_policy_list(plan_runner):
|
||||
|
||||
def test_factory_policy_boolean(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
dest = tmp_path / 'policies.yaml'
|
||||
tfvars_to_yaml('test.orgpolicies-boolean.tfvars', dest, 'org_policies')
|
||||
tfvars_to_yaml('fixture/test.orgpolicies-boolean.tfvars', dest,
|
||||
'org_policies')
|
||||
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
|
||||
validate_policy_boolean(resources)
|
||||
|
||||
|
||||
def test_factory_policy_list(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
dest = tmp_path / 'policies.yaml'
|
||||
tfvars_to_yaml('test.orgpolicies-list.tfvars', dest, 'org_policies')
|
||||
tfvars_to_yaml('fixture/test.orgpolicies-list.tfvars', dest, 'org_policies')
|
||||
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
|
||||
validate_policy_list(resources)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pytest>=6.2.5
|
||||
PyYAML>=6.0
|
||||
tftest>=1.7.6
|
||||
tftest>=1.8.1
|
||||
marko>=1.2.0
|
||||
deepdiff>=5.7.0
|
||||
python-hcl2>=3.0.5
|
||||
|
||||
44
tools/plan_summary.py
Executable file
44
tools/plan_summary.py
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2022 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.
|
||||
|
||||
import click
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
BASEDIR = Path(__file__).parents[1]
|
||||
sys.path.append(str(BASEDIR / 'tests'))
|
||||
|
||||
import fixtures
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument('module', type=click.Path(), nargs=1)
|
||||
@click.argument('tfvars', type=click.Path(exists=True), nargs=-1)
|
||||
def main(module, tfvars):
|
||||
module = BASEDIR / module
|
||||
summary = fixtures.plan_summary(module, Path(), tfvars)
|
||||
print(yaml.dump({'values': summary.values}))
|
||||
print(yaml.dump({'counts': summary.counts}))
|
||||
outputs = {
|
||||
k: v.get('value', '__missing__') for k, v in summary.outputs.items()
|
||||
}
|
||||
print(yaml.dump({'outputs': outputs}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user