Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terraform to manage the required resources #85

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@
/vendor/bundle
Downloads
newrelic_agent.log
.terraform*
21 changes: 5 additions & 16 deletions deployment/chart/templates/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,14 @@ kind: ConfigMap
metadata:
name: {{ .Release.Name }}-config
data:
RAILS_ENV: "{{ .Values.ticketsEnvironment }}"
SMTP_ADDRESS: "{{ .Values.ticketsSMTPHost }}"
SMTP_PORT: "{{ .Values.ticketsSMTPPort }}"
SMTP_DOMAIN: "{{ .Values.ticketsSMTPDomain }}"
TICKETS_HOST: "{{ .Values.ticketsHost }}"
ACTION_MAILER_DEFAULT_FROM: "{{ .Values.ticketsEmailAddress }}"
ACTION_MAILER_DEFAULT_TO: "{{ .Values.ticketsEmailAddress }}"
STRIPE_PUBLIC_KEY: "{{ .Values.stripePublicKey }}"
WEB_CONCURRENCY: "{{ .Values.pumaConcurrency }}"
RAILS_MAX_THREADS: "{{ .Values.pumaThreads }}"
{{ .Values.config | nindent 2 }}
---
{{- if .Values.secret.create }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-secrets
name: {{ default {{ .Release.Name }}"-secrets" .Values.secret.name }}
type: Opaque
stringData:
SECRET_KEY_BASE: "{{ .Values.ticketsSecretToken }}"
RAILS_SERVE_STATIC_FILES: "{{ .Values.ticketsServeStaticFiles }}"
SMTP_USERNAME: "{{ .Values.ticketsSMTPUser }}"
SMTP_PASSWORD: "{{ .Values.ticketsSMTPPassword }}"
STRIPE_SECRET_KEY: "{{ .Values.stripeSecretKey }}"
{{ .Values.secret.data | nindent 2 }}
{{- end }}
3 changes: 2 additions & 1 deletion deployment/chart/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ spec:
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
# These should really come from a autogenerate value
- name: TICKET_DB_HOST
value: {{ include "chart.postgresql.fullname" . }}
- name: TICKET_DB_PORT
Expand All @@ -62,7 +63,7 @@ spec:
value: {{ .Values.postgresql.auth.password }}
envFrom:
- secretRef:
name: {{ .Release.Name }}-secrets
name: {{ default {{ .Release.Name }}"-secrets" .Value.secrets.name }}
- configMapRef:
name: {{ .Release.Name }}-config
# Fix for: https://github.com/puma/puma/issues/2343
Expand Down
47 changes: 32 additions & 15 deletions deployment/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,39 @@ tolerations: []

affinity: {}

ticketsEnvironment: "production"
ticketsSecretToken: "this-is-a-very-bogus-key"
ticketsServeStaticFiles: "true"
ticketsSMTPHost: ""
ticketsSMTPPort: ""
ticketsSMTPDomain: ""
ticketsSMTPUser: ""
ticketsSMTPPassword: ""
ticketsDBSeed: ""
ticketsHost: ""
ticketsEmailAddress: ""
stripeSecretKey: ""
stripePublicKey: ""
pumaConcurrency: 2
pumaThreads: 2
config:
ticketsEnvironment: "production"
ticketsSMTPHost: ""
ticketsSMTPPort: ""
ticketsSMTPDomain: ""
ticketsDBSeed: ""
ticketsHost: ""
ticketsEmailAddress: ""
stripePublicKey: ""
pumaConcurrency: 2
pumaThreads: 2

secret:
# The secret should never be created with Helm because the values get stored
# someplace. Do this only with non-production resources.
create: false

# Any secrets should be contained within GCP Se

# The name of the secret to use.
# Default: {{ .Release.Name }}-secrets
#name: ""

# Data to put into a created secret. Does not need to be base64 encoded
# data:
# ticketsSecretToken: "this-is-a-very-bogus-key"
# ticketsServeStaticFiles: "true"
# ticketsSMTPUser: ""
# ticketsSMTPPassword: ""
# stripeSecretKey: ""



postgresql:
auth:
database: tickets
Expand Down
19 changes: 19 additions & 0 deletions deployment/env/base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
image:
pullPolicy: Always

serviceAccount:
create: true

service:
type: NodePort
port: 3000

ingress:
enabled: true
className: nginx
annotations:
kubernetes.io/ingress.allow-http: "false"
# Associate a GCP FrontendConfig with the Ingress
# networking.gke.io/v1beta1.FrontendConfig: "<name>"

14 changes: 14 additions & 0 deletions deployment/env/production.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
replicaCount: 3

image:
# The tag should be provided on the cmdline to prevent accidental deployments
# of untesed code to production.
tag: null

serviceAccount:
annotations:
# Any secrets should be contained within GCP managed secrets, so an IAM
# ServiceAccount is needed that has the permissions to read from the named
# secret
iam.gke.io/gcp-service-account: [email protected]
38 changes: 38 additions & 0 deletions deployment/gcp/iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# IAM roles and permissions for the app to talk to external GCP services
resource "google_project_iam_custom_role" "ticket_booth" {
role_id = "fnfTicketBoothAppRole"
title = "Fnf Ticket Booth App Role"
description = "Role that provides all the permissions needed by the app"
permissions = [
"cloudsql.databases.get",
"cloudsql.databases.create",
"cloudsql.databases.delete",
"cloudsql.databases.get",
"cloudsql.databases.update",
"cloudsql.instances.connect",
"cloudsql.instances.executeSql",
"iam.serviceAccounts.get",
"iam.serviceAccounts.getAccessToken",
"iam.serviceAccounts.getOpenIdToken",
"iam.serviceAccounts.list",
]
}

resource "google_service_account" "ticket_booth_gke" {
account_id = var.project_id
description = "Ticket Booth GKE Service Account"
display_name = "ticket-booth-gke"

create_ignore_already_exists = true
}

resource "google_service_account_iam_binding" "ticket_booth_app" {
service_account_id = google_service_account.ticket_booth_gke.name
role = google_project_iam_custom_role.ticket_booth.name

members = [
"serviceAccount:${google_service_account.ticket_booth_gke.name}",
]
}


28 changes: 28 additions & 0 deletions deployment/gcp/secrets.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This creates the secret store locations, entries will need to be manually updated

resource "google_secret_manager_secret" "ticket_booth_db" {
secret_id = "ticket-booth-db"

replication {
auto {}
}
}

resource "google_secret_manager_secret_version" "ticket_booth_db" {
secret = google_secret_manager_secret.ticket_booth_db.id

secret_data = base64encode(jsonencode({
"TICKET_DB_HOST" = tostring(google_sql_database_instance.ticket_booth.connection_name)
"TICKET_DB_NAME" = tostring(google_sql_database.ticket_booth.name)
"TICKET_DB_USER" = "fnf-breakglass"
"TICKET_DB_PASSWORD" = tostring(random_password.ticket_booth_db.result)
}))
}

resource "google_secret_manager_secret" "ticket_booth_app" {
secret_id = "ticket-booth-app"

replication {
auto {}
}
}
72 changes: 72 additions & 0 deletions deployment/gcp/sql.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
locals {
db_admins = [
"[email protected]",
]
}

resource "google_sql_database_instance" "ticket_booth" {
name = "fnf-apps-db-instance"
database_version = "POSTGRES_15"
region = var.location

settings {
tier = "e2-small"

# Only allow access from the GKE cluster
ip_configuration {
enable_private_path_for_google_cloud_services = true
# authorized_networks {
# name = ""
# value = ""
# }
}

database_flags {
name = "cloudsql.iam_authentication"
value = "on"
}

activation_policy = "ON_DEMAND"
deletion_protection_enabled = true

backup_configuration {
enabled = true
point_in_time_recovery_enabled = true

backup_retention_settings {
retained_backups = 10
}
}
}
}

resource "google_sql_database" "ticket_booth" {
name = "ticket-booth"
instance = google_sql_database_instance.ticket_booth.name
}

resource "google_sql_user" "fnf_apps_admin_users" {
for_each = toset(local.db_admins)
name = each.value
instance = google_sql_database_instance.ticket_booth.name
type = "CLOUD_IAM_GROUP"
}

resource "google_sql_user" "fnf_apps_gke_sa" {
name = trimsuffix(google_service_account.ticket_booth_gke.email, ".gserviceaccount.com")
instance = google_sql_database_instance.ticket_booth.name
type = "CLOUD_IAM_SERVICE_ACCOUNT"
}

resource "google_sql_user" "breakglass" {
name = "fnf-breakglass"
instance = google_sql_database_instance.ticket_booth.name
password = random_password.ticket_booth_db.result
}

resource "random_password" "ticket_booth_db" {
length = 20
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}

19 changes: 19 additions & 0 deletions deployment/gcp/terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
terraform {
backend "gcs" {
bucket = "fnf-apps-terraform-state"
prefix = "ticket-booth"
}
required_providers {
google = {
source = "hashicorp/google"
version = "5.25.0"
}
}
}

provider "google" {
project = var.project_id
region = var.location

impersonate_service_account = var.tf_service_account != "" ? var.tf_service_account : ""
}
14 changes: 14 additions & 0 deletions deployment/gcp/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
variable "tf_service_account" {
default = "[email protected]"
type = string
}

variable "project_id" {
default = "fnf-apps-341500"
type = string
}

variable "location" {
default = "us-central1"
type = string
}
Loading