commit 46bf054e0bfad18bf13b077dbc18cd85f2031657 Author: Clawdbot Date: Fri Jan 30 10:18:11 2026 +0000 Initial OpenClaw GitOps skeleton diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa7081b --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# openclaw (OpenShift) + +GitOps repo for deploying **OpenClaw** into the `openclaw` namespace. + +Layout: +- `bootstrap/` – ArgoCD `Application` (apply manually once) +- `manifests/` – Namespace + workload resources (ArgoCD-managed) + +Target: +- Namespace: `openclaw` +- Route host: `openclaw.apps.lab.apilab.us` +- Image: `default-route-openshift-image-registry.apps.lab.apilab.us/openclaw/openclaw:latest` +- Gateway port: `18789` + +Notes: +- This repo intentionally does **not** deploy TEI; it assumes the existing service: + `http://text-embeddings.tei.svc.cluster.local:8080/v1/` diff --git a/bootstrap/argocd-application.yaml b/bootstrap/argocd-application.yaml new file mode 100644 index 0000000..5375e8e --- /dev/null +++ b/bootstrap/argocd-application.yaml @@ -0,0 +1,21 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: openclaw + namespace: openshift-gitops +spec: + project: default + source: + repoURL: https://gitea.apilab.us/cscott/openclaw.git + targetRevision: main + path: manifests + destination: + server: https://kubernetes.default.svc + namespace: openclaw + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ApplyOutOfSyncOnly=true diff --git a/manifests/config-secret.yaml b/manifests/config-secret.yaml new file mode 100644 index 0000000..f8113b5 --- /dev/null +++ b/manifests/config-secret.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Secret +metadata: + name: openclaw-config + namespace: openclaw +type: Opaque +stringData: + # IMPORTANT: Fill this in with the OpenClaw gateway config JSON (or whatever the new format is). + # This is a placeholder so Argo has a resource to manage. + openclaw.json: | + { + "agents": { + "defaults": { + "memorySearch": { + "enabled": true, + "provider": "openai", + "model": "BAAI/bge-base-en-v1.5", + "remote": { + "baseUrl": "http://text-embeddings.tei.svc.cluster.local:8080/v1/", + "apiKey": "not-needed", + "batch": {"enabled": false} + } + } + } + } + } diff --git a/manifests/deployment.yaml b/manifests/deployment.yaml new file mode 100644 index 0000000..d69f8d6 --- /dev/null +++ b/manifests/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: openclaw + namespace: openclaw + labels: + app.kubernetes.io/name: openclaw +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: openclaw + template: + metadata: + labels: + app.kubernetes.io/name: openclaw + spec: + serviceAccountName: openclaw + containers: + - name: openclaw + image: default-route-openshift-image-registry.apps.lab.apilab.us/openclaw/openclaw:latest + imagePullPolicy: Always + ports: + - name: http + containerPort: 18789 + env: + # Keep caches out of $HOME under restricted SCC + - name: NPM_CONFIG_CACHE + value: /tmp/npm-cache + - name: XDG_CACHE_HOME + value: /tmp + # If OpenClaw uses HOME for state, keep it in the PVC-backed /home/clawd + - name: HOME + value: /home/clawd + volumeMounts: + - name: config + mountPath: /home/clawd/.openclaw + - name: tmp + mountPath: /tmp + - name: config-secret + mountPath: /clawd-init + readOnly: true + volumes: + - name: config + persistentVolumeClaim: + claimName: openclaw-config + - name: tmp + emptyDir: {} + - name: config-secret + secret: + secretName: openclaw-config diff --git a/manifests/maintenance/README.md b/manifests/maintenance/README.md new file mode 100644 index 0000000..fdb644f --- /dev/null +++ b/manifests/maintenance/README.md @@ -0,0 +1,9 @@ +Maintenance notes + +We intentionally avoid assumptions about UID/GID/FSGroup under OpenShift restricted SCC. + +If PVC permissions ever wedge, prefer: +- designing the container entrypoint to `umask 0002` and create needed dirs/files with group-writable perms, OR +- use an initContainer that only does `chmod -R g+rwX` (no chown) if your cluster policy allows. + +Avoid hardcoding specific numeric UIDs/GIDs. diff --git a/manifests/maintenance/fix-openclaw-pvc-perms-job.yaml b/manifests/maintenance/fix-openclaw-pvc-perms-job.yaml new file mode 100644 index 0000000..c7fb6f0 --- /dev/null +++ b/manifests/maintenance/fix-openclaw-pvc-perms-job.yaml @@ -0,0 +1,33 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: openclaw-fix-pvc-perms + namespace: openclaw +spec: + template: + spec: + restartPolicy: Never + serviceAccountName: openclaw + containers: + - name: fix + image: registry.access.redhat.com/ubi9/ubi-minimal:9.5 + command: + - /bin/sh + - -lc + - | + set -eu + echo "Fixing group permissions under /data (PVC)" + echo "NOTE: This job intentionally avoids chown or any hardcoded UID/GID assumptions." + + chmod -R g+rwX /data + find /data -type d -exec chmod 2775 {} \; + + echo "Done" + ls -la /data | head -n 100 || true + volumeMounts: + - name: config + mountPath: /data + volumes: + - name: config + persistentVolumeClaim: + claimName: openclaw-config diff --git a/manifests/namespace.yaml b/manifests/namespace.yaml new file mode 100644 index 0000000..25dea43 --- /dev/null +++ b/manifests/namespace.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: openclaw + labels: + app.kubernetes.io/name: openclaw + app.kubernetes.io/part-of: openclaw + # Uncomment if you want autonomous ephemeral diagnostics allowed here + # mcp.demo/ephemeral: "true" diff --git a/manifests/pvc.yaml b/manifests/pvc.yaml new file mode 100644 index 0000000..04fb7a9 --- /dev/null +++ b/manifests/pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: openclaw-config + namespace: openclaw +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi + # storageClassName: diff --git a/manifests/route.yaml b/manifests/route.yaml new file mode 100644 index 0000000..b7009ae --- /dev/null +++ b/manifests/route.yaml @@ -0,0 +1,17 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: openclaw + namespace: openclaw +spec: + host: openclaw.apps.lab.apilab.us + to: + kind: Service + name: openclaw + weight: 100 + port: + targetPort: http + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None diff --git a/manifests/service.yaml b/manifests/service.yaml new file mode 100644 index 0000000..fdadee7 --- /dev/null +++ b/manifests/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: openclaw + namespace: openclaw +spec: + selector: + app.kubernetes.io/name: openclaw + ports: + - name: http + port: 80 + targetPort: 18789 diff --git a/manifests/serviceaccount-rbac.yaml b/manifests/serviceaccount-rbac.yaml new file mode 100644 index 0000000..2c8e657 --- /dev/null +++ b/manifests/serviceaccount-rbac.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: openclaw + namespace: openclaw +--- +# Minimal Role/RoleBinding placeholder. +# Adjust permissions once we know what OpenClaw needs (MCP, secrets read, etc.) +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: openclaw-basic + namespace: openclaw +rules: + - apiGroups: [""] + resources: ["pods", "pods/log"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: openclaw-basic + namespace: openclaw +subjects: + - kind: ServiceAccount + name: openclaw +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: openclaw-basic