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

DM-45494: Add fastapi-safir-uws starter #3573

Merged
merged 1 commit into from
Aug 2, 2024
Merged
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
8 changes: 7 additions & 1 deletion docs/developers/helm-chart/create-new-chart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@ Use the ``--starter`` flag to specify a different Helm chart starter.
There are three options:

fastapi-safir
Use this starter for FastAPI web services based on Safir, created from the "FastAPI application (Safir)" template.
Use this starter for FastAPI web services based on Safir, created from the "FastAPI application (Safir)" template with the "Default" flavor selected.
This is the default.

fastapi-safir-uws
Use this starter for FastAPI web services based on Safir that use UWS.
These services separate the work of the service into a frontend and several backend workers, connected by a queuing system.
It is used for services that have a backend that needs to run on a stack container.
This starter corresponds to applications created from the "FastAPI application (Safir)" template with the "UWS" flavor selected.

web-service
Use this starter if the new Helm application is some other web service.

Expand Down
1 change: 1 addition & 0 deletions src/phalanx/models/helm.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ class HelmStarter(Enum):
EMPTY = "empty"
WEB_SERVICE = "web-service"
FASTAPI_SAFIR = "fastapi-safir"
FASTAPI_SAFIR_UWS = "fastapi-safir-uws"
15 changes: 7 additions & 8 deletions src/phalanx/storage/helm.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,13 @@ def create(
with (path / "Chart.yaml").open("w") as fh:
yaml.dump(chart, fh)

# Support an additional substitution variable, <CHARTENVPREFIX>,
# that's only used in templates/configmap.yaml.
configmap_path = path / "templates" / "configmap.yaml"
if configmap_path.exists():
configmap = configmap_path.read_text().replace(
"<CHARTENVPREFIX>", application.upper().replace("-", "_")
)
configmap_path.write_text(configmap)
# Support an additional substitution variable, <CHARTENVPREFIX>.
for env_path in (path / "templates").iterdir():
if env_path.is_file() and env_path.suffix == ".yaml":
text = env_path.read_text().replace(
"<CHARTENVPREFIX>", application.upper().replace("-", "_")
)
env_path.write_text(text)

def dependency_update(
self, application: str, *, quiet: bool = False
Expand Down
9 changes: 4 additions & 5 deletions starters/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# Helm starters for Phalanx

Each subdirectory of this directory is a Helm starter for a class of Phalanx service.
Use the starters with the `-p` option to `helm create`.
For example, from the `applications` directory:
These are used by the `phalanx application create` command.

For example, from the top of a Phalanx checkout:

```sh
helm create new-service -p $(pwd)/../starters/rsp-web-service
phalanx application create --starter fastapi-safir <application>
```

The path to the starter directory must be absolute, not relative, or Helm will try to use it has a path relative to `$HOME/.local/share/helm`.
12 changes: 12 additions & 0 deletions starters/fastapi-safir-uws/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v2
name: <CHARTNAME>
version: 1.0.0
description: "Helm starter chart for a new FastAPI Safir app using UWS"
sources:
- "https://github.com/lsst-sqre/<CHARTNAME>"
appVersion: 0.1.0

dependencies:
- name: redis
version: 1.0.12
repository: https://lsst-sqre.github.io/charts/
13 changes: 13 additions & 0 deletions starters/fastapi-safir-uws/secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
database-password:
description: >-
Password used to authenticate to the PostgreSQL database used to store job
information. This password may be changed at any time.
redis-password:
description: >-
Password used to authenticate to the internal Redis server, deployed as
part of the same Argo CD application and used to manage the work
queue. This secret can be changed at any time, but both the Redis server
and all deployments will then have to be restarted to pick up the new
value.
generate:
type: password
27 changes: 27 additions & 0 deletions starters/fastapi-safir-uws/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "<CHARTNAME>.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "<CHARTNAME>.labels" -}}
helm.sh/chart: {{ include "<CHARTNAME>.chart" . }}
{{ include "<CHARTNAME>.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "<CHARTNAME>.selectorLabels" -}}
app.kubernetes.io/name: "<CHARTNAME>"
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
20 changes: 20 additions & 0 deletions starters/fastapi-safir-uws/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: <CHARTNAME>
labels:
{{- include "<CHARTNAME>.labels" . | nindent 4 }}
data:
<CHARTENVPREFIX>_ARQ_QUEUE_URL: "redis://<CHARTNAME>-redis.{{ .Release.Namespace }}"
{{- if .Values.cloudsql.enabled }}
<CHARTENVPREFIX>_DATABASE_URL: "postgresql://<CHARTNAME>@localhost/<CHARTNAME>"
{{- end }}
<CHARTENVPREFIX>_GRACE_PERIOD: {{ .Values.config.gracePeriod | quote }}
<CHARTENVPREFIX>_LIFETIME: {{ .Values.config.lifetime | quote }}
<CHARTENVPREFIX>_SERVICE_ACCOUNT: {{ required "config.serviceAccount must be set" .Values.config.serviceAccount | quote }}
<CHARTENVPREFIX>_STORAGE_URL: {{ required "config.storageBucketUrl must be set" .Values.config.storageBucketUrl | quote }}
<CHARTENVPREFIX>_SYNC_TIMEOUT: {{ .Values.config.syncTimeout | quote }}
<CHARTENVPREFIX>_TIMEOUT: {{ .Values.config.timeout | quote }}
<CHARTENVPREFIX>_LOG_LEVEL: {{ .Values.config.loglevel | quote }}
<CHARTENVPREFIX>_PATH_PREFIX: {{ .Values.config.pathPrefix | quote }}
<CHARTENVPREFIX>_PROFILE: {{ .Values.config.logProfile | quote }}
96 changes: 96 additions & 0 deletions starters/fastapi-safir-uws/templates/db-worker-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: <CHARTNAME>-db-worker
labels:
{{- include "<CHARTNAME>.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.databaseWorker.replicaCount }}
selector:
matchLabels:
{{- include "<CHARTNAME>.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: "db-worker"
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.databaseWorker.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "<CHARTNAME>.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: "db-worker"
<CHARTNAME>-redis-client: "true"
spec:
{{- with .Values.databaseWorker.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.cloudsql.enabled }}
serviceAccountName: "<CHARTNAME>"
{{- else }}
automountServiceAccountToken: false
{{- end }}
containers:
{{- if .Values.cloudsql.enabled }}
- name: "cloud-sql-proxy"
command:
- "/cloud_sql_proxy"
- "-ip_address_types=PRIVATE"
- "-log_debug_stdout=true"
- "-structured_logs=true"
- "-instances={{ required "cloudsql.instanceConnectionName must be specified" .Values.cloudsql.instanceConnectionName }}=tcp:5432"
image: "{{ .Values.cloudsql.image.repository }}:{{ .Values.cloudsql.image.tag }}"
imagePullPolicy: {{ .Values.cloudsql.image.pullPolicy | quote }}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "all"
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
{{- end }}
- name: "db-worker"
command:
- "arq"
- "<CHARTNAME>.workers.uws.WorkerSettings"
env:
- name: "<CHARTENVPREFIX>_ARQ_QUEUE_PASSWORD"
valueFrom:
secretKeyRef:
name: "<CHARTNAME>"
key: "redis-password"
- name: "<CHARTENVPREFIX>_DATABASE_PASSWORD"
valueFrom:
secretKeyRef:
name: "<CHARTNAME>"
key: "database-password"
envFrom:
- configMapRef:
name: "<CHARTNAME>"
{{- with .Values.databaseWorker.resources }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "all"
readOnlyRootFilesystem: true
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
{{- with .Values.databaseWorker.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.databaseWorker.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
15 changes: 15 additions & 0 deletions starters/fastapi-safir-uws/templates/db-worker-networkpolicy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: <CHARTNAME>-db-worker
labels:
{{- include "<CHARTNAME>.labels" . | nindent 4 }}
spec:
podSelector:
# This policy controls inbound access to the database workers.
matchLabels:
{{- include "<CHARTNAME>.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: "db-worker"
policyTypes:
# Block all inbound access.
- Ingress
105 changes: 105 additions & 0 deletions starters/fastapi-safir-uws/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: "<CHARTNAME>"
labels:
{{- include "<CHARTNAME>.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.frontend.replicaCount }}
selector:
matchLabels:
{{- include "<CHARTNAME>.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: "frontend"
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.frontend.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "<CHARTNAME>.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: "frontend"
<CHARTNAME>-redis-client: "true"
spec:
{{- with .Values.frontend.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.cloudsql.enabled }}
serviceAccountName: "<CHARTNAME>"
{{- else }}
automountServiceAccountToken: false
{{- end }}
containers:
{{- if .Values.cloudsql.enabled }}
- name: "cloud-sql-proxy"
command:
- "/cloud_sql_proxy"
- "-ip_address_types=PRIVATE"
- "-log_debug_stdout=true"
- "-structured_logs=true"
- "-instances={{ required "cloudsql.instanceConnectionName must be specified" .Values.cloudsql.instanceConnectionName }}=tcp:5432"
image: "{{ .Values.cloudsql.image.repository }}:{{ .Values.cloudsql.image.tag }}"
imagePullPolicy: {{ .Values.cloudsql.image.pullPolicy | quote }}
{{- with .Values.cloudsql.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "all"
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
{{- end }}
- name: "<CHARTNAME>"
env:
- name: "<CHARTENVPREFIX>_ARQ_QUEUE_PASSWORD"
valueFrom:
secretKeyRef:
name: "<CHARTNAME>"
key: "redis-password"
- name: "<CHARTENVPREFIX>_DATABASE_PASSWORD"
valueFrom:
secretKeyRef:
name: "<CHARTNAME>"
key: "database-password"
envFrom:
- configMapRef:
name: "<CHARTNAME>"
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
ports:
- containerPort: 8080
name: "http"
protocol: "TCP"
readinessProbe:
httpGet:
path: "/api/<CHARTNAME>/availability"
port: "http"
{{- with .Values.frontend.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "all"
readOnlyRootFilesystem: true
{{- with .Values.frontend.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
{{- with .Values.frontend.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
37 changes: 37 additions & 0 deletions starters/fastapi-safir-uws/templates/ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: gafaelfawr.lsst.io/v1alpha1
kind: GafaelfawrIngress
metadata:
name: "<CHARTNAME>"
labels:
{{- include "<CHARTNAME>.labels" . | nindent 4 }}
config:
baseUrl: {{ .Values.global.baseUrl | quote }}
scopes:
all:
- "read:image"
# Request a delegated token to use for making calls to Butler server with the
# end-user's credentials.
delegate:
internal:
service: "<CHARTNAME>"
scopes:
- "read:image"
template:
metadata:
name: "<CHARTNAME>"
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 6 }}
{{- end }}
spec:
rules:
- host: {{ required "global.host must be set" .Values.global.host | quote }}
http:
paths:
- path: {{ .Values.config.pathPrefix | quote }}
pathType: "Prefix"
backend:
service:
name: "<CHARTNAME>"
port:
number: 8080
Loading
Loading