Skip to content

Commit b24f017

Browse files
feat: Add Terraform and GitHub Actions for GCP Cloud Function
This commit introduces Terraform configuration and a GitHub Actions workflow to deploy a Java-based Google Cloud Function triggered by HTTP. Key features: - Terraform scripts to define the Cloud Function, GCS bucket for source code, and IAM permissions for public invocation. - Parameterized Terraform configuration for different environments (dev, qa, prod) using .tfvars and GitHub Actions inputs. - GitHub Actions workflow that automates: - Setting up GCP and Java environments. - Authenticating to GCP using a service account. - Dynamically configuring Terraform variables based on the deployment branch. - Running Terraform init, validate, plan, and apply. - Storing Terraform state in a GCS backend, segregated by environment. - Assumes the JAR file for the function is available in the repository or built in a preceding step in the workflow. Instructions for setting up GCP prerequisites (service accounts, buckets, APIs), GitHub secrets, and customizing workflow variables (JAR path, entry point, project IDs) have been provided to you.
1 parent 5498311 commit b24f017

File tree

5 files changed

+260
-0
lines changed

5 files changed

+260
-0
lines changed

.github/workflows/deploy.yml

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: Deploy GBFS Validator Cloud Function
2+
3+
on:
4+
push:
5+
branches:
6+
- main # Or your specific branches for dev, qa, prod
7+
# Example:
8+
# - dev
9+
# - qa
10+
# - production
11+
12+
jobs:
13+
deploy:
14+
runs-on: ubuntu-latest
15+
env:
16+
# Will be dynamically set based on the branch
17+
TF_VAR_gcp_project_id: ""
18+
TF_VAR_environment: ""
19+
TF_VAR_source_bucket_name: "" # e.g., gbfs-validator-src-dev
20+
# These should be configured based on your function's needs
21+
TF_VAR_function_name: "gbfs-validator-function" # Can be customized per env if needed
22+
TF_VAR_gcp_region: "us-central1" # Change if needed
23+
TF_VAR_function_entry_point: "com.example.YourFunctionEntryPoint" # ** IMPORTANT: User needs to change this **
24+
TF_VAR_jar_file_path: "path/to/your/validator.jar" # ** IMPORTANT: User needs to change this **
25+
TF_VAR_function_runtime: "java11" # Or java17, java21
26+
TF_VAR_function_memory_mb: 256
27+
TF_VAR_function_timeout_s: 60
28+
29+
steps:
30+
- name: Checkout code
31+
uses: actions/checkout@v3
32+
33+
- name: Set up JDK
34+
uses: actions/setup-java@v3
35+
with:
36+
distribution: 'temurin' # Or any other distribution
37+
java-version: '11' # Or 17, 21, matching TF_VAR_function_runtime
38+
39+
# Add a step here to build the JAR if it's not pre-built and checked into the repo
40+
# - name: Build JAR (if needed)
41+
# run: |
42+
# # e.g., mvn package -DskipTests
43+
# echo "JAR build step - customize this if your JAR is not pre-built"
44+
# # Ensure TF_VAR_jar_file_path points to the built JAR
45+
46+
- name: Set up Google Cloud SDK
47+
uses: google-github-actions/setup-gcloud@v1
48+
with:
49+
project_id: ${{ env.TF_VAR_gcp_project_id }} # Will be set dynamically
50+
51+
- name: Authenticate to GCP
52+
uses: google-github-actions/auth@v1
53+
with:
54+
credentials_json: ${{ secrets.GCP_SA_KEY }} # User needs to set this secret
55+
56+
- name: Set environment-specific variables
57+
run: |
58+
BRANCH_NAME=${GITHUB_REF#refs/heads/}
59+
if [[ "$BRANCH_NAME" == "main" ]]; then # Assuming 'main' is for 'prod'
60+
echo "Setting environment for PROD"
61+
echo "TF_VAR_gcp_project_id=${{ secrets.GCP_PROJECT_ID_PROD }}" >> $GITHUB_ENV
62+
echo "TF_VAR_environment=prod" >> $GITHUB_ENV
63+
echo "TF_VAR_source_bucket_name=gbfs-validator-src-prod" >> $GITHUB_ENV
64+
# Add other prod-specific TF_VARs if needed
65+
elif [[ "$BRANCH_NAME" == "qa" ]]; then
66+
echo "Setting environment for QA"
67+
echo "TF_VAR_gcp_project_id=${{ secrets.GCP_PROJECT_ID_QA }}" >> $GITHUB_ENV
68+
echo "TF_VAR_environment=qa" >> $GITHUB_ENV
69+
echo "TF_VAR_source_bucket_name=gbfs-validator-src-qa" >> $GITHUB_ENV
70+
# Add other qa-specific TF_VARs if needed
71+
elif [[ "$BRANCH_NAME" == "dev" ]]; then
72+
echo "Setting environment for DEV"
73+
echo "TF_VAR_gcp_project_id=${{ secrets.GCP_PROJECT_ID_DEV }}" >> $GITHUB_ENV
74+
echo "TF_VAR_environment=dev" >> $GITHUB_ENV
75+
echo "TF_VAR_source_bucket_name=gbfs-validator-src-dev" >> $GITHUB_ENV
76+
# Add other dev-specific TF_VARs if needed
77+
else
78+
echo "Branch $BRANCH_NAME is not configured for deployment."
79+
exit 1
80+
fi
81+
echo "VERIFYING ENV VARS:"
82+
echo "Project ID: ${{ env.TF_VAR_gcp_project_id }}"
83+
echo "Environment: ${{ env.TF_VAR_environment }}"
84+
echo "Source Bucket: ${{ env.TF_VAR_source_bucket_name }}"
85+
echo "Entry Point: ${{ env.TF_VAR_function_entry_point }}"
86+
echo "JAR Path: ${{ env.TF_VAR_jar_file_path }}"
87+
88+
89+
- name: Set up Terraform
90+
uses: hashicorp/setup-terraform@v2
91+
with:
92+
terraform_version: 1.2.0 # Or your desired version
93+
94+
- name: Terraform Init
95+
run: terraform init
96+
env:
97+
# Pass GCS backend config if you decide to use it
98+
# GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.auth.outputs.credentials_path }} # Not needed if using default auth
99+
TF_CLI_ARGS_init: "-backend-config=bucket=${{ env.TF_VAR_environment }}-gbfs-tf-state -backend-config=prefix=gbfs-validator"
100+
101+
102+
- name: Terraform Validate
103+
run: terraform validate
104+
105+
- name: Terraform Plan
106+
id: plan
107+
run: terraform plan -input=false -no-color -out=tfplan
108+
continue-on-error: true # To allow viewing the plan even if there are errors for PRs
109+
110+
- name: Terraform Plan Status
111+
if: steps.plan.outcome == 'failure'
112+
run: |
113+
echo "Terraform Plan failed!"
114+
exit 1
115+
116+
# On pull requests, you might only want to run init, validate, and plan.
117+
# The apply step should only run on merges to specific branches.
118+
- name: Terraform Apply
119+
if: github.event_name == 'push' # Only apply on direct pushes to configured branches
120+
run: terraform apply -auto-approve -input=false tfplan
121+
122+
# Optional: Add a step to output the function URL
123+
- name: Show Function URL
124+
if: github.event_name == 'push'
125+
run: |
126+
echo "Cloud Function URL: $(terraform output -raw function_url)"

iam.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# IAM policy for invoking the function
2+
# This makes the function publicly accessible.
3+
resource "google_cloudfunctions_function_iam_member" "invoker" {
4+
project = google_cloudfunctions_function.function.project
5+
region = google_cloudfunctions_function.function.region
6+
cloud_function = google_cloudfunctions_function.function.name
7+
8+
role = "roles/cloudfunctions.invoker"
9+
member = "allUsers"
10+
}

main.tf

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
terraform {
2+
required_providers {
3+
google = {
4+
source = "hashicorp/google"
5+
version = "~> 4.0"
6+
}
7+
}
8+
}
9+
10+
provider "google" {
11+
project = var.gcp_project_id
12+
region = var.gcp_region
13+
}
14+
15+
resource "google_storage_bucket" "source_bucket" {
16+
name = var.source_bucket_name
17+
location = var.gcp_region
18+
uniform_bucket_level_access = true
19+
# Adding labels for better resource management and cost tracking
20+
labels = {
21+
environment = var.environment
22+
app = var.function_name
23+
}
24+
}
25+
26+
data "archive_file" "source_zip" {
27+
type = "zip"
28+
source_file = var.jar_file_path # Path to the JAR file provided by the user
29+
output_path = "/tmp/function-source.zip" # Temporary path for the zipped JAR
30+
}
31+
32+
resource "google_storage_bucket_object" "source_archive" {
33+
name = "function-source-${data.archive_file.source_zip.output_md5}.zip"
34+
bucket = google_storage_bucket.source_bucket.name
35+
source = data.archive_file.source_zip.output_path # Path to the zipped JAR
36+
}
37+
38+
resource "google_cloudfunctions_function" "function" {
39+
name = var.function_name
40+
description = "GBFS Validator Cloud Function"
41+
runtime = var.function_runtime
42+
region = var.gcp_region
43+
project = var.gcp_project_id
44+
45+
available_memory_mb = var.function_memory_mb
46+
timeout_seconds = var.function_timeout_s
47+
entry_point = var.function_entry_point
48+
trigger_http = true
49+
source_archive_bucket = google_storage_bucket.source_bucket.name
50+
source_archive_object = google_storage_bucket_object.source_archive.name
51+
52+
labels = {
53+
environment = var.environment
54+
app = var.function_name
55+
}
56+
57+
# Depending on the function's needs, you might need to configure environment variables
58+
# environment_variables = {
59+
# FOO = "bar"
60+
# }
61+
}

outputs.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
output "function_url" {
2+
description = "The HTTPS trigger URL for the Cloud Function."
3+
value = google_cloudfunctions_function.function.https_trigger_url
4+
}
5+
6+
output "source_bucket_url" {
7+
description = "The URL of the GCS bucket used for storing the function source."
8+
value = google_storage_bucket.source_bucket.url
9+
}

variables.tf

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
variable "gcp_project_id" {
2+
description = "The GCP project ID."
3+
type = string
4+
}
5+
6+
variable "gcp_region" {
7+
description = "The GCP region for the Cloud Function."
8+
type = string
9+
default = "us-central1"
10+
}
11+
12+
variable "function_name" {
13+
description = "The name of the Cloud Function."
14+
type = string
15+
default = "gbfs-validator-function"
16+
}
17+
18+
variable "function_entry_point" {
19+
description = "The entry point of the function in the JAR file (e.g., com.example.MyFunction)."
20+
type = string
21+
}
22+
23+
variable "jar_file_path" {
24+
description = "The path to the JAR file within the repository."
25+
type = string
26+
}
27+
28+
variable "environment" {
29+
description = "The deployment environment (e.g., dev, qa, prod)."
30+
type = string
31+
}
32+
33+
variable "source_bucket_name" {
34+
description = "The name of the GCS bucket to store the function's source code."
35+
type = string
36+
}
37+
38+
variable "function_runtime" {
39+
description = "The runtime for the Cloud Function."
40+
type = string
41+
default = "java11" # Or java17, java21 depending on the JAR
42+
}
43+
44+
variable "function_memory_mb" {
45+
description = "The memory allocated to the Cloud Function in MB."
46+
type = number
47+
default = 256
48+
}
49+
50+
variable "function_timeout_s" {
51+
description = "The timeout for the Cloud Function in seconds."
52+
type = number
53+
default = 60
54+
}

0 commit comments

Comments
 (0)