Skip to content

Commit 353f7a7

Browse files
authored
Build Observatory API Image & Push to Google Cloud Artifact Registry (#558)
1 parent e47f13f commit 353f7a7

17 files changed

Lines changed: 173 additions & 110 deletions

.github/workflows/build-api-container-develop.yml

Lines changed: 0 additions & 22 deletions
This file was deleted.

.github/workflows/build-api-container-production.yml

Lines changed: 0 additions & 25 deletions
This file was deleted.

.github/workflows/build-api-container-staging.yml

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Build Observatory API
2+
3+
# Run workflow on push to any branch or tag
4+
on: [ push ]
5+
6+
env:
7+
REGISTRY: us-docker.pkg.dev
8+
USER_NAME: _json_key_base64
9+
IMAGE_PATH: observatory-platform/observatory-api
10+
11+
jobs:
12+
build-and-push-image:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
packages: write
16+
contents: read
17+
18+
strategy:
19+
matrix:
20+
environment: [ develop, staging, production ]
21+
include:
22+
- environment: develop
23+
gcp_project_id: DEVELOP_GCP_PROJECT_ID
24+
- environment: staging
25+
gcp_project_id: STAGING_GCP_PROJECT_ID
26+
- environment: production
27+
gcp_project_id: PRODUCTION_GCP_PROJECT_ID
28+
29+
steps:
30+
- name: Checkout project
31+
uses: actions/checkout@v3
32+
33+
- name: Log in to the Container registry
34+
uses: docker/login-action@v1
35+
with:
36+
registry: ${{ env.REGISTRY }}
37+
username: ${{ env.USER_NAME }}
38+
password: ${{ secrets.ARTIFACT_REGISTRY_GCP_SERVICE_KEY }}
39+
40+
- name: Extract metadata (tags, labels) for Docker
41+
id: meta
42+
uses: docker/metadata-action@v3
43+
with:
44+
images: ${{ env.REGISTRY }}/${{ secrets[matrix.gcp_project_id] }}/${{ env.IMAGE_PATH }}
45+
tags: |
46+
# On branch
47+
type=ref,event=branch
48+
49+
# Set latest tag for main branch
50+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
51+
52+
# On push tag use version
53+
type=semver,pattern={{version}}
54+
55+
- name: Build and push Docker image
56+
uses: docker/build-push-action@v3
57+
with:
58+
context: ./observatory-api/
59+
push: true
60+
61+
tags: ${{ steps.meta.outputs.tags }}
62+
labels: ${{ steps.meta.outputs.labels }}

observatory-api/Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ ENV PYTHONUNBUFFERED True
3030
RUN mkdir /observatory-api
3131
COPY . ./observatory-api
3232

33+
# Setup correct requirements
34+
WORKDIR /observatory-api
35+
RUN mv -f requirements.cloudrun.txt requirements.txt
36+
3337
# Install observatory-api
34-
RUN pip3 install /observatory-api
38+
RUN pip3 install .
3539

3640
# Install berglas
3741
COPY --from=gcr.io/berglas/berglas:latest /bin/berglas /bin/berglas
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
gunicorn>=20.1.0,<21
2+
Flask>=1.1.4,<2
3+
connexion[swagger-ui]>=2.7.0,<3
4+
elasticsearch>=7.13.3,<8
5+
pyyaml>=5.1,<6
6+
SQLAlchemy>=1.3.18,<2
7+
psycopg2-binary>=2.9.1,<3
8+
urllib3>=1.25.11,<2
9+
python-dateutil>=2.8.2,<3
10+
certifi>=2021.5.30
11+
pendulum>=2.1.2,<3
12+
openapi-spec-validator>=0.3.1,<1
13+
markupsafe==2.0.1

observatory-platform/observatory/platform/cli/generate_command.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,7 @@ def config_api(config: TerraformConfig):
10111011

10121012
click.echo("Configuring the Observatory API")
10131013

1014-
text = "Custom domain name for the API, used for the google cloud endpoints service"
1014+
text = "Custom domain name for the API, used for the Google Cloud Endpoints service"
10151015
default = "api.observatory.academy"
10161016
domain_name = click.prompt(text=text, type=str, default=default, show_default=True)
10171017

@@ -1020,7 +1020,17 @@ def config_api(config: TerraformConfig):
10201020
choices = click.Choice(choices=["project_id", "environment"], case_sensitive=False)
10211021
subdomain = click.prompt(text=text, type=choices, default=default, show_default=True, show_choices=True)
10221022

1023+
text = "Observatory API Docker Image"
1024+
default = "us-docker.pkg.dev/academic-observatory/observatory-platform/observatory-api:latest"
1025+
api_image = click.prompt(text=text, type=str, default=default, show_default=True)
1026+
1027+
text = "Endpoints Runtime Docker Image"
1028+
default = "gcr.io/endpoints-release/endpoints-runtime-serverless:2"
1029+
er_image = click.prompt(text=text, type=str, default=default, show_default=True)
1030+
10231031
config.api = Api(
10241032
domain_name=domain_name,
10251033
subdomain=subdomain,
1034+
api_image=api_image,
1035+
er_image=er_image,
10261036
)

observatory-platform/observatory/platform/observatory_config.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,18 @@
2929
import yaml
3030
from cerberus import Validator
3131
from cryptography.fernet import Fernet
32+
3233
from observatory.platform.cli.click_utils import (
3334
INDENT1,
34-
INDENT2,
3535
INDENT3,
3636
comment,
3737
indent,
3838
)
3939
from observatory.platform.terraform_api import TerraformVariable
4040
from observatory.platform.utils.airflow_utils import AirflowVars
41-
from observatory.platform.utils.config_utils import module_file_path
4241
from observatory.platform.utils.config_utils import (
4342
observatory_home as default_observatory_home,
4443
)
45-
from observatory.platform.utils.jinja2_utils import render_template
4644

4745

4846
def generate_fernet_key() -> str:
@@ -601,13 +599,24 @@ class Api:
601599
domain_name: the custom domain name of the API
602600
subdomain: the subdomain of the API, can be either based on the google project id or the environment. When
603601
based on the environment, there is no subdomain for the production environment.
602+
api_image: the path to the Observatory API image on Google Cloud Artifact Registry.
603+
er_image: the path to the Google Cloud Endpoints Runtime image.
604604
"""
605605

606606
domain_name: str
607607
subdomain: str
608+
api_image: str
609+
er_image: str = "gcr.io/endpoints-release/endpoints-runtime-serverless:2"
608610

609611
def to_hcl(self):
610-
return to_hcl({"domain_name": self.domain_name, "subdomain": self.subdomain})
612+
return to_hcl(
613+
{
614+
"domain_name": self.domain_name,
615+
"subdomain": self.subdomain,
616+
"api_image": self.api_image,
617+
"er_image": self.er_image,
618+
}
619+
)
611620

612621
@staticmethod
613622
def from_dict(dict_: Dict) -> Api:
@@ -619,7 +628,9 @@ def from_dict(dict_: Dict) -> Api:
619628

620629
domain_name = dict_.get("domain_name")
621630
subdomain = dict_.get("subdomain")
622-
return Api(domain_name, subdomain)
631+
api_image = dict_.get("api_image")
632+
er_image = dict_.get("er_image", Api.er_image)
633+
return Api(domain_name, subdomain, api_image, er_image=er_image)
623634

624635

625636
def is_base64(text: bytes) -> bool:
@@ -1554,6 +1565,8 @@ def make_schema(backend_type: BackendType) -> Dict:
15541565
"schema": {
15551566
"domain_name": {"required": True, "type": "string"},
15561567
"subdomain": {"required": True, "type": "string", "allowed": ["project_id", "environment"]},
1568+
"api_image": {"required": True, "type": "string"},
1569+
"er_image": {"required": False, "type": "string"},
15571570
},
15581571
}
15591572

@@ -1866,12 +1879,15 @@ def api(api: Api) -> List[str]:
18661879
api = Api(
18671880
domain_name="api.observatory.academy",
18681881
subdomain="project_id",
1882+
api_image="us-docker.pkg.dev/gcp-project-id/observatory-platform/observatory-api:latest",
18691883
)
18701884

18711885
lines = [
18721886
"api:\n",
18731887
indent(f"domain_name: {api.domain_name}\n", INDENT1),
18741888
indent(f"subdomain: {api.subdomain}\n", INDENT1),
1889+
indent(f"api_image: {api.api_image}\n", INDENT1),
1890+
indent(f"er_image: {api.er_image}\n", INDENT1),
18751891
]
18761892

18771893
return lines

observatory-platform/observatory/platform/terraform/api/main.tf

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,14 @@ module "observatory_db_uri" {
5454
service_account_email = google_service_account.api-backend_service_account.email
5555
}
5656

57-
# Create data resource to keep track of latest image change
58-
data "archive_file" "build_image_info"{
59-
# the only available type
60-
type = "zip"
61-
source_file = "./api_image_build.txt"
62-
output_path = "./api_image_build.zip"
63-
}
64-
65-
6657
resource "google_cloud_run_service" "api_backend" {
67-
name = "api-backend"
58+
name = "api-backend"
6859
location = var.google_cloud.region
6960

7061
template {
7162
spec {
7263
containers {
73-
image = "gcr.io/${var.google_cloud.project_id}/observatory-api"
64+
image = var.api.api_image
7465
env {
7566
name = "ES_API_KEY"
7667
value = "sm://${var.google_cloud.project_id}/elasticsearch-api_key"
@@ -89,8 +80,6 @@ resource "google_cloud_run_service" "api_backend" {
8980
metadata {
9081
annotations = {
9182
"autoscaling.knative.dev/maxScale" = "10"
92-
# make resource dependent on sha256 of file describing image info
93-
build_image = data.archive_file.build_image_info.output_base64sha256
9483
"run.googleapis.com/vpc-access-egress" : "private-ranges-only"
9584
"run.googleapis.com/vpc-access-connector" = "projects/${var.google_cloud.project_id}/locations/${var.google_cloud.region}/connectors/${var.vpc_connector_name}"
9685
}
@@ -151,15 +140,14 @@ resource "google_project_iam_member" "api-gateway_service_account_servicecontrol
151140
member = "serviceAccount:${google_service_account.api-gateway_service_account.email}"
152141
}
153142

154-
# Create/update Cloud Run service
155143
resource "google_cloud_run_service" "api_gateway" {
156144
name = "api-gateway"
157145
location = var.google_cloud.region
158146
project = var.google_cloud.project_id
159147
template {
160148
spec {
161149
containers {
162-
image = "gcr.io/endpoints-release/endpoints-runtime-serverless:2"
150+
image = var.api.er_image
163151
env {
164152
name = "ENDPOINTS_SERVICE_NAME"
165153
value = google_endpoints_service.api.service_name

observatory-platform/observatory/platform/terraform/api/variables.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ Settings related to the Observatory API
44
55
domain_name: the custom domain name for the API, used for the google cloud endpoints service
66
subdomain: can be either 'project_id' or 'environment', used to determine a prefix for the domain_name
7+
api_image: the path to the Observatory API image on Google Cloud Artifact Registry.
8+
er_image: the path to the Google Cloud Endpoints Runtime image.
79
EOF
810
type = object({
911
domain_name = string
1012
subdomain = string
13+
api_image = string
14+
er_image = string
1115
})
1216
}
1317

0 commit comments

Comments
 (0)