blackopsrepl commited on
Commit
ae122c6
Β·
1 Parent(s): f870b34

feat(deploy): add helm chart

Browse files
.pre-commit-config.yaml CHANGED
@@ -4,6 +4,7 @@ repos:
4
  hooks:
5
  - id: check-yaml
6
  args: ['--allow-multiple-documents']
 
7
  - id: end-of-file-fixer
8
  - id: trailing-whitespace
9
 
 
4
  hooks:
5
  - id: check-yaml
6
  args: ['--allow-multiple-documents']
7
+ exclude: '^deploy/helm/templates/.*\.yaml$'
8
  - id: end-of-file-fixer
9
  - id: trailing-whitespace
10
 
Makefile CHANGED
@@ -1,4 +1,4 @@
1
- .PHONY: help venv install run test lint format clean setup-secrets deploy-k8s
2
 
3
  PYTHON=python
4
  PIP=pip
@@ -15,7 +15,11 @@ help:
15
  @echo " lint Run pre-commit hooks (includes black, yaml, gitleaks)"
16
  @echo " format Format code with black"
17
  @echo " setup-secrets Copy and edit secrets template for local dev"
 
18
  @echo " deploy-k8s Deploy application to Kubernetes"
 
 
 
19
  @echo " clean Remove Python cache and virtual environment"
20
 
21
  venv:
@@ -42,9 +46,22 @@ setup-secrets:
42
  cp -n tests/secrets/nebius_secrets.py.template tests/secrets/cred.py; \
43
  echo "Edit tests/secrets/cred.py to add your own API credentials."
44
 
 
 
 
 
45
  deploy-k8s:
46
  ./scripts/deploy-k8s.sh
47
 
 
 
 
 
 
 
 
 
 
48
  clean:
49
  rm -rf $(VENV) __pycache__ */__pycache__ .pytest_cache .mypy_cache .coverage .hypothesis
50
  find . -type f -name '*.pyc' -delete
 
1
+ .PHONY: help venv install run test lint format clean setup-secrets check-creds deploy-k8s deploy-helm cleanup-k8s cleanup-helm
2
 
3
  PYTHON=python
4
  PIP=pip
 
15
  @echo " lint Run pre-commit hooks (includes black, yaml, gitleaks)"
16
  @echo " format Format code with black"
17
  @echo " setup-secrets Copy and edit secrets template for local dev"
18
+ @echo " check-creds Check and validate all required credentials"
19
  @echo " deploy-k8s Deploy application to Kubernetes"
20
+ @echo " deploy-helm Deploy application using Helm"
21
+ @echo " cleanup-k8s Clean up Kubernetes deployment"
22
+ @echo " cleanup-helm Clean up Helm deployment"
23
  @echo " clean Remove Python cache and virtual environment"
24
 
25
  venv:
 
46
  cp -n tests/secrets/nebius_secrets.py.template tests/secrets/cred.py; \
47
  echo "Edit tests/secrets/cred.py to add your own API credentials."
48
 
49
+ check-creds:
50
+ @echo "πŸ” Checking credentials..."
51
+ @./scripts/load-credentials.sh
52
+
53
  deploy-k8s:
54
  ./scripts/deploy-k8s.sh
55
 
56
+ deploy-helm:
57
+ ./scripts/deploy-helm.sh
58
+
59
+ cleanup-k8s:
60
+ ./scripts/cleanup-k8s.sh
61
+
62
+ cleanup-helm:
63
+ ./scripts/cleanup-helm.sh
64
+
65
  clean:
66
  rm -rf $(VENV) __pycache__ */__pycache__ .pytest_cache .mypy_cache .coverage .hypothesis
67
  find . -type f -name '*.pyc' -delete
deploy/helm/.helmignore ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Patterns to ignore when building packages.
2
+ # This supports shell glob matching, relative path matching, and
3
+ # negation (prefixed with !). Only one pattern per line.
4
+ .DS_Store
5
+ # Common VCS dirs
6
+ .git/
7
+ .gitignore
8
+ .bzr/
9
+ .bzrignore
10
+ .hg/
11
+ .hgignore
12
+ .svn/
13
+ # Common backup files
14
+ *.swp
15
+ *.bak
16
+ *.tmp
17
+ *.orig
18
+ *~
19
+ # Various IDEs
20
+ .project
21
+ .idea/
22
+ *.tmproj
23
+ .vscode/
deploy/helm/Chart.yaml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ apiVersion: v2
2
+ name: yuga-planner
3
+ description: A Helm chart for Yuga Planner application
4
+ type: application
5
+ version: 0.1.0
6
+ appVersion: "latest"
7
+ maintainers:
8
+ - name: Yuga Planner Team
9
+ keywords:
10
+ - planner
11
+ - ai
12
+ - application
13
+ home: https://github.com/blackopsrepl/yuga-planner
deploy/helm/templates/NOTES.txt ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 1. Get the application URL by running these commands:
2
+ {{- if eq .Values.service.type "NodePort" }}
3
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ .Values.app.name }}-service)
4
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
5
+ echo http://$NODE_IP:$NODE_PORT
6
+ {{- else if eq .Values.service.type "LoadBalancer" }}
7
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
8
+ You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ .Values.app.name }}-service'
9
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ .Values.app.name }}-service --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
10
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
11
+ {{- else if eq .Values.service.type "ClusterIP" }}
12
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "yuga-planner.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
13
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
14
+ echo "Visit http://127.0.0.1:8080 to use your application"
15
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
16
+ {{- end }}
17
+
18
+ 2. Check the deployment status:
19
+ kubectl get deployments {{ .Values.app.name }} -n {{ .Release.Namespace }}
20
+
21
+ 3. View the pods:
22
+ kubectl get pods -l app={{ .Values.app.name }} -n {{ .Release.Namespace }}
23
+
24
+ 4. Check the logs:
25
+ kubectl logs -l app={{ .Values.app.name }} -n {{ .Release.Namespace }} -f
26
+
27
+ Note: Make sure to set the required environment variables before deployment:
28
+ - NEBIUS_API_KEY
29
+ - NEBIUS_MODEL
30
+ - MODAL_TOKEN_ID
31
+ - MODAL_TOKEN_SECRET
32
+ - HF_MODEL
33
+ - HF_TOKEN
34
+
35
+ You can substitute these values using envsubst before installation:
36
+ envsubst < values.yaml | helm install yuga-planner ./helm --values -
deploy/helm/templates/_helpers.tpl ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {{/*
2
+ Expand the name of the chart.
3
+ */}}
4
+ {{- define "yuga-planner.name" -}}
5
+ {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6
+ {{- end }}
7
+
8
+ {{/*
9
+ Create a default fully qualified app name.
10
+ We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11
+ If release name contains chart name it will be used as a full name.
12
+ */}}
13
+ {{- define "yuga-planner.fullname" -}}
14
+ {{- if .Values.fullnameOverride }}
15
+ {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16
+ {{- else }}
17
+ {{- $name := default .Chart.Name .Values.nameOverride }}
18
+ {{- if contains $name .Release.Name }}
19
+ {{- .Release.Name | trunc 63 | trimSuffix "-" }}
20
+ {{- else }}
21
+ {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22
+ {{- end }}
23
+ {{- end }}
24
+ {{- end }}
25
+
26
+ {{/*
27
+ Create chart name and version as used by the chart label.
28
+ */}}
29
+ {{- define "yuga-planner.chart" -}}
30
+ {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31
+ {{- end }}
32
+
33
+ {{/*
34
+ Common labels
35
+ */}}
36
+ {{- define "yuga-planner.labels" -}}
37
+ helm.sh/chart: {{ include "yuga-planner.chart" . }}
38
+ {{ include "yuga-planner.selectorLabels" . }}
39
+ {{- if .Chart.AppVersion }}
40
+ app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41
+ {{- end }}
42
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
43
+ {{- end }}
44
+
45
+ {{/*
46
+ Selector labels
47
+ */}}
48
+ {{- define "yuga-planner.selectorLabels" -}}
49
+ app.kubernetes.io/name: {{ include "yuga-planner.name" . }}
50
+ app.kubernetes.io/instance: {{ .Release.Name }}
51
+ {{- end }}
deploy/helm/templates/configmap.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ apiVersion: v1
2
+ kind: ConfigMap
3
+ metadata:
4
+ name: {{ .Values.app.name }}-config
5
+ labels:
6
+ app: {{ .Values.app.name }}
7
+ {{- include "yuga-planner.labels" . | nindent 4 }}
8
+ data:
9
+ JAVA_HOME: {{ .Values.env.java.home | quote }}
10
+ PATH: {{ .Values.env.java.path | quote }}
deploy/helm/templates/deployment.yaml ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ apiVersion: apps/v1
2
+ kind: Deployment
3
+ metadata:
4
+ name: {{ .Values.app.name }}
5
+ labels:
6
+ app: {{ .Values.app.name }}
7
+ {{- include "yuga-planner.labels" . | nindent 4 }}
8
+ spec:
9
+ replicas: {{ .Values.deployment.replicas }}
10
+ selector:
11
+ matchLabels:
12
+ app: {{ .Values.app.name }}
13
+ {{- include "yuga-planner.selectorLabels" . | nindent 6 }}
14
+ template:
15
+ metadata:
16
+ labels:
17
+ app: {{ .Values.app.name }}
18
+ {{- include "yuga-planner.selectorLabels" . | nindent 8 }}
19
+ spec:
20
+ containers:
21
+ - name: {{ .Values.app.name }}
22
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
23
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
24
+ ports:
25
+ - containerPort: {{ .Values.container.port }}
26
+ name: {{ .Values.container.name }}
27
+ protocol: {{ .Values.service.protocol }}
28
+ env:
29
+ - name: JAVA_HOME
30
+ valueFrom:
31
+ configMapKeyRef:
32
+ name: {{ .Values.app.name }}-config
33
+ key: JAVA_HOME
34
+ - name: PATH
35
+ valueFrom:
36
+ configMapKeyRef:
37
+ name: {{ .Values.app.name }}-config
38
+ key: PATH
39
+ - name: NEBIUS_API_KEY
40
+ valueFrom:
41
+ secretKeyRef:
42
+ name: {{ .Values.app.name }}-secrets
43
+ key: NEBIUS_API_KEY
44
+ - name: NEBIUS_MODEL
45
+ valueFrom:
46
+ secretKeyRef:
47
+ name: {{ .Values.app.name }}-secrets
48
+ key: NEBIUS_MODEL
49
+ - name: MODAL_TOKEN_ID
50
+ valueFrom:
51
+ secretKeyRef:
52
+ name: {{ .Values.app.name }}-secrets
53
+ key: MODAL_TOKEN_ID
54
+ - name: MODAL_TOKEN_SECRET
55
+ valueFrom:
56
+ secretKeyRef:
57
+ name: {{ .Values.app.name }}-secrets
58
+ key: MODAL_TOKEN_SECRET
59
+ - name: HF_MODEL
60
+ valueFrom:
61
+ secretKeyRef:
62
+ name: {{ .Values.app.name }}-secrets
63
+ key: HF_MODEL
64
+ - name: HF_TOKEN
65
+ valueFrom:
66
+ secretKeyRef:
67
+ name: {{ .Values.app.name }}-secrets
68
+ key: HF_TOKEN
69
+ command: {{ .Values.deployment.command | toJson }}
70
+ args:
71
+ {{- range .Values.deployment.args }}
72
+ - {{ . | quote }}
73
+ {{- end }}
74
+ resources:
75
+ {{- toYaml .Values.resources | nindent 10 }}
76
+ livenessProbe:
77
+ {{- toYaml .Values.probes.liveness | nindent 10 }}
78
+ readinessProbe:
79
+ {{- toYaml .Values.probes.readiness | nindent 10 }}
80
+ securityContext:
81
+ {{- toYaml .Values.securityContext | nindent 10 }}
deploy/helm/templates/secret.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ apiVersion: v1
2
+ kind: Secret
3
+ metadata:
4
+ name: {{ .Values.app.name }}-secrets
5
+ labels:
6
+ app: {{ .Values.app.name }}
7
+ {{- include "yuga-planner.labels" . | nindent 4 }}
8
+ type: Opaque
9
+ stringData:
10
+ # These values will be populated from environment variables during deployment
11
+ NEBIUS_API_KEY: {{ .Values.secrets.nebiusApiKey | quote }}
12
+ NEBIUS_MODEL: {{ .Values.secrets.nebiusModel | quote }}
13
+ MODAL_TOKEN_ID: {{ .Values.secrets.modalTokenId | quote }}
14
+ MODAL_TOKEN_SECRET: {{ .Values.secrets.modalTokenSecret | quote }}
15
+ HF_MODEL: {{ .Values.secrets.hfModel | quote }}
16
+ HF_TOKEN: {{ .Values.secrets.hfToken | quote }}
deploy/helm/templates/service.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ apiVersion: v1
2
+ kind: Service
3
+ metadata:
4
+ name: {{ .Values.app.name }}-service
5
+ labels:
6
+ app: {{ .Values.app.name }}
7
+ {{- include "yuga-planner.labels" . | nindent 4 }}
8
+ spec:
9
+ type: {{ .Values.service.type }}
10
+ ports:
11
+ - port: {{ .Values.service.port }}
12
+ targetPort: {{ .Values.service.targetPort }}
13
+ {{- if eq .Values.service.type "NodePort" }}
14
+ nodePort: {{ .Values.service.nodePort }}
15
+ {{- end }}
16
+ protocol: {{ .Values.service.protocol }}
17
+ name: {{ .Values.container.name }}
18
+ selector:
19
+ app: {{ .Values.app.name }}
20
+ {{- include "yuga-planner.selectorLabels" . | nindent 4 }}
deploy/helm/values.yaml ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Default values for yuga-planner.
2
+ # This is a YAML-formatted file.
3
+ # Declare variables to be passed into your templates.
4
+
5
+ # Application configuration
6
+ app:
7
+ name: yuga-planner
8
+
9
+ # Image configuration
10
+ image:
11
+ repository: ghcr.io/blackopsrepl/yuga-planner-test
12
+ tag: latest
13
+ pullPolicy: Always
14
+
15
+ # Service configuration
16
+ service:
17
+ type: NodePort
18
+ port: 80
19
+ targetPort: 7860
20
+ nodePort: 30860
21
+ protocol: TCP
22
+
23
+ # Container configuration
24
+ container:
25
+ port: 7860
26
+ name: http
27
+
28
+ # Resource configuration
29
+ resources:
30
+ requests:
31
+ memory: "512Mi"
32
+ cpu: "250m"
33
+ limits:
34
+ memory: "2Gi"
35
+ cpu: "1000m"
36
+
37
+ # Deployment configuration
38
+ deployment:
39
+ replicas: 1
40
+ command: ["python", "src/app.py"]
41
+ args:
42
+ - "--server-name"
43
+ - "0.0.0.0"
44
+ - "--server-port"
45
+ - "7860"
46
+
47
+ # Health check configuration
48
+ probes:
49
+ liveness:
50
+ httpGet:
51
+ path: /
52
+ port: 7860
53
+ initialDelaySeconds: 30
54
+ periodSeconds: 10
55
+ timeoutSeconds: 5
56
+ failureThreshold: 3
57
+ readiness:
58
+ httpGet:
59
+ path: /
60
+ port: 7860
61
+ initialDelaySeconds: 10
62
+ periodSeconds: 5
63
+ timeoutSeconds: 3
64
+ failureThreshold: 3
65
+
66
+ # Security context
67
+ securityContext:
68
+ runAsNonRoot: true
69
+ runAsUser: 1000
70
+ runAsGroup: 1000
71
+ allowPrivilegeEscalation: false
72
+ readOnlyRootFilesystem: false
73
+ capabilities:
74
+ drop:
75
+ - ALL
76
+
77
+ # Environment configuration
78
+ env:
79
+ java:
80
+ home: "/usr/lib/jvm/temurin-21-jdk-amd64"
81
+ path: "/usr/lib/jvm/temurin-21-jdk-amd64/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
82
+
83
+ # Secret configuration (values will be populated from environment variables during deployment)
84
+ secrets:
85
+ nebiusApiKey: "${NEBIUS_API_KEY}"
86
+ nebiusModel: "${NEBIUS_MODEL}"
87
+ modalTokenId: "${MODAL_TOKEN_ID}"
88
+ modalTokenSecret: "${MODAL_TOKEN_SECRET}"
89
+ hfModel: "${HF_MODEL}"
90
+ hfToken: "${HF_TOKEN}"
scripts/cleanup-helm.sh ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Yuga Planner Helm Cleanup Script
5
+ # This script removes the Helm release and all associated resources
6
+
7
+ echo "🧹 Cleaning up Yuga Planner Helm deployment..."
8
+
9
+ # Check if helm is available
10
+ if ! command -v helm &> /dev/null; then
11
+ echo "❌ Error: helm is required but not installed."
12
+ echo "πŸ’‘ Install Helm from: https://helm.sh/docs/intro/install/"
13
+ exit 1
14
+ fi
15
+
16
+ # Configuration (same defaults as deploy script)
17
+ RELEASE_NAME="${HELM_RELEASE_NAME:-yuga-planner}"
18
+ # Get current namespace, fallback to default if not set
19
+ CURRENT_NAMESPACE=$(kubectl config view --minify --output 'jsonpath={..namespace}' 2>/dev/null || echo "default")
20
+ NAMESPACE="${HELM_NAMESPACE:-$CURRENT_NAMESPACE}"
21
+
22
+ echo "πŸ“¦ Helm Release: $RELEASE_NAME"
23
+ echo "🏷️ Namespace: $NAMESPACE"
24
+
25
+ # Check if the release exists
26
+ if ! helm list -n "$NAMESPACE" | grep -q "^$RELEASE_NAME"; then
27
+ echo "ℹ️ Helm release '$RELEASE_NAME' not found in namespace '$NAMESPACE'."
28
+ echo "βœ… Nothing to clean up."
29
+ exit 0
30
+ fi
31
+
32
+ # Show release information
33
+ echo "πŸ” Found Helm release:"
34
+ helm list -n "$NAMESPACE" | grep "^$RELEASE_NAME" || true
35
+
36
+ echo ""
37
+ echo "πŸ“Š Release details:"
38
+ helm status "$RELEASE_NAME" -n "$NAMESPACE" || true
39
+
40
+ # Confirm deletion
41
+ echo ""
42
+ read -p "❓ Are you sure you want to uninstall the Helm release '$RELEASE_NAME'? (y/N): " -n 1 -r
43
+ echo
44
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
45
+ echo "❌ Cleanup cancelled."
46
+ exit 0
47
+ fi
48
+
49
+ echo "πŸ—‘οΈ Uninstalling Helm release..."
50
+ helm uninstall "$RELEASE_NAME" -n "$NAMESPACE"
51
+
52
+ echo "βœ… Helm release uninstalled successfully!"
53
+
54
+ # Check if namespace should be cleaned up (optional)
55
+ if [ "$NAMESPACE" != "default" ]; then
56
+ echo ""
57
+ echo "πŸ€” The namespace '$NAMESPACE' still exists."
58
+ read -p "❓ Do you want to delete the namespace '$NAMESPACE' as well? (y/N): " -n 1 -r
59
+ echo
60
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
61
+ # Check if namespace has other resources
62
+ OTHER_RESOURCES=$(kubectl get all -n "$NAMESPACE" --ignore-not-found=true 2>/dev/null | grep -v "^NAME" | wc -l)
63
+ if [ "$OTHER_RESOURCES" -gt 0 ]; then
64
+ echo "⚠️ Warning: Namespace '$NAMESPACE' contains other resources."
65
+ kubectl get all -n "$NAMESPACE" 2>/dev/null || true
66
+ read -p "❓ Are you sure you want to delete the entire namespace? (y/N): " -n 1 -r
67
+ echo
68
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
69
+ kubectl delete namespace "$NAMESPACE"
70
+ echo "βœ… Namespace '$NAMESPACE' deleted."
71
+ else
72
+ echo "ℹ️ Namespace '$NAMESPACE' preserved."
73
+ fi
74
+ else
75
+ kubectl delete namespace "$NAMESPACE"
76
+ echo "βœ… Empty namespace '$NAMESPACE' deleted."
77
+ fi
78
+ else
79
+ echo "ℹ️ Namespace '$NAMESPACE' preserved."
80
+ fi
81
+ fi
82
+
83
+ echo ""
84
+ echo "πŸ” Useful commands to verify cleanup:"
85
+ echo " β€’ List remaining releases: helm list -A"
86
+ echo " β€’ Check for remaining resources: kubectl get all -l app.kubernetes.io/instance=$RELEASE_NAME -A"
scripts/cleanup-k8s.sh ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Yuga Planner Kubernetes Cleanup Script
5
+ # This script removes all Kubernetes resources created by the deployment
6
+
7
+ echo "🧹 Cleaning up Yuga Planner Kubernetes deployment..."
8
+
9
+ # Check if kubectl is available
10
+ if ! command -v kubectl &> /dev/null; then
11
+ echo "❌ Error: kubectl is required but not installed."
12
+ exit 1
13
+ fi
14
+
15
+ # Check if we're in the correct directory (project root)
16
+ if [ ! -f "deploy/kubernetes.yaml" ]; then
17
+ echo "❌ Error: kubernetes.yaml not found. Please run this script from the project root."
18
+ exit 1
19
+ fi
20
+
21
+ # Function to check if resources exist
22
+ check_resources() {
23
+ local resource_exists=false
24
+
25
+ if kubectl get deployment yuga-planner &> /dev/null; then
26
+ resource_exists=true
27
+ fi
28
+
29
+ if kubectl get service yuga-planner-service &> /dev/null; then
30
+ resource_exists=true
31
+ fi
32
+
33
+ if kubectl get secret yuga-planner-secrets &> /dev/null; then
34
+ resource_exists=true
35
+ fi
36
+
37
+ if kubectl get configmap yuga-planner-config &> /dev/null; then
38
+ resource_exists=true
39
+ fi
40
+
41
+ if [ "$resource_exists" = false ]; then
42
+ echo "ℹ️ No Yuga Planner resources found in the current namespace."
43
+ return 1
44
+ fi
45
+
46
+ return 0
47
+ }
48
+
49
+ # Check if any resources exist
50
+ if ! check_resources; then
51
+ echo "βœ… Nothing to clean up."
52
+ exit 0
53
+ fi
54
+
55
+ # Show what will be deleted
56
+ echo "πŸ” Found the following Yuga Planner resources:"
57
+ kubectl get deployment,service,secret,configmap -l app=yuga-planner 2>/dev/null || true
58
+
59
+ # Confirm deletion
60
+ read -p "❓ Are you sure you want to delete these resources? (y/N): " -n 1 -r
61
+ echo
62
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
63
+ echo "❌ Cleanup cancelled."
64
+ exit 0
65
+ fi
66
+
67
+ echo "πŸ—‘οΈ Deleting Kubernetes resources..."
68
+
69
+ # Delete resources by label selector (safer approach)
70
+ echo " β€’ Deleting deployment..."
71
+ kubectl delete deployment -l app=yuga-planner --ignore-not-found=true
72
+
73
+ echo " β€’ Deleting service..."
74
+ kubectl delete service -l app=yuga-planner --ignore-not-found=true
75
+
76
+ echo " β€’ Deleting secrets..."
77
+ kubectl delete secret -l app=yuga-planner --ignore-not-found=true
78
+
79
+ echo " β€’ Deleting configmaps..."
80
+ kubectl delete configmap -l app=yuga-planner --ignore-not-found=true
81
+
82
+ echo "βœ… Cleanup complete!"
83
+ echo "πŸ” Verify cleanup: kubectl get all -l app=yuga-planner"
scripts/deploy-helm.sh ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Yuga Planner Helm Deployment Script
5
+ # This script loads credentials from environment variables or creds.py and deploys using Helm
6
+
7
+ echo "πŸš€ Deploying Yuga Planner using Helm..."
8
+
9
+ # Check if we're in the correct directory (project root)
10
+ if [ ! -d "deploy/helm" ] && [ ! -f "deploy/helm/Chart.yaml" ]; then
11
+ echo "❌ Error: Helm chart not found. Please ensure deploy/helm/Chart.yaml exists."
12
+ exit 1
13
+ fi
14
+
15
+ # Get the script directory
16
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
+
18
+ # Source the credential loading script
19
+ source "${SCRIPT_DIR}/load-credentials.sh"
20
+
21
+ # Check and load credentials
22
+ if ! check_credentials; then
23
+ exit 1
24
+ fi
25
+
26
+ # Check if helm is available
27
+ if ! command -v helm &> /dev/null; then
28
+ echo "❌ Error: helm is required but not installed."
29
+ echo "πŸ’‘ Install Helm from: https://helm.sh/docs/intro/install/"
30
+ exit 1
31
+ fi
32
+
33
+ # Configuration
34
+ RELEASE_NAME="${HELM_RELEASE_NAME:-yuga-planner}"
35
+ # Get current namespace, fallback to default if not set
36
+ CURRENT_NAMESPACE=$(kubectl config view --minify --output 'jsonpath={..namespace}' 2>/dev/null || echo "default")
37
+ NAMESPACE="${HELM_NAMESPACE:-$CURRENT_NAMESPACE}"
38
+ CHART_PATH="${HELM_CHART_PATH:-deploy/helm}"
39
+
40
+ echo "πŸ“¦ Helm Release: $RELEASE_NAME"
41
+ echo "🏷️ Namespace: $NAMESPACE"
42
+ echo "πŸ“‚ Chart Path: $CHART_PATH"
43
+
44
+ # Create namespace if it doesn't exist
45
+ if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
46
+ echo "πŸ—οΈ Creating namespace: $NAMESPACE"
47
+ kubectl create namespace "$NAMESPACE"
48
+ fi
49
+
50
+ # Prepare Helm values with environment variables
51
+ HELM_VALUES=""
52
+ HELM_VALUES="$HELM_VALUES --set env.NEBIUS_API_KEY=$NEBIUS_API_KEY"
53
+ HELM_VALUES="$HELM_VALUES --set env.NEBIUS_MODEL=$NEBIUS_MODEL"
54
+ HELM_VALUES="$HELM_VALUES --set env.MODAL_TOKEN_ID=$MODAL_TOKEN_ID"
55
+ HELM_VALUES="$HELM_VALUES --set env.MODAL_TOKEN_SECRET=$MODAL_TOKEN_SECRET"
56
+ HELM_VALUES="$HELM_VALUES --set env.HF_MODEL=$HF_MODEL"
57
+ HELM_VALUES="$HELM_VALUES --set env.HF_TOKEN=$HF_TOKEN"
58
+
59
+ # Add optional environment variables if set
60
+ if [ ! -z "$IMAGE_TAG" ]; then
61
+ HELM_VALUES="$HELM_VALUES --set image.tag=$IMAGE_TAG"
62
+ fi
63
+
64
+ if [ ! -z "$REPLICAS" ]; then
65
+ HELM_VALUES="$HELM_VALUES --set replicaCount=$REPLICAS"
66
+ fi
67
+
68
+ # Check if release already exists
69
+ if helm list -n "$NAMESPACE" | grep -q "^$RELEASE_NAME"; then
70
+ echo "πŸ”„ Upgrading existing Helm release..."
71
+ helm upgrade "$RELEASE_NAME" "$CHART_PATH" \
72
+ --namespace "$NAMESPACE" \
73
+ $HELM_VALUES \
74
+ --timeout 300s \
75
+ --wait
76
+ else
77
+ echo "πŸ†• Installing new Helm release..."
78
+ helm install "$RELEASE_NAME" "$CHART_PATH" \
79
+ --namespace "$NAMESPACE" \
80
+ $HELM_VALUES \
81
+ --timeout 300s \
82
+ --wait
83
+ fi
84
+
85
+ echo "βœ… Deployment complete!"
86
+ echo ""
87
+ echo "πŸ“Š Release Information:"
88
+ helm status "$RELEASE_NAME" -n "$NAMESPACE"
89
+ echo ""
90
+ echo "πŸ” Useful commands:"
91
+ echo " β€’ Check pods: kubectl get pods -l app.kubernetes.io/instance=$RELEASE_NAME -n $NAMESPACE"
92
+ echo " β€’ View logs: kubectl logs -l app.kubernetes.io/instance=$RELEASE_NAME -n $NAMESPACE -f"
93
+ echo " β€’ Port forward: kubectl port-forward svc/$RELEASE_NAME 8080:80 -n $NAMESPACE"
94
+ echo " β€’ Uninstall: helm uninstall $RELEASE_NAME -n $NAMESPACE"
scripts/deploy-k8s.sh CHANGED
@@ -12,106 +12,17 @@ if [ ! -f "deploy/kubernetes.yaml" ]; then
12
  exit 1
13
  fi
14
 
15
- # Function to load credentials from creds.py if environment variables are not set
16
- load_credentials() {
17
- local creds_file="tests/secrets/creds.py"
18
 
19
- if [ -f "$creds_file" ]; then
20
- echo "πŸ“‹ Loading credentials from $creds_file..."
21
 
22
- # Extract credentials from creds.py if environment variables are not set
23
- if [ -z "$NEBIUS_API_KEY" ]; then
24
- export NEBIUS_API_KEY=$(python3 -c "
25
- import sys
26
- sys.path.append('tests/secrets')
27
- import creds
28
- print(creds.NEBIUS_API_KEY)
29
- " 2>/dev/null || echo "")
30
- fi
31
-
32
- if [ -z "$NEBIUS_MODEL" ]; then
33
- export NEBIUS_MODEL=$(python3 -c "
34
- import sys
35
- sys.path.append('tests/secrets')
36
- import creds
37
- print(creds.NEBIUS_MODEL)
38
- " 2>/dev/null || echo "")
39
- fi
40
-
41
- if [ -z "$MODAL_TOKEN_ID" ]; then
42
- export MODAL_TOKEN_ID=$(python3 -c "
43
- import sys
44
- sys.path.append('tests/secrets')
45
- import creds
46
- print(creds.MODAL_TOKEN_ID)
47
- " 2>/dev/null || echo "")
48
- fi
49
-
50
- if [ -z "$MODAL_TOKEN_SECRET" ]; then
51
- export MODAL_TOKEN_SECRET=$(python3 -c "
52
- import sys
53
- sys.path.append('tests/secrets')
54
- import creds
55
- print(creds.MODAL_TOKEN_SECRET)
56
- " 2>/dev/null || echo "")
57
- fi
58
-
59
- if [ -z "$HF_MODEL" ]; then
60
- export HF_MODEL=$(python3 -c "
61
- import sys
62
- sys.path.append('tests/secrets')
63
- import creds
64
- print(creds.HF_MODEL)
65
- " 2>/dev/null || echo "")
66
- fi
67
-
68
- if [ -z "$HF_TOKEN" ]; then
69
- export HF_TOKEN=$(python3 -c "
70
- import sys
71
- sys.path.append('tests/secrets')
72
- import creds
73
- print(creds.HF_TOKEN)
74
- " 2>/dev/null || echo "")
75
- fi
76
- else
77
- echo "⚠️ Warning: $creds_file not found"
78
- fi
79
- }
80
-
81
- # Check if credentials are available in environment variables
82
- echo "πŸ” Checking for credentials..."
83
-
84
- missing_vars=()
85
- required_vars=("NEBIUS_API_KEY" "NEBIUS_MODEL" "MODAL_TOKEN_ID" "MODAL_TOKEN_SECRET" "HF_MODEL" "HF_TOKEN")
86
-
87
- for var in "${required_vars[@]}"; do
88
- if [ -z "${!var}" ]; then
89
- missing_vars+=("$var")
90
- fi
91
- done
92
-
93
- if [ ${#missing_vars[@]} -gt 0 ]; then
94
- echo "πŸ“‚ Missing environment variables: ${missing_vars[*]}"
95
- echo "πŸ”„ Attempting to load from creds.py..."
96
- load_credentials
97
-
98
- # Check again after loading from creds.py
99
- missing_vars=()
100
- for var in "${required_vars[@]}"; do
101
- if [ -z "${!var}" ]; then
102
- missing_vars+=("$var")
103
- fi
104
- done
105
-
106
- if [ ${#missing_vars[@]} -gt 0 ]; then
107
- echo "❌ Error: The following required environment variables are not set: ${missing_vars[*]}"
108
- echo "πŸ’‘ Please set them in your environment or ensure tests/secrets/creds.py exists with the required values."
109
- exit 1
110
- fi
111
  fi
112
 
113
- echo "βœ… All credentials found"
114
-
115
  # Check if envsubst is available
116
  if ! command -v envsubst &> /dev/null; then
117
  echo "❌ Error: envsubst is required but not installed. Please install gettext-base package."
 
12
  exit 1
13
  fi
14
 
15
+ # Get the script directory
16
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 
17
 
18
+ # Source the credential loading script
19
+ source "${SCRIPT_DIR}/load-credentials.sh"
20
 
21
+ # Check and load credentials
22
+ if ! check_credentials; then
23
+ exit 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  fi
25
 
 
 
26
  # Check if envsubst is available
27
  if ! command -v envsubst &> /dev/null; then
28
  echo "❌ Error: envsubst is required but not installed. Please install gettext-base package."
scripts/load-credentials.sh ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Yuga Planner Credential Loading Script
4
+ # This script loads credentials from environment variables or creds.py
5
+
6
+ # Function to load credentials from creds.py if environment variables are not set
7
+ load_credentials() {
8
+ local creds_file="tests/secrets/creds.py"
9
+
10
+ if [ -f "$creds_file" ]; then
11
+ echo "πŸ“‹ Loading credentials from $creds_file..."
12
+
13
+ # Extract credentials from creds.py if environment variables are not set
14
+ if [ -z "$NEBIUS_API_KEY" ]; then
15
+ export NEBIUS_API_KEY=$(python3 -c "
16
+ import sys
17
+ sys.path.append('tests/secrets')
18
+ import creds
19
+ print(creds.NEBIUS_API_KEY)
20
+ " 2>/dev/null || echo "")
21
+ fi
22
+
23
+ if [ -z "$NEBIUS_MODEL" ]; then
24
+ export NEBIUS_MODEL=$(python3 -c "
25
+ import sys
26
+ sys.path.append('tests/secrets')
27
+ import creds
28
+ print(creds.NEBIUS_MODEL)
29
+ " 2>/dev/null || echo "")
30
+ fi
31
+
32
+ if [ -z "$MODAL_TOKEN_ID" ]; then
33
+ export MODAL_TOKEN_ID=$(python3 -c "
34
+ import sys
35
+ sys.path.append('tests/secrets')
36
+ import creds
37
+ print(creds.MODAL_TOKEN_ID)
38
+ " 2>/dev/null || echo "")
39
+ fi
40
+
41
+ if [ -z "$MODAL_TOKEN_SECRET" ]; then
42
+ export MODAL_TOKEN_SECRET=$(python3 -c "
43
+ import sys
44
+ sys.path.append('tests/secrets')
45
+ import creds
46
+ print(creds.MODAL_TOKEN_SECRET)
47
+ " 2>/dev/null || echo "")
48
+ fi
49
+
50
+ if [ -z "$HF_MODEL" ]; then
51
+ export HF_MODEL=$(python3 -c "
52
+ import sys
53
+ sys.path.append('tests/secrets')
54
+ import creds
55
+ print(creds.HF_MODEL)
56
+ " 2>/dev/null || echo "")
57
+ fi
58
+
59
+ if [ -z "$HF_TOKEN" ]; then
60
+ export HF_TOKEN=$(python3 -c "
61
+ import sys
62
+ sys.path.append('tests/secrets')
63
+ import creds
64
+ print(creds.HF_TOKEN)
65
+ " 2>/dev/null || echo "")
66
+ fi
67
+ else
68
+ echo "⚠️ Warning: $creds_file not found"
69
+ fi
70
+ }
71
+
72
+ # Function to check and validate required credentials
73
+ check_credentials() {
74
+ echo "πŸ” Checking for credentials..."
75
+
76
+ local missing_vars=()
77
+ local required_vars=("NEBIUS_API_KEY" "NEBIUS_MODEL" "MODAL_TOKEN_ID" "MODAL_TOKEN_SECRET" "HF_MODEL" "HF_TOKEN")
78
+
79
+ for var in "${required_vars[@]}"; do
80
+ if [ -z "${!var}" ]; then
81
+ missing_vars+=("$var")
82
+ fi
83
+ done
84
+
85
+ if [ ${#missing_vars[@]} -gt 0 ]; then
86
+ echo "πŸ“‚ Missing environment variables: ${missing_vars[*]}"
87
+ echo "πŸ”„ Attempting to load from creds.py..."
88
+ load_credentials
89
+
90
+ # Check again after loading from creds.py
91
+ missing_vars=()
92
+ for var in "${required_vars[@]}"; do
93
+ if [ -z "${!var}" ]; then
94
+ missing_vars+=("$var")
95
+ fi
96
+ done
97
+
98
+ if [ ${#missing_vars[@]} -gt 0 ]; then
99
+ echo "❌ Error: The following required environment variables are not set: ${missing_vars[*]}"
100
+ echo "πŸ’‘ Please set them in your environment or ensure tests/secrets/creds.py exists with the required values."
101
+ return 1
102
+ fi
103
+ fi
104
+
105
+ echo "βœ… All credentials found"
106
+ return 0
107
+ }
108
+
109
+ # Main execution when script is run directly
110
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
111
+ check_credentials
112
+ exit $?
113
+ fi