Skip to content
This repository was archived by the owner on Jan 23, 2024. It is now read-only.

Argocd support #286

Merged
merged 4 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions bq-workers/argocd-parser/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


# Use the official Python image.
# https://hub.docker.com/_/python
FROM python:3.7

# Allow statements and log messages to immediately appear in the Cloud Run logs
ENV PYTHONUNBUFFERED True

# Copy application dependency manifests to the container image.
# Copying this separately prevents re-running pip install on every code change.
COPY requirements.txt .

# Install production dependencies.
RUN pip install -r requirements.txt

# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . .

# Run the web service on container startup.
# Use gunicorn webserver with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
41 changes: 41 additions & 0 deletions bq-workers/argocd-parser/cloudbuild.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

steps:
- # Build argocd-parser image
name: gcr.io/cloud-builders/docker:latest
args: ['build',
'--tag=gcr.io/$PROJECT_ID/argocd-parser:${_TAG}', '.']
dir: 'bq-workers/argocd-parser'
id: build

- # Push the container image to Artifact Registry
name: gcr.io/cloud-builders/docker
args: ['push', 'gcr.io/$PROJECT_ID/argocd-parser:${_TAG}']
waitFor: build
id: push

- # Deploy to Cloud Run
name: google/cloud-sdk
args: ['gcloud', 'run', 'deploy', 'argocd-parser',
'--image', 'gcr.io/$PROJECT_ID/argocd-parser:${_TAG}',
'--region', '${_REGION}',
'--platform', 'managed'
]
id: deploy
waitFor: push

images: [
'gcr.io/$PROJECT_ID/argocd-parser:${_TAG}'
]
90 changes: 90 additions & 0 deletions bq-workers/argocd-parser/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import base64
import os
import json

import shared

from flask import Flask, request

app = Flask(__name__)


@app.route("/", methods=["POST"])
def index():
"""
Receives messages from a push subscription from Pub/Sub.
Parses the message, and inserts it into BigQuery.
"""
event = None
envelope = request.get_json()
print(f"envelope recieved: {envelope}")

# Check that data has been posted
if not envelope:
raise Exception("Expecting JSON payload")
# Check that message is a valid pub/sub message
if "message" not in envelope:
raise Exception("Not a valid Pub/Sub Message")
msg = envelope["message"]

if "attributes" not in msg:
raise Exception("Missing pubsub attributes")

try:
event = process_argocd_event(msg)

# [Do not edit below]
shared.insert_row_into_bigquery(event)

except Exception as e:
entry = {
"severity": "WARNING",
"msg": "Data not saved to BigQuery",
"errors": str(e),
"json_payload": envelope
}
print(json.dumps(entry))

return "", 204


def process_argocd_event(msg):
metadata = json.loads(base64.b64decode(msg["data"]).decode("utf-8").strip())

# Unique hash for the event
signature = shared.create_unique_id(msg)

argocd_event = {
"event_type": "deployment", # Event type, eg "push", "pull_reqest", etc
"id": metadata["id"], # Object ID, eg pull request ID
"metadata": json.dumps(metadata), # The body of the msg
"time_created": metadata["time"], # The timestamp of with the event
"signature": signature, # The unique event signature
"msg_id": msg["message_id"], # The pubsub message id
"source": "argocd", # The name of the source, eg "github"
}

print(argocd_event)
return argocd_event


if __name__ == "__main__":
PORT = int(os.getenv("PORT")) if os.getenv("PORT") else 8080

# This is used when running locally. Gunicorn is used to run the
# application on Cloud Run. See entrypoint in Dockerfile.
app.run(host="127.0.0.1", port=PORT, debug=True)
89 changes: 89 additions & 0 deletions bq-workers/argocd-parser/main_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright 2020 Google, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import base64
import json

import main
import shared

import mock
import pytest


@pytest.fixture
def client():
main.app.testing = True
return main.app.test_client()


def test_not_json(client):
with pytest.raises(Exception) as e:
client.post("/", data="foo")

assert "Expecting JSON payload" in str(e.value)


def test_not_pubsub_message(client):
with pytest.raises(Exception) as e:
client.post(
"/",
data=json.dumps({"foo": "bar"}),
headers={"Content-Type": "application/json"},
)

assert "Not a valid Pub/Sub Message" in str(e.value)


def test_missing_msg_attributes(client):
with pytest.raises(Exception) as e:
client.post(
"/",
data=json.dumps({"message": "bar"}),
headers={"Content-Type": "application/json"},
)

assert "Missing pubsub attributes" in str(e.value)


def test_argocd_event_processed(client):
data = json.dumps({"foo": "bar", "id": "foo", "time": 0}).encode("utf-8")
pubsub_msg = {
"message": {
"data": base64.b64encode(data).decode("utf-8"),
"attributes": {"foo": "bar"},
"message_id": "foobar",
},
}

event = {
"event_type": "deployment",
"id": "foo",
"metadata": '{"foo": "bar", "id": "foo", "time": 0}',
"time_created": 0,
"signature": "a424b5326ac45bde4c42c9b74dc878e56623d84f",
"msg_id": "foobar",
"source": "argocd",
}

shared.insert_row_into_bigquery = mock.MagicMock()

r = client.post(
"/",
data=json.dumps(pubsub_msg),
headers={"Content-Type": "application/json"},
)

shared.insert_row_into_bigquery.assert_called_with(event)
assert r.status_code == 204
2 changes: 2 additions & 0 deletions bq-workers/argocd-parser/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-r requirements.txt
pytest~=6.0.0
4 changes: 4 additions & 0 deletions bq-workers/argocd-parser/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Flask==2.0.3
gunicorn==19.9.0
google-cloud-bigquery==1.23.1
git+https://github.com/GoogleCloudPlatform/fourkeys.git#egg=shared&subdirectory=shared
6 changes: 6 additions & 0 deletions event_handler/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ def get_source(headers):
if "Circleci-Event-Type" in headers:
return "circleci"

if "Argo-CD" in headers.get("User-Agent", ""):
return "argocd"

return headers.get("User-Agent")


Expand All @@ -131,4 +134,7 @@ def get_source(headers):
"circleci": EventSource(
"Circleci-Signature", circleci_verification
),
"argocd": EventSource(
"Argo-Signature", simple_token_verification
),
}
5 changes: 4 additions & 1 deletion queries/deployments.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ WITH deploys_cloudbuild_github_gitlab AS (# Cloud Build, Github, Gitlab pipeline
# REGEX to get the commit sha from the URL
REGEXP_EXTRACT(
JSON_EXTRACT_SCALAR(metadata, '$.commit_url'), r".*commit\/(.*)")
) end as main_commit,
)
WHEN source = "argocd" then JSON_EXTRACT_SCALAR(metadata, '$.commit_sha') end as main_commit,
CASE WHEN source LIKE "github%" THEN ARRAY(
SELECT JSON_EXTRACT_SCALAR(string_element, '$')
FROM UNNEST(JSON_EXTRACT_ARRAY(metadata, '$.deployment.additional_sha')) AS string_element)
Expand All @@ -29,6 +30,8 @@ WITH deploys_cloudbuild_github_gitlab AS (# Cloud Build, Github, Gitlab pipeline
OR (source LIKE "gitlab%" AND event_type = "pipeline" AND JSON_EXTRACT_SCALAR(metadata, '$.object_attributes.status') = "success")
# GitLab Deployments
OR (source LIKE "gitlab%" AND event_type = "deployment" AND JSON_EXTRACT_SCALAR(metadata, '$.status') = "success")
# ArgoCD Deployments
OR (source = "argocd" AND JSON_EXTRACT_SCALAR(metadata, '$.status') = "SUCCESS")
)
),
deploys_tekton AS (# Tekton Pipelines
Expand Down
6 changes: 4 additions & 2 deletions setup/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ else
(2) Tekton
(3) GitLab
(4) CircleCI
(5) Other
(5) ArgoCD
(6) Other

Enter a selection (1 - 5): " cicd_system_id
Enter a selection (1 - 6): " cicd_system_id

printf "\n"

Expand Down Expand Up @@ -91,6 +92,7 @@ case $cicd_system_id in
2) CICD_SYSTEM="tekton" ;;
3) CICD_SYSTEM="gitlab" ;;
4) CICD_SYSTEM="circleci" ;;
5) CICD_SYSTEM="argocd" ;;
*) echo "Please see the documentation to learn how to extend to CI/CD sources other than Cloud Build, Tekton, GitLab, CircleCI or GitHub."
esac

Expand Down