def render(stackvars): # Terraform configuration. Terraform(required_version=stackvars.get("config.terraform.version")) # Backend configuration. Terraform( dict(backend=dict(gcs=dict( bucket=stackvars.get("config.terraform.backend.gcp.bucket"), prefix=stackvars.stack, credentials=stackvars.get("config.gcp.credentials"), )))) # Providers. Provider("google", **stackvars.get("config.gcp")) # Modules. # Resources. cluster = Resource( "google_container_cluster", "cluster", name="project-" + stackvars.environment, location=stackvars.get("config.gcp.region"), # The API requires a node pool or an initial count to be defined; that initial # count creates the "default node pool" with that # of nodes. So, we need to set # an initial_node_count of 1. This will make a default node pool with # server-defined defaults that Terraform will immediately delete as part of # Create. This leaves us in our desired state- with a cluster master with no # node pools. remove_default_node_pool=True, initial_node_count=1, ) Resource( "google_container_node_pool", "preemptible_nodes", name="preemptible-node-pool", location=stackvars.get("config.gcp.region"), cluster=cluster.name, # `node_count` represents the number of nodes PER ZONE. node_count=1, node_config=dict( preemptible=True, machine_type="n1-standard-1", oauth_scopes=[ "https://www.googleapis.com/auth/logging.write", "https://www.googleapis.com/auth/monitoring", ], ), # `node_count` represents the number of nodes PER ZONE. autoscaling=dict( min_node_count=1, max_node_count=3, ), management=dict( auto_repair=True, auto_upgrade=True, ), )
def test_interpolated(): foo = Resource('aws_security_group', 'sg', name='sg') assert foo.name == 'sg' assert foo.interpolated('name') == '${aws_security_group.sg.name}' # call .name again to ensure ._frozen is reset correctly and we can still mutate the original assert foo.name == 'sg'
def test_interpolated(): foo = Resource("aws_security_group", "sg", name="sg") assert foo.name == "sg" assert foo.interpolated("name") == "${aws_security_group.sg.name}" # call .name again to ensure ._frozen is reset correctly and we can still mutate the original assert foo.name == "sg"
def test_setattr(): res1 = Resource('res1', 'foo', attr='value') assert res1.attr == "value" assert res1._values['attr'] == "value" res1.not_tf_attr = "value" assert res1.not_tf_attr == "value" assert 'not_tf_attr' not in res1._values
def test_setattr(): res1 = Resource("res1", "foo", attr="value") assert res1.attr == "value" assert res1._values["attr"] == "value" res1.not_tf_attr = "value" assert res1.not_tf_attr == "value" assert "not_tf_attr" not in res1._values
def test_compile(): TFObject.reset() Resource("res1", "foo", attr="value") Resource("res1", "bar", attr="other") Variable("var1", default="value") assert TFObject.compile() == { "resource": {"res1": {"foo": {"attr": "value",}, "bar": {"attr": "other",}},}, "variable": {"var1": {"default": "value"}}, }
def test_object_instances(): res = Resource("res1", "foo", attr="value") var = Variable("var1", default="foo") assert TFObject._instances is None assert Resource._instances == [res] assert Variable._instances == [var]
def test_object_instances(): res = Resource('res1', 'foo', attr='value') var = Variable('var1', default='foo') assert TFObject._instances is None assert Resource._instances == [res] assert Variable._instances == [var]
def create_backend_service(self): self.backend_service_name = self.name + "-backend-service" self.backend_service = Resource( "google_compute_backend_service", self.backend_service_name, name=self.backend_service_name, health_checks=[self.health_check], backend={"group": self.managed_instance_group})
def test_getattr(): res1 = Resource("res1", "foo", attr="value") assert res1.id == "${res1.foo.id}" var1 = Variable("var1", default="value") assert "{0}".format(var1) == "${var.var1}" with pytest.raises(AttributeError): assert var1.id, "nope! vars do not have attrs!"
def test_explicit_provider(): # When a resource is given an explicit provider argument it should take precendence # Even if we're in a provider resource block p1 = Provider("aws", region="us-west-2", alias="west2") with Provider("aws", region="us-east-1", alias="east1"): sg1 = Resource("aws_security_group", "sg", ingress=["foo"], provider=p1.as_provider()) sg2 = Resource("aws_security_group", "sg", ingress=["foo"]) with Provider("aws", region="eu-west-2", alias="london"): sg3 = Resource("aws_security_group", "sg", ingress=["foo"]) assert sg1.provider == "aws.west2" assert sg2.provider == "aws.east1" assert sg3.provider == "aws.london"
def test_getattr(): res1 = Resource('res1', 'foo', attr='value') assert res1.id == '${res1.foo.id}' var1 = Variable('var1', default='value') assert '{0}'.format(var1) == '${var.var1}' with pytest.raises(AttributeError): assert var1.id, 'nope! vars do not have attrs!'
def test_access_before_compile(): sg = Resource('aws_security_group', 'sg', ingress=['foo']) assert sg.id == '${aws_security_group.sg.id}' assert sg.ingress == ['foo'] TFObject._frozen = True assert sg.ingress == '${aws_security_group.sg.ingress}'
def test_access_before_compile(): sg = Resource("aws_security_group", "sg", ingress=["foo"]) assert sg.id == "${aws_security_group.sg.id}" assert sg.ingress == ["foo"] TFObject._frozen = True assert sg.ingress == "${aws_security_group.sg.ingress}"
def test_equality(): # NamedObject p1 = Provider("mysql", host="db") p2 = Provider("mysql", host="db") v1 = Variable("mysql", host="db") assert p1 == p2 assert p1 != v1 # TypedObject r1 = Resource("aws_security_group", "sg", name="sg") r2 = Resource("aws_security_group", "sg", name="sg") d1 = Data("aws_security_group", "sg", name="sg") assert r1 == r2 assert r1 != d1 # Invalid comparisons assert r1 != "string" assert r1 != 0
def create_frontend_service(self): self.frontend_service_name = self.name + "-frontend-service" self.frontend_service = Resource( "google_compute_global_forwarding_rule", self.frontend_service_name, name=self.frontend_service_name, target=self.http_proxy.self_link, port_range="8080", load_balancing_scheme="EXTERNAL", ip_address=self.global_ip_address.address)
def test_object_variants(): with Variant('foo', default='value'): sg = Resource('aws_security_group', 'sg', foo_variant=dict(ingress=['foo']), bar_variant=dict(ingress=['bar'])) assert sg.ingress == ['foo'] # objects do not pickup defaults from variants assert sg.default != 'value'
def create_managed_instance_group(self): self.managed_instance_group_name = self.instance_template + "-group" self.managed_instance_group = Resource( "google_compute_instance_group_manager", self.managed_instance_group_name, name=self.managed_instance_group_name, base_instance_name=self.instance_template.name, zone=self.zone, version={ "instance_template": self.instance_template.self_link, "initial_delay_sec": 180 })
def test_typed_object_hooks(mocker): """Ensure that our hooks work as expected and can modify resource data during compilation""" TFObject.reset() def attr_always_true(object_id, object_attrs): """attr_always_true is a contrived hook that ensures `attr` of `some_type` resources is always True""" object_attrs = object_attrs.copy() for attr_name in object_attrs: if attr_name == "attr": object_attrs[attr_name] = True return object_attrs # Make our hook a mock so we can assert on calls mock_hook = mocker.MagicMock(side_effect=attr_always_true) # Add the hook for resources Resource.add_hook("some_type", mock_hook) Resource("some_type", "some_id", attr=True) Resource("some_type", "other_id", attr=False) compiled = TFObject.compile() assert mock_hook.mock_calls == [ mocker.call("some_id", {"attr": True}), mocker.call("other_id", {"attr": False}), ] assert compiled == { "resource": { "some_type": { "some_id": { "attr": True, }, "other_id": { "attr": True, }, } } }
def create_health_check(self): self.health_check_name = self.instance_template + "-health-check" self.health_check = Resource("google_compute_health_check", self.health_check_name, name=self.health_check_name, check_interval_sec=10, timeout_sec=10, healthy_threshold=3, unhealthy_threshold=3, http_health_check={ "request_path": "/api/v1/healthcheck", "port": 8080 })
def test_provider_context(): with Provider("aws", region="us-east-1", alias="east1"): sg1 = Resource("aws_security_group", "sg", ingress=["foo"]) # Since thing1 is not an aws_ resource it will not get the provider by default thing1 = Resource("some_thing", "foo", bar="baz") # var1 is not a typedobject so it will not get a provider either var1 = Variable("var1", default="foo") with Provider("aws", region="us-west-2", alias="west2"): sg2 = Resource("aws_security_group", "sg", ingress=["foo"]) assert sg1.provider == "aws.east1" assert sg2.provider == "aws.west2" # thing1's provider is the default interpolation string assert thing1.provider == "${some_thing.foo.provider}" # var1 will raise a AttributeError with pytest.raises(AttributeError): assert var1.provider
def test_object_variants(): with Variant("foo", default="value"): sg = Resource( "aws_security_group", "sg", foo_variant=dict(ingress=["foo"]), bar_variant=dict(ingress=["bar"]), ) assert sg.ingress == ["foo"] # objects do not pickup defaults from variants assert sg.default != "value"
def test_provider_context(): with Provider("aws", region="us-east-1", alias="east1"): sg1 = Resource('aws_security_group', 'sg', ingress=['foo']) # Since thing1 is not an aws_ resource it will not get the provider by default thing1 = Resource('some_thing', 'foo', bar='baz') # var1 is not a typedobject so it will not get a provider either var1 = Variable('var1', default='foo') with Provider("aws", region="us-west-2", alias="west2"): sg2 = Resource('aws_security_group', 'sg', ingress=['foo']) assert sg1.provider == 'aws.east1' assert sg2.provider == 'aws.west2' # thing1's provider is the default interpolation string assert thing1.provider == '${some_thing.foo.provider}' # var1 will raise a AttributeError with pytest.raises(AttributeError): assert var1.provider
def create_autoscaler(self): self.autoscaler_name = self.instance_template + "-autoscaler" self.autoscaler = Resource("google_compute_autoscaler", self.autoscaler_name, name=self.autoscaler_name, zone=self.zone, target=self.managed_instance_group.id, autoscaling_policy={ "max_replicas": 5, "min_replicas": 2, "cooldown_period": 60, "cpu_utilization": { "target": 0.75 } })
def test_compile(): TFObject.reset() Resource('res1', 'foo', attr='value') Resource('res1', 'bar', attr='other') Variable('var1', default='value') assert TFObject.compile() == { 'resource': { 'res1': { 'foo': { 'attr': 'value', }, 'bar': { 'attr': 'other', } }, }, 'variable': { 'var1': { 'default': 'value' } } }
def create_resources(self): self.instance_template = Resource( "google_compute_instance_template", self.name, name=self.name, machine_type="f1-micro", disk={ "source_image":"cos-cloud/cos-stable-81-12871-69-0", "disk_size_gb":5 }, metadata={ "gce-container-declaration":"spec:\n containers:\n - name: {}\n image: '{}'\n stdin: false\n tty: false\n restartPolicy: Always\n".format(self.name, self.container) }, network_interface={ "network":"development_network", "subnetwork":"development_subnet", "access_config":{} }, tags=self.tags )
def create_external_ip_address(self): self.global_ip_address_name = self.name + "-ip-address" self.global_ip_address = Resource("google_compute_global_address", self.global_ip_address_name, name=self.global_ip_address_name)
def create_url_map(self): self.url_map_name = self.name + "-url-map" self.url_map = Resource("google_compute_url_map", self.url_map_name, name=self.url_map_name, default_service=self.backend_service.self_link)
def create_http_proxy(self): self.http_proxy_name = self.name + "-http-proxy" self.http_proxy = Resource("google_compute_target_http_proxy", self.http_proxy_name, name=self.http_proxy_name, url_map=self.url_map.self_link)
ami = Data('aws_ami', 'ecs_ami', most_recent=True, filter=[ dict(name='name', values=['*amazon-ecs-optimized']), dict(name='owner-alias', values=['amazon']) ]) spot_fleet_role = Resource('aws_iam_role', 'spot_fleet', name='spot_fleet', assume_role_policy='''{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "spotfleet.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }''') Resource( 'aws_iam_policy_attachment', 'spot_fleet', name='spot_fleet', roles=[spot_fleet_role.name], policy_arn='arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetRole')