Loading README.md +1 −1 Changes for README.md: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ Intended for local development, integration testing, and demos. **Not for produc | PostgreSQL | `oop` | Shared instance; one database and role per component | | AI2 MCP | `oop` | MCP server exposing CAMARA-compliant tools | | AI2 AI Agent | `oop` | Natural language interface backed by Groq | | Portal | `oop` | Next.js web frontend for the OOP platform | | Portal | `oop` | Developer portal: web UI and its backend (login, OEG and AI2 proxy, API clients) | --- Loading environments/kind/values.yaml +2 −0 Changes for environments/kind/values.yaml: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -94,6 +94,8 @@ ai2: portal: image: pullPolicy: IfNotPresent oeg: publicUrl: http://localhost:32263 databus: Loading oop-platform-chart/charts/portal/Chart.yaml +3 −4 Changes for oop-platform-chart/charts/portal/Chart.yaml: 3 added lines, 4 removed lines. Original line number Diff line number Diff line apiVersion: v2 name: portal description: Helm chart for the OOP Portal description: Helm chart for the OOP Portal (developer portal UI and its backend) type: application version: 1.0.0 appVersion: "1.0.0" appVersion: "2.0.0" keywords: - frontend - oop - etsi - portal home: https://labs.etsi.org/rep/oop/code/dev-portal home: https://labs.etsi.org/rep/oop/code/portal maintainers: - name: OOP Team oop-platform-chart/charts/portal/README.md +37 −76 Changes for oop-platform-chart/charts/portal/README.md: 37 added lines, 76 removed lines. Original line number Diff line number Diff line # Portal Helm Chart Next.js web frontend for the Open Operator Platform (OOP). Deploys the OOP developer portal: one container in which the backend serves the web UI and its API (`/api/*`) on the same address, so the login cookies stay first-party. ## Overview The portal needs, from the umbrella chart: This Helm chart deploys the **Portal** — a browser-based UI that lets operators interact with the platform through the OEG northbound API. - **PostgreSQL**: database and role `portal`, an entry in `postgresql.databases`. An init container waits until the role can log in. - **Keycloak** (unless `global.auth.enabled=false`): the `oop-portal` client logs users in, and `oop-portal-provisioner` creates developers' API clients. Their secrets come from the `keycloak-credentials` Secret. - **OEG** and the **AI2 agent**, which the backend forwards `/api/oeg/*` and `/api/ai2/chat` to. ## Prerequisites ## Addresses - Kubernetes 1.19+ - Helm 3.0+ - OEG deployed and reachable at the configured `config.oegServiceUrl` Browsers reach the portal and Keycloak on public URLs built from `global.publicHost`, `global.scheme` and `global.nodePorts`, as the keycloak chart builds them. The backend's own calls to Keycloak use `global.keycloak.host`/`port` instead, since the public address may not be reachable from pods. ## Installation When deploying via the OOP umbrella chart this sub-chart is included automatically. To install it standalone: ```bash helm install portal . -n oop --create-namespace ``` If you set the keycloak chart's `hostname` or `realms.oop.portal.baseUrl`, set `keycloak.publicUrl` or `publicUrl` here to match. ## Configuration | Parameter | Description | Default | |-----------|-------------|---------| | `replicaCount` | Number of pod replicas | `1` | | `image.repository` | Container image repository | `labs.etsi.org:5050/oop/code/dev-portal` | | `image.tag` | Container image tag | `1.0.0` | | `image.pullPolicy` | Image pull policy | `IfNotPresent` | | `config.oegServiceUrl` | In-cluster URL of the OEG service | `http://oeg:80/oeg/1.0.0` | | `service.type` | Kubernetes service type | `ClusterIP` | | `service.port` | Service port | `80` | | `service.targetPort` | Container port (Next.js) | `3000` | | `service.nodePort` | NodePort value (when `type: NodePort`) | `""` | | `resources.limits.cpu` | CPU limit | `500m` | | `resources.limits.memory` | Memory limit | `512Mi` | | `resources.requests.cpu` | CPU request | `100m` | | `resources.requests.memory` | Memory request | `256Mi` | ## Kind / local development overrides The `environments/kind/values.yaml` file exposes the portal on NodePort `30082`: ```yaml portal: image: pullPolicy: IfNotPresent service: type: NodePort nodePort: 30082 ``` Access the portal at **http://localhost:30082** after deploying on kind. | `image.repository` | Image repository | `labs.etsi.org:5050/oop/code/portal` | | `image.tag` | Image tag, also reported as the app version | `2.0.0` | | `publicUrl` | Address browsers reach the portal on | `<scheme>://<public host>:<NodePort>` | | `oeg.url` | Where `/api/oeg/*` is forwarded to | `http://oeg:80` | | `oeg.publicUrl` | OEG address shown with developers' API clients | `""` (not shown) | | `ai2.chatUrl` | AI2 agent chat endpoint; empty turns the chat off | `http://ai2-ai-agent:9013/groq-mcp` | | `keycloak.publicUrl` | Keycloak's public URL (token issuer) | `<scheme>://<public host>:<global.nodePorts.keycloak>` | | `keycloak.existingSecret` | Secret with the client secrets | `keycloak-credentials` | | `keycloak.provisioner.enabled` | API clients feature | `true` | | `keycloak.provisioner.oegClientId` | The keycloak chart's `realms.oop.oeg.internalId` | `oeg-client` | | `database.name` / `database.user` | Database and role | `portal` | | `database.passwordKey` | Key of the role's password in `global.postgresql.secretName` | `portal-password` | | `service.type` | Service type | `ClusterIP` (`NodePort` in the umbrella chart) | | `service.nodePort` | NodePort | `global.nodePorts.portal` | The API products shown in the portal come from `global.apiProducts`, the same list Keycloak's roles are built from. API clients' registration tokens are encrypted with a key generated into the Secret `portal-credentials` on first install. It is kept on `helm uninstall`, like the database it belongs with. ## Troubleshooting ```bash # Check pod status kubectl get pods -n oop -l app.kubernetes.io/name=portal # View logs kubectl logs -f deployment/portal -n oop # Inspect the ConfigMap kubectl get configmap -n oop | grep portal kubectl describe configmap <portal-config-name> -n oop ``` ## Upgrade ```bash helm upgrade oop-platform ./oop-platform-chart \ -n oop \ -f environments/kind/values.yaml ``` ## Uninstall Uninstall via the umbrella chart: ```bash helm uninstall oop-platform -n oop ``` Or disable only the portal without removing the release: ```bash helm upgrade oop-platform ./oop-platform-chart \ -n oop \ -f environments/kind/values.yaml \ --set portal.enabled=false kubectl logs deployment/portal -n oop -c wait-for-database ``` oop-platform-chart/charts/portal/templates/NOTES.txtdeleted 100644 → 0 +0 −16 Changes for oop-platform-chart/charts/portal/templates/NOTES.txt: 0 added lines, 16 removed lines. Original line number Diff line number Diff line Portal frontend has been deployed. 1. Get the application URL by running these commands: {{- if contains "NodePort" .Values.service.type }} export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "portal.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. kubectl get --namespace {{ .Release.Namespace }} svc {{ include "portal.fullname" . }} -w {{- else if contains "ClusterIP" .Values.service.type }} kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "portal.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }} echo "Visit http://127.0.0.1:{{ .Values.service.port }}" {{- end }} OEG_SERVICE_URL is set to: {{ .Values.config.oegServiceUrl }} Loading
README.md +1 −1 Changes for README.md: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ Intended for local development, integration testing, and demos. **Not for produc | PostgreSQL | `oop` | Shared instance; one database and role per component | | AI2 MCP | `oop` | MCP server exposing CAMARA-compliant tools | | AI2 AI Agent | `oop` | Natural language interface backed by Groq | | Portal | `oop` | Next.js web frontend for the OOP platform | | Portal | `oop` | Developer portal: web UI and its backend (login, OEG and AI2 proxy, API clients) | --- Loading
environments/kind/values.yaml +2 −0 Changes for environments/kind/values.yaml: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -94,6 +94,8 @@ ai2: portal: image: pullPolicy: IfNotPresent oeg: publicUrl: http://localhost:32263 databus: Loading
oop-platform-chart/charts/portal/Chart.yaml +3 −4 Changes for oop-platform-chart/charts/portal/Chart.yaml: 3 added lines, 4 removed lines. Original line number Diff line number Diff line apiVersion: v2 name: portal description: Helm chart for the OOP Portal description: Helm chart for the OOP Portal (developer portal UI and its backend) type: application version: 1.0.0 appVersion: "1.0.0" appVersion: "2.0.0" keywords: - frontend - oop - etsi - portal home: https://labs.etsi.org/rep/oop/code/dev-portal home: https://labs.etsi.org/rep/oop/code/portal maintainers: - name: OOP Team
oop-platform-chart/charts/portal/README.md +37 −76 Changes for oop-platform-chart/charts/portal/README.md: 37 added lines, 76 removed lines. Original line number Diff line number Diff line # Portal Helm Chart Next.js web frontend for the Open Operator Platform (OOP). Deploys the OOP developer portal: one container in which the backend serves the web UI and its API (`/api/*`) on the same address, so the login cookies stay first-party. ## Overview The portal needs, from the umbrella chart: This Helm chart deploys the **Portal** — a browser-based UI that lets operators interact with the platform through the OEG northbound API. - **PostgreSQL**: database and role `portal`, an entry in `postgresql.databases`. An init container waits until the role can log in. - **Keycloak** (unless `global.auth.enabled=false`): the `oop-portal` client logs users in, and `oop-portal-provisioner` creates developers' API clients. Their secrets come from the `keycloak-credentials` Secret. - **OEG** and the **AI2 agent**, which the backend forwards `/api/oeg/*` and `/api/ai2/chat` to. ## Prerequisites ## Addresses - Kubernetes 1.19+ - Helm 3.0+ - OEG deployed and reachable at the configured `config.oegServiceUrl` Browsers reach the portal and Keycloak on public URLs built from `global.publicHost`, `global.scheme` and `global.nodePorts`, as the keycloak chart builds them. The backend's own calls to Keycloak use `global.keycloak.host`/`port` instead, since the public address may not be reachable from pods. ## Installation When deploying via the OOP umbrella chart this sub-chart is included automatically. To install it standalone: ```bash helm install portal . -n oop --create-namespace ``` If you set the keycloak chart's `hostname` or `realms.oop.portal.baseUrl`, set `keycloak.publicUrl` or `publicUrl` here to match. ## Configuration | Parameter | Description | Default | |-----------|-------------|---------| | `replicaCount` | Number of pod replicas | `1` | | `image.repository` | Container image repository | `labs.etsi.org:5050/oop/code/dev-portal` | | `image.tag` | Container image tag | `1.0.0` | | `image.pullPolicy` | Image pull policy | `IfNotPresent` | | `config.oegServiceUrl` | In-cluster URL of the OEG service | `http://oeg:80/oeg/1.0.0` | | `service.type` | Kubernetes service type | `ClusterIP` | | `service.port` | Service port | `80` | | `service.targetPort` | Container port (Next.js) | `3000` | | `service.nodePort` | NodePort value (when `type: NodePort`) | `""` | | `resources.limits.cpu` | CPU limit | `500m` | | `resources.limits.memory` | Memory limit | `512Mi` | | `resources.requests.cpu` | CPU request | `100m` | | `resources.requests.memory` | Memory request | `256Mi` | ## Kind / local development overrides The `environments/kind/values.yaml` file exposes the portal on NodePort `30082`: ```yaml portal: image: pullPolicy: IfNotPresent service: type: NodePort nodePort: 30082 ``` Access the portal at **http://localhost:30082** after deploying on kind. | `image.repository` | Image repository | `labs.etsi.org:5050/oop/code/portal` | | `image.tag` | Image tag, also reported as the app version | `2.0.0` | | `publicUrl` | Address browsers reach the portal on | `<scheme>://<public host>:<NodePort>` | | `oeg.url` | Where `/api/oeg/*` is forwarded to | `http://oeg:80` | | `oeg.publicUrl` | OEG address shown with developers' API clients | `""` (not shown) | | `ai2.chatUrl` | AI2 agent chat endpoint; empty turns the chat off | `http://ai2-ai-agent:9013/groq-mcp` | | `keycloak.publicUrl` | Keycloak's public URL (token issuer) | `<scheme>://<public host>:<global.nodePorts.keycloak>` | | `keycloak.existingSecret` | Secret with the client secrets | `keycloak-credentials` | | `keycloak.provisioner.enabled` | API clients feature | `true` | | `keycloak.provisioner.oegClientId` | The keycloak chart's `realms.oop.oeg.internalId` | `oeg-client` | | `database.name` / `database.user` | Database and role | `portal` | | `database.passwordKey` | Key of the role's password in `global.postgresql.secretName` | `portal-password` | | `service.type` | Service type | `ClusterIP` (`NodePort` in the umbrella chart) | | `service.nodePort` | NodePort | `global.nodePorts.portal` | The API products shown in the portal come from `global.apiProducts`, the same list Keycloak's roles are built from. API clients' registration tokens are encrypted with a key generated into the Secret `portal-credentials` on first install. It is kept on `helm uninstall`, like the database it belongs with. ## Troubleshooting ```bash # Check pod status kubectl get pods -n oop -l app.kubernetes.io/name=portal # View logs kubectl logs -f deployment/portal -n oop # Inspect the ConfigMap kubectl get configmap -n oop | grep portal kubectl describe configmap <portal-config-name> -n oop ``` ## Upgrade ```bash helm upgrade oop-platform ./oop-platform-chart \ -n oop \ -f environments/kind/values.yaml ``` ## Uninstall Uninstall via the umbrella chart: ```bash helm uninstall oop-platform -n oop ``` Or disable only the portal without removing the release: ```bash helm upgrade oop-platform ./oop-platform-chart \ -n oop \ -f environments/kind/values.yaml \ --set portal.enabled=false kubectl logs deployment/portal -n oop -c wait-for-database ```
oop-platform-chart/charts/portal/templates/NOTES.txtdeleted 100644 → 0 +0 −16 Changes for oop-platform-chart/charts/portal/templates/NOTES.txt: 0 added lines, 16 removed lines. Original line number Diff line number Diff line Portal frontend has been deployed. 1. Get the application URL by running these commands: {{- if contains "NodePort" .Values.service.type }} export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "portal.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. kubectl get --namespace {{ .Release.Namespace }} svc {{ include "portal.fullname" . }} -w {{- else if contains "ClusterIP" .Values.service.type }} kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "portal.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }} echo "Visit http://127.0.0.1:{{ .Values.service.port }}" {{- end }} OEG_SERVICE_URL is set to: {{ .Values.config.oegServiceUrl }}