extras/cloudsql/populate-jobs/populate-ledger-db.yaml (238 lines of code) (raw):
# 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.
# kubectl create configmap ledger-schema-config --from-file=src/ledger-db/initdb/0-ledger-schema.sql --dry-run -o yaml (copy and add below)
# kubectl create configmap ledger-schema-config --from-file=src/ledger-db/initdb/1-load-testdata.sh --dry-run -o yaml (copy and add below)
apiVersion: v1
kind: ConfigMap
metadata:
name: ledger-schema-config
data:
wait-to-complete-sidecar.sh: |
#!/bin/bash
COUNTER=0
TARGET=$1
PROCESS=$2
echo "Looking for $TARGET"
sleep 10s # Give 10s to allow processes to start
# 20 minutes (60 x 10s increments)
while [ $COUNTER -lt 120 ]; do
let COUNTER=$COUNTER+1
IS_RUNNING=$(ps -A | grep "scripts/$TARGET" | grep -v grep)
if [ "$IS_RUNNING" != "" ]; then
echo "Attempt # ${COUNTER}: Process not completed, trying again in 10 seconds -- ${IS_RUNNING}"
sleep 10s
else
echo "'${TARGET}' Process Finished, Stopping '${PROCESS}'"
killall ${PROCESS}
exit 0
fi
done
echo "Could not determine if the import finished, killing the proxy"
killall ${PROCESS}
exit 0
initialize-database.sh: |
#!/bin/bash
COUNTER=0
SLEEP_TIME=60 # 1 minute
DB_READY=0 # false
INIT_SQL_SCRIPT="/scripts/0-ledger-schema.sql"
TEST_SQL_SCRIPT="/scripts/1-load-testdata.sh"
HOST=${1:-'127.0.0.1'}
PORT=${2:-'5432'}
DB_NAME=${3:-'default'}
# Initial wait for sql-proxy to catch up
sleep 20
while [ $COUNTER -lt 10 ]; do
let COUNTER=$COUNTER+1
pg_isready --host=${HOST} --port=${PORT} --dbname=${DB_NAME}
if [ $? -gt 0 ]; then
echo "Attempt # ${COUNTER}: Database is not ready, trying again in 1 minute"
sleep $SLEEP_TIME
else
echo "Database is ready to connect"
let DB_READY=1
break
fi
done
if [ "${DB_READY}" -eq 1 ]; then
echo "Running initialization script"
psql --host=${HOST} --port=${PORT} --dbname=${DB_NAME} -f ${INIT_SQL_SCRIPT}
if [ $? -gt 0 ]; then
echo "Problems running the initialization script"
else
echo "Run Test Data"
. ${TEST_SQL_SCRIPT}
fi
fi
0-ledger-schema.sql: |
/*
* 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.
*/
CREATE TABLE TRANSACTIONS (
TRANSACTION_ID BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
FROM_ACCT CHAR(10) NOT NULL,
TO_ACCT CHAR(10) NOT NULL,
FROM_ROUTE CHAR(9) NOT NULL,
TO_ROUTE CHAR(9) NOT NULL,
AMOUNT INT NOT NULL,
TIMESTAMP TIMESTAMP NOT NULL
);
-- index account number/routing number pairs
CREATE INDEX ON TRANSACTIONS (FROM_ACCT, FROM_ROUTE, TIMESTAMP);
CREATE INDEX ON TRANSACTIONS (TO_ACCT, TO_ROUTE, TIMESTAMP);
-- append only ledger; prevent updates or deletes
CREATE RULE PREVENT_UPDATE AS
ON UPDATE TO TRANSACTIONS
DO INSTEAD NOTHING;
CREATE RULE PREVENT_DELETE AS
ON DELETE TO TRANSACTIONS
DO INSTEAD NOTHING;
1-load-testdata.sh: |
#!/bin/bash
# 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.
# Create demo transactions in the ledger for the demo user accounts.
#
# Gerenated transactions follow a pattern of biweekly large deposits with
# periodic small payments to randomly choosen accounts.
#
# To run, set environment variable USE_DEMO_DATA="True"
set -u
# skip adding transactions if not enabled
if [ -z "$USE_DEMO_DATA" ] && [ "$USE_DEMO_DATA" != "True" ]; then
echo "\$USE_DEMO_DATA not \"True\"; no demo transactions added"
exit 0
fi
# Expected environment variables
readonly ENV_VARS=(
"POSTGRES_DB"
"POSTGRES_USER"
"LOCAL_ROUTING_NUM"
)
add_transaction() {
DATE=$(date -u +"%Y-%m-%d %H:%M:%S%z" --date="@$(($6))")
echo "adding demo transaction: $1 -> $2"
psql -X -v ON_ERROR_STOP=1 -v fromacct="$1" -v toacct="$2" -v fromroute="$3" -v toroute="$4" -v amount="$5" --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
INSERT INTO TRANSACTIONS (FROM_ACCT, TO_ACCT, FROM_ROUTE, TO_ROUTE, AMOUNT, TIMESTAMP)
VALUES (:'fromacct', :'toacct', :'fromroute', :'toroute', :'amount', '$DATE');
EOSQL
}
create_transactions() {
PAY_PERIODS=3
DAYS_BETWEEN_PAY=14
SECONDS_IN_PAY_PERIOD=$(( 86400 * $DAYS_BETWEEN_PAY ))
DEPOSIT_AMOUNT=250000
# create a UNIX timestamp in seconds since the Epoch
START_TIMESTAMP=$(( $(date +%s) - $(( $(($PAY_PERIODS+1)) * $SECONDS_IN_PAY_PERIOD )) ))
for i in $(seq 1 $PAY_PERIODS); do
# create deposit transaction for each user
for account in ${USER_ACCOUNTS[@]}; do
add_transaction "$EXTERNAL_ACCOUNT" "$account" "$EXTERNAL_ROUTING" "$LOCAL_ROUTING_NUM" $DEPOSIT_AMOUNT $START_TIMESTAMP
done
# create 15-20 payments between users
TRANSACTIONS_PER_PERIOD=$(shuf -i 15-20 -n1)
for p in $(seq 1 $TRANSACTIONS_PER_PERIOD); do
# randomly generate an amount between $10-$100
AMOUNT=$(shuf -i 1000-10000 -n1)
# randomly select a sender and receiver
SENDER_ACCOUNT=${USER_ACCOUNTS[$RANDOM % ${#USER_ACCOUNTS[@]}]}
RECIPIENT_ACCOUNT=${USER_ACCOUNTS[$RANDOM % ${#USER_ACCOUNTS[@]}]}
# if sender equals receiver, send to a random anonymous account
if [[ "$SENDER_ACCOUNT" == "$RECIPIENT_ACCOUNT" ]]; then
RECIPIENT_ACCOUNT=$(shuf -i 1000000000-9999999999 -n1)
fi
TIMESTAMP=$(( $START_TIMESTAMP + $(( $SECONDS_IN_PAY_PERIOD * $p / $(($TRANSACTIONS_PER_PERIOD + 1 )) )) ))
add_transaction "$SENDER_ACCOUNT" "$RECIPIENT_ACCOUNT" "$LOCAL_ROUTING_NUM" "$LOCAL_ROUTING_NUM" $AMOUNT $TIMESTAMP
done
START_TIMESTAMP=$(( $START_TIMESTAMP + $(( $i * $SECONDS_IN_PAY_PERIOD )) ))
done
}
create_ledger() {
# Account numbers for users 'testuser', 'alice', 'bob', and 'eve'.
USER_ACCOUNTS=("1011226111" "1033623433" "1055757655" "1077441377")
# Numbers for external account 'External Bank'
EXTERNAL_ACCOUNT="9099791699"
EXTERNAL_ROUTING="808889588"
create_transactions
}
main() {
# Check environment variables are set
for env_var in ${ENV_VARS[@]}; do
if [[ -z "${env_var}" ]]; then
echo "Error: environment variable '$env_var' not set. Aborting."
exit 1
fi
done
create_ledger
}
main
---
apiVersion: batch/v1
kind: Job
metadata:
name: populate-ledger-db
spec:
template:
spec:
shareProcessNamespace: true # Important to stop all processes
serviceAccountName: boa-ksa
containers:
- name: sidecar-controller
image: bash@sha256:acbac47fb9ea642a211c35013f50481bec20bd23db852c9c2634a4d153e631f1
command: ['bash', '-c', '. /scripts/wait-to-complete-sidecar.sh "initialize-database.sh" "cloud_sql_proxy"']
volumeMounts:
- name: scripts
mountPath: "/scripts"
readOnly: true
resources:
limits:
cpu: "200m"
memory: "100Mi"
- name: populate-ledger-db
image: postgres:16-alpine@sha256:acf5271bbecd4b8733f4e93959a8d2b536a57aeee6cc4b6a71890aaf646425b8
command: ['bash', '-c','. /scripts/initialize-database.sh 127.0.0.1 5432 ledger-db']
volumeMounts:
- name: scripts
mountPath: "/scripts"
readOnly: true
env:
- name: PGUSER
valueFrom:
secretKeyRef:
name: cloud-sql-admin
key: username
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: cloud-sql-admin
key: password
# /start: Required for testing data
- name: LOCAL_ROUTING_NUM
valueFrom:
configMapKeyRef:
name: environment-config
key: LOCAL_ROUTING_NUM
- name: USE_DEMO_DATA
valueFrom:
configMapKeyRef:
name: demo-data-config
key: USE_DEMO_DATA
- name: POSTGRES_DB
value: "ledger-db"
- name: PGHOSTADDR
value: "127.0.0.1"
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: cloud-sql-admin
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: cloud-sql-admin
key: password
# /end: Required for testing data
# CloudSQL Proxy
- name: cloudsql-proxy
resources:
limits:
cpu: "200m"
memory: "100Mi"
image: gcr.io/cloudsql-docker/gce-proxy:1.33.12@sha256:89530a19852370b176e91cfef02a24646019fcbffc7a5332cf2c9423bd5e910f
env:
- name: CONNECTION_NAME
valueFrom:
secretKeyRef:
name: cloud-sql-admin
key: connectionName
command: ["/cloud_sql_proxy",
"-instances=$(CONNECTION_NAME)=tcp:5432"]
securityContext:
runAsNonRoot: true
volumes:
- name: scripts
configMap:
name: ledger-schema-config
restartPolicy: Never
backoffLimit: 4