def check_sa(service):
    sa = get_sa(service)
    echo(f"Associated service account: {sa}")

    result(
        f"Associated service account is not the default service account",
        details="Ensure a custom service account is associated to the service",
        success=("compute-" not in sa),
    )
def check_bucket(media_bucket):
    header("Object storage checks")
    sapi = build("storage", "v1")
    try:
        bucket = sapi.buckets().get(bucket=media_bucket).execute()
        result(
            f"Storage bucket {bucket['name']} exists in {bucket['location']}")
    except googleapiclient.errors.HttpError as e:
        result(f"Storage bucket error {e}", success=False)
def get_secret(project, secret_name):
    sm = sml.SecretManagerServiceClient()  # using static library
    secret_path = f"projects/{project}/secrets/{secret_name}/versions/latest"
    try:
        payload = sm.access_secret_version(
            name=secret_path).payload.data.decode("UTF-8")
        return payload
    except exceptions.PermissionDenied as e:
        result(f"Secret error: {e}", success=False)
        return ""
def check_database(project, service, secrets):

    header("Database checks")
    database_name = service["spec"]["template"]["metadata"]["annotations"][
        "run.googleapis.com/cloudsql-instances"]
    echo(f"Associated database: {database_name}")
    _, dbregion, dbinstance = database_name.split(":")

    result(
        f"Associated database instance matches secret connection URL instance",
        success=(secrets["dbinstance"] == database_name),
    )

    dbapi = build("sqladmin", "v1beta4")
    instance = dbapi.instances().get(project=project,
                                     instance=dbinstance).execute()
    result(
        f"Instance exists: {instance['name']}, running {instance['databaseVersion']}"
    )

    database = (dbapi.databases().get(project=project,
                                      instance=dbinstance,
                                      database=secrets["dbname"]).execute())
    result(
        f"Database exists: {database['name']}, collation {database['collation']}"
    )

    users = dbapi.users().list(project=project, instance=dbinstance).execute()
    result(
        f"User exists: {secrets['dbuser']}",
        details=users["items"],
        success=(secrets["dbuser"]
                 in [user["name"] for user in users["items"]]),
    )
def check_roles(service, project):
    sa = f"serviceAccount:{get_sa(service)}"
    crm = build("cloudresourcemanager", "v1")
    iam = crm.projects().getIamPolicy(resource=f"{project}").execute()

    required_roles = ["roles/run.admin", "roles/cloudsql.client"]

    member_roles = [b["role"] for b in iam["bindings"] if sa in b["members"]]

    for role in required_roles:
        result(
            f"SA has {role}",
            details=f"Ensure SA has {role}",
            success=(role in member_roles),
        )
def check_bindings(service, project):
    sa = get_sa(service)
    echo(f"Associated service account (SA): {sa}")

    success = True
    crm = build("cloudresourcemanager", "v1")
    iam = crm.projects().getIamPolicy(resource=f"{project}").execute()
    for binding in iam["bindings"]:
        if binding["role"] == "roles/owner":
            for member in binding["members"]:
                if member == sa:
                    success = False
    result(
        "SA doesn't have Owner role",
        details="Remove service account from having Owner role",
        success=success,
    )
def check_envvars(project, service):
    envvars = service["spec"]["template"]["spec"]["containers"][0]["env"]
    current_host = [x["value"] for x in envvars if x["name"] == "CURRENT_HOST"]

    if not current_host:
        result(
            "CURRENT_HOST envvar not found",
            details="Check the service environment variables.",
            success=False,
        )
    else:
        host = current_host[0]
        service_host = service["status"]["url"]
        if host == service_host:
            result(f"CURRENT_HOST set to service URL ({host}).")
        else:
            result(
                f"CURRENT_HOST ({host}) and service URL ({service_host}) don't match.",
                success=False)
def check_run(service):
    header(f"Service configuration checks")

    sn = service["metadata"]["name"]
    result(f"Service {sn} exists")
def check_secrets(values):
    header("Settings checks")
    for key in ["DATABASE_URL", "GS_BUCKET_NAME", "SECRET_KEY"]:
        result(f"{key} is defined", success=(key in values.keys()))
def check_unicodex(project, service):
    header("Deployed service checks")

    fixture_code = "1F44B"
    fixture_slug = f"/u/{fixture_code}"
    login_slug = "/admin/login/?next=/admin/"
    model_admin_slug = "/admin/unicodex/codepoint/"

    if "url" not in service["status"].keys():
        message = service["status"]["conditions"][0]["message"]
        result(f"Service does not have a deployed URL: {message}",
               success=False)
    else:
        url = service["status"]["url"]
        echo(f"Service deployment URL: {url}")

        try:
            response = httpx.get(url, timeout=30)

        except httpx.ReadTimeout as e:
            result(e, success=False)
            return

        print(cleanhtml(response.text))
        if response.status_code == 200:
            result("Index page loaded successfully")
        else:
            result(f"Index page returns an error: {response.status_code}",
                   success=False)

        if "Unicodex" in response.text:
            result("Index page contains 'Unicodex'")
        else:
            result("Index page does not contain the string 'Unicodex'",
                   success=False)

        fixture = httpx.get(url + fixture_slug)
        print(cleanhtml(fixture.text))

        admin = httpx.get(url + login_slug)
        if not admin.is_error:
            result(f"Django admin returns status okay ({admin.status_code})")
        else:
            result(f"Django admin returns an error: {admin.status_code}",
                   success=False)

        if "Log in" in admin.text and "Django administration" in admin.text:
            result("Django admin login screen successfully loaded")
        else:
            result("Django admin login not found",
                   success=False,
                   details=admin.text)

        headers = {"Referer": url}
        with httpx.Client(headers=headers, follow_redirects=True,
                          timeout=30) as client:

            # Login
            admin_username = get_secret(project, "SUPERUSER")
            admin_password = get_secret(project, "SUPERPASS")

            header("Test Django Admin")
            client.get(url + login_slug)
            response = client.post(
                url + login_slug,
                data={
                    "username": admin_username,
                    "password": admin_password,
                    "csrfmiddlewaretoken": client.cookies["csrftoken"],
                },
            )
            assert not response.is_error
            assert "Site administration" in response.text
            assert "Codepoints" in response.text
            result(f"Django Admin logged in")

            # Try admin action
            response = client.post(
                url + model_admin_slug,
                data={
                    "action": "generate_designs",
                    "_selected_action": 1,
                    "csrfmiddlewaretoken": client.cookies["csrftoken"],
                },
            )
            assert not response.is_error
            assert "Imported vendor versions" in response.text
            result(f"Django Admin action completed")

            # check updated feature
            response = client.get(url + f"/u/{fixture_code}")
            assert fixture_code in response.text
            assert "Android" in response.text
            result(f"Django Admin action verified")

            print(cleanhtml(response.text))
def check_deploy(project, service_name, region, secret_name):
    click.secho(f"🛠  Checking {service_name} in {region} in {project}", bold=True)

    header(f"Service configuration checks")

    api = build("run", "v1")
    fqname = f"projects/{project}/locations/{region}/services/{service_name}"
    service = api.projects().locations().services().get(name=fqname).execute()

    sn = service["metadata"]["name"]
    result(f"Service {sn} exists")

    sa = service["spec"]["template"]["spec"]["serviceAccountName"]
    echo(f"Associated service account: {sa}")

    ###
    result(
        f"Associated service account is not default",
        details="Ensure a custom service account is associated to the service",
        success=("compute-" not in sa),
    )

    ###
    success = True
    crm = build("cloudresourcemanager", "v1")
    iam = crm.projects().getIamPolicy(resource=f"{project}").execute()
    for binding in iam["bindings"]:
        if binding["role"] == "roles/owner":
            echo("Checking roles/owner bindings")
            for member in binding["members"]:
                echo(member, indent="> ")
                if member == sa:
                    success = True
    result(
        "Associated service account permissions aren't owner",
        details="Ensure the service account isn't using owner permissions",
        success=success,
    )

    ###
    header("Deployed service checks")
    url = service["status"]["url"]
    echo(f"Service deployment URL: {url}")

    page = httpx.get(url)
    if page.status_code == 200:
        result("Index page loaded successfully")
    else:
        result(f"Index page returns an error: {page.status_code}", success=False)

    if "Unicodex" in page.text:
        result("Index page contains 'Unicodex'")
    else:
        result("Index page does not contain the string 'Unicodex'", success=False)

    admin = httpx.get(url + "/admin")
    if admin.status_code == 200:
        result("Django admin returns status 200")
    else:
        result(f"Django admin returns an error: {page.status_code}", success=False)

    if "Log in" in admin.text and "Django administration" in admin.text:
        result("Django admin login screen successfully loaded")
    else:
        result("Django admin login not found", success=False, details=admin.text)

    ###
    header("Secret checks")
    sm = sml.SecretManagerServiceClient()  # using static library
    secret_path = f"projects/{project}/secrets/{secret_name}/versions/latest"
    payload = sm.access_secret_version(name=secret_path).payload.data.decode("UTF-8")

    result(f"Secret {secret_path} exist")
    # https://github.com/theskumar/python-dotenv#in-memory-filelikes
    values = dotenv_values(stream=StringIO(payload))
    for key in ["DATABASE_URL", "GS_BUCKET_NAME", "SECRET_KEY"]:
        result(f"{key} is defined", success=(key in values.keys()))

    secret_dburl = urlparse(values["DATABASE_URL"])
    secret_dbuser = secret_dburl.netloc.split(":")[0]
    secret_dbinstance, secret_dbname = secret_dburl.path.split("/")[3:]
    media_bucket = values["GS_BUCKET_NAME"]

    ###
    header("Object storage checks")
    sapi = build("storage", "v1")
    bucket = sapi.buckets().get(bucket=media_bucket).execute()
    result(f"Storage bucket {bucket['name']} exists in {bucket['location']}")
    # TODO check bucket permissions.

    ###
    header("Database checks")
    database_name = service["spec"]["template"]["metadata"]["annotations"][
        "run.googleapis.com/cloudsql-instances"
    ]
    echo(f"Associated database: {database_name}")
    _, dbregion, dbinstance = database_name.split(":")

    result(
        f"Associated database instance matches secret connection URL instance",
        success=(secret_dbinstance == database_name),
    )

    dbapi = build("sqladmin", "v1beta4")
    instance = dbapi.instances().get(project=project, instance=dbinstance).execute()
    result(
        f"Instance exists: {instance['name']}, running {instance['databaseVersion']}"
    )

    database = (
        dbapi.databases()
        .get(project=project, instance=dbinstance, database=secret_dbname)
        .execute()
    )
    result(f"Database exists: {database['name']}, collation {database['collation']}")

    users = dbapi.users().list(project=project, instance=dbinstance).execute()
    result(
        f"User exists: {secret_dbuser}",
        details=users["items"],
        success=(secret_dbuser in [user["name"] for user in users["items"]]),
    )

    # All checks complete; show results.
    summary()