feat: Manage OpenVSX as Che operand#2130
Conversation
Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
…bled to enable Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
The OpenVSX WebUI cannot work behind the Che gateway path prefix (/openvsx) because its JS derives API URLs from location.host and makes calls to root paths (/api, /user, /login, etc.), and static assets use absolute root paths (/bundle-xxx.js). A dedicated Kubernetes Ingress with its own hostname provides multi-path routing: server API paths go to openvsx-server:8080, everything else goes to openvsx-webui:3000. This works on both Kubernetes (nginx ingress) and OpenShift 4.x (HAProxy ingress controller handles Ingress resources natively), matching the upstream eclipse-openvsx deployment pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
On OpenShift the ingress controller is HAProxy with class openshift-default, not nginx. Omitting ingressClassName lets the platform use its default class. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
On OpenShift, the ingress-to-route controller rejects Ingress resources
with an empty TLS secretName ("Invalid or missing TLS secret"). Instead,
omit the TLS section entirely and let the router use its default wildcard
certificate. Add route.openshift.io/termination=edge annotation so the
generated Route uses TLS edge termination.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
The openvsx-server image now includes the WebUI (static assets copied into BOOT-INF/classes/static/). This eliminates the need for a separate webui component and the custom multi-path Ingress. - Remove webui reconciler, constants, defaults, and API type - Remove dedicated Ingress expose reconciler - Expose OpenVSX through Che Traefik gateway (same pattern as Dashboard and PluginRegistry) under /openvsx path prefix - Set server.servlet.context-path=/openvsx so Spring Boot serves everything under /openvsx natively (no gateway strip-prefix needed) - Rename RELATED_IMAGE_openvsx_server to RELATED_IMAGE_openvsx Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
Add /openvsx to oauth-proxy skip_auth_regex so OpenVSX is accessible without authentication, same pattern as plugin-registry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a PVC is already bound, Kubernetes populates volumeName and storageClassName which are immutable. The sync was trying to update these fields, causing reconciliation to fail. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Revert deepcopy, CRD, CSV, and combined YAML files to their state on main. These will be regenerated properly later. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
Status.Phase is set to "Pending" by Kubernetes immediately after PVC creation, causing a diff against the blueprint which has empty status. This triggered an update that failed on immutable spec fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without explicit RestartPolicy, the blueprint has empty string while Kubernetes sets it to "Always", causing a diff on every reconciliation and blocking subsequent reconcilers (including gateway) from running. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
The OpenVSX webui (Vite base='/') uses absolute root paths for static assets (/bundle-*.js), which is incompatible with the Che gateway's path-prefix routing. Switch to a dedicated Ingress with its own hostname (openvsx-<ns>.<domain>), matching the upstream deployment pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
Add a Kubernetes Job that runs after the OpenVSX server deployment is ready, inserting a privileged user and personal access token into the database for the ovsx CLI to publish extensions. The Job uses idempotent SQL (ON CONFLICT DO NOTHING) and reads credentials from the existing openvsx-postgres-credentials secret, which now includes userName and userPAT fields with defaults. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The user setup Job was failing because the readiness probe passes before Flyway finishes creating tables. Add a wait-for-schema init container that polls until the user_data table exists, ensuring the main container's INSERT statements succeed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add openvsx-admin user (role: admin) with PAT to the database seeding Job alongside the existing publisher user. Propagate OPENVSX_USER_PAT, OPENVSX_ADMIN_PAT, and OPENVSX_URL env vars into the server deployment so they are available inside the pod. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mount the ca-certs-merged ConfigMap into the server pod and set NODE_EXTRA_CA_CERTS so the ovsx CLI trusts self-signed certificates. Rename OPENVSX_URL to OVSX_REGISTRY_URL to match the env var name the ovsx CLI expects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce a user-editable ConfigMap (openvsx-extensions) that holds a list of VSIX URLs and a Job that downloads and publishes them to the OpenVSX registry. The ConfigMap is created with empty defaults only if absent, preserving user modifications. A content hash env var triggers Job recreation when extensions change. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TTL controller was deleting completed Jobs before the next reconcile, causing deploy.Sync to recreate them and orphan the old pods. Without TTL, completed Jobs persist and deploy.Sync skips them when no spec diff is detected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch to create-only pattern so user modifications are preserved across reconcile loops. Add CM_REVISION env var to trigger pod rollout when the ConfigMap content changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch to create-only pattern so the Job is created on first reconcile and skipped on subsequent ones, preventing unnecessary re-runs when other resources like ConfigMaps are updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ephemeral /tmp/extensions with a PersistentVolumeClaim to persist published extensions across pod restarts and avoid storage limits that cause 406 errors with many extensions. Configurable via server.claimSize in the CRD, defaults to 2Gi. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Valerii Svydenko <vsvydenk@redhat.com>
Increase OpenVSX server memory limit to 2Gi and request to 512Mi to match the JVM -Xmx2048m setting, preventing OOM kills during extension publishing. Also ignore additional immutable PVC spec fields (VolumeAttributesClassName, DataSource, DataSourceRef) in both server and postgres PVC diff options to prevent false diffs that cause reconcile errors on bound PVCs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace deploy.Sync for PVC updates with manual Get/Update that only patches resources.requests.storage. deploy.Sync sends the full PVC spec which Kubernetes rejects because PVC spec is immutable after creation except for resources.requests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace bare ClaimSize field in OpenVSXServer and OpenVSXPostgres with Storage *PVC to reuse existing PVC type (adds storageClass and storageAccessMode support) - Add diffs.PVC comparator for PVC reconciliation via deploy.Sync - Add missing webhook validation for Server claimSize to prevent runtime panics from resource.MustParse - Change PostgreSQL deployment strategy from RollingUpdate to Recreate to prevent data corruption with single-replica RWO PVC Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
resource.Quantity struct equality (==) fails when values have different internal formats (binary SI vs decimal SI), causing PVC updates to be silently skipped. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
deploy.Sync sends the full blueprint on update, which Kubernetes rejects for bound PVCs because spec fields like volumeName and storageClassName are immutable. Read the existing PVC and update only resources.requests directly. Also only allow expansion (desiredSize > currentSize) since most StorageClasses don't support shrinking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…names Rename the OpenVSX struct fields (Server -> OpenVSXServer, Postgres -> OpenVSXDatabase) and the type (OpenVSXPostgres -> OpenVSXDatabase) to use generic database terminology. Move the postgres package to database, update constants, K8s resource names, and JDBC URL accordingly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace deploy.DeleteNamespacedObject with the preferred ClientWrapper.DeleteByKeyIgnoreNotFound API in OpenVSX reconcilers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace deploy.SyncSecretToCluster with direct Secret construction, controllerutil.SetControllerReference, and ClientWrapper.Sync. Replace deprecated k8s.io/utils/pointer with k8s.io/utils/ptr. Remove redundant embedded field selector in test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace static userPAT and adminPAT strings with randomly generated 32-character passwords for better security. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The expose reconciler runs after the server reconciler, so Status.OpenVSXURL is empty on the first reconcile. Defer Job creation until the URL is available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kubernetes Jobs are immutable after creation. Instead of relying on deploy.Sync to update the Job (which would fail), compare the EXTENSIONS_HASH and delete the old Job before creating a new one. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| @@ -445,6 +449,44 @@ type PluginRegistry struct { | |||
| OpenVSXURL *string `json:"openVSXURL,omitempty"` | |||
There was a problem hiding this comment.
Is it still possible to configure when OpenVSX server is deployed?
There was a problem hiding this comment.
- Update comment for
OpenVSXURL, to outline that this url is not used if OpenVSX registry is deployed. - Update
pluginregistry.goto update status field withOpenVSXURLonly if OpenVSX is not deployed - Dashboard reads status.openvsxurl
There was a problem hiding this comment.
Done
When openVSX.enable=true, pluginRegistry.openVSXURL is ignored. The IsEmbeddedOpenVSXRegistryConfigured() method checks IsOpenVSXOperandEnabled() first and returns false immediately, bypassing the pluginRegistry.openVSXURL check entirely. The dashboard receives the managed OpenVSX URL from status.openVSXURL via an env var override.
When openVSX.enable=false, pluginRegistry.openVSXURL works as before - controls the embedded OpenVSX and plugin registry lifecycle.
| Enable bool `json:"enable,omitempty"` | ||
| // OpenVSX server configuration. | ||
| // +optional | ||
| OpenVSXServer *OpenVSXServer `json:"server,omitempty"` |
There was a problem hiding this comment.
| OpenVSXServer *OpenVSXServer `json:"server,omitempty"` | |
| OpenVSXServer *OpenVSXServer `json:"openVSXServer,omitempty"` |
| OpenVSXServer *OpenVSXServer `json:"server,omitempty"` | ||
| // Database configuration for OpenVSX. | ||
| // +optional | ||
| OpenVSXDatabase *OpenVSXDatabase `json:"database,omitempty"` |
There was a problem hiding this comment.
| OpenVSXDatabase *OpenVSXDatabase `json:"database,omitempty"` | |
| OpenVSXDatabase *OpenVSXDatabase `json:"openVSXDatabase,omitempty"` |
| // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Plugin registry URL" | ||
| // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:org.w3:link" | ||
| PluginRegistryURL string `json:"pluginRegistryURL"` | ||
| // The public URL of the managed OpenVSX registry. |
There was a problem hiding this comment.
What if pluginRegistry.openVSXURL is configured?
There was a problem hiding this comment.
When openVSX.enable=true, pluginRegistry.openVSXURL is ignored. The IsEmbeddedOpenVSXRegistryConfigured() method checks IsOpenVSXOperandEnabled() first and returns false immediately, bypassing the pluginRegistry.openVSXURL check entirely. The dashboard receives the managed OpenVSX URL from status.openVSXURL via an env var override.
When openVSX.enable=false, pluginRegistry.openVSXURL works as before - controls the embedded OpenVSX and plugin registry lifecycle.
| Labels: deploy.GetLabels(defaults.GetCheFlavor()), | ||
| }, | ||
| Type: corev1.SecretTypeOpaque, | ||
| Data: map[string][]byte{ |
There was a problem hiding this comment.
We should allow configure credentials by admin:
- Admin create a secret
- Set secret name in checluster CR
| return &OpenVSXExposeReconciler{} | ||
| } | ||
|
|
||
| func (r *OpenVSXExposeReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { |
There was a problem hiding this comment.
See exposeCheEndpoint to create either ingress or route
|
/che-ai-assistant ok-pr-review Review is complete. Please check the review comments below. |
tolusha
left a comment
There was a problem hiding this comment.
Comprehensive PR Review - #2130
I've completed a comprehensive review including standard, deep, and impact analysis. Here are the key findings:
Critical Issues (Must Fix)
-
SQL injection risk via shell variable interpolation (
pkg/deploy/openvsx/server/openvsx_server.go:204)- User-setup Job constructs SQL via shell expansion, which becomes exploitable if admin-supplied secrets are allowed (as requested by reviewer)
- Use parameterized queries via
psql -vvariables
-
Test package imported in production code (
pkg/deploy/openvsx/openvsx_expose.go:17)- Creates compile-time dependency on test utilities in production binaries
- Use dependency injection instead of
test.IsTestMode()
-
CRD JSON tag naming doesn't match reviewer's explicit suggestion (
api/v2/checluster_types.go:462-465)- Current:
json:"server"andjson:"database"(too generic) - Requested:
json:"openVSXServer"andjson:"openVSXDatabase" - Settle this before release to avoid migration later
- Current:
High Priority
- resource.MustParse can panic the operator - Use
resource.ParseQuantityin reconcile paths - No logging in new reconcilers - Zero visibility into OpenVSX lifecycle operations
- User-setup Job stuck permanently if it fails - No retry mechanism for failed Jobs
- Custom ingress implementation duplicates existing infrastructure - Reviewer explicitly requested using
exposeCheEndpoint - No test coverage for OpenVSXExposeReconciler - Most complex reconciler has zero tests
Operational Readiness Concerns
- ConfigMap ResourceVersion causes unnecessary rollouts - Use content hash instead
- Image env vars use os.Getenv - Empty images cause cryptic failures; validate when
enable: true - Reconciler ordering dependency is implicit - Add documentation for the dependency chain
- Cleanup silently discards delete errors - At minimum, log these errors
- Container images use mutable tags - Pin by digest for production releases
- Interaction with pluginRegistry.openVSXURL undefined - Document the interaction to avoid user confusion
Design & Testing
- Server reconciler is overloaded - Manages 7 resources; consider splitting
- Duplicate PVC sync logic - Extract shared helper for create-or-expand pattern
- Missing test scenarios: PVC expansion, Job hash comparison, failure handling, expose reconciler
See the full review files for detailed analysis:
- Standard review: findings on correctness, security, code quality
- Deep review: design patterns, thoughtfulness, testing rigor
- Impact review: operational readiness, supply chain, compatibility
Verdict: 🔄 Request Changes - The core implementation is solid, but the critical issues (#1-3) and operational gaps (#4-14) should be addressed before merge.
Co-authored-by: Anatolii Bazko <abazko@redhat.com>
Co-authored-by: Anatolii Bazko <abazko@redhat.com>
Co-authored-by: Anatolii Bazko <abazko@redhat.com>
When OpenVSX.Enable=true, the managed operand provides the registry. Disable the embedded OpenVSX in the plugin registry and override the dashboard's OpenVSX URL with Status.OpenVSXURL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
What does this PR do?
enabled via
spec.components.openVSX.enableand Kubernetes
openvsx-extensionsConfigMapspec.components.openVSX.server.claimSize)openvsx-server-configConfigMap user-editable with automatic pod rollout onchanges
New CRD fields
spec.components.openVSX.enable— enables the OpenVSX operand (default is false)spec.components.openVSX.server.claimSize— PVC size for extension storage (default:3Gi)
spec.components.openVSX.server.deployment— deployment overridesspec.components.openVSX.database.claimSize— PVC size for PostgreSQL data (default:1Gi)
spec.components.openVSX.database.deployment— deployment overridesstatus.openVSXURL— URL of the managed OpenVSX instanceResources created when enabled
(extensions list)
All resources are cleaned up when
openVSX.enableis set to false.Screenshot/screencast of this PR
What issues does this PR fix or reference?
https://issues.redhat.com/browse/CRW-10323
How to test this PR?
OpenShift
or
on Minikube
Common Test Scenarios
openvsx-extensionsConfigMapopenvsx-server-configConfigMap and verify pod rolloutclaimSizeand verify expansionPR Checklist
As the author of this Pull Request I made sure that:
Reviewers
Reviewers, please comment how you tested the PR when approving it.