Repository navigation
[linstor-gui] Add package for LINBIT linstor-gui web UI - #2382
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a LINSTOR GUI system package: PackageSource, Helm chart (templates, values, helpers), container image build (Dockerfile, Makefile), Helm tests, README, and bundle inclusion. Deploys an nginx proxy that uses mTLS to forward GUI API requests to the LINSTOR controller. Changes
Sequence DiagramsequenceDiagram
participant Client as External Client
participant Nginx as Nginx Proxy (linstor-gui pod)
participant Controller as LINSTOR Controller REST API
Client->>Nginx: HTTP request (e.g. /v1/...)
activate Nginx
Nginx->>Controller: Establish mTLS connection and forward request
activate Controller
Controller-->>Nginx: HTTP response over mTLS
deactivate Controller
Nginx-->>Client: HTTP response
deactivate Nginx
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces the linstor-gui package, providing a web interface for managing LINSTOR nodes and resources. The implementation includes a hardened Nginx-based container that proxies the LINSTOR REST API using mTLS. Feedback focuses on resolving a group ID inconsistency between the Dockerfile and deployment, reducing duplication in the Nginx configuration by lifting common proxy settings to the server block, and reconsidering the use of the system-cluster-critical priority class for a non-essential management UI.
| tar -xzf /tmp/linstor-gui.tar.gz -C /usr/share/nginx/html --strip-components=2 "linstor-gui-${LINSTOR_GUI_VERSION}/dist" && \ | ||
| rm -f /tmp/linstor-gui.tar.gz && \ | ||
| apk del curl tar && \ | ||
| chown -R 101:0 /usr/share/nginx/html |
There was a problem hiding this comment.
There is an inconsistency between the group ownership set here (group 0) and the runAsGroup setting in the deployment (group 101). Since the base image nginx-unprivileged uses GID 101 for the nginx user, it is recommended to use group 101 here as well to match the deployment configuration.
chown -R 101:101 /usr/share/nginx/html
| # Static UI assets | ||
| location / { | ||
| try_files $uri $uri/ /index.html; | ||
| } | ||
|
|
||
| # Proxy LINSTOR REST API over mTLS to the controller | ||
| location /v1 { | ||
| proxy_pass {{ .Values.linstor.endpoint }}; | ||
| proxy_set_header Host $host; | ||
| proxy_set_header X-Real-IP $remote_addr; | ||
| proxy_ssl_certificate /etc/linstor/client/tls.crt; | ||
| proxy_ssl_certificate_key /etc/linstor/client/tls.key; | ||
| proxy_ssl_trusted_certificate /etc/linstor/client/ca.crt; | ||
| proxy_ssl_verify on; | ||
| proxy_ssl_verify_depth 2; | ||
| proxy_ssl_server_name on; | ||
| proxy_ssl_name {{ $endpointHost }}; | ||
| } | ||
|
|
||
| location /metrics { | ||
| proxy_pass {{ .Values.linstor.endpoint }}; | ||
| proxy_set_header Host $host; | ||
| proxy_set_header X-Real-IP $remote_addr; | ||
| proxy_ssl_certificate /etc/linstor/client/tls.crt; | ||
| proxy_ssl_certificate_key /etc/linstor/client/tls.key; | ||
| proxy_ssl_trusted_certificate /etc/linstor/client/ca.crt; | ||
| proxy_ssl_verify on; | ||
| proxy_ssl_verify_depth 2; | ||
| proxy_ssl_server_name on; | ||
| proxy_ssl_name {{ $endpointHost }}; | ||
| } |
There was a problem hiding this comment.
The proxy configuration for /v1 and /metrics contains significant duplication of mTLS and SSL settings. These directives can be moved to the server block level to improve maintainability and reduce the risk of configuration drift between endpoints.
# Common proxy settings
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_ssl_certificate /etc/linstor/client/tls.crt;
proxy_ssl_certificate_key /etc/linstor/client/tls.key;
proxy_ssl_trusted_certificate /etc/linstor/client/ca.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_server_name on;
proxy_ssl_name {{ $endpointHost }};
# Static UI assets
location / {
try_files $uri $uri/ /index.html;
}
# Proxy LINSTOR REST API over mTLS to the controller
location /v1 {
proxy_pass {{ .Values.linstor.endpoint }};
}
location /metrics {
proxy_pass {{ .Values.linstor.endpoint }};
}| spec: | ||
| serviceAccountName: linstor-gui | ||
| automountServiceAccountToken: false | ||
| priorityClassName: system-cluster-critical |
There was a problem hiding this comment.
The system-cluster-critical priority class is typically reserved for essential cluster components like the LINSTOR controller or CNI plugins. Since linstor-gui is an optional management interface, using such a high priority might lead to the eviction of more critical components during resource pressure. Consider using a lower priority class or omitting it if it's not strictly required for cluster stability.
Ships LINBIT's LINSTOR web UI (GPL-3.0) as an opt-in system package under packages/system/linstor-gui so operators can manage LINSTOR nodes, resources, and volumes from a browser instead of the linstor CLI. The UI is built from the upstream pkg.linbit.com tarball onto an nginx-unprivileged base image. A chart-supplied nginx.conf proxies /v1 and /metrics to the LINSTOR controller REST API over mTLS using the existing linstor-client-tls secret created by the linstor package. Only a ClusterIP Service is created; no Ingress is shipped, because LINSTOR's controller API is a privileged cluster-wide storage surface and auth depends on the deployment's OIDC setup. Operators opt in via bundles.enabledPackages and wire up ingress + auth themselves (port-forward works out of the box). Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
a82dd25 to
ae88fe3
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
packages/system/linstor-gui/Makefile (1)
9-32: Add.PHONYdeclarations for targets.The
image,image-linstor-gui, andtesttargets are not declared as phony. While this works, declaring them prevents issues if files with these names are ever created.♻️ Proposed fix to add PHONY declarations
include ../../../hack/package.mk LINSTOR_GUI_VERSION ?= 2.3.0 +.PHONY: image image-linstor-gui test + image: image-linstor-gui image-linstor-gui:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/system/linstor-gui/Makefile` around lines 9 - 32, The Makefile is missing .PHONY declarations for the targets which can cause unexpected behavior if files named "image", "image-linstor-gui", or "test" appear; add a .PHONY line listing image, image-linstor-gui, and test (e.g. .PHONY: image image-linstor-gui test) near the top of the Makefile before the rules so the Makefile treats these as phony targets rather than files.packages/system/linstor-gui/images/linstor-gui/Dockerfile (1)
9-13: Consider adding checksum verification for supply chain security.The tarball is downloaded from an external source without integrity verification. Adding a checksum validation step would strengthen the supply chain security of the build.
🛡️ Proposed approach for checksum verification
ARG LINSTOR_GUI_VERSION +ARG LINSTOR_GUI_SHA256 USER root RUN apk add --no-cache curl tar && \ curl -fsSL "https://pkg.linbit.com/downloads/linstor/linstor-gui-${LINSTOR_GUI_VERSION}.tar.gz" -o /tmp/linstor-gui.tar.gz && \ + echo "${LINSTOR_GUI_SHA256} /tmp/linstor-gui.tar.gz" | sha256sum -c - && \ mkdir -p /usr/share/nginx/html && \The checksum should be maintained alongside
LINSTOR_GUI_VERSIONin the Makefile and passed as a build arg.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/system/linstor-gui/images/linstor-gui/Dockerfile` around lines 9 - 13, Add SHA256 checksum verification for the downloaded tarball: introduce a build-arg (e.g., LINSTOR_GUI_SHA256) alongside LINSTOR_GUI_VERSION (maintained in the Makefile), pass it into the Docker build, and in the Dockerfile's RUN sequence (the lines that curl the tarball and extract it) compute the tarball's sha256 and compare it to LINSTOR_GUI_SHA256 (fail/exit non-zero on mismatch) before extracting; ensure the comparison uses a reliable tool (sha256sum or sha256sum -c via an echo "checksum filename" | sha256sum -c -) and remove the tarball on failure to avoid continuing with an unverified artifact.packages/system/linstor-gui/templates/deployment.yaml (1)
28-28: Consider loweringpriorityClassNamefor a non-critical UI component.
system-cluster-criticalis typically reserved for core cluster components whose failure would impact cluster operation. The LINSTOR GUI is an optional management interface - operators can still use the CLI if the GUI is unavailable. Consider using a lower priority or removing this setting to allow proper pod eviction during resource pressure.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/system/linstor-gui/templates/deployment.yaml` at line 28, The deployment template sets priorityClassName to the heavyweight "system-cluster-critical" for the LINSTOR GUI; remove that setting or replace "priorityClassName: system-cluster-critical" with a lower/non-critical class (or omit the priorityClassName key entirely) in the Deployment template so the GUI pods can be evicted under pressure; update the templates/deployment.yaml manifest where the priorityClassName key appears in the LINSTOR GUI Deployment spec.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/system/linstor-gui/Chart.yaml`:
- Around line 1-3: Convert this standalone Helm chart into the repository
umbrella-chart pattern: rename or update the Chart.yaml for the umbrella chart
(keeping name "cozy-linstor-gui") and add a charts/ directory containing the
vendored upstream chart(s) for linstor; add a dependencies section in Chart.yaml
(or Chart.lock) pointing to the vendored chart names and ensure values.yaml
merges/forwards any required subchart values to the vendored chart; remove
standalone upstream layout files from the root and ensure templates in the
umbrella chart only include wrapper templates or hooks that reference the
vendored charts under charts/ so the package conforms to the packages/system/**
umbrella pattern.
In `@packages/system/linstor-gui/templates/configmap-nginx.yaml`:
- Around line 6-7: Add a pre-check in the Helm template before the existing
trimming logic to ensure .Values.linstor.endpoint starts with "https://"; if it
does not, call the Helm fail function with a clear message so rendering aborts
(e.g. use if not (hasPrefix "https://" .Values.linstor.endpoint) then fail
"linstor.endpoint must start with 'https://'"). Update the same check for the
other occurrences referenced (lines 51-74) so all places that compute
$endpoint/$endpointHost validate .Values.linstor.endpoint first; keep the
existing trimPrefix/splitList logic unchanged after the validation.
---
Nitpick comments:
In `@packages/system/linstor-gui/images/linstor-gui/Dockerfile`:
- Around line 9-13: Add SHA256 checksum verification for the downloaded tarball:
introduce a build-arg (e.g., LINSTOR_GUI_SHA256) alongside LINSTOR_GUI_VERSION
(maintained in the Makefile), pass it into the Docker build, and in the
Dockerfile's RUN sequence (the lines that curl the tarball and extract it)
compute the tarball's sha256 and compare it to LINSTOR_GUI_SHA256 (fail/exit
non-zero on mismatch) before extracting; ensure the comparison uses a reliable
tool (sha256sum or sha256sum -c via an echo "checksum filename" | sha256sum -c
-) and remove the tarball on failure to avoid continuing with an unverified
artifact.
In `@packages/system/linstor-gui/Makefile`:
- Around line 9-32: The Makefile is missing .PHONY declarations for the targets
which can cause unexpected behavior if files named "image", "image-linstor-gui",
or "test" appear; add a .PHONY line listing image, image-linstor-gui, and test
(e.g. .PHONY: image image-linstor-gui test) near the top of the Makefile before
the rules so the Makefile treats these as phony targets rather than files.
In `@packages/system/linstor-gui/templates/deployment.yaml`:
- Line 28: The deployment template sets priorityClassName to the heavyweight
"system-cluster-critical" for the LINSTOR GUI; remove that setting or replace
"priorityClassName: system-cluster-critical" with a lower/non-critical class (or
omit the priorityClassName key entirely) in the Deployment template so the GUI
pods can be evicted under pressure; update the templates/deployment.yaml
manifest where the priorityClassName key appears in the LINSTOR GUI Deployment
spec.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c9e0a0ba-0817-4a30-bdba-a9468065466f
📒 Files selected for processing (13)
packages/core/platform/sources/linstor-gui.yamlpackages/core/platform/templates/bundles/system.yamlpackages/system/linstor-gui/Chart.yamlpackages/system/linstor-gui/Makefilepackages/system/linstor-gui/README.mdpackages/system/linstor-gui/images/linstor-gui/Dockerfilepackages/system/linstor-gui/templates/_helpers.tplpackages/system/linstor-gui/templates/configmap-nginx.yamlpackages/system/linstor-gui/templates/deployment.yamlpackages/system/linstor-gui/templates/service.yamlpackages/system/linstor-gui/templates/serviceaccount.yamlpackages/system/linstor-gui/tests/deployment_test.yamlpackages/system/linstor-gui/values.yaml
| apiVersion: v2 | ||
| name: cozy-linstor-gui | ||
| version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Align this package to the repository umbrella-chart pattern.
This new system package is introduced as a standalone chart layout; repository rules require the umbrella pattern with vendored upstream charts under charts/. Please restructure before merge to avoid drifting from the platform packaging standard.
As per coding guidelines: "Applies to packages/system/**: Use Helm Chart umbrella pattern with vendored upstream charts in charts/ directory".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/system/linstor-gui/Chart.yaml` around lines 1 - 3, Convert this
standalone Helm chart into the repository umbrella-chart pattern: rename or
update the Chart.yaml for the umbrella chart (keeping name "cozy-linstor-gui")
and add a charts/ directory containing the vendored upstream chart(s) for
linstor; add a dependencies section in Chart.yaml (or Chart.lock) pointing to
the vendored chart names and ensure values.yaml merges/forwards any required
subchart values to the vendored chart; remove standalone upstream layout files
from the root and ensure templates in the umbrella chart only include wrapper
templates or hooks that reference the vendored charts under charts/ so the
package conforms to the packages/system/** umbrella pattern.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/system/linstor-gui/Makefile (1)
9-32: Consider adding.PHONYdeclarations for non-file targets.The
image,image-linstor-gui, andtesttargets don't produce files with those names, so declaring them as.PHONYprevents issues if files with those names are ever created and ensures they always run.🔧 Proposed fix
+.PHONY: image image-linstor-gui test + image: image-linstor-gui image-linstor-gui:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/system/linstor-gui/Makefile` around lines 9 - 32, Add a .PHONY declaration for the non-file Makefile targets to ensure they always run: declare .PHONY for the targets image, image-linstor-gui, and test in the Makefile (e.g., add a line like ".PHONY: image image-linstor-gui test" near the top or before the target definitions) so these names are treated as phony targets rather than real files.packages/system/linstor-gui/templates/configmap-nginx.yaml (1)
50-61: Consider extracting repeated mTLS proxy directives into a shared snippet.The
/v1and/metricslocations duplicate the same 8proxy_ssl_*directives. While functional, this increases maintenance burden if TLS settings need adjustment.Also applies to: 63-74
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/system/linstor-gui/templates/configmap-nginx.yaml` around lines 50 - 61, The nginx config duplicates the same mTLS proxy directives in the location blocks (e.g., location /v1 and location /metrics) — extract the repeated proxy_ssl_* lines (proxy_ssl_certificate, proxy_ssl_certificate_key, proxy_ssl_trusted_certificate, proxy_ssl_verify, proxy_ssl_verify_depth, proxy_ssl_server_name, proxy_ssl_name, etc.) into a reusable template/snippet (e.g., define a Helm template or nginx include named something like "linstor-mtls-proxy") and replace the repeated blocks with a single call to that snippet, preserving interpolation of {{ $endpointHost }} and any existing proxy_pass/headers in the location blocks so TLS settings are managed in one place.packages/system/linstor-gui/templates/deployment.yaml (1)
7-8: Redundant config reload mechanisms.Both
reloader.stakater.com/auto: "true"(line 8) andchecksum/nginx-config(line 22) trigger pod restarts on ConfigMap changes. The reloader annotation automatically watches mounted ConfigMaps, making the manual checksum annotation unnecessary when Stakater Reloader is deployed.Consider keeping only one approach for clarity. The Reloader annotation is simpler but requires the operator to be installed; the checksum is self-contained.
Also applies to: 21-22
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/system/linstor-gui/templates/deployment.yaml` around lines 7 - 8, The deployment currently uses two restart mechanisms: the annotation reloader.stakater.com/auto: "true" on the Deployment and the manual checksum/nginx-config Pod annotation; remove the redundant approach by choosing one strategy—prefer keeping reloader.stakater.com/auto and delete the checksum/nginx-config annotation (or vice versa if you require self-contained behavior) so only a single ConfigMap-watching restart mechanism remains; update the templates/deployment.yaml to remove the unused checksum/nginx-config entry (or remove the reloader annotation) and ensure any references to checksum-based rolling restarts (e.g., label or annotation generation code that sets checksum/nginx-config) are also cleaned up.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/system/linstor-gui/templates/configmap-nginx.yaml`:
- Around line 76-80: The location block for health checks uses "return 200
'ok';" but "add_header Content-Type" placed after return will never be sent;
update the location = /healthz block to set the response content type via
nginx's default_type (or otherwise emit the body with the desired Content-Type)
instead of relying on add_header after return—modify the location = /healthz
block (look for the add_header and return directives) to use default_type
text/plain (or an equivalent approach that sets Content-Type before returning
the body).
---
Nitpick comments:
In `@packages/system/linstor-gui/Makefile`:
- Around line 9-32: Add a .PHONY declaration for the non-file Makefile targets
to ensure they always run: declare .PHONY for the targets image,
image-linstor-gui, and test in the Makefile (e.g., add a line like ".PHONY:
image image-linstor-gui test" near the top or before the target definitions) so
these names are treated as phony targets rather than real files.
In `@packages/system/linstor-gui/templates/configmap-nginx.yaml`:
- Around line 50-61: The nginx config duplicates the same mTLS proxy directives
in the location blocks (e.g., location /v1 and location /metrics) — extract the
repeated proxy_ssl_* lines (proxy_ssl_certificate, proxy_ssl_certificate_key,
proxy_ssl_trusted_certificate, proxy_ssl_verify, proxy_ssl_verify_depth,
proxy_ssl_server_name, proxy_ssl_name, etc.) into a reusable template/snippet
(e.g., define a Helm template or nginx include named something like
"linstor-mtls-proxy") and replace the repeated blocks with a single call to that
snippet, preserving interpolation of {{ $endpointHost }} and any existing
proxy_pass/headers in the location blocks so TLS settings are managed in one
place.
In `@packages/system/linstor-gui/templates/deployment.yaml`:
- Around line 7-8: The deployment currently uses two restart mechanisms: the
annotation reloader.stakater.com/auto: "true" on the Deployment and the manual
checksum/nginx-config Pod annotation; remove the redundant approach by choosing
one strategy—prefer keeping reloader.stakater.com/auto and delete the
checksum/nginx-config annotation (or vice versa if you require self-contained
behavior) so only a single ConfigMap-watching restart mechanism remains; update
the templates/deployment.yaml to remove the unused checksum/nginx-config entry
(or remove the reloader annotation) and ensure any references to checksum-based
rolling restarts (e.g., label or annotation generation code that sets
checksum/nginx-config) are also cleaned up.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: fdd0e81c-9e78-44b4-abd9-ae8a09e22f4f
📒 Files selected for processing (13)
packages/core/platform/sources/linstor-gui.yamlpackages/core/platform/templates/bundles/system.yamlpackages/system/linstor-gui/Chart.yamlpackages/system/linstor-gui/Makefilepackages/system/linstor-gui/README.mdpackages/system/linstor-gui/images/linstor-gui/Dockerfilepackages/system/linstor-gui/templates/_helpers.tplpackages/system/linstor-gui/templates/configmap-nginx.yamlpackages/system/linstor-gui/templates/deployment.yamlpackages/system/linstor-gui/templates/service.yamlpackages/system/linstor-gui/templates/serviceaccount.yamlpackages/system/linstor-gui/tests/deployment_test.yamlpackages/system/linstor-gui/values.yaml
✅ Files skipped from review due to trivial changes (8)
- packages/system/linstor-gui/Chart.yaml
- packages/core/platform/templates/bundles/system.yaml
- packages/system/linstor-gui/values.yaml
- packages/system/linstor-gui/README.md
- packages/system/linstor-gui/templates/_helpers.tpl
- packages/core/platform/sources/linstor-gui.yaml
- packages/system/linstor-gui/images/linstor-gui/Dockerfile
- packages/system/linstor-gui/tests/deployment_test.yaml
- Dockerfile: chown html root to 101:101 (match runAsGroup, not 101:0)
- Makefile: declare .PHONY targets (image, image-linstor-gui, test)
- deployment: drop priorityClassName system-cluster-critical (GUI is
not control-plane critical; chart does not need to reserve the slot)
- configmap-nginx:
- fail the render if linstor.endpoint is not https:// so a misconfig
cannot silently downgrade the mTLS-protected controller path
- hoist proxy_set_header and proxy_ssl_* directives to the server
block instead of duplicating across /v1 and /metrics locations
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
|
Pushed ba9f9e9 addressing review feedback. Applied:
Skipped (with reasoning):
|
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
packages/system/linstor-gui/templates/configmap-nginx.yaml (1)
72-76:⚠️ Potential issue | 🟡 Minor
Content-Typeheader is not applied in/healthzdue toreturnordering.On Line 74,
returnfinalizes the response; Line 75add_headeris ineffective in this block.🔧 Proposed fix
location = /healthz { access_log off; + default_type text/plain; return 200 'ok'; - add_header Content-Type text/plain; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/system/linstor-gui/templates/configmap-nginx.yaml` around lines 72 - 76, The Content-Type header in the location = /healthz block is not applied because the return directive finalizes the response before add_header runs; move the add_header directive (use add_header Content-Type text/plain always;) above the return in the location = /healthz block (refer to the location = /healthz block to find and update the lines), keeping access_log off and the same return body.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/system/linstor-gui/templates/deployment.yaml`:
- Around line 1-100: The deployment manifest in templates/deployment.yaml
violates the repo rule to use an umbrella Helm chart with a vendored upstream
chart; instead of owning the primary workload here, vendor the upstream
linstor-gui chart under charts/ and convert this package into an umbrella that
declares that chart as a dependency. Specifically: remove the full Deployment
from templates/, add the upstream chart tarball or folder to charts/ (vendored),
update Chart.yaml to list the vendored linstor-gui as a dependency and move only
package-specific overrides into values.yaml (and any small wrapper templates
like configmap checksums if needed), and ensure templates/ contains only
umbrella-specific resources (values overrides, NOTES, RBAC/serviceaccount if
truly required). Update references to .Values.image,
.Values.linstor.clientSecret, and the checksum template usage to be provided via
values.yaml so the vendored chart uses them.
- Around line 72-73: The TLS file permission blocks are incompatible with the
pod running as runAsUser/runAsGroup 101 because secret files are mounted with
defaultMode: 0400 and owned by root:root, so add an fsGroup (e.g., fsGroup: 101)
to the pod/securityContext so the non-root process can read the certs used by
nginx proxy_ssl_certificate directives; additionally refactor this package to
follow the repository Helm umbrella pattern by moving the chart into a charts/
subdirectory and vendoring the upstream linstor-gui chart there (matching other
packages), updating Chart.yaml/values and any chart references to reflect the
umbrella layout.
---
Duplicate comments:
In `@packages/system/linstor-gui/templates/configmap-nginx.yaml`:
- Around line 72-76: The Content-Type header in the location = /healthz block is
not applied because the return directive finalizes the response before
add_header runs; move the add_header directive (use add_header Content-Type
text/plain always;) above the return in the location = /healthz block (refer to
the location = /healthz block to find and update the lines), keeping access_log
off and the same return body.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3da63e12-011e-4def-9643-ec389f46e324
📒 Files selected for processing (4)
packages/system/linstor-gui/Makefilepackages/system/linstor-gui/images/linstor-gui/Dockerfilepackages/system/linstor-gui/templates/configmap-nginx.yamlpackages/system/linstor-gui/templates/deployment.yaml
✅ Files skipped from review due to trivial changes (1)
- packages/system/linstor-gui/images/linstor-gui/Dockerfile
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: linstor-gui | ||
| labels: | ||
| {{- include "linstor-gui.labels" . | nindent 4 }} | ||
| annotations: | ||
| reloader.stakater.com/auto: "true" | ||
| spec: | ||
| replicas: {{ .Values.replicas }} | ||
| selector: | ||
| matchLabels: | ||
| {{- include "linstor-gui.selectorLabels" . | nindent 6 }} | ||
| strategy: | ||
| type: RollingUpdate | ||
| rollingUpdate: | ||
| maxUnavailable: 0 | ||
| maxSurge: 1 | ||
| template: | ||
| metadata: | ||
| annotations: | ||
| checksum/nginx-config: {{ include (print $.Template.BasePath "/configmap-nginx.yaml") . | sha256sum }} | ||
| labels: | ||
| {{- include "linstor-gui.labels" . | nindent 8 }} | ||
| spec: | ||
| serviceAccountName: linstor-gui | ||
| automountServiceAccountToken: false | ||
| securityContext: | ||
| runAsNonRoot: true | ||
| seccompProfile: | ||
| type: RuntimeDefault | ||
| affinity: | ||
| podAntiAffinity: | ||
| preferredDuringSchedulingIgnoredDuringExecution: | ||
| - weight: 100 | ||
| podAffinityTerm: | ||
| labelSelector: | ||
| matchLabels: | ||
| {{- include "linstor-gui.selectorLabels" . | nindent 20 }} | ||
| topologyKey: kubernetes.io/hostname | ||
| containers: | ||
| - name: linstor-gui | ||
| image: {{ .Values.image.repository }}:{{ .Values.image.tag }} | ||
| imagePullPolicy: IfNotPresent | ||
| ports: | ||
| - name: http | ||
| containerPort: 3373 | ||
| protocol: TCP | ||
| resources: | ||
| requests: | ||
| cpu: 50m | ||
| memory: 64Mi | ||
| limits: | ||
| cpu: 200m | ||
| memory: 128Mi | ||
| livenessProbe: | ||
| httpGet: | ||
| path: /healthz | ||
| port: http | ||
| initialDelaySeconds: 5 | ||
| periodSeconds: 20 | ||
| readinessProbe: | ||
| httpGet: | ||
| path: /healthz | ||
| port: http | ||
| initialDelaySeconds: 2 | ||
| periodSeconds: 10 | ||
| securityContext: | ||
| allowPrivilegeEscalation: false | ||
| readOnlyRootFilesystem: true | ||
| runAsNonRoot: true | ||
| runAsUser: 101 | ||
| runAsGroup: 101 | ||
| capabilities: | ||
| drop: | ||
| - ALL | ||
| volumeMounts: | ||
| - name: nginx-config | ||
| mountPath: /etc/nginx/nginx.conf | ||
| subPath: nginx.conf | ||
| readOnly: true | ||
| - name: linstor-client-tls | ||
| mountPath: /etc/linstor/client | ||
| readOnly: true | ||
| - name: tmp | ||
| mountPath: /tmp | ||
| - name: nginx-cache | ||
| mountPath: /var/cache/nginx | ||
| volumes: | ||
| - name: nginx-config | ||
| configMap: | ||
| name: linstor-gui-nginx | ||
| - name: linstor-client-tls | ||
| secret: | ||
| secretName: {{ .Values.linstor.clientSecret }} | ||
| defaultMode: 0400 | ||
| - name: tmp | ||
| emptyDir: {} | ||
| - name: nginx-cache | ||
| emptyDir: {} |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Package structure should follow the repository’s system-package umbrella chart rule.
This file implements the workload directly in templates/; this package should be structured as an umbrella chart consuming a vendored upstream chart in charts/ instead of owning primary workload manifests here.
As per coding guidelines: packages/system/**: "Use Helm Chart umbrella pattern with vendored upstream charts in charts/ directory".
🧰 Tools
🪛 YAMLlint (1.38.0)
[error] 6-6: syntax error: expected the node content, but found '-'
(syntax)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/system/linstor-gui/templates/deployment.yaml` around lines 1 - 100,
The deployment manifest in templates/deployment.yaml violates the repo rule to
use an umbrella Helm chart with a vendored upstream chart; instead of owning the
primary workload here, vendor the upstream linstor-gui chart under charts/ and
convert this package into an umbrella that declares that chart as a dependency.
Specifically: remove the full Deployment from templates/, add the upstream chart
tarball or folder to charts/ (vendored), update Chart.yaml to list the vendored
linstor-gui as a dependency and move only package-specific overrides into
values.yaml (and any small wrapper templates like configmap checksums if
needed), and ensure templates/ contains only umbrella-specific resources (values
overrides, NOTES, RBAC/serviceaccount if truly required). Update references to
.Values.image, .Values.linstor.clientSecret, and the checksum template usage to
be provided via values.yaml so the vendored chart uses them.
The linstor-client-tls secret was mounted with defaultMode 0400 and owned by root, so the nginx process (UID 101) got EACCES on tls.crt and crash-looped with: [emerg] cannot load certificate "/etc/linstor/client/tls.crt": BIO_new_file() failed ... Permission denied Set defaultMode to 0444. The secret volume is pod-local and readOnlyRootFilesystem is on, so making it world-readable inside the pod is not a broader disclosure. Verified on dev10: pod Ready, /healthz 200, /v1/nodes proxied through mTLS to linstor-controller:3371 returns real node data. Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
packages/system/linstor-gui/templates/deployment.yaml (1)
1-102: 🛠️ Refactor suggestion | 🟠 MajorUse umbrella + vendored-chart structure for this system package.
This package still owns the primary workload manifest directly in
templates/, which conflicts with the repository rule forpackages/system/**.As per coding guidelines:
packages/system/**: "Use Helm Chart umbrella pattern with vendored upstream charts incharts/directory".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/system/linstor-gui/templates/deployment.yaml` around lines 1 - 102, The deployment manifest (Deployment named linstor-gui, container name linstor-gui, etc.) lives directly under templates/ which violates the packages/system umbrella+vendored-chart rule; fix by converting this package into an umbrella chart that vendors the workload as a subchart: create a charts/linstor-gui subchart (Chart.yaml, values.yaml, templates/) and move the existing deployment.yaml contents into that subchart's templates, update the umbrella Chart.yaml to declare the linstor-gui chart as a dependency and mirror any values used (.Values.image, .Values.replicas, .Values.linstor.clientSecret) into the umbrella values.yaml, then remove the original templates/deployment.yaml so the vendored subchart owns the Deployment manifest.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/system/linstor-gui/templates/deployment.yaml`:
- Around line 1-102: The deployment manifest (Deployment named linstor-gui,
container name linstor-gui, etc.) lives directly under templates/ which violates
the packages/system umbrella+vendored-chart rule; fix by converting this package
into an umbrella chart that vendors the workload as a subchart: create a
charts/linstor-gui subchart (Chart.yaml, values.yaml, templates/) and move the
existing deployment.yaml contents into that subchart's templates, update the
umbrella Chart.yaml to declare the linstor-gui chart as a dependency and mirror
any values used (.Values.image, .Values.replicas, .Values.linstor.clientSecret)
into the umbrella values.yaml, then remove the original
templates/deployment.yaml so the vendored subchart owns the Deployment manifest.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a8fff507-171b-4f1d-8023-3b119aa27c9f
📒 Files selected for processing (1)
packages/system/linstor-gui/templates/deployment.yaml
Follow-up to 4b76a93: the assertion still expected defaultMode 0400 and would fail in CI. Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
Follow-up to the initial linstor-gui package (#2382). Publishes the UI on https://linstor-gui.<root-host> behind the cluster Keycloak realm using the same oauth2-proxy gatekeeper pattern that dashboard uses, so the LINSTOR REST API is never exposed unauthenticated through Ingress. Rendered only when both gates are satisfied: * _cluster.oidc-enabled == "true" * "linstor-gui" is listed in _cluster.expose-services (i.e. in publishing.exposedServices in the core cozystack values). If OIDC is disabled cluster-wide we intentionally do not ship an unauthenticated token-proxy fallback — unlike dashboard, there is no reason to front a raw storage-management API with k8s bearer tokens. Operators can still reach the UI via `kubectl port-forward` to the ClusterIP service. New resources: * templates/gatekeeper.yaml — oauth2-proxy Deployment (OIDC mode) * templates/gatekeeper-svc.yaml — ClusterIP :8000 in front of it * templates/gatekeeper-sa.yaml — dedicated SA, no autotoken * templates/keycloakclient.yaml — persistent client + cookie Secrets, KeycloakClient CRD with redirectUris for /oauth2/callback * templates/ingress.yaml — Ingress to the gatekeeper Service, cert-manager ClusterIssuer, gated on expose-services + oidc-enabled * tests/ingress_auth_test.yaml — unit tests for each conditional branch and the client CRD README.md updated with both Option 1 (Keycloak-protected Ingress) and the existing Option 2 (port-forward). Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
…tation - Fix /healthz Content-Type header: nginx silently drops `add_header` after `return` because the response is already finalized. Switch to `default_type text/plain;` placed BEFORE the `return 200` so the probe response is actually labeled as text/plain. - Drop the redundant `checksum/nginx-config` pod-template annotation: the deployment already carries `reloader.stakater.com/auto: "true"`, so Stakater Reloader handles ConfigMap rollouts. Matches the sister `linstor` package convention. Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
|
Reviewed all bot comments on this PR. Status: Addressed in 727401c (this commit):
Already addressed in earlier commits on this branch:
Deliberately not addressed:
|
The matchRegex assertion used 5 literal spaces but the template renders 9 (aligned with proxy_ssl_trusted_certificate). Use \s+ so the test is resilient to alignment changes. Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
Follow-up to the initial linstor-gui package (#2382). Publishes the UI on https://linstor-gui.<root-host> behind the cluster Keycloak realm using the same oauth2-proxy gatekeeper pattern that dashboard uses, so the LINSTOR REST API is never exposed unauthenticated through Ingress. Rendered only when both gates are satisfied: * _cluster.oidc-enabled == "true" * "linstor-gui" is listed in _cluster.expose-services (i.e. in publishing.exposedServices in the core cozystack values). If OIDC is disabled cluster-wide we intentionally do not ship an unauthenticated token-proxy fallback — unlike dashboard, there is no reason to front a raw storage-management API with k8s bearer tokens. Operators can still reach the UI via `kubectl port-forward` to the ClusterIP service. New resources: * templates/gatekeeper.yaml — oauth2-proxy Deployment (OIDC mode) * templates/gatekeeper-svc.yaml — ClusterIP :8000 in front of it * templates/gatekeeper-sa.yaml — dedicated SA, no autotoken * templates/keycloakclient.yaml — persistent client + cookie Secrets, KeycloakClient CRD with redirectUris for /oauth2/callback * templates/ingress.yaml — Ingress to the gatekeeper Service, cert-manager ClusterIssuer, gated on expose-services + oidc-enabled * tests/ingress_auth_test.yaml — unit tests for each conditional branch and the client CRD README.md updated with both Option 1 (Keycloak-protected Ingress) and the existing Option 2 (port-forward). Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM. Well-structured chart with proper security hardening (read-only rootfs, non-root UID 101, dropped capabilities, RuntimeDefault seccomp, no SA token), correct mTLS proxy to the LINSTOR controller, HTTPS-only validation via fail, and comprehensive helm-unittest coverage. The PackageSource correctly depends on cozystack.linstor and the bundle registration follows the optional package pattern. Bot comments have been addressed by the author.
Non-blocking action items for follow-up
A1: Missing /api/v2 proxy route for optional LINSTOR Gateway
The upstream nginx.conf.template includes a location /api/v2 block for the optional LINSTOR Gateway service (iSCSI/NFS/NVMe management). The GUI gracefully degrades when Gateway is unavailable, so this is not a bug — but if LINSTOR Gateway is later deployed, this chart would need updating. Consider adding a commented-out or conditionally-enabled /api/v2 location block.
A2: Makefile does not use shared $(BUILDX_ARGS) macro
Most system packages use $(BUILDX_ARGS) from hack/common-envs.mk for centralized build flags. This Makefile specifies them manually (same as dashboard). Not a bug, but diverges from the common pattern and makes future build system changes harder to propagate.
## What this PR does Stacked on top of #2382. Publishes the linstor-gui UI on `https://linstor-gui.<root-host>` behind the cluster Keycloak realm using the same oauth2-proxy gatekeeper pattern that the dashboard uses, so the LINSTOR REST API is never exposed unauthenticated through Ingress. The Ingress is rendered only when **both** gates are satisfied: - `_cluster.oidc-enabled == "true"` - `linstor-gui` is listed in `_cluster.expose-services` (i.e. in `publishing.exposedServices` in the core cozystack values) If OIDC is disabled cluster-wide we intentionally do **not** ship an unauthenticated ``token-proxy`` fallback — unlike dashboard, there is no reason to front a raw storage-management API with k8s bearer tokens. Operators can still reach the UI via \`kubectl port-forward\` to the ClusterIP service. ## What's new - \`templates/gatekeeper.yaml\` — oauth2-proxy Deployment (OIDC mode), upstream \`linstor-gui.<ns>.svc:80\` - \`templates/gatekeeper-svc.yaml\` — ClusterIP :8000 in front of it - \`templates/gatekeeper-sa.yaml\` — dedicated SA, no auto-token - \`templates/keycloakclient.yaml\` — persistent \`linstor-gui-client\` + \`linstor-gui-auth-config\` Secrets, \`KeycloakClient\` CRD that auto-provisions the OIDC client with \`redirectUris: [/oauth2/callback/*]\` - \`templates/ingress.yaml\` — Ingress to the gatekeeper Service with cert-manager ClusterIssuer, gated on expose-services + oidc-enabled - \`tests/ingress_auth_test.yaml\` — unit tests for each conditional branch, KeycloakClient rendering, oauth2-proxy args README updated with Option 1 (Keycloak-protected Ingress) and Option 2 (port-forward). ### Release note \`\`\`release-note Added an opt-in Keycloak-protected Ingress for the linstor-gui package. Add \`linstor-gui\` to \`publishing.exposedServices\` to publish \`https://linstor-gui.<root-host>\` behind the cluster OIDC realm. \`\`\` ## Test plan - [ ] Deploy to dev10 with \`linstor-gui\` added to \`publishing.exposedServices\` - [ ] Verify cert-manager issues the TLS certificate - [ ] Open \`https://linstor-gui.<root-host>\` in a browser, confirm Keycloak login challenge - [ ] After login, confirm LINSTOR node list loads via the nginx → mTLS proxy - [ ] Verify helm-unittest cases pass in CI - [ ] Sanity-check that when \`oidc-enabled=false\` or the service is not in \`expose-services\`, the Ingress + gatekeeper resources are skipped
The linstor-gui package was added in #2382 with its own per-package Makefile and Dockerfile, but the root Makefile's `build:` target was not updated to invoke it. As a result `ghcr.io/cozystack/cozystack/ linstor-gui` has never been published (registry returns NAME_UNKNOWN) and the chart's `image.tag` was never digest-pinned. Any cluster deploying the chart hits ImagePullBackOff. Wire the package into the root build alongside the other system images. The next CI build will publish the image and the per-package Makefile will rewrite values.yaml `image.repository`/`image.tag` to a digest-pinned reference automatically. Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
The per-package Makefile added in #2382 hardcoded buildx flags (--provenance, --builder, --platform=linux/amd64,linux/arm64, --push, --load, --label) instead of using the shared $(BUILDX_ARGS) macro from hack/common-envs.mk. This broke CI: the runner's default docker driver does not support multi-platform builds, and the hardcoded multi-arch platform list crashed `make build` with "Multi-platform build is not supported for the docker driver." Replace the hardcoded flags with $(BUILDX_ARGS) to match every other package (e.g. linstor, dashboard, cilium). $(BUILDX_ARGS) injects --push, --load, --label, --provenance=false, and only sets --builder or --platform when the operator explicitly exports BUILDER/PLATFORM. Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
## What this PR does The `linstor-gui` package (added in #2382) was never wired into the root `Makefile`'s `build:` target, so the image is not built or published by CI. `ghcr.io/cozystack/cozystack/linstor-gui` returns `NAME_UNKNOWN` from the GHCR API, and `values.yaml` still pins `tag: 2.3.0` without a digest because the per-package Makefile that rewrites the tag after a successful push has never run in CI. This PR adds the missing line so the next build publishes the image and digest-pins `values.yaml` automatically. ### Release note ```release-note build(linstor): include linstor-gui in root image build target so the image is built and published by CI (the chart previously referenced an image that did not exist in the registry) ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Integrated the linstor-gui container image build into the main image build workflow so the GUI image is produced as part of standard builds. * Streamlined the GUI image build configuration to use consolidated build arguments, improving consistency and maintainability of automated image builds and metadata updates. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
What this PR does
Adds a new Cozystack system package
linstor-guithat ships LINBIT's LINSTOR web UI (GPL-3.0) so operators can manage nodes, resources, volumes and snapshots from a browser instead of thelinstorCLI.Changes:
packages/system/linstor-gui/— umbrella chart with:images/linstor-gui/Dockerfilebuilds the image from the upstreampkg.linbit.comtarball (v2.3.0) on top ofnginxinc/nginx-unprivileged:1.29-alpine, mirroring the build pattern already used forpiraeus-serverandlinstor-csi.templates/configmap-nginx.yaml— chart-suppliednginx.confthat proxies/v1and/metricstolinstor-controller.cozy-linstor.svc:3371over mTLS, using the existinglinstor-client-tlssecret created by thelinstorpackage.templates/deployment.yaml— read-only rootfs, non-root (UID 101),runAsNonRoot,RuntimeDefaultseccomp, no SA token mounted,reloader.stakater.com/autoon the Deployment.templates/service.yaml— ClusterIP only. No Ingress is shipped because the LINSTOR controller API is a privileged cluster-wide storage surface and auth depends on the deployment's OIDC setup; operators wire up ingress + auth explicitly.tests/deployment_test.yaml—helm-unittestcovering Service shape, TLS secret mount, securityContext, and nginx proxy+mTLS config.packages/core/platform/sources/linstor-gui.yaml— newPackageSourcedepending oncozystack.linstor.packages/core/platform/templates/bundles/system.yaml— registerslinstor-guias an optional system package (controlled viabundles.enabledPackages), alongsidevelero,telepresence, etc.Why standalone (not a sidecar in the controller pod)
LINBIT's upstream Dockerfile is designed for this standalone reverse-proxy topology, and keeping the UI as its own Deployment means it scales, upgrades, and restarts independently of
linstor-controller. The price is the small amount of mTLS config innginx.conf, all of which lives in the chart's ConfigMap.Release note
Test plan
make unit-tests(requireshelm-unittestplugin) passes on the newtests/deployment_test.yamlmake imageinpackages/system/linstor-gui/builds and pushes the imagekubectl -n cozy-linstor port-forward svc/linstor-gui 3373:80→ browser loads the UI atlocalhost:3373/v1works)bundles.enabledPackagesremoval; verify thePackageresource is cleaned upSummary by CodeRabbit
New Features
Documentation
Tests