def config(): return types.MachConfig( mach_composer=types.MachComposerConfig(version="1.1"), general_config=types.GlobalConfig( environment="test", terraform_config=types.TerraformConfig( aws_remote_state=types.AWSTFState( bucket="unittest", key_prefix="test", region="eu-central-1", )), cloud=types.CloudOption.AWS, sentry=types.SentryConfig(dsn="sentry-dsn"), ), sites=[ types.Site( identifier="unittest-nl", components=[types.Component(name="api-extensions", )], aws=types.SiteAWSSettings( account_id=1234567890, region="eu-central-1", ), ), ], components=[ types.ComponentConfig( name="api-extensions", source="some-source//terraform", version="1.0", ) ], output_path=tempfile.gettempdir(), )
def test_generate_azure_w_endpoints(azure_config: types.MachConfig, tf_mock): config = azure_config config.sites[0].endpoints = [ types.Endpoint(key="public", url="api.mach-example.com") ] data = tf.generate(parse.parse_config(config)) # 'public' endpoint not used in component yet; no resources created assert tf.get_resource_ids(data) == [ "azurerm_app_service_plan.functionapps", "azurerm_resource_group.main", ] config.components[0].endpoints = { "main": "public", } data = tf.generate(parse.parse_config(config)) # Frontdoor instance need to be created since a component now uses it expected_resources = [ "azurerm_app_service_plan.functionapps", "azurerm_dns_cname_record.public", "azurerm_frontdoor.app-service", "azurerm_frontdoor_custom_https_configuration.public", "azurerm_resource_group.main", ] assert tf.get_resource_ids(data) == expected_resources config.sites[0].endpoints.append( types.Endpoint(key="private", url="private-api.mach-example.com")) data = tf.generate(parse.parse_config(config)) # We've added an extra endpoint definition, but hasn't been used. # List of resources should be the same as previous check assert tf.get_resource_ids(data) == expected_resources config.components.append( types.ComponentConfig( name="logger", source="some-source//terraform", version="1.0", endpoints={ "main": "private", }, )) config.sites[0].components.append(types.Component(name="logger", )) data = tf.generate(parse.parse_config(config)) assert tf.get_resource_ids(data) == [ "azurerm_app_service_plan.functionapps", "azurerm_dns_cname_record.private", "azurerm_dns_cname_record.public", "azurerm_frontdoor.app-service", "azurerm_frontdoor_custom_https_configuration.private", "azurerm_frontdoor_custom_https_configuration.public", "azurerm_resource_group.main", ]
def azure_config(): return types.MachConfig( mach_composer=types.MachComposerConfig(version="1.1"), general_config=types.GlobalConfig( environment="test", terraform_config=types.TerraformConfig( azure_remote_state=types.AzureTFState( resource_group="shared-rg", storage_account="machsaterra", container_name="tfstate", state_folder="test", )), azure=types.AzureConfig( tenant_id="6f10659d-4227-43e6-95ab-80d12a18acf9", subscription_id="5f34d95d-4dd8-40b3-9d18-f9007e2ce6ac", region="westeurope", frontdoor=types.FrontdoorSettings( dns_resource_group="shared-rg"), ), cloud=types.CloudOption.AZURE, sentry=types.SentryConfig(dsn="sentry-dsn"), ), sites=[ types.Site( identifier="unittest-nl", components=[types.Component(name="api-extensions", )], ), ], components=[ types.ComponentConfig( name="api-extensions", source="some-source//terraform", version="1.0", azure=types.ComponentAzureConfig(short_name="apiexts", service_plan="default"), ), types.ComponentConfig( name="product-types", source="product-types//terraform", version="v0.1.0", integrations=[""], ), types.ComponentConfig( name="payment", source="payment//terraform", azure=types.ComponentAzureConfig(short_name="payment"), version="1.0", ), ], output_path=tempfile.gettempdir(), )
def test_generate_azure_service_plans(azure_config: types.MachConfig, tf_mock): config = azure_config config.components[0].azure = None config.sites[0].components.append( types.Component( name="payment", ) ) data = tf.generate(parse.parse_config(config)) assert tf.get_resource_ids(data) == [ "azurerm_resource_group.main", ] config.sites[0].components[0].azure.service_plan = "default" data = tf.generate(parse.parse_config(config)) assert tf.get_resource_ids(data) == [ "azurerm_app_service_plan.functionapps", "azurerm_resource_group.main", ] config.general_config.azure.service_plans["premium"] = types.ServicePlan( kind="Linux", tier="PremiumV2", size="P2v2", ) data = tf.generate(parse.parse_config(config)) assert tf.get_resource_ids(data) == [ "azurerm_app_service_plan.functionapps", "azurerm_resource_group.main", ] config.sites[0].components[0].azure.service_plan = "premium" data = tf.generate(parse.parse_config(config)) assert tf.get_resource_ids(data) == [ "azurerm_app_service_plan.functionapps_premium", "azurerm_resource_group.main", ] config.sites[0].components[1].azure.service_plan = "default" data = tf.generate(parse.parse_config(config)) assert tf.get_resource_ids(data) == [ "azurerm_app_service_plan.functionapps", "azurerm_app_service_plan.functionapps_premium", "azurerm_resource_group.main", ]
def test_validate_component_endpoint(config: types.MachConfig): """An endpoint defined on a component must exist for all sites that use that component.""" config.components[0].endpoints = { "main": "public", } config = parse.parse_config(config) with pytest.raises(ValidationError): validate.validate_config(config) config.sites[0].endpoints = [ types.Endpoint( key="public", url="api.mach-example.com", ), types.Endpoint( key="services", url="services.mach-example.com", ), ] validate.validate_config(config) new_site = types.Site( identifier="unittest-nl", components=[], aws=types.SiteAWSSettings( account_id=1234567890, region="eu-central-1", ), ) config.sites.append(new_site) validate.validate_config(config) new_site.components.append(types.Component(name="api-extensions", )) config = parse.parse_config(config) with pytest.raises(ValidationError): validate.validate_config(config)
def test_generate_aws_w_endpoints(config: types.MachConfig, tf_mock): config.sites[0].endpoints = [ types.Endpoint(key="public", url="api.mach-example.com") ] data = tf.generate(parse.parse_config(config)) # 'public' endpoint not used in component yet; no resources created assert "resource" not in data config.components[0].endpoints = { "main": "public", } data = tf.generate(parse.parse_config(config)) # API gateway items need to be created since a component now uses it expected_resources = [ "aws_acm_certificate.public", "aws_apigatewayv2_api.public_gateway", "aws_apigatewayv2_api_mapping.public", "aws_apigatewayv2_domain_name.public", "aws_apigatewayv2_route.public_application", "aws_apigatewayv2_stage.public_default", "aws_route53_record.public", "aws_route53_record.public_acm_validation", ] expected_data_sources = [ "aws_route53_zone.mach_examplecom", ] assert tf.get_resource_ids(data) == expected_resources assert tf.get_data_ids(data) == expected_data_sources config.sites[0].endpoints.append( types.Endpoint(key="private", url="private-api.mach-services.io")) data = tf.generate(parse.parse_config(config)) # We've added an extra endpoint definition, but hasn't been used. # List of resources should be the same as previous check assert tf.get_resource_ids(data) == expected_resources assert tf.get_data_ids(data) == expected_data_sources config.components.append( types.ComponentConfig( name="logger", source="some-source//terraform", version="1.0", endpoints={ "main": "private", }, )) config.sites[0].components.append(types.Component(name="logger", )) data = tf.generate(parse.parse_config(config)) assert tf.get_resource_ids(data) == [ "aws_acm_certificate.private", "aws_acm_certificate.public", "aws_apigatewayv2_api.private_gateway", "aws_apigatewayv2_api.public_gateway", "aws_apigatewayv2_api_mapping.private", "aws_apigatewayv2_api_mapping.public", "aws_apigatewayv2_domain_name.private", "aws_apigatewayv2_domain_name.public", "aws_apigatewayv2_route.private_application", "aws_apigatewayv2_route.public_application", "aws_apigatewayv2_stage.private_default", "aws_apigatewayv2_stage.public_default", "aws_route53_record.private", "aws_route53_record.private_acm_validation", "aws_route53_record.public", "aws_route53_record.public_acm_validation", ] assert tf.get_data_ids(data) == [ "aws_route53_zone.mach_examplecom", "aws_route53_zone.mach_servicesio", ]
def test_site(config: types.MachConfig): config.components.append( types.ComponentConfig( name="private-api", source="some-private-source/terraform", version="1.0", )) config.sites[0].components.append(types.Component(name="private-api")) config.sites[0].endpoints = [ types.Endpoint( key="public", url="api.example.com", ), types.Endpoint( key="private", url="private-api.example.com", ), ] config = parse.parse_config(config) site = config.sites[0] assert site.used_endpoints == [] config.components[0].endpoints = { "main": "public", } config = parse.parse_config(config) assert site.used_endpoints == [ types.Endpoint(key="public", url="api.example.com", components=[site.components[0]]) ] config.components[1].endpoints = { "main": "public", } config = parse.parse_config(config) assert site.used_endpoints == [ types.Endpoint( key="public", url="api.example.com", components=[site.components[0], site.components[1]], ) ] config.components[1].endpoints = { "main": "private", } config = parse.parse_config(config) assert site.used_endpoints == [ types.Endpoint( key="public", url="api.example.com", components=[ site.components[0], ], ), types.Endpoint( key="private", url="private-api.example.com", components=[site.components[1]], ), ]
def _create_config() -> types.MachConfig: # noqa: C901 environment = click.prompt("Environment", "test") cloud = click.prompt("Cloud environment", type=click.Choice(["aws", "azure"]), default="aws") site_id = click.prompt("Site identifier") use_commercetools = click.confirm("Use commercetools?", default=True) ct_project = "" if use_commercetools: ct_project = click.prompt("commercetools project name", default=site_id) use_sentry = click.confirm("Use Sentry?", default=False) use_contentful = click.confirm("Use Contentful?", default=False) use_amplience = click.confirm("Use Amplience?", default=False) integrations = [] if use_commercetools: integrations.append("commercetools") if use_sentry: integrations.append("sentry") if use_contentful: integrations.append("contentful") if use_amplience: integrations.append("amplience") # If we do have integrations, add the default (cloud) integration here as well if integrations: integrations = [cloud] + integrations if cloud == "aws": tf_config = types.TerraformConfig(aws_remote_state=types.AWSTFState( bucket="<your bucket>", key_prefix="mach", region="eu-central-1", )) else: tf_config = types.TerraformConfig( azure_remote_state=types.AzureTFState( resource_group="<your-resource-group>", storage_account="<your-storage-account>", container_name="<your-container-name>", state_folder=environment, )) general_config_kwargs = dict( environment=environment, terraform_config=tf_config, cloud=cloud, ) if cloud == "azure": general_config_kwargs["azure"] = types.AzureConfig( tenant_id="<your-tenant-id>", subscription_id="<your-subscription-id>", region="westeurope", ) if use_sentry: general_config_kwargs["sentry"] = types.SentryConfig( auth_token="<your-auth-token>", project="<your-project>", organization="<your-organization>", ) if use_contentful: general_config_kwargs["contentful"] = types.ContentfulConfig( cma_token="<your-cma-token>", organization_id="<your-organization-id>", ) if use_amplience: general_config_kwargs["amplience"] = types.AmplienceConfig( client_id="<your-client-id>", client_secret="<your-client-secret>", ) site = types.Site( identifier=site_id, components=[ types.Component( name="your-component", variables={"FOO_VAR": "my-value"}, secrets={"MY_SECRET": "secretvalue"}, ) ], ) if cloud == "aws": site.aws = types.SiteAWSSettings( account_id=123456789, region="eu-central-1", ) if use_commercetools: site.commercetools = types.CommercetoolsSettings( project_key=ct_project, client_id="<client-id>", client_secret="<client-secret>", scopes=(f"manage_api_clients:{ct_project} " f"manage_project:{ct_project} " f"view_api_clients:{ct_project}"), project_settings=types.CommercetoolsProjectSettings( # TODO: Improve this by letting user select one or more countries # and generate/guess the correct languages and currencies that probably # need to be applied for that project languages=["nl-NL"], countries=["NL"], currencies=["EUR"], ), ) component_config = types.ComponentConfig( name="your-component", source= "git::https://github.com/<username>/<your-component>.git//terraform", version="0.1.0", integrations=integrations, ) if cloud == "azure": component_config.azure = types.ComponentAzureConfig( short_name="yourcomp", ) return types.MachConfig( mach_composer=types.MachComposerConfig(version=__version__), general_config=types.GlobalConfig(**general_config_kwargs, ), sites=[site], components=[component_config], )