Example #1
0
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(),
    )
Example #2
0
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",
    ]
Example #3
0
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(),
    )
Example #4
0
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",
    ]
Example #5
0
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)
Example #6
0
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",
    ]
Example #7
0
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]],
        ),
    ]
Example #8
0
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],
    )