Skip to content
Draft
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
10 changes: 10 additions & 0 deletions examples/shop/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ substitutions:
_PAYMENT: "true"
_RECOMMENDATIONS: "true"
_CHECKOUT: "true"
_LOADGENERATOR: "true"
_FRONTEND: "true"

tags:
Expand Down Expand Up @@ -119,4 +120,13 @@ steps:
- |
[[ "${_CHECKOUT}" == "false" ]] && exit 0
exec gcloud builds submit --config ./ci.yaml --substitutions=_ARCHETYPE="APR",_PROJECT_ID=${_PROJECT_ID},_SHORT_SHA=${_SHORT_SHA},_APP_NAME="shop",_SERVICE="checkout",_SLO=true,_BUILD=true,_APP=true,_INFRA=true
- name: "gcr.io/cloud-builders/gcloud"
id: "loadgenerator-deploy"
dir: ./examples/shop/loadgenerator
entrypoint: bash
args:
- -c
- |
[[ "${_LOADGENERATOR}" == "false" ]] && exit 0
exec gcloud builds submit --config ./ci.yaml --substitutions=_ARCHETYPE="SZ",_PROJECT_ID=${_PROJECT_ID},_SHORT_SHA=${_SHORT_SHA},_APP_NAME="shop",_SERVICE="loadgenerator",_SLO=false,_BUILD=true,_APP=true,_INFRA=true

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure when the build.yaml is moved from frontend to loadgen. Did you mean to copy-paste?

Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ steps:
- name: 'gcr.io/cloud-builders/docker'
dir: "./app-repo/src"
args: [ 'build',
'-t', '${_REGION}-docker.pkg.dev/${_PROJECT_ID}/${_APP_NAME}-${_SERVICE}/service:${_SHORT_SHA}',
'-t', '${_REGION}-docker.pkg.dev/${_PROJECT_ID}/${_APP_NAME}-${_SERVICE}/service:latest',
'.', '-f', './Dockerfile' ]
'-t', '${_REGION}-docker.pkg.dev/${_PROJECT_ID}/${_APP_NAME}-${_SERVICE}/service:${_SHORT_SHA}',
'-t', '${_REGION}-docker.pkg.dev/${_PROJECT_ID}/${_APP_NAME}-${_SERVICE}/service:latest',
'.', '-f', './Dockerfile' ]
images:
- '${_REGION}-docker.pkg.dev/${_PROJECT_ID}/${_APP_NAME}-${_SERVICE}/service'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure when the deploy-app.yaml is moved from frontend to loadgen. Did you mean to copy-paste?

Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

timeout: 7200s # 2hr
tags:
- app-cicd-shop-frontend
- app-cicd-shop-loadgenerator
substitutions:
_PROJECT_ID: ${_PROJECT_ID}
_REGION: us-central1
_APP_NAME: "shop"
_SERVICE: "frontend"
_SERVICE: "loadgenerator"
steps:
# Create release in Google Cloud Deploy for App Clusters
- name: gcr.io/cloud-builders/gcloud
Expand Down
97 changes: 97 additions & 0 deletions examples/shop/loadgenerator/app-repo/k8s/loadgenerator.yaml
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The k8s folder needs to be structured to work with skaffold and kustomize as the deployments are different for workload clusters (where the archetype based workload is deployed) and other clusters (where just the virutal service and namespace) definitions are applied. If you look at the structure of any other k8s folder such as frontend/app-repo/k8s, it'll become apparent

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Copyright 2018 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.
apiVersion: apps/v1
kind: Deployment
metadata:
name: loadgenerator
labels:
app: loadgenerator
spec:
selector:
matchLabels:
app: loadgenerator
replicas: 1
template:
metadata:
labels:
app: loadgenerator
annotations:
sidecar.istio.io/rewriteAppHTTPProbers: "true"
spec:
serviceAccountName: loadgenerator
terminationGracePeriodSeconds: 5
restartPolicy: Always
securityContext:
fsGroup: 1000
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 1000
initContainers:
- command:
- /bin/sh
- -exc
- |
MAX_RETRIES=12
RETRY_INTERVAL=10
for i in $(seq 1 $MAX_RETRIES); do
echo "Attempt $i: Pinging frontend: ${FRONTEND_ADDR}..."
STATUSCODE=$(wget --server-response http://${FRONTEND_ADDR} 2>&1 | awk '/^ HTTP/{print $2}')
if [ $STATUSCODE -eq 200 ]; then
echo "Frontend is reachable."
exit 0
fi
echo "Error: Could not reach frontend - Status code: ${STATUSCODE}"
sleep $RETRY_INTERVAL
done
echo "Failed to reach frontend after $MAX_RETRIES attempts."
exit 1
name: frontend-check
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
image: busybox:latest
env:
- name: FRONTEND_ADDR
value: "frontend:80"
containers:
- name: main
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
image: loadgenerator
env:
- name: FRONTEND_ADDR
value: "frontend:80"
- name: USERS
value: "10"
resources:
requests:
cpu: 300m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: loadgenerator
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be copy-paste instead of move?

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: skaffold/v4beta6
kind: Config
metadata:
name: shop-frontend-vs
name: shop-loadgenerator-vs
manifests:
kustomize:
paths:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: skaffold/v4beta6
kind: Config
metadata:
name: shop-frontend-app
name: shop-loadgenerator-app
manifests:
kustomize:
paths:
Expand Down
35 changes: 35 additions & 0 deletions examples/shop/loadgenerator/app-repo/src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 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.

FROM python:3.12.3-slim@sha256:afc139a0a640942491ec481ad8dda10f2c5b753f5c969393b12480155fe15a63 as base

FROM base as builder

COPY requirements.txt .

RUN pip install --prefix="/install" -r requirements.txt

FROM base

WORKDIR /loadgen

COPY --from=builder /install /usr/local

# Add application code.
COPY locustfile.py .

# enable gevent support in debugger
ENV GEVENT_SUPPORT=True

ENTRYPOINT locust --host="http://${FRONTEND_ADDR}" --headless -u "${USERS:-10}" 2>&1
92 changes: 92 additions & 0 deletions examples/shop/loadgenerator/app-repo/src/locustfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/python
#
# Copyright 2018 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 random
from locust import FastHttpUser, TaskSet, between
from faker import Faker
import datetime
fake = Faker()

products = [
'0PUK6V6EV0',
'1YMWWN1N4O',
'2ZYFJ3GM2N',
'66VCHSJNUP',
'6E92ZMYYFZ',
'9SIQT8TOJO',
'L9ECAV7KIM',
'LS4PSXUNUM',
'OLJCESPC7Z']

def index(l):
l.client.get("/")

def setCurrency(l):
currencies = ['EUR', 'USD', 'JPY', 'CAD', 'GBP', 'TRY']
l.client.post("/setCurrency",
{'currency_code': random.choice(currencies)})

def browseProduct(l):
l.client.get("/product/" + random.choice(products))

def viewCart(l):
l.client.get("/cart")

def addToCart(l):
product = random.choice(products)
l.client.get("/product/" + product)
l.client.post("/cart", {
'product_id': product,
'quantity': random.randint(1,10)})

def empty_cart(l):
l.client.post('/cart/empty')

def checkout(l):
addToCart(l)
current_year = datetime.datetime.now().year+1
l.client.post("/cart/checkout", {
'email': fake.email(),
'street_address': fake.street_address(),
'zip_code': fake.zipcode(),
'city': fake.city(),
'state': fake.state_abbr(),
'country': fake.country(),
'credit_card_number': fake.credit_card_number(card_type="visa"),
'credit_card_expiration_month': random.randint(1, 12),
'credit_card_expiration_year': random.randint(current_year, current_year + 70),
'credit_card_cvv': f"{random.randint(100, 999)}",
})

def logout(l):
l.client.get('/logout')


class UserBehavior(TaskSet):

def on_start(self):
index(self)

tasks = {index: 1,
setCurrency: 2,
browseProduct: 10,
addToCart: 2,
viewCart: 3,
checkout: 1}

class WebsiteUser(FastHttpUser):
tasks = [UserBehavior]
wait_time = between(1, 10)
2 changes: 2 additions & 0 deletions examples/shop/loadgenerator/app-repo/src/requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
locust==2.29.0
faker==25.8.0
79 changes: 79 additions & 0 deletions examples/shop/loadgenerator/app-repo/src/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --output-file=requirements.txt requirements.in
#
blinker==1.8.2
# via flask
brotli==1.1.0
# via geventhttpclient
certifi==2024.6.2
# via
# geventhttpclient
# requests
charset-normalizer==3.3.2
# via requests
click==8.1.7
# via flask
configargparse==1.7
# via locust
faker==25.8.0
# via -r requirements.in
flask==3.0.3
# via
# flask-cors
# flask-login
# locust
flask-cors==4.0.1
# via locust
flask-login==0.6.3
# via locust
gevent==24.2.1
# via
# geventhttpclient
# locust
geventhttpclient==2.3.1
# via locust
greenlet==3.0.3
# via gevent
idna==3.7
# via requests
itsdangerous==2.2.0
# via flask
jinja2==3.1.4
# via flask
locust==2.29.0
# via -r requirements.in
markupsafe==2.1.5
# via
# jinja2
# werkzeug
msgpack==1.0.8
# via locust
psutil==5.9.8
# via locust
python-dateutil==2.9.0.post0
# via faker
pyzmq==26.0.3
# via locust
requests==2.32.3
# via locust
six==1.16.0
# via python-dateutil
urllib3==2.2.2
# via
# geventhttpclient
# requests
werkzeug==3.0.3
# via
# flask
# flask-login
# locust
zope-event==5.0
# via gevent
zope-interface==6.4.post2
# via gevent

# The following packages are considered to be unsafe in a requirements file:
# setuptools
Loading