Skip to content

Refactor backend to be API first, with dedicated frontend#351

Merged
mjudeikis merged 5 commits into
kbind-dev:mainfrom
mjudeikis:mjudeikis/api.refactor
Nov 5, 2025
Merged

Refactor backend to be API first, with dedicated frontend#351
mjudeikis merged 5 commits into
kbind-dev:mainfrom
mjudeikis:mjudeikis/api.refactor

Conversation

@mjudeikis

@mjudeikis mjudeikis commented Oct 27, 2025

Copy link
Copy Markdown
Contributor

Summary

This project starts working toward a full api first backend. Where we start wiring /api prefix into backend and add proper frontend based on VueJS to serve as our front gate. This enables more flexible things, like more modular UI, better DX/UX.

UI serves as original backen, served via SPA server so there should not be breaking change from user perspective.

What Type of PR Is This?

/kind feature
/kind api-change
/kind deprecation

Related Issue(s)

Fixes #

Release Notes

Split backend to API, UI, and CLI making it api first

Summary by CodeRabbit

  • New Features

    • Full web UI for resource binding with SSO/login, session & CLI callback flows; embedded frontend assets for single-binary deployment.
    • CLI: login, templates, collections, and bind subcommands with template-driven binding and konnector orchestration.
  • Bug Fixes

    • OIDC/callback paths and related auth routing normalized to API endpoints.
  • Documentation

    • New frontend docs and updated quickstart/helm/kcp setup guides.
  • Chores

    • Multi-arch Docker builds, Makefile build/test enhancements, CI checkout upgrade, gitignore/npm/Vite tooling added.

@mjudeikis mjudeikis requested a review from a team as a code owner October 27, 2025 12:20
@coderabbitai

coderabbitai Bot commented Oct 27, 2025

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • ai-code-review

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This PR adds a Vue 3 frontend (built/embedded), implements OIDC + JWT auth (UI cookie and CLI token flows), reworks HTTP routing to an API-first model with SPA support and middleware, converts the bind CLI to a UI-driven callback flow with binder tooling, updates SDK CRD types (rename + new request types), adds multi-stage multi-arch Docker builds and Makefile targets, and adapts e2e tests and docs to the new flows.

Changes

Cohort / File(s) Summary
Frontend app & embed
web/*, web/src/*, web/embed.go, web/go.mod, web/package.json, web/tsconfig*.json, web/vite.config*.ts, web/.eslintrc.cjs, web/.npmrc, web/index.html, web/README.md
New Vite + Vue 3 frontend, TypeScript/ESLint configs, auth/http clients, components/views for resources/binding, build & Docker Vite configs, and embed of built dist into Go via web/embed.go.
Backend auth & middleware
backend/auth/*.go, backend/session/session.go
New auth package: authorize/callback handlers, JWT service (RSA), auth middleware supporting cookie (UI) and bearer JWT (CLI); session msgpack tag changed.
HTTP server, SPA support & options
backend/http/handler.go, backend/server.go, backend/options/options.go, backend/spaserver/spaserver.go, backend/client/client.go, backend/kubernetes/manager.go
Routes reorganized under /api, AddRoutes now returns error, handler wiring includes authHandler/authMiddleware/frontend, SPA file-server and reverse-proxy added, Manager gained ListTemplates, Frontend option added.
CLI client, commands & config
cli/pkg/client/client.go, cli/cmd/*, cli/pkg/kubectl/*, cli/pkg/config/config.go
New authenticated CLI client, login/templates/collections commands, browser opener, new CLI config management (servers/clusters), flags/options for server/cluster/insecure/config-file.
Bind CLI & binder
cli/pkg/kubectl/bind/plugin/*, cli/pkg/kubectl/bind-apiservice/plugin/*, cli/pkg/kubectl/bind-templates/*, cli/pkg/kubectl/bind-collections/*, cli/pkg/kubectl/bind-login/*
Bind flow converted to UI-driven callback with local callback servers and session IDs; new Binder to convert BindingResourceResponse → APIServiceBindings; many plugin files added/rewritten; removed legacy flags file and tests.
SDK types & codegen
sdk/apis/kubebind/v1alpha2/*
Renamed BindingResponse → BindingResourceResponse, added BindableResourcesRequest and APIServiceExportTemplateRef, added Description to APIServiceExportTemplateSpec, updated deepcopy and register logic and validation.
Docker, Makefile, CI & infra scripts
Dockerfile, Dockerfile.konnector, Makefile, .github/workflows/goreleaser.yml, .gitignore, hack/run-frontend.sh
Multi-stage multi-arch Docker builds (backend with embedded UI + konnector), Makefile targets for build-web, multi-arch image-local via buildx, KIND/kind-load updates, goreleaser workflow checkout bumped to actions/checkout@v4, web ignore entries and frontend run script added.
E2E tests & framework
test/e2e/*, test/e2e/framework/*, contrib/kcp/test/e2e/*, contrib/kcp/*
Tests migrated from server-fixture to rest.Config-based flows, Dex callback URL updated to /api/callback, new helpers for login/client/binding, many e2e files adapted or removed to match new UI-driven flows.
Docs & examples
README.md, docs/content/setup/*, contrib/kcp/README.md, docs/content/setup/.pages
README references frontend docs; quickstart/kcp docs updated to use /api/callback, added example resource creation, Helm example updated, and setup pages reordered.
Go modules & minor cleanup
go.mod, cli/go.mod, contrib/kcp/go.mod, docs/generators/cli-doc/go.mod
Added jwt dependency, added local replace for web module, updated indirect deps, removed some QR-related transitive deps.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Browser as Browser (UI)
    participant CLI as kubectl-bind (CLI)
    participant Server as kube-bind (Backend)
    participant OIDC as OIDC Provider

    CLI->>Server: POST /api/authorize {session_id, redirect_url, client_type=cli}
    Browser->>Server: POST /api/authorize {session_id, redirect_url, client_type=ui}
    Server->>OIDC: Redirect to OIDC authorize (state encodes session)
    OIDC->>Server: GET /api/callback?code&state
    Server->>Server: Exchange code -> tokens, create session / JWT
    alt CLI flow
        Server->>CLI: Redirect to callback_url?access_token=JWT
    else UI flow
        Server->>Browser: Set secure session cookie and redirect to /resources
    end
    Browser->>Server: GET /api/templates (cookie auth)
    CLI->>Server: GET /api/templates (bearer JWT)
    Server->>Server: AuthMiddleware validates cookie or JWT
    Server->>Browser/CLI: Respond JSON (templates / bind result)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas needing focused review:

  • backend/auth/ (handler, jwt, middleware): security, cookie names, token creation/validation, state encoding
  • cli bind refactor and Binder: callback coordination, local server race conditions, secret handling and konnector lifecycle
  • backend/http/handler.go routing migration and SPA proxy vs static serving behavior
  • SDK rename (BindingResponse → BindingResourceResponse): CRD compatibility, deepcopy and registration correctness
  • E2E changes (kcp bootstrap/workspace changes and Dex callback updates) for test stability

Possibly related PRs

Suggested reviewers

  • xrstf
  • moadqassem
  • ntnn

Poem

RSA keys hum behind the door,
Frontend wakes and serves the core,
Callbacks whisper session IDs,
Routes become API melodies,
Tests and builds march—new paths explored.

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main objective: refactoring the backend to be API-first while introducing a dedicated Vue.js frontend. It accurately reflects the core changes in the changeset.
Description check ✅ Passed The pull request description includes all required sections: Summary, What Type of PR Is This (with kind labels), Related Issue(s), and Release Notes.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🧹 Nitpick comments (21)
sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1)

75-82: Documentation improvement approved; optional marker is redundant.

The capitalization of "Namespaces" in the documentation improves consistency with the field name.

The addition of +kubebuilder:validation:Optional on line 81 is redundant since the field is already marked with +optional on line 80 and fields are optional by default in kubebuilder unless explicitly marked as required. Note that the PermissionClaims field (lines 72-73) uses only the +optional marker without the explicit validation annotation, creating a minor inconsistency within this file.

backend/static/embed.go (1)

26-36: Add period to comment on line 27.

The code changes are correct. The gofmt command has addressed formatting issues. One minor style issue remains: the comment "// Static files embedded in the binary" on line 27 should end with a period per godot linting standards.

Update line 27 to:

// Static files embedded in the binary.
web/index.html (1)

5-5: Update placeholder favicon.

The favicon reference to /vite.svg appears to be a default Vite placeholder. Consider replacing it with a project-specific icon for the Kube Bind application.

web/.eslintrc.cjs (1)

14-16: Consider enabling multi-word component name enforcement.

The vue/multi-word-component-names rule is disabled, which goes against the Vue style guide recommendation. This rule prevents naming conflicts with existing or future HTML elements. If intentional, consider documenting the rationale in a comment.

web/README.md (1)

26-38: Clarify SPA vs API ownership

Document that the SPA must not define routes under /api/* to avoid breaking deep links and CLI/JSON responses. Frontend should use web-friendly paths (e.g., /resources, /clusters/:cluster/resources) and only redirect to /api/* for auth flows. I see /api/* routes in the router; see my comment in web/src/main.ts.

Would you like me to add a short “Routing Guarantees” section here?

web/vite.config.docker.ts (1)

39-42: Note: optimizeDeps affects dev, not build

optimizeDeps doesn’t run in vite build. Safe to keep, but it won’t influence production bundles.

backend/options/options.go (3)

65-67: Fix comment name and clarify scheme

The comment mentions “ControllerFrontend” but the field is Frontend. Also allow https:// in the description.

Apply:

-// If ControllerFrontend starts with http:// it is treated as a URL to a SPA server
-// Else - it is treated as a path to static files to be served.
+// If Frontend starts with http:// or https:// it is treated as a URL to an SPA server;
+// otherwise it is treated as a filesystem path with static files to be served.

102-103: Default looks good; consider minimal validation

Defaulting to /www matches the Docker image. Consider validating path existence (when not URL) during startup to fail fast.


147-147: Improve flag help and accept https://

Update help text and add basic normalization/validation.

-fs.StringVar(&options.Frontend, "frontend", options.Frontend, "If starts with http:// it is treated as a URL to a SPA server Else - it is treated as a path to static files to be served.")
+fs.StringVar(&options.Frontend, "frontend", options.Frontend, "If starts with http:// or https:// it is treated as a URL to an SPA server; otherwise, it is treated as a filesystem path with static assets to serve.")

Optionally in Validate():

+ if strings.HasPrefix(options.Frontend, "http://") || strings.HasPrefix(options.Frontend, "https://") {
+   if _, err := url.ParseRequestURI(options.Frontend); err != nil {
+     return fmt.Errorf("invalid --frontend URL: %v", err)
+   }
+ } else if options.Frontend != "" {
+   if _, err := os.Stat(options.Frontend); err != nil {
+     return fmt.Errorf("invalid --frontend path %q: %v", options.Frontend, err)
+   }
+ }
Dockerfile (3)

52-56: Remove redundant npm install

npm ci installs from lockfile; running npm install beforehand is unnecessary and risks mutating the lockfile.

-RUN npm install
-
 # Install dependencies with specific flags to handle optional deps and architecture issues
 RUN npm ci --prefer-offline --no-audit --no-fund --no-optional

68-69: Avoid apk cache in final image

Use --no-cache to reduce image size.

-RUN apk --update add ca-certificates
+RUN apk add --no-cache ca-certificates

25-31: Improve Go module caching

Leverage go mod download before copying the full source for better layer caching.

-# <- COPY go.mod and go.sum files to the workspace
-COPY go.mod .
-COPY go.sum .
-
-# COPY the source code as the last step
-COPY . .
+# Copy and download dependencies first for cache efficiency
+COPY go.mod go.sum ./
+RUN go mod download
+
+# Then copy the rest
+COPY . .
web/src/main.ts (1)

30-59: Apply the same query builder to external redirects

Use the helper to preserve multi-valued params reliably.

-redirect: (to: RouteLocationGeneric) => {
-  const query = new URLSearchParams(to.query as Record<string, string>).toString()
-  const url = `/api/authorize${query ? '?' + query : ''}`
+redirect: (to: RouteLocationNormalized) => {
+  const url = `/api/authorize${toQueryString(to.query as any)}`
   setTimeout(() => window.location.href = url, 0)
   return '/'
 }

Repeat similarly for /clusters/:cluster/authorize and /callback.

web/vite.config.ts (2)

5-11: Factor shared config to reduce drift

vite.config.ts and vite.config.docker.ts diverge slightly (hash formats, minify, chunks). Consider extracting a small vite.config.base.ts and import/extend it in both to keep parity.


12-20: Dev proxy: consider ws and rewrite for future

If you later add SSE/WS endpoints, add ws: true. If backend serves API under a prefix, a rewrite may be needed. For now this is fine.

web/src/views/Resources.vue (2)

621-647: Robust redirect param handling

response may already exist; prefer append semantics to preserve existing values and handle arrays.

- parsedUrl.searchParams.set('response', response.encodedResponse)
+ parsedUrl.searchParams.append('response', response.encodedResponse)

694-703: Avoid double loading on mount

watch(..., { immediate: true }) already triggers loadResources(); calling it again in onMounted() duplicates requests.

 onMounted(() => {
   // Handle OAuth callback first, then load resources
   handleOAuthCallback()
-  loadResources()
 })
backend/http/handler.go (1)

123-132: Consider graceful error handling instead of panic.

Line 126 uses panic for a reverse proxy creation failure. While the comment indicates this is for development only, panicking in the server initialization could be problematic. Consider returning an error from NewHandler or AddRoutes instead.

Apply this approach:

if strings.HasPrefix(h.frontend, "http://") {
	spaserver, err := spaserver.NewSPAReverseProxyServer(h.frontend)
	if err != nil {
		// Log error and fall back to file server or return error from AddRoutes
		return fmt.Errorf("failed to create SPA reverse proxy server: %w", err)
	}
	mux.PathPrefix("/").Handler(spaserver)
} else {
	fileSystem := http.Dir(h.frontend)
	mux.PathPrefix("/").Handler(spaserver.NewSPAFileServer(fileSystem))
}

Note: This would require changing the AddRoutes signature to return an error.

web/src/services/auth.ts (3)

68-82: Consider adding actual session verification.

The checkAuthentication method only validates that sessionID is non-empty but doesn't verify the session with the backend. While the comment indicates cookies are HttpOnly, consider making a lightweight API call (e.g., to /api/healthz with credentials) to confirm the session is still valid.

async checkAuthentication(sessionID: string): Promise<boolean> {
  try {
    if (!sessionID) {
      return false
    }
    
    // Optional: verify session with backend
    const response = await axios.get('/api/healthz', {
      withCredentials: true // Include HttpOnly cookies
    })
    return response.status === 200
  } catch (error) {
    return false
  }
}

119-127: Document limitation of client-side cookie clearing.

The clearSessionCookie method attempts to clear cookies via JavaScript, but HttpOnly cookies (which the backend uses for sessions) cannot be cleared this way. Consider documenting this limitation and/or adding a logout endpoint on the backend that clears the session cookie server-side.

Add a backend logout endpoint and call it:

async logout(): Promise<void> {
  try {
    await axios.post('/api/logout', {}, { withCredentials: true })
  } catch (error) {
    // Log but don't throw - best effort logout
  }
  this.sessionInfo = null
  this.clearSessionCookie() // Clear any non-HttpOnly cookies
}

142-182: Consider refactoring the bindResourceWithSession signature.

The method has 11 parameters, making it difficult to use and maintain. Consider accepting a structured options object instead.

Refactor to use an options object:

interface BindResourceOptions {
  group: string
  resource: string
  apiVersion: string
  clusterId?: string
  sessionId: string
  scope?: string
  kind?: string
  name?: string
  permissionClaims?: PermissionClaim[]
  customRequestName?: string
  namespaces?: Array<{ name: string }>
}

async bindResourceWithSession(options: BindResourceOptions): Promise<any> {
  const {
    group,
    resource,
    apiVersion,
    clusterId = '',
    sessionId,
    scope = 'Namespaced',
    kind = '',
    name = '',
    permissionClaims = [],
    customRequestName,
    namespaces = []
  } = options
  
  // ... rest of implementation
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fadb9ed and 28a13a3.

⛔ Files ignored due to path filters (14)
  • .github/workflows/ci.yaml is excluded by !**/*.yaml
  • .github/workflows/image.yaml is excluded by !**/*.yaml
  • backend/static/web/dist/assets/index.54024e19.css is excluded by !**/dist/**
  • backend/static/web/dist/assets/index.fdabfc06.js is excluded by !**/dist/**
  • backend/static/web/dist/index.html is excluded by !**/dist/**
  • contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml is excluded by !**/*.yaml
  • contrib/kcp/deploy/resources/apiresourceschema-apiserviceexporttemplates.kube-bind.io.yaml is excluded by !**/*.yaml
  • contrib/kcp/deploy/resources/apiresourceschema-bindableresourcesrequests.kube-bind.io.yaml is excluded by !**/*.yaml
  • deploy/charts/backend/crds/kube-bind.io_apiserviceexporttemplates.yaml is excluded by !**/*.yaml
  • deploy/charts/backend/crds/kube-bind.io_bindableresourcesrequests.yaml is excluded by !**/*.yaml
  • deploy/charts/backend/examples/values-local-development.yaml is excluded by !**/*.yaml
  • deploy/crd/kube-bind.io_apiserviceexporttemplates.yaml is excluded by !**/*.yaml
  • deploy/crd/kube-bind.io_bindableresourcesrequests.yaml is excluded by !**/*.yaml
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (31)
  • .github/workflows/goreleaser.yml (1 hunks)
  • .gitignore (1 hunks)
  • Dockerfile (1 hunks)
  • Dockerfile.konnector (1 hunks)
  • Makefile (1 hunks)
  • README.md (1 hunks)
  • backend/http/handler.go (10 hunks)
  • backend/options/options.go (3 hunks)
  • backend/server.go (2 hunks)
  • backend/spaserver/spaserver.go (1 hunks)
  • backend/static/embed.go (1 hunks)
  • cli/pkg/kubectl/bind/plugin/bind.go (2 hunks)
  • docs/content/setup/helm.md (1 hunks)
  • docs/content/setup/kcp-setup.md (2 hunks)
  • hack/run-frontend.sh (1 hunks)
  • sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1 hunks)
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1 hunks)
  • sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go (1 hunks)
  • web/.eslintrc.cjs (1 hunks)
  • web/.npmrc (1 hunks)
  • web/README.md (1 hunks)
  • web/index.html (1 hunks)
  • web/package.json (1 hunks)
  • web/src/App.vue (1 hunks)
  • web/src/main.ts (1 hunks)
  • web/src/services/auth.ts (1 hunks)
  • web/src/views/Resources.vue (1 hunks)
  • web/tsconfig.json (1 hunks)
  • web/tsconfig.node.json (1 hunks)
  • web/vite.config.docker.ts (1 hunks)
  • web/vite.config.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
cli/pkg/kubectl/bind/plugin/bind.go (1)
sdk/apis/kubebind/v1alpha2/register.go (1)
  • GroupVersion (35-35)
backend/http/handler.go (5)
backend/spaserver/spaserver.go (2)
  • NewSPAReverseProxyServer (79-88)
  • NewSPAFileServer (33-38)
backend/session/session.go (1)
  • State (29-34)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (4)
  • BindableResourcesRequest (143-168)
  • BindingResponse (32-53)
  • BindingResponseAuthentication (57-63)
  • BindingResponseAuthenticationOAuth2CodeGrant (68-75)
sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go (8)
  • APIServiceExportRequestResponse (66-79)
  • NameObjectMeta (81-84)
  • APIServiceExportRequestSpec (95-127)
  • APIServiceExportRequestResource (137-143)
  • PermissionClaim (206-213)
  • Namespaces (129-135)
  • GroupResource (182-198)
  • Selector (154-162)
sdk/apis/kubebind/v1alpha2/claimable_apis.go (2)
  • ResolveClaimableAPI (83-90)
  • ClaimableAPIs (38-81)
web/src/main.ts (1)
backend/static/web/dist/assets/index.fdabfc06.js (5)
  • routes (11904-11949)
  • Resources (11903-11903)
  • router (11950-11953)
  • app (11954-11954)
  • createApp (6627-6649)
sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go (2)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (3)
  • BindableResource (89-138)
  • BindableResourcesRequest (143-168)
  • BindableResourcesResponse (77-86)
sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go (2)
  • PermissionClaim (206-213)
  • Namespaces (129-135)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (2)
web/src/services/auth.ts (2)
  • BindableResource (11-19)
  • PermissionClaim (45-52)
sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go (1)
  • Namespaces (129-135)
backend/server.go (2)
backend/options/options.go (1)
  • Options (33-40)
pkg/konnector/options/options.go (1)
  • Options (29-33)
web/src/services/auth.ts (2)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (2)
  • BindableResource (89-138)
  • BindableResourcesRequest (143-168)
backend/static/web/dist/assets/index.fdabfc06.js (1)
  • cookies (9866-9896)
🪛 golangci-lint (2.5.0)
backend/static/embed.go

[error] 25-25: File is not properly formatted

(gci)


[error] 29-29: Comment should end in a period

(godot)

backend/spaserver/spaserver.go

[error] 58-58: File is not properly formatted

(gci)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: goreleaser
  • GitHub Check: lint
  • GitHub Check: go-test
🔇 Additional comments (26)
docs/content/setup/helm.md (1)

53-57: Documentation update for development version deployment is properly formatted.

The helm upgrade command correctly specifies a full git SHA (0.0.0-fadb9edd26c0202f4a9511ee9d71b9e5f43672b9) in the version field, which improves reproducibility compared to short SHAs. The command syntax and formatting are consistent with the recommended approach shown in lines 45–50.

Please confirm that the git SHA fadb9edd26c0202f4a9511ee9d71b9e5f43672b9 corresponds to a valid commit on the main branch and represents the intended development build for this PR.

.gitignore (1)

18-24: LGTM! Standard frontend artifact patterns.

The ignore patterns are appropriate for the Vue.js/Vite frontend being introduced. They properly exclude build artifacts, dependencies, and tooling files from version control.

.github/workflows/goreleaser.yml (1)

20-20: LGTM! Checkout action upgrade.

Upgrading to actions/checkout@v4 is appropriate and aligns with current GitHub Actions best practices.

README.md (1)

61-61: LGTM! Documentation cross-reference.

The reference to the frontend documentation is helpful for users working with the new Vue.js frontend.

web/tsconfig.node.json (1)

1-11: LGTM! Standard Vite Node configuration.

The TypeScript configuration for the Vite build tooling is appropriate, with strict mode enabled and proper module resolution for the bundler.

docs/content/setup/kcp-setup.md (2)

92-92: LGTM! Backend configuration update.

The addition of the --consumer-scope=cluster flag is properly documented and aligns with the backend API changes in this PR.


136-139: LGTM! Additional setup steps documented.

The new template and catalog resource creation steps are clearly documented and enhance the setup workflow.

web/src/App.vue (1)

1-133: LGTM! Clean Vue app shell.

The component provides a solid foundation for the SPA with:

  • Semantic HTML structure
  • Comprehensive design system styles
  • Proper router integration

The global styles establish consistent UI patterns across the application.

web/tsconfig.json (1)

1-31: LGTM!

The TypeScript configuration is well-structured with strict type checking enabled and appropriate compiler options for a Vue 3 + Vite project. The path alias setup and project references are correctly configured.

backend/server.go (2)

68-71: LGTM!

The callback URL update to include the /api prefix aligns with the API-first architecture introduced in this PR. The change maintains backward compatibility with the conditional default URL logic.


110-122: LGTM!

The addition of the Frontend parameter to the NewHandler call enables the handler to serve the SPA and coordinate frontend routing, which is essential for the API-first refactoring.

Dockerfile.konnector (1)

30-30: Alpine version 3.22.1 exists and is valid.

The Docker Hub API confirms that Alpine version 3.22.1 is an available and valid Docker image. The review comment's concern that this version may not exist is unfounded. The manifest inspect command failure in the verification script reflects sandbox environment limitations, not the non-existence of the image.

Likely an incorrect or invalid review comment.

web/vite.config.docker.ts (1)

5-12: Config is correctly specified in build script

Verification confirms the build script in package.json explicitly passes --config vite.config.docker.ts to Vite. The Docker-specific configuration will be used during image builds.

Dockerfile (1)

64-66: No changes required to Dockerfile.

The review comment is incorrect. The npm build script in web/package.json already explicitly specifies the Docker config: "build": "vite build --config vite.config.docker.ts". Running npm run build in the Dockerfile executes this script as-is, which means the Docker-specific configuration is already being applied explicitly. Adding --config vite.config.docker.ts again would be redundant and unnecessary.

Likely an incorrect or invalid review comment.

sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)

77-168: LGTM! Types are well-structured and consistent.

The new binding resource types are properly defined with appropriate kubebuilder validation markers. The types align with the frontend TypeScript interfaces in web/src/services/auth.ts, ensuring type safety across the stack.

backend/spaserver/spaserver.go (2)

28-38: LGTM! Clean constructor pattern.

The SPAFileServer struct and constructor follow a clean design pattern, properly encapsulating the file system and delegating to http.FileServer.


74-92: LGTM! Simple and effective reverse proxy implementation.

The SPAReverseProxyServer provides a clean wrapper for development-time proxying to a frontend server.

sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go (1)

738-809: LGTM! Auto-generated DeepCopy methods.

The DeepCopy methods for the new binding resource types are properly generated and follow the standard Kubernetes code generation patterns.

backend/http/handler.go (6)

68-68: LGTM! Clean integration of frontend configuration.

The frontend field is properly added to the handler struct and initialized through the constructor.

Also applies to: 78-78, 87-87


135-155: LGTM! Clean redirect handler for CLI compatibility.

The handleExportsRedirect properly redirects legacy CLI requests to the new /api endpoints while preserving query parameters.


399-447: LGTM! Clean migration to JSON-only response.

The handleResources method correctly outputs JSON instead of HTML, aligning with the API-first architecture.


550-596: Verify session and request validation completeness.

The session retrieval and basic validation look correct. However, ensure that the sessionID parameter is properly validated against the cookie name to prevent session fixation attacks.

Consider adding explicit validation:

 sessionID := r.URL.Query().Get("s")
 if sessionID == "" {
 	logger.Error(errors.New("missing session parameter"), "failed to get session from query")
 	http.Error(w, "missing session parameter 's'", http.StatusBadRequest)
 	return
 }
+
+// Validate sessionID format to prevent injection
+if !isValidSessionID(sessionID) {
+	logger.Error(errors.New("invalid session ID format"), "validation failed")
+	http.Error(w, "invalid session ID format", http.StatusBadRequest)
+	return
+}

698-719: Verify intended exposure of encoded response in JSON.

The JSON response includes encodedResponse which contains the base64-encoded binding response with kubeconfig credentials. Ensure this is the intended behavior for AJAX requests from the frontend, as it exposes sensitive credentials in the HTTP response body.

If the frontend needs to pass this to the CLI, verify that:

  1. The frontend properly handles and protects this sensitive data
  2. The response is served over HTTPS in production
  3. The encoded response doesn't contain more sensitive data than necessary

746-758: LGTM! Simple and correct implementation.

The handleBindableResources handler properly exposes the claimable APIs as JSON.

web/src/services/auth.ts (2)

3-63: LGTM! Type definitions are consistent with backend.

The TypeScript interfaces properly mirror the Go types defined in the backend SDK, ensuring type safety across the full stack.


184-202: LGTM! Clean API methods with proper cluster-aware routing.

Both getExports and getClaimableResources are properly implemented with appropriate error handling.

Comment thread backend/http/handler.go Outdated
Comment thread backend/spaserver/spaserver.go Outdated
Comment thread cli/pkg/kubectl/bind/plugin/bind.go Outdated
Comment thread Dockerfile Outdated
Comment thread Dockerfile.konnector
Comment thread web/src/main.ts Outdated
Comment thread web/src/main.ts Outdated
Comment thread web/src/services/auth.ts Outdated
Comment thread web/src/views/Resources.vue Outdated
Comment thread web/src/views/Resources.vue Outdated
@mjudeikis mjudeikis force-pushed the mjudeikis/api.refactor branch from 28a13a3 to 5f0cfbe Compare October 28, 2025 07:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (11)
web/README.md (1)

50-56: Fix flag typo and align Go version.

The backend flag is --frontend, not --frontent. Additionally, the Dockerfile uses Go 1.24, so the prerequisite should be updated to match.

Apply:

-# Terminal 1: Start Go backend
-go run ./cmd/backend --listen-port=8080 --frontent http://localhost:3000
+# Terminal 1: Start Go backend
+go run ./cmd/backend --listen-port=8080 --frontend http://localhost:3000
-- Go 1.19+ for running the backend server
+- Go 1.24+ for running the backend server
Dockerfile (1)

32-39: Verify cross-compile variables are passed to make.

The else branch sets GOOS and GOARCH environment variables, but if the Makefile doesn't use these and instead derives OS/ARCH from the builder host, the produced binary may have the wrong architecture.

Consider passing the cross-compile variables explicitly to make:

     else \
-        GOOS=$TARGETOS GOARCH=$TARGETARCH make build; \
+        make build OS=$TARGETOS ARCH=$TARGETARCH; \
     fi

Alternatively, replace the make call with an explicit go build invocation that uses GOOS and GOARCH.

web/package.json (1)

15-18: Update axios to address critical security vulnerabilities.

The specified version of axios (^1.5.0) contains multiple HIGH severity vulnerabilities, including DoS and SSRF issues. Update to version 1.12.0 or later.

Apply:

   "dependencies": {
     "vue": "^3.3.4",
     "vue-router": "^4.2.4",
-    "axios": "^1.5.0"
+    "axios": "^1.12.0"
   },

Run npm install and update the lockfile after making this change.

hack/run-frontend.sh (1)

3-5: Correct misleading comments.

The comments state this script "builds the Vue.js frontend and embeds it into the Go binary," but the script actually runs the development server (npm run dev), which does not build or embed anything.

Apply:

-# Build frontend script for kube-bind
-# This script builds the Vue.js frontend and embeds it into the Go binary
+# Development frontend script for kube-bind
+# This script runs the Vue.js frontend development server
web/src/main.ts (2)

3-16: Replace non-existent RouteLocationGeneric and handle query arrays.

RouteLocationGeneric is not a public vue-router v4 type. Additionally, casting to.query to Record<string,string> loses array values.

Apply:

-import type { RouteLocationGeneric } from 'vue-router'
+import type { RouteLocationNormalized } from 'vue-router'
+
+const toQueryString = (q: Record<string, unknown>) => {
+  const sp = new URLSearchParams()
+  for (const [k, v] of Object.entries(q)) {
+    if (Array.isArray(v)) v.forEach(x => sp.append(k, String(x ?? '')))
+    else if (v != null) sp.append(k, String(v))
+  }
+  const s = sp.toString()
+  return s ? `?${s}` : ''
+}

Then update the redirect callbacks:

-  redirect: (to: RouteLocationGeneric) => {
-    const query = new URLSearchParams(to.query as Record<string, string>).toString()
-    return `/resources${query ? '?' + query : ''}`
-  }
+  redirect: (to: RouteLocationNormalized) => `/resources${toQueryString(to.query)}`

Apply similar changes to other redirect callbacks.


21-27: Do not claim /api/ paths in the SPA router.*

Routing /api/resources and /api/clusters/:cluster/resources in the SPA conflicts with backend JSON endpoints and breaks deep links on page refresh.

Remove these routes:

   // Main resources route
   { path: '/resources', component: Resources },
   
-  // API routes that match backend endpoints - all serve the same resources view
-  { path: '/api/resources', component: Resources },
-  { path: '/api/clusters/:cluster/resources', component: Resources, props: true },
-  
   // Web-friendly cluster-aware routes
   { path: '/clusters/:cluster/resources', component: Resources, props: true },
web/src/views/Resources.vue (2)

200-206: Disable Bind button when template has no resources.

The Bind button can be clicked for templates with no resources, which may cause runtime errors. Add a check to disable the button when the template has no resources.

Apply:

 :disabled="binding === template.Name 
   || bindingSuccess === template.Name 
   || !isValidName(resourceNames[template.Name] || '')
+  || template.Resources.length === 0"

584-592: Correct kind and apiVersion derivation.

The kind field uses the plural resource name instead of the singular PascalCase Kind, and apiVersion uses a bare version string instead of the group/version format. Additionally, only the first version is used without guarding against empty arrays.

Consider this adjustment:

 const bindableResources = template.Resources.map(resource => ({
   name: `${resource.resource}.${resource.group}`,
-  kind: resource.resource,
+  // TODO: Map resource.resource (plural) to proper Kind (singular PascalCase)
+  kind: resource.resource,
   scope: template.Scope,
-  apiVersion: resource.versions[0],
+  version: resource.versions[0] ?? '',
+  apiVersion: resource.group ? `${resource.group}/${resource.versions[0] ?? ''}` : resource.versions[0] ?? '',
   group: resource.group,
   resource: resource.resource,
   sessionID: currentSessionId
 }))
+
+if (bindableResources.length === 0 || bindableResources.some(r => !r.version)) {
+  throw new Error('Template has no valid resources to bind')
+}

Verify that the backend API expects this format and adjust accordingly.

backend/spaserver/spaserver.go (1)

58-59: Fix formatting issue.

The if statement spanning lines 58-59 has improper indentation, causing a linter error.

Apply:

-	if strings.Contains(path, "/assets/") || strings.Contains(path, "/api/") ||
-		(strings.Contains(path, ".") && !strings.HasSuffix(path, "/")) {
+	if strings.Contains(path, "/assets/") || strings.Contains(path, "/api/") ||
+		(strings.Contains(path, ".") && !strings.HasSuffix(path, "/")) {

Run gofmt or gci to ensure proper formatting.

web/src/services/auth.ts (1)

114-116: Use cryptographically secure random for session ID generation.

The generateSessionId method uses Math.random(), which is not cryptographically secure and could allow session prediction attacks. Use crypto.getRandomValues() for session IDs used in OAuth flows.

backend/http/handler.go (1)

606-619: Validate bindRequest.Name before use.

Line 612 uses bindRequest.Name directly from user input without validation. This value must conform to Kubernetes DNS-1123 subdomain naming rules (lowercase alphanumeric, hyphens, dots, max 253 characters) when used as ObjectMeta.Name.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28a13a3 and 5f0cfbe.

⛔ Files ignored due to path filters (13)
  • .github/workflows/ci.yaml is excluded by !**/*.yaml
  • .github/workflows/image.yaml is excluded by !**/*.yaml
  • backend/static/web/dist/assets/index.54024e19.css is excluded by !**/dist/**
  • backend/static/web/dist/assets/index.fdabfc06.js is excluded by !**/dist/**
  • backend/static/web/dist/index.html is excluded by !**/dist/**
  • contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml is excluded by !**/*.yaml
  • contrib/kcp/deploy/resources/apiresourceschema-apiserviceexporttemplates.kube-bind.io.yaml is excluded by !**/*.yaml
  • contrib/kcp/deploy/resources/apiresourceschema-bindableresourcesrequests.kube-bind.io.yaml is excluded by !**/*.yaml
  • deploy/charts/backend/crds/kube-bind.io_apiserviceexporttemplates.yaml is excluded by !**/*.yaml
  • deploy/charts/backend/crds/kube-bind.io_bindableresourcesrequests.yaml is excluded by !**/*.yaml
  • deploy/crd/kube-bind.io_apiserviceexporttemplates.yaml is excluded by !**/*.yaml
  • deploy/crd/kube-bind.io_bindableresourcesrequests.yaml is excluded by !**/*.yaml
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (31)
  • .github/workflows/goreleaser.yml (1 hunks)
  • .gitignore (1 hunks)
  • Dockerfile (1 hunks)
  • Dockerfile.konnector (1 hunks)
  • Makefile (1 hunks)
  • README.md (1 hunks)
  • backend/http/handler.go (10 hunks)
  • backend/options/options.go (3 hunks)
  • backend/server.go (2 hunks)
  • backend/spaserver/spaserver.go (1 hunks)
  • cli/go.mod (1 hunks)
  • cli/pkg/kubectl/bind/plugin/bind.go (2 hunks)
  • docs/content/setup/helm.md (1 hunks)
  • docs/content/setup/kcp-setup.md (2 hunks)
  • hack/run-frontend.sh (1 hunks)
  • sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1 hunks)
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1 hunks)
  • sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go (1 hunks)
  • web/.eslintrc.cjs (1 hunks)
  • web/.npmrc (1 hunks)
  • web/README.md (1 hunks)
  • web/index.html (1 hunks)
  • web/package.json (1 hunks)
  • web/src/App.vue (1 hunks)
  • web/src/main.ts (1 hunks)
  • web/src/services/auth.ts (1 hunks)
  • web/src/views/Resources.vue (1 hunks)
  • web/tsconfig.json (1 hunks)
  • web/tsconfig.node.json (1 hunks)
  • web/vite.config.docker.ts (1 hunks)
  • web/vite.config.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
  • web/tsconfig.json
  • sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go
  • web/.npmrc
  • backend/options/options.go
  • web/index.html
  • cli/pkg/kubectl/bind/plugin/bind.go
  • .github/workflows/goreleaser.yml
  • docs/content/setup/kcp-setup.md
  • .gitignore
  • Dockerfile.konnector
  • web/vite.config.docker.ts
  • web/tsconfig.node.json
  • web/src/App.vue
  • web/.eslintrc.cjs
  • Makefile
  • docs/content/setup/helm.md
  • README.md
  • backend/server.go
🧰 Additional context used
🧬 Code graph analysis (5)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (3)
web/src/services/auth.ts (2)
  • BindableResource (11-19)
  • PermissionClaim (45-52)
cli/pkg/kubectl/bind/plugin/bind.go (1)
  • SessionID (318-324)
sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go (1)
  • Namespaces (129-135)
sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go (2)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (3)
  • BindableResource (89-138)
  • BindableResourcesRequest (143-168)
  • BindableResourcesResponse (77-86)
sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go (2)
  • PermissionClaim (206-213)
  • Namespaces (129-135)
web/src/main.ts (1)
backend/static/web/dist/assets/index.fdabfc06.js (5)
  • routes (11904-11949)
  • Resources (11903-11903)
  • router (11950-11953)
  • app (11954-11954)
  • createApp (6627-6649)
web/src/services/auth.ts (2)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (2)
  • BindableResource (89-138)
  • BindableResourcesRequest (143-168)
backend/static/web/dist/assets/index.fdabfc06.js (1)
  • cookies (9866-9896)
backend/http/handler.go (4)
backend/spaserver/spaserver.go (2)
  • NewSPAReverseProxyServer (79-88)
  • NewSPAFileServer (33-38)
backend/session/session.go (1)
  • State (29-34)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (4)
  • BindableResourcesRequest (143-168)
  • BindingResponse (32-53)
  • BindingResponseAuthentication (57-63)
  • BindingResponseAuthenticationOAuth2CodeGrant (68-75)
sdk/apis/kubebind/v1alpha2/claimable_apis.go (2)
  • ResolveClaimableAPI (83-90)
  • ClaimableAPIs (38-81)
🪛 markdownlint-cli2 (0.18.1)
web/README.md

84-84: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: lint
  • GitHub Check: verify
  • GitHub Check: go-test-e2e
  • GitHub Check: go-test
  • GitHub Check: goreleaser
🔇 Additional comments (12)
cli/go.mod (1)

13-13: Verify the go-spew version choice; an unreleased pre-release is currently pinned.

The go-spew dependency has been moved from indirect to direct require, which is correct given its direct usage in cli/pkg/kubectl/bind/plugin/bind.go. However, the latest go-spew release is v1.1.1, released August 17, 2018. The pinned version v1.1.2-0.20180830191138-d8f796af33cc is a pre-release/commit hash from August 30, 2018, and is not an official release. Confirm that this specific unreleased version is necessary, or consider updating to the latest official release.

sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (3)

77-86: LGTM!

The BindableResourcesResponse type definition is clean and well-documented, with appropriate validation ensuring at least one resource is present.


140-168: LGTM!

The BindableResourcesRequest type is well-structured with appropriate validation. The reuse of existing PermissionClaim and Namespaces types maintains consistency across the API.


88-138: Align TypeScript interface with Go struct definition.

The Go struct BindableResource includes an optional Description field, but the corresponding TypeScript interface in web/src/services/auth.ts omits this field. This inconsistency may cause the description to be dropped when the API response is consumed by the frontend. Verify whether this omission is intentional or an oversight and update the TypeScript interface to include description?: string if it should be preserved.

sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go (3)

738-752: LGTM!

The BindableResource deep copy implementation correctly uses a shallow copy since all fields are primitive string types.


754-787: LGTM!

The BindableResourcesRequest deep copy implementation correctly handles all fields. The use of copy() for Resources and Namespaces slices is appropriate since they contain only value types, while PermissionClaims properly uses per-element DeepCopyInto() to handle nested pointer fields.


789-809: LGTM!

The BindableResourcesResponse deep copy implementation correctly copies the Resources slice using copy(), which is appropriate for the value-type elements.

web/vite.config.ts (1)

1-44: Configuration looks good.

The Vite configuration is properly set up for Vue 3 development with appropriate proxy settings for the backend API and build options for production.

backend/http/handler.go (4)

68-68: LGTM: Frontend integration field.

The addition of the frontend field to the handler and its integration through the constructor aligns well with the SPA serving functionality introduced in this PR.

Also applies to: 78-78, 87-87


135-155: LGTM: CLI redirect implementation.

The redirect handler correctly preserves backward compatibility for CLI tools that can't execute JavaScript, while routing them to the new API endpoints. Query parameter preservation and appropriate use of 302 redirects are both correct.


399-447: LGTM: Conversion to JSON-based API.

The refactoring from HTML template rendering to JSON encoding correctly supports the API-first architecture. Error handling and response structure are appropriate.


746-758: LGTM: Bindable resources endpoint.

The implementation correctly exposes the claimable APIs as a JSON endpoint. The error handling and response formatting are appropriate.

Comment thread backend/http/handler.go Outdated
Comment thread backend/http/handler.go Outdated
Comment thread backend/http/handler.go Outdated
Comment thread web/README.md Outdated
Comment thread web/src/services/auth.ts Outdated
Comment thread web/src/services/auth.ts Outdated
Comment thread web/src/services/auth.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
backend/http/handler.go (1)

294-301: Decode OAuth state with URL-safe base64 (current decode will fail).

state was encoded with base64.URLEncoding; decoding with StdEncoding is incorrect and breaks the callback.

-decoded, err := base64.StdEncoding.DecodeString(state)
+decoded, err := base64.URLEncoding.DecodeString(state)
test/e2e/bind/happy-case_test.go (1)

728-734: Fix template selector mismatch and typo in simulateBrowser.

“Bind again” passes serviceGVR.Resource while earlier steps use templateClassIdentifier (“bind-*”). Also, tempalteName is misspelled.

- go simulateBrowser(t, authURLCh, serviceGVR.Resource)
+ go simulateBrowser(t, authURLCh, templateClassIdentifier)
-func simulateBrowser(t *testing.T, authURLCh chan string, tempalteName string) {
+func simulateBrowser(t *testing.T, authURLCh chan string, templateName string) {
@@
- t.Logf("Clicking template %s", tempalteName)
- err = browser.Click("a." + tempalteName)
+ t.Logf("Clicking template %s", templateName)
+ err = browser.Click("a." + templateName)

Also applies to: 743-756

♻️ Duplicate comments (6)
web/src/main.ts (2)

21-21: Replace RouteLocationGeneric with RouteLocationNormalized.

The redirect callback parameter uses the non-public RouteLocationGeneric type. Use the imported RouteLocationNormalized type instead.

Apply this diff:

-    redirect: (to: RouteLocationGeneric) => `/resources${toQueryString(to.query as any)}`
+    redirect: (to: RouteLocationNormalized) => `/resources${toQueryString(to.query as any)}`

3-3: Remove the non-public RouteLocationGeneric type import.

The import includes RouteLocationGeneric, which is not a public vue-router v4 type as previously flagged. Although RouteLocationNormalized is correctly imported, RouteLocationGeneric is still being used on line 21.

Apply this diff to remove the unused type:

-import type { RouteLocationNormalized, RouteLocationGeneric } from 'vue-router'
+import type { RouteLocationNormalized } from 'vue-router'
hack/run-frontend.sh (1)

3-5: Correct misleading comments about script purpose.

The comments state "builds the Vue.js frontend and serves it using a development server," but the script executes npm run dev which runs a development server without performing a production build. The comments should accurately reflect that this is a development script.

Apply this diff to correct the comments:

-# Build frontend script for kube-bind
-# This script builds the Vue.js frontend  and serves it using a development server.
+# Development frontend script for kube-bind
+# This script runs the Vue.js frontend development server for local development.
backend/http/handler.go (3)

457-471: Don’t pass session/cookie name in query; derive cookie name from route.

Reading the cookie name from ?s= leaks in URLs/logs and is brittle. Use generateCookieName with {cluster} param (or default).

- cookieName := r.URL.Query().Get("s")
- ck, err := r.Cookie(r.URL.Query().Get("s"))
+ cookieName := generateCookieName(mux.Vars(r)["cluster"])
+ ck, err := r.Cookie(cookieName)
  if err != nil {
      ...
  }

Optionally keep ?s= as a backward-compat fallback if present. I can provide a patch if you prefer that path.


557-571: Same as above for POST /api/bind: avoid session in query; use deterministic cookie name.

Mirror the GET handler behavior; prefer cookie by generateCookieName.

- // Get session ID from query parameter
- sessionID := r.URL.Query().Get("s")
- if sessionID == "" {
-     logger.Error(errors.New("missing session parameter"), "failed to get session from query")
-     http.Error(w, "missing session parameter 's'", http.StatusBadRequest)
-     return
- }
-
- // Get session cookie
- ck, err := r.Cookie(sessionID)
+ // Get session cookie using deterministic name
+ cookieName := generateCookieName(mux.Vars(r)["cluster"])
+ ck, err := r.Cookie(cookieName)
  if err != nil {
      ...
  }
 
- // Decode session state
+ // Decode session state
  state := session.State{}
  s := securecookie.New(h.cookieSigningKey, h.cookieEncryptionKey)
- if err := s.Decode(sessionID, ck.Value, &state); err != nil {
+ if err := s.Decode(cookieName, ck.Value, &state); err != nil {
      ...
  }

622-635: Validate or generate export request name before use.

bindRequest.Name is user input; validate per DNS-1123 rules or generate a safe default to avoid server-side create failures.

- ObjectMeta: kubebindv1alpha2.NameObjectMeta{
-     Name: bindRequest.Name,
- },
+ // Validate name if provided; otherwise generate a safe default
+ name := bindRequest.Name
+ if name == "" {
+     name = fmt.Sprintf("bind-%d", time.Now().Unix())
+ } else {
+     if errs := validation.IsDNS1123Subdomain(name); len(errs) > 0 {
+         http.Error(w, "invalid name: "+strings.Join(errs, "; "), http.StatusBadRequest)
+         return
+     }
+ }
+ ObjectMeta: kubebindv1alpha2.NameObjectMeta{
+     Name: name,
+ },

Add import:

import "k8s.io/apimachinery/pkg/util/validation"
🧹 Nitpick comments (9)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)

112-116: Missing validation for Scope values.

The comment indicates that Scope should be either "Cluster" or "Namespaced", but the Validate() method does not enforce this constraint. Consider adding validation or a kubebuilder enum marker.

Apply this diff to add validation in the Validate() method:

 		if res.Scope == "" {
 			return fmt.Errorf("resource at index %d has no scope", i)
 		}
+		if res.Scope != "Cluster" && res.Scope != "Namespaced" {
+			return fmt.Errorf("resource at index %d has invalid scope %q: must be either 'Cluster' or 'Namespaced'", i, res.Scope)
+		}
web/embed.go (1)

30-37: Consider more graceful error handling instead of panic.

The GetFileSystem() function panics if creating the sub-filesystem fails. This is appropriate for initialization-time failures that indicate build/packaging errors. However, consider if a sentinel error or compile-time check would be more idiomatic.

If you prefer avoiding runtime panics, consider returning an error:

-// GetFileSystem returns the embedded file system for serving static files.
-func GetFileSystem() http.FileSystem {
+// GetFileSystem returns the embedded file system for serving static files.
+// Returns an error if the dist subdirectory cannot be accessed.
+func GetFileSystem() (http.FileSystem, error) {
 	// Create a sub-filesystem starting from dist
 	subFS, err := fs.Sub(StaticFiles, "dist")
 	if err != nil {
-		panic("failed to create sub filesystem: " + err.Error())
+		return nil, fmt.Errorf("failed to create sub filesystem: %w", err)
 	}
-	return http.FS(subFS)
+	return http.FS(subFS), nil
 }

However, since this function is likely called during server initialization, the panic behavior may be acceptable for fail-fast semantics.

Dockerfile (3)

26-30: Remove redundant npm install; use npm ci with production flags.

Running npm install followed by npm ci wastes time and cache. Prefer a single deterministic install.

-RUN npm install
-
-# Install dependencies with specific flags to handle optional deps and architecture issues
-RUN npm ci --prefer-offline --no-audit --no-fund --no-optional
+# Deterministic, production-only install
+RUN npm ci --omit=dev --no-audit --no-fund --no-optional

74-75: Drop unused /www copy (assets are embedded) or wire the server to use it.

With embedded FS via web.GetFileSystem(), /www is unused and adds size. Remove it unless you plan to serve from disk.

-COPY --from=ui-build-env /app/dist /www
+# UI assets are embedded; skip copying to runtime image

71-71: Use --no-cache for apk in final image.

Smaller, cleaner layer.

-RUN apk --update add ca-certificates
+RUN apk add --no-cache ca-certificates
backend/http/handler.go (2)

123-135: Support https frontend in dev and prefer scheme detection via url.Parse.

Only http:// is recognized; https:// dev servers won’t be proxied.

-switch {
-case strings.HasPrefix(h.frontend, "http://"):
+switch u, err := url.Parse(h.frontend); {
+case err == nil && (u.Scheme == "http" || u.Scheme == "https"):
     // Development mode: proxy to frontend dev server
-    spaserver, err := spaserver.NewSPAReverseProxyServer(h.frontend)
+    spaserver, err := spaserver.NewSPAReverseProxyServer(h.frontend)
     if err != nil {
         return err
     }
     mux.PathPrefix("/").Handler(spaserver)
 default:
     fs := web.GetFileSystem()
     mux.PathPrefix("/").Handler(spaserver.NewSPAFileServer(fs))
 }

582-597: Close request body after reading.

Wrap with MaxBytesReader but still defer close.

 const maxBodySize = 1 << 20 // 1 MB
 r.Body = http.MaxBytesReader(w, r.Body, maxBodySize)
+defer r.Body.Close()
 var bindRequest kubebindv1alpha2.BindableResourcesRequest
 if err := json.NewDecoder(r.Body).Decode(&bindRequest); err != nil {
test/e2e/bind/happy-case_test.go (2)

132-146: Prefer /api/exports consistently (redirect exists but avoid reliance).

Current tests mix /exports and /api/exports. Use /api/exports to reduce redirect coupling.

- fmt.Sprintf("http://%s/exports", addr.String())
+ fmt.Sprintf("http://%s/api/exports", addr.String())

Also applies to: 730-734


66-68: Fix misleading comment about cleanup.

Comment says “Commented out …” but t.Cleanup(cancel) is active. Update or remove the comment.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f0cfbe and 454f03b.

⛔ Files ignored due to path filters (2)
  • .github/workflows/ci.yaml is excluded by !**/*.yaml
  • hack/dex-config-dev.yaml is excluded by !**/*.yaml
📒 Files selected for processing (20)
  • Dockerfile (1 hunks)
  • Makefile (1 hunks)
  • backend/http/handler.go (11 hunks)
  • backend/server.go (2 hunks)
  • backend/spaserver/spaserver.go (1 hunks)
  • cli/pkg/kubectl/bind/plugin/bind.go (1 hunks)
  • contrib/kcp/README.md (3 hunks)
  • docs/content/setup/quickstart.md (1 hunks)
  • go.mod (2 hunks)
  • hack/run-frontend.sh (1 hunks)
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (2 hunks)
  • test/e2e/bind/happy-case_test.go (7 hunks)
  • test/e2e/framework/backend.go (1 hunks)
  • test/e2e/framework/dex.go (1 hunks)
  • web/README.md (1 hunks)
  • web/embed.go (1 hunks)
  • web/go.mod (1 hunks)
  • web/src/main.ts (1 hunks)
  • web/src/services/auth.ts (1 hunks)
  • web/src/views/Resources.vue (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • web/go.mod
🚧 Files skipped from review as they are similar to previous changes (5)
  • cli/pkg/kubectl/bind/plugin/bind.go
  • web/src/services/auth.ts
  • web/README.md
  • backend/server.go
  • web/src/views/Resources.vue
🧰 Additional context used
🧬 Code graph analysis (3)
test/e2e/bind/happy-case_test.go (1)
test/e2e/framework/bind.go (1)
  • Bind (36-75)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (3)
web/src/services/auth.ts (3)
  • BindableResource (11-19)
  • PermissionClaim (45-52)
  • BindableResourcesRequest (54-63)
cli/pkg/kubectl/bind/plugin/bind.go (1)
  • SessionID (317-323)
sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go (1)
  • Namespaces (129-135)
backend/http/handler.go (6)
backend/spaserver/spaserver.go (2)
  • NewSPAReverseProxyServer (116-125)
  • NewSPAFileServer (33-38)
web/embed.go (1)
  • GetFileSystem (31-38)
backend/session/session.go (1)
  • State (29-34)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (4)
  • BindableResourcesRequest (148-173)
  • BindingResponse (37-58)
  • BindingResponseAuthentication (62-68)
  • BindingResponseAuthenticationOAuth2CodeGrant (73-80)
sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go (7)
  • APIServiceExportRequestResponse (66-79)
  • NameObjectMeta (81-84)
  • APIServiceExportRequestSpec (95-127)
  • APIServiceExportRequestResource (137-143)
  • PermissionClaim (206-213)
  • Namespaces (129-135)
  • GroupResource (182-198)
sdk/apis/kubebind/v1alpha2/claimable_apis.go (2)
  • ResolveClaimableAPI (83-90)
  • ClaimableAPIs (38-81)
🪛 GitHub Check: go-test
web/embed.go

[failure] 27-27:
pattern dist/*: no matching files found


[failure] 27-27:
pattern dist/*: no matching files found


[failure] 27-27:
pattern dist/*: no matching files found


[failure] 27-27:
pattern dist/*: no matching files found

🪛 golangci-lint (2.5.0)
backend/http/handler.go

[error] 43-43: could not import github.com/kube-bind/kube-bind/web (web/embed.go:27:12: pattern dist/*: no matching files found)

(typecheck)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: go-test-e2e
  • GitHub Check: goreleaser
🔇 Additional comments (15)
web/src/main.ts (2)

7-15: Well-implemented query string serialization.

The helper correctly handles array values and null checks, preserving all query parameters during redirects as intended.


27-34: Standard Vue 3 application bootstrap.

The router creation with web history mode and app initialization follow Vue 3 best practices.

sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (2)

82-91: LGTM!

The type definition follows Kubernetes API conventions with appropriate validation markers.


145-173: LGTM!

The type definition is well-structured with appropriate validation markers and documentation.

docs/content/setup/quickstart.md (1)

89-89: LGTM! Documentation updated to reflect API path migration.

The OIDC callback URL is correctly updated to /api/callback, consistent with the backend API path refactoring across the PR.

go.mod (1)

9-9: LGTM! Web module replacement correctly configured.

The local module replacement for the web component is properly set up for the monorepo structure.

contrib/kcp/README.md (2)

58-58: LGTM! KCP documentation updated for API path migration.

The OIDC callback URL is correctly updated to /api/callback.


111-111: Verify the cluster ID changes are intentional.

The LogicalCluster URL reference and kubectl-bind exports path have been updated with different cluster IDs. Please confirm these are example placeholders and not hardcoded values that should be dynamic.

Also applies to: 127-127

hack/run-frontend.sh (1)

6-21: LGTM! Script logic is sound.

The script properly checks for node_modules and installs dependencies only when needed before starting the development server.

Makefile (2)

378-380: LGTM! Frontend build target is correctly implemented.

The build-web target appropriately navigates to the web directory and runs the npm build command.


416-424: LGTM! Repository variable consistency resolved.

The introduction of DOCKER_REPO defaulting to IMAGE_REPO and its usage in kind-load resolves the previous mismatch issue.

test/e2e/framework/backend.go (1)

86-86: LGTM! Test framework updated for API path migration.

The callback URL construction is correctly updated to /api/callback, ensuring test framework alignment with the backend API changes.

test/e2e/framework/dex.go (1)

107-107: LGTM! Dex redirect URI updated correctly.

The OAuth redirect URI is properly updated to /api/callback, maintaining consistency with the API path refactoring.

web/embed.go (1)

27-28: Build order is correctly configured.

The Dockerfile properly sequences the frontend build before the Go binary compilation. The npm run build command executes at line 39 in the builder stage, while the Go build stage begins at line 42, ensuring the dist directory exists when the //go:embed dist/* directive is processed. The explanation in the review comment is accurate.

Dockerfile (1)

24-24: No credential leakage risk detected — .npmrc contains only configuration flags.

The .npmrc file at ./web/.npmrc contains only npm configuration settings (optional=false, fund=false, audit=false) with no authentication tokens or credentials. The verification condition specified in the review comment ("if yes") is not met, and the code is acceptable as-is from a credential security perspective.

Comment thread backend/spaserver/spaserver.go
Comment thread Dockerfile
Comment thread go.mod Outdated
Comment thread Makefile
Comment thread sdk/apis/kubebind/v1alpha2/bindingresponse_types.go Outdated
Comment thread sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
Comment thread sdk/apis/kubebind/v1alpha2/bindingresponse_types.go Outdated
Comment thread test/e2e/bind/happy-case_test.go Outdated
@mjudeikis mjudeikis force-pushed the mjudeikis/api.refactor branch 2 times, most recently from 3cdbbf9 to 6d5d903 Compare November 3, 2025 15:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cli/pkg/kubectl/bind/cmd/cmd.go (1)

103-103: Correct the nil parameter passed to Run method.

The Run method signature expects chan<- string as the second parameter, but the code passes nil. Line 103 in cli/pkg/kubectl/bind/cmd/cmd.go must be corrected to pass a valid send-only string channel instead of nil.

Reference: cli/pkg/kubectl/bind/plugin/bind.go:337 shows the method signature func (b *BindOptions) Run(ctx context.Context, urlCh chan<- string) error.

contrib/kcp/test/e2e/kcp.go (1)

40-49: Do not mutate the shared REST config in-place.

cfg := rest keeps the original pointer, so the subsequent host rewrite changes the caller’s config. After the first invocation every later client ends up with /clusters/.../clusters/..., breaking requests and racing with other users of the base config. Please work on a copy before adjusting the host.

-func wsConfig(t testing.TB, rest *rest.Config, workspace logicalcluster.Path) (*rest.Config, string) {
-	cfg := rest
-	cfg.Host += "/clusters/" + workspace.String()
+func wsConfig(t testing.TB, base *rest.Config, workspace logicalcluster.Path) (*rest.Config, string) {
+	cfg := rest.CopyConfig(base)
+	cfg.Host = strings.TrimSuffix(base.Host, "/") + "/clusters/" + workspace.String()
♻️ Duplicate comments (3)
web/README.md (1)

43-45: Update Go prerequisite to match the current toolchain.

The backend builds against Go 1.24 (see Dockerfile), so keeping “Go 1.19+” here misleads contributors and will lead to failed local builds. Please align the prerequisite with the actual minimum version.

-- Go 1.19+ for running the backend server
+- Go 1.24+ for running the backend server
Dockerfile (1)

59-66: Fix embedded asset copy path to prevent build failure.

web/embed.go uses //go:embed dist/*, which resolves to web/dist/* at build time. Copying the Vue build output into ./backend/static/web/dist leaves web/dist empty, so go build fails with “pattern dist/*: no matching files found” and the binary misses the SPA assets. Copy the files into ./web/dist before running the Go build.

-# Copy built UI assets for embedding
-COPY --from=ui-build-env /app/dist ./backend/static/web/dist
+# Copy built UI assets for embedding
+COPY --from=ui-build-env /app/dist ./web/dist
backend/http/handler.go (1)

333-341: Validate bindable request name before serializing it.

bindRequest.Name still flows straight into ObjectMeta.Name, so an empty or non-DNS1123 value from the client will bubble up as a late failure when the CLI posts the request to Kubernetes. Please reject invalid names here (e.g. ensure non-empty and use validation.IsDNS1123Subdomain), returning http.StatusBadRequest on failure, and only populate the object when validation passes. Remember to add the k8s.io/apimachinery/pkg/util/validation import.

-	request := kubebindv1alpha2.APIServiceExportRequestResponse{
+	if bindRequest.Name == "" {
+		http.Error(w, "name is required", http.StatusBadRequest)
+		return
+	}
+	if errs := validation.IsDNS1123Subdomain(bindRequest.Name); len(errs) > 0 {
+		http.Error(w, "invalid name: "+strings.Join(errs, ", "), http.StatusBadRequest)
+		return
+	}
+
+	request := kubebindv1alpha2.APIServiceExportRequestResponse{
 		TypeMeta: metav1.TypeMeta{
 			APIVersion: kubebindv1alpha2.SchemeGroupVersion.String(),
 			Kind:       "APIServiceExportRequest",
 		},
🧹 Nitpick comments (7)
test/e2e/framework/apply.go (1)

91-95: LGTM! The []byte case is implemented correctly.

The logic correctly unmarshals YAML from a byte slice and integrates seamlessly with existing manifest handling.

Optionally, the string and []byte cases share identical unmarshal logic. You could consolidate them, though the current duplication is minimal and acceptable for test utilities.

test/e2e/framework/kcp.go (1)

66-71: Consider consolidating with WithName to simplify the API surface.

The new WithStaticName function provides the same behavior as calling WithName("some-name") without format arguments. While the explicit naming may offer semantic clarity, maintaining both functions increases the API surface and may introduce confusion about which to use.

If the intent is to signal non-formatted names explicitly, the current implementation is acceptable. Otherwise, WithName already covers this use case.

test/e2e/framework/browser.go (1)

41-48: Consider adding authentication completion verification.

The function opens the authentication URL but does not verify that the authentication flow completes successfully. If the test expects authentication to finish, consider using BrowserEventuallyAtPath or adding a mechanism to verify the authentication callback completes.

Example enhancement:

 func SimulateBrowser(t *testing.T, authURLCh chan string) {
 	browser := surf.NewBrowser()
 	authURL := <-authURLCh
 
 	t.Logf("Browsing to auth URL: %s", authURL)
 	err := browser.Open(authURL)
 	require.NoError(t, err)
+	
+	// Wait for authentication to complete (adjust path as needed)
+	BrowserEventuallyAtPath(t, browser, "/callback")
 }
web/vite.config.docker.ts (1)

19-20: Reconsider disabling minification.

Disabling minification significantly increases bundle size. Minification (terser/esbuild) typically does not involve native dependencies—the native dependency issues are addressed by excluding the Rollup native bundles (lines 40-41). Consider re-enabling minification for production builds.

Apply this diff to enable minification:

-    // Disable minification to avoid potential native dep issues
-    minify: false,
+    // Use esbuild for minification (no native deps)
+    minify: 'esbuild',
contrib/kcp/test/e2e/backend.go (1)

42-42: Validate the host extraction logic.

The code assumes rest.Host contains /clusters/ and uses string splitting to extract the base host. If the format differs, the extraction may produce incorrect results. Add validation to ensure the expected structure.

Apply this diff to add validation:

-	rest.Host = strings.Split(rest.Host, "/clusters/")[0]
+	parts := strings.Split(rest.Host, "/clusters/")
+	if len(parts) < 2 {
+		t.Fatalf("Unexpected rest.Host format: %s (expected format with /clusters/)", rest.Host)
+	}
+	rest.Host = parts[0]
cli/pkg/kubectl/bind-templates/cmd/cmd.go (1)

61-68: Cap positional args at one server URL.

Today a user can pass multiple positional URLs (all starting with http/https) and only the first one would be consumed, silently ignoring the rest. Please hard-limit the positional args to at most one while keeping the scheme validation.

 		Args: func(cmd *cobra.Command, args []string) error {
+			if len(args) > 1 {
+				return fmt.Errorf("expected at most one server URL, got %d", len(args))
+			}
 			for _, arg := range args {
 				if !strings.HasPrefix(arg, "http://") && !strings.HasPrefix(arg, "https://") {
 					return fmt.Errorf("invalid server URL: %s", arg)
 				}
 			}
 			return nil
cli/pkg/kubectl/base/client.go (1)

52-64: Preserve client creation error context.

Line 63 replaces the original client.NewClient error with a generic “not authenticated” message, making real failures (e.g., corrupt config, missing server definition) hard to diagnose. Wrap and return the original error instead so downstream callers and users understand what actually failed.

-	return nil, s.URL, fmt.Errorf("not authenticated to server: %s", o.Server)
+	return nil, s.URL, fmt.Errorf("failed to create authenticated client: %w", err)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 454f03b and 6d5d903.

⛔ Files ignored due to path filters (15)
  • .github/workflows/ci.yaml is excluded by !**/*.yaml
  • .github/workflows/image.yaml is excluded by !**/*.yaml
  • .golangci.yaml is excluded by !**/*.yaml
  • certficate.yaml is excluded by !**/*.yaml
  • contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml is excluded by !**/*.yaml
  • contrib/kcp/deploy/resources/apiresourceschema-apiserviceexporttemplates.kube-bind.io.yaml is excluded by !**/*.yaml
  • contrib/kcp/deploy/resources/apiresourceschema-bindableresourcesrequests.kube-bind.io.yaml is excluded by !**/*.yaml
  • contrib/kcp/go.sum is excluded by !**/*.sum
  • deploy/charts/backend/crds/kube-bind.io_apiserviceexporttemplates.yaml is excluded by !**/*.yaml
  • deploy/charts/backend/crds/kube-bind.io_bindableresourcesrequests.yaml is excluded by !**/*.yaml
  • deploy/crd/kube-bind.io_apiserviceexporttemplates.yaml is excluded by !**/*.yaml
  • deploy/crd/kube-bind.io_bindableresourcesrequests.yaml is excluded by !**/*.yaml
  • go.sum is excluded by !**/*.sum
  • hack/dex-config-dev.yaml is excluded by !**/*.yaml
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (85)
  • .github/workflows/goreleaser.yml (1 hunks)
  • .gitignore (1 hunks)
  • Dockerfile (1 hunks)
  • Dockerfile.konnector (1 hunks)
  • Makefile (4 hunks)
  • README.md (1 hunks)
  • backend/auth/handler.go (1 hunks)
  • backend/auth/jwt.go (1 hunks)
  • backend/auth/middleware.go (1 hunks)
  • backend/auth/types.go (2 hunks)
  • backend/client/client.go (1 hunks)
  • backend/http/handler.go (8 hunks)
  • backend/kubernetes/manager.go (1 hunks)
  • backend/options/options.go (3 hunks)
  • backend/server.go (4 hunks)
  • backend/spaserver/spaserver.go (1 hunks)
  • cli/cmd/kubectl-bind/cmd/kubectlBind.go (2 hunks)
  • cli/go.mod (1 hunks)
  • cli/pkg/client/client.go (1 hunks)
  • cli/pkg/kubectl/base/browser.go (1 hunks)
  • cli/pkg/kubectl/base/client.go (1 hunks)
  • cli/pkg/kubectl/base/options.go (4 hunks)
  • cli/pkg/kubectl/base/printer.go (1 hunks)
  • cli/pkg/kubectl/bind-apiservice/plugin/bind.go (6 hunks)
  • cli/pkg/kubectl/bind-apiservice/plugin/binder.go (1 hunks)
  • cli/pkg/kubectl/bind-apiservice/plugin/konnector.go (3 hunks)
  • cli/pkg/kubectl/bind-apiservice/plugin/secret.go (2 hunks)
  • cli/pkg/kubectl/bind-apiservice/plugin/servicebindings.go (2 hunks)
  • cli/pkg/kubectl/bind-collections/cmd/cmd.go (1 hunks)
  • cli/pkg/kubectl/bind-collections/plugin/collections.go (1 hunks)
  • cli/pkg/kubectl/bind-login/cmd/cmd.go (1 hunks)
  • cli/pkg/kubectl/bind-login/plugin/login.go (1 hunks)
  • cli/pkg/kubectl/bind-templates/cmd/cmd.go (1 hunks)
  • cli/pkg/kubectl/bind-templates/plugin/templates.go (1 hunks)
  • cli/pkg/kubectl/bind/cmd/cmd.go (1 hunks)
  • cli/pkg/kubectl/bind/plugin/authenticate.go (1 hunks)
  • cli/pkg/kubectl/bind/plugin/bind.go (5 hunks)
  • cli/pkg/kubectl/bind/plugin/flags.go (0 hunks)
  • cli/pkg/kubectl/bind/plugin/flags_test.go (0 hunks)
  • cli/pkg/kubectl/config/config.go (1 hunks)
  • contrib/kcp/README.md (3 hunks)
  • contrib/kcp/deploy/bootstrap.go (2 hunks)
  • contrib/kcp/go.mod (4 hunks)
  • contrib/kcp/test/e2e/backend.go (2 hunks)
  • contrib/kcp/test/e2e/binding.go (1 hunks)
  • contrib/kcp/test/e2e/browser.go (0 hunks)
  • contrib/kcp/test/e2e/kcp.go (2 hunks)
  • contrib/kcp/test/e2e/kcp_test.go (3 hunks)
  • docs/content/setup/.pages (1 hunks)
  • docs/content/setup/helm.md (1 hunks)
  • docs/content/setup/kcp-setup.md (2 hunks)
  • docs/content/setup/kubectl-plugin.md (1 hunks)
  • docs/content/setup/quickstart.md (1 hunks)
  • go.mod (3 hunks)
  • hack/run-frontend.sh (1 hunks)
  • sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (2 hunks)
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (2 hunks)
  • sdk/apis/kubebind/v1alpha2/register.go (1 hunks)
  • sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go (4 hunks)
  • test/e2e/bind/happy-case_test.go (4 hunks)
  • test/e2e/framework/apply.go (1 hunks)
  • test/e2e/framework/backend.go (1 hunks)
  • test/e2e/framework/bind.go (1 hunks)
  • test/e2e/framework/browser.go (2 hunks)
  • test/e2e/framework/dex.go (2 hunks)
  • test/e2e/framework/kcp.go (1 hunks)
  • web/.eslintrc.cjs (1 hunks)
  • web/.npmrc (1 hunks)
  • web/README.md (1 hunks)
  • web/embed.go (1 hunks)
  • web/go.mod (1 hunks)
  • web/index.html (1 hunks)
  • web/package.json (1 hunks)
  • web/src/App.vue (1 hunks)
  • web/src/components/BindingResult.vue (1 hunks)
  • web/src/components/TemplateBindingModal.vue (1 hunks)
  • web/src/main.ts (1 hunks)
  • web/src/services/auth.ts (1 hunks)
  • web/src/services/http.ts (1 hunks)
  • web/src/types/binding.ts (1 hunks)
  • web/src/views/Resources.vue (1 hunks)
  • web/tsconfig.json (1 hunks)
  • web/tsconfig.node.json (1 hunks)
  • web/vite.config.docker.ts (1 hunks)
  • web/vite.config.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • cli/pkg/kubectl/bind/plugin/flags_test.go
  • contrib/kcp/test/e2e/browser.go
  • cli/pkg/kubectl/bind/plugin/flags.go
✅ Files skipped from review due to trivial changes (6)
  • web/tsconfig.json
  • web/tsconfig.node.json
  • docs/content/setup/kubectl-plugin.md
  • cli/pkg/kubectl/bind-apiservice/plugin/servicebindings.go
  • cli/pkg/kubectl/bind-apiservice/plugin/secret.go
  • Makefile
🚧 Files skipped from review as they are similar to previous changes (19)
  • cli/go.mod
  • test/e2e/framework/backend.go
  • backend/options/options.go
  • docs/content/setup/quickstart.md
  • web/src/App.vue
  • web/index.html
  • .github/workflows/goreleaser.yml
  • web/src/services/auth.ts
  • web/.npmrc
  • docs/content/setup/helm.md
  • hack/run-frontend.sh
  • backend/spaserver/spaserver.go
  • web/vite.config.ts
  • .gitignore
  • web/src/main.ts
  • contrib/kcp/README.md
  • README.md
  • web/package.json
  • web/go.mod
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-09-12T09:05:29.762Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 295
File: sdk/client/listers/kubebind/v1alpha2/boundschema.go:46-48
Timestamp: 2025-09-12T09:05:29.762Z
Learning: In the kube-bind project, lister-gen is generating BoundSchema listers with singular resource names ("boundschema") instead of plural ("boundschemas"), which breaks client-go conventions and can cause cache lookup issues. This is identified as a generator issue that needs upstream investigation rather than manual code fixes.

Applied to files:

  • web/src/views/Resources.vue
  • test/e2e/bind/happy-case_test.go
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
  • backend/http/handler.go
  • backend/auth/types.go
📚 Learning: 2025-09-18T16:39:25.771Z
Learnt from: xrstf
Repo: kube-bind/kube-bind PR: 0
File: :0-0
Timestamp: 2025-09-18T16:39:25.771Z
Learning: The lister-gen issue with singular vs plural resource names is an upstream bug in kubernetes/code-generator that has existed for 8+ years since inception. The bug is located in lister.go line 304 where singular resource names are incorrectly used instead of plural names, affecting any project using lister-gen.

Applied to files:

  • web/src/views/Resources.vue
📚 Learning: 2025-09-22T13:32:29.499Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: pkg/konnector/controllers/cluster/claimedresources/claimedresources_controller.go:255-263
Timestamp: 2025-09-22T13:32:29.499Z
Learning: In kube-bind's claimedresources controller (pkg/konnector/controllers/cluster/claimedresources/claimedresources_controller.go), the controller does not run if apiServiceExport is not set. This means nil-checks for c.apiServiceExport are unnecessary since the controller lifecycle ensures it's always non-nil when active.

Applied to files:

  • test/e2e/bind/happy-case_test.go
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
  • cli/pkg/kubectl/bind/plugin/bind.go
  • cli/pkg/kubectl/bind-apiservice/plugin/binder.go
  • backend/http/handler.go
  • backend/server.go
📚 Learning: 2025-09-22T13:20:49.952Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: sdk/apis/kubebind/v1alpha2/boundchema_types.go:49-0
Timestamp: 2025-09-22T13:20:49.952Z
Learning: In kube-bind, there are two different ResourceGroupName() methods: BoundSchema.ResourceGroupName() for CRDs (always non-empty groups) uses simple fmt.Sprintf formatting, while APIServiceExportRequestResource.ResourceGroupName() for export requests handles empty groups by converting to "core". BoundSchema is exclusively for CRDs which cannot have empty API groups per Kubernetes validation.

Applied to files:

  • test/e2e/bind/happy-case_test.go
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
  • sdk/apis/kubebind/v1alpha2/register.go
  • backend/http/handler.go
📚 Learning: 2025-09-22T13:20:49.952Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: sdk/apis/kubebind/v1alpha2/boundchema_types.go:49-0
Timestamp: 2025-09-22T13:20:49.952Z
Learning: In kube-bind, BoundSchema.ResourceGroupName() is only used for CRDs (Custom Resource Definitions), and CRDs must always have non-empty API groups. Therefore, handling empty groups (core API group "") is not necessary in this context, unlike general Kubernetes GroupResource handling.

Applied to files:

  • test/e2e/bind/happy-case_test.go
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
  • sdk/apis/kubebind/v1alpha2/register.go
  • backend/http/handler.go
  • backend/auth/types.go
📚 Learning: 2025-10-22T06:41:37.475Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 342
File: docs/content/usage/migration.md:34-37
Timestamp: 2025-10-22T06:41:37.475Z
Learning: In the kube-bind repository documentation and examples, "mangodb" (with group "mangodb.com" and resource "mangodbs") is an intentional fictional example name chosen to avoid referencing real products like MongoDB. It should not be treated as a typo.

Applied to files:

  • test/e2e/bind/happy-case_test.go
  • backend/auth/types.go
📚 Learning: 2025-09-19T05:56:35.969Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: backend/controllers/servicenamespace/servicenamespace_reconcile.go:81-98
Timestamp: 2025-09-19T05:56:35.969Z
Learning: In kube-bind, RBAC permissions for PermissionClaims use "*" verbs intentionally. This is a design decision based on: 1) permissions are scoped to consumer-owned provider namespaces, limiting blast radius, 2) bidirectional resource flow requires broad permissions for operations like initial resource creation from consumer side, 3) kube-bind's architecture prioritizes operational simplicity over granular RBAC within the namespace security boundary.

Applied to files:

  • test/e2e/bind/happy-case_test.go
  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
  • cli/pkg/kubectl/bind-apiservice/plugin/bind.go
  • backend/http/handler.go
  • backend/auth/types.go
📚 Learning: 2025-09-12T08:40:15.290Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 295
File: backend/controllers/serviceexportrequest/serviceexportrequest_reconcile.go:140-143
Timestamp: 2025-09-12T08:40:15.290Z
Learning: APIServiceExportRequest resources in kube-bind are short-lived and automatically deleted after 10 minutes, so they should not be used as owner references for longer-lived resources like BoundSchema. The proper lifecycle management for BoundSchema resources created by APIServiceExportRequest is tracked in issue #297.

Applied to files:

  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
  • web/src/types/binding.ts
  • backend/http/handler.go
📚 Learning: 2025-09-19T06:28:44.853Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: pkg/konnector/controllers/cluster/serviceexport/serviceexport_reconcile.go:148-150
Timestamp: 2025-09-19T06:28:44.853Z
Learning: In kube-bind, permission claims must all have the same scope (either all cluster-scoped or all namespace-scoped). Mixed scopes are not allowed, which means the isClusterScoped flag can be safely determined from any processed schema in the export.

Applied to files:

  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
  • backend/http/handler.go
  • backend/auth/types.go
  • contrib/kcp/test/e2e/kcp_test.go
📚 Learning: 2025-09-12T08:55:41.860Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 295
File: sdk/apis/kubebind/v1alpha2/helpers/boundschema.go:115-123
Timestamp: 2025-09-12T08:55:41.860Z
Learning: In BoundSchemasSpecHash function in sdk/apis/kubebind/v1alpha2/helpers/boundschema.go, silent error handling during JSON encoding (continuing on encoding errors) is acceptable to mjudeikis for the current implementation, even though it could potentially lead to incorrect hash values.

Applied to files:

  • sdk/apis/kubebind/v1alpha2/bindingresponse_types.go
  • backend/http/handler.go
  • backend/auth/types.go
📚 Learning: 2025-10-01T15:55:45.035Z
Learnt from: ntnn
Repo: kube-bind/kube-bind PR: 333
File: contrib/kcp/test/e2e/kcp_test.go:270-279
Timestamp: 2025-10-01T15:55:45.035Z
Learning: In KCP, when constructing kubeconfig server URLs for workspaces, the correct pattern is to append ":" + workspace.Base() to an existing server URL that already targets the root workspace, rather than using "/clusters/" + workspace.String(). This colon-separated syntax is KCP's valid format for workspace targeting.

Applied to files:

  • contrib/kcp/test/e2e/backend.go
  • contrib/kcp/test/e2e/kcp.go
  • contrib/kcp/test/e2e/kcp_test.go
🧬 Code graph analysis (30)
backend/kubernetes/manager.go (1)
sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1)
  • APIServiceExportTemplateList (98-103)
cli/cmd/kubectl-bind/cmd/kubectlBind.go (1)
cli/pkg/kubectl/bind/cmd/cmd.go (1)
  • New (57-109)
backend/client/client.go (1)
backend/auth/middleware.go (1)
  • ClientType (37-37)
cli/pkg/kubectl/bind-collections/cmd/cmd.go (1)
cli/pkg/kubectl/bind-collections/plugin/collections.go (1)
  • NewCollectionsOptions (44-50)
test/e2e/bind/happy-case_test.go (5)
test/e2e/framework/env.go (1)
  • WorkDir (26-26)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (2)
  • BindingResourceResponse (37-58)
  • APIServiceExportTemplateRef (92-98)
test/e2e/framework/browser.go (1)
  • SimulateBrowser (41-48)
test/e2e/framework/bind.go (2)
  • Login (42-68)
  • GetKubeBindRestClient (31-40)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
cli/pkg/kubectl/bind-apiservice/plugin/konnector.go (2)
cli/pkg/kubectl/base/options.go (1)
  • Options (32-59)
pkg/bootstrap/bootstrap.go (1)
  • Bootstrap (74-93)
contrib/kcp/test/e2e/binding.go (4)
test/e2e/framework/bind.go (1)
  • GetKubeBindRestClient (31-40)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • APIServiceExportTemplateRef (92-98)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
test/e2e/framework/clients.go (1)
  • ApiextensionsClient (45-49)
cli/pkg/kubectl/base/printer.go (2)
sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1)
  • APIServiceExportTemplateList (98-103)
sdk/apis/kubebind/v1alpha2/collection_types.go (1)
  • CollectionList (89-94)
cli/pkg/kubectl/bind/cmd/cmd.go (2)
cli/pkg/kubectl/bind-apiservice/cmd/cmd.go (1)
  • New (46-80)
cli/pkg/kubectl/bind/plugin/bind.go (1)
  • NewBindOptions (88-100)
cli/pkg/kubectl/base/client.go (2)
cli/pkg/client/client.go (2)
  • Client (32-42)
  • NewClient (73-97)
cli/pkg/kubectl/config/config.go (1)
  • LoadConfigFromFile (67-90)
sdk/apis/kubebind/v1alpha2/register.go (1)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
contrib/kcp/test/e2e/backend.go (1)
sdk/apis/kubebind/v1alpha2/boundchema_types.go (1)
  • InformerScope (59-59)
cli/pkg/kubectl/bind-collections/plugin/collections.go (3)
cli/pkg/kubectl/bind-login/plugin/login.go (1)
  • NewLoginOptions (83-92)
sdk/apis/kubebind/v1alpha2/collection_types.go (1)
  • CollectionList (89-94)
cli/pkg/kubectl/base/printer.go (1)
  • PrintObject (34-46)
cli/pkg/kubectl/bind/plugin/bind.go (6)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
cli/pkg/kubectl/base/options.go (1)
  • Options (32-59)
cli/pkg/kubectl/config/config.go (3)
  • LoadConfigFromFile (67-90)
  • BuildServerKey (274-280)
  • ParseServerKey (283-288)
cli/pkg/kubectl/bind-login/plugin/login.go (1)
  • NewLoginOptions (83-92)
cli/pkg/kubectl/base/browser.go (1)
  • OpenBrowser (9-23)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
backend/auth/handler.go (6)
backend/auth/types.go (3)
  • OIDCServiceProvider (59-67)
  • AuthorizeRequest (44-50)
  • AuthResponse (27-33)
backend/auth/jwt.go (1)
  • JWTService (28-32)
backend/client/client.go (2)
  • GetQueryParams (20-30)
  • ClientParameters (8-17)
backend/auth/middleware.go (2)
  • ClientType (37-37)
  • ClientTypeCLI (41-41)
backend/session/cookie.go (1)
  • MakeCookie (24-35)
backend/session/session.go (2)
  • State (29-34)
  • TokenInfo (36-39)
test/e2e/framework/bind.go (2)
cli/pkg/client/client.go (2)
  • Client (32-42)
  • NewClient (73-97)
cli/pkg/kubectl/bind-login/plugin/login.go (1)
  • NewLoginOptions (83-92)
cli/pkg/kubectl/bind-templates/cmd/cmd.go (1)
cli/pkg/kubectl/bind-templates/plugin/templates.go (1)
  • NewTemplatesOptions (50-56)
cli/pkg/client/client.go (4)
sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1)
  • APIServiceExportTemplateList (98-103)
sdk/apis/kubebind/v1alpha2/collection_types.go (1)
  • CollectionList (89-94)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
cli/pkg/kubectl/config/config.go (1)
  • LoadConfigFromFile (67-90)
sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go (1)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (2)
  • APIServiceExportTemplateRef (92-98)
  • BindingResourceResponse (37-58)
cli/pkg/kubectl/bind-login/cmd/cmd.go (2)
cli/pkg/kubectl/bind-login/plugin/login.go (1)
  • NewLoginOptions (83-92)
cli/pkg/help/help.go (1)
  • Doc (33-37)
cli/pkg/kubectl/bind-templates/plugin/templates.go (2)
sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1)
  • APIServiceExportTemplateList (98-103)
cli/pkg/kubectl/base/printer.go (1)
  • PrintObject (34-46)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (4)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
sdk/apis/kubebind/v1alpha1/bindingprovider_types.go (1)
  • OAuth2CodeGrant (73-81)
cli/pkg/kubectl/base/kubeconfig.go (3)
  • ParseRemoteKubeconfig (32-46)
  • FindRemoteKubeconfig (48-79)
  • EnsureKubeconfigSecret (86-139)
cli/pkg/kubectl/bind-apiservice/plugin/bind.go (1)
  • BindAPIServiceOptions (50-72)
cli/pkg/kubectl/bind-apiservice/plugin/bind.go (6)
cli/pkg/kubectl/base/options.go (1)
  • Options (32-59)
cli/pkg/kubectl/config/config.go (1)
  • LoadConfigFromFile (67-90)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
cli/pkg/kubectl/bind-login/plugin/login.go (1)
  • NewLoginOptions (83-92)
cli/pkg/kubectl/base/kubeconfig.go (3)
  • ParseRemoteKubeconfig (32-46)
  • FindRemoteKubeconfig (48-79)
  • EnsureKubeconfigSecret (86-139)
cli/pkg/client/client.go (1)
  • Client (32-42)
cli/pkg/kubectl/base/options.go (1)
cli/pkg/kubectl/config/config.go (1)
  • LoadConfigFromFile (67-90)
backend/http/handler.go (12)
backend/auth/types.go (1)
  • OIDCServiceProvider (59-67)
backend/auth/handler.go (2)
  • AuthHandler (37-42)
  • NewAuthHandler (44-51)
backend/auth/middleware.go (4)
  • AuthMiddleware (50-54)
  • NewAuthMiddleware (56-62)
  • RequireAuth (127-145)
  • GetAuthContext (120-125)
backend/kubernetes/manager.go (1)
  • Manager (39-49)
backend/auth/jwt.go (1)
  • NewJWTService (43-54)
backend/spaserver/spaserver.go (2)
  • NewSPAReverseProxyServer (103-112)
  • NewSPAFileServer (33-38)
web/embed.go (1)
  • GetFileSystem (31-38)
backend/client/client.go (1)
  • GetQueryParams (20-30)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1)
  • APIServiceExportTemplateList (98-103)
sdk/apis/kubebind/v1alpha2/collection_types.go (1)
  • CollectionList (89-94)
sdk/apis/kubebind/v1alpha2/claimable_apis.go (1)
  • ClaimableAPIs (38-81)
cli/pkg/kubectl/bind-login/plugin/login.go (4)
cli/pkg/client/client.go (1)
  • Client (32-42)
cli/pkg/kubectl/config/config.go (2)
  • LoadConfigFromFile (67-90)
  • BuildServerKey (274-280)
cli/pkg/kubectl/base/browser.go (1)
  • OpenBrowser (9-23)
sdk/apis/kubebind/v1alpha1/bindingprovider_types.go (2)
  • BindingProvider (27-52)
  • OAuth2CodeGrant (73-81)
backend/auth/types.go (2)
backend/auth/middleware.go (1)
  • ClientType (37-37)
backend/session/session.go (1)
  • State (29-34)
backend/auth/middleware.go (2)
backend/session/session.go (2)
  • State (29-34)
  • TokenInfo (36-39)
backend/auth/jwt.go (1)
  • JWTService (28-32)
backend/server.go (2)
backend/auth/types.go (2)
  • OIDCServiceProvider (59-67)
  • NewOIDCServiceProvider (69-83)
backend/options/options.go (1)
  • Options (33-40)
contrib/kcp/test/e2e/kcp_test.go (7)
test/e2e/framework/kubeconfig.go (1)
  • ClientConfig (30-38)
test/e2e/framework/kcp.go (3)
  • RandomString (73-78)
  • NewWorkspace (101-162)
  • WithStaticName (66-71)
contrib/kcp/deploy/bootstrap.go (1)
  • Examples (40-40)
test/e2e/framework/apply.go (1)
  • ApplyManifest (72-126)
test/e2e/framework/env.go (1)
  • WorkDir (26-26)
test/e2e/framework/browser.go (1)
  • SimulateBrowser (41-48)
test/e2e/framework/bind.go (1)
  • Login (42-68)
🪛 ast-grep (0.39.7)
cli/pkg/client/client.go

[warning] 80-82: MinVersionis missing from this TLS configuration. By default, TLS 1.2 is currently used as the minimum when acting as a client, and TLS 1.0 when acting as a server. General purpose web applications should default to TLS 1.3 with all other protocols disabled. Only where it is known that a web server must support legacy clients with unsupported an insecure browsers (such as Internet Explorer 10), it may be necessary to enable TLS 1.0 to provide support. AddMinVersion: tls.VersionTLS13' to the TLS configuration to bump the minimum version to TLS 1.3.
Context: tls.Config{
InsecureSkipVerify: true,
}
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
https://owasp.org/Top10/A02_2021-Cryptographic_Failures

(missing-ssl-minversion-go)

cli/pkg/kubectl/bind-login/plugin/login.go

[warning] 121-123: MinVersionis missing from this TLS configuration. By default, TLS 1.2 is currently used as the minimum when acting as a client, and TLS 1.0 when acting as a server. General purpose web applications should default to TLS 1.3 with all other protocols disabled. Only where it is known that a web server must support legacy clients with unsupported an insecure browsers (such as Internet Explorer 10), it may be necessary to enable TLS 1.0 to provide support. AddMinVersion: tls.VersionTLS13' to the TLS configuration to bump the minimum version to TLS 1.3.
Context: tls.Config{
InsecureSkipVerify: true, //nolint:gosec
}
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
https://owasp.org/Top10/A02_2021-Cryptographic_Failures

(missing-ssl-minversion-go)

🪛 GitHub Check: go-test
web/embed.go

[failure] 27-27:
pattern dist/*: no matching files found


[failure] 27-27:
pattern dist/*: no matching files found


[failure] 27-27:
pattern dist/*: no matching files found


[failure] 27-27:
pattern dist/*: no matching files found

🪛 golangci-lint (2.5.0)
backend/http/handler.go

[error] 40-40: could not import github.com/kube-bind/kube-bind/web (web/embed.go:27:12: pattern dist/*: no matching files found)

(typecheck)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: goreleaser
  • GitHub Check: go-test-e2e
🔇 Additional comments (16)
docs/content/setup/.pages (1)

1-6: Documentation navigation reordered.

The navigation entries have been reordered to prioritize the kubectl plugin documentation earlier in the setup flow. This change improves the logical organization of the documentation.

docs/content/setup/kcp-setup.md (2)

92-92: Flag implementation verified.

The --consumer-scope flag is properly implemented in the backend code at backend/options/options.go line 142 as a string variable. The flag accepts "cluster" (for kcp workspaces) and "namespaced" (for Kubernetes) values, matching the documented usage in docs/content/setup/kcp-setup.md line 92. The implementation aligns with test code usage across the codebase.


135-139: File paths verified and confirmed correct.

The referenced YAML files exist at the documented paths in contrib/kcp/deploy/examples/ with the expected resource types:

  • template-cowboys.yaml and template-sheriffs.yaml contain APIServiceExportTemplate resources
  • collection-wildwest.yaml contains a Collection resource

The documentation paths and file references are accurate. The progression of creating templates before a collection follows a logical order appropriate for the resource types involved.

cli/pkg/kubectl/bind/cmd/cmd.go (3)

36-54: LGTM! Clear and comprehensive examples.

The updated examples effectively demonstrate both UI and CLI workflows, covering common use cases for binding templates and opening the kube-bind UI.


60-70: LGTM! Command metadata is clear and well-documented.

The updated Use, Short, and Long fields accurately describe the dual-mode functionality (UI and CLI), providing users with clear guidance on how to use the command.


73-86: LGTM! Argument validation is sound.

The validation logic correctly enforces the 0-or-1 argument constraint and ensures URL arguments have the proper scheme prefix. Error messages are clear and actionable.

cli/pkg/kubectl/bind/plugin/authenticate.go (1)

39-42: Update the comment to reference the v0.6.0 API changes.

The comment on line 39 is outdated and should clarify why the 0.6.0 minimum is required. Version v0.6.0 introduces significant API changes including new Catalog API CRDs (Collection, APIServiceExportTemplate), enhanced permission claims with NamedResources, and provider-side namespace management. Update the comment as follows:

-	// Check if provider is higher than 0.4.8, we need to have same version of kube-bind to use this provider.
+	// Providers must be v0.6.0 or later to support the new Catalog API CRDs, enhanced Permission Claims with NamedResources, and provider-side namespace management.

The documentation reference at docs/content/contributing/guides/publishing-a-new-kube-bind-release.md:85 (PREV_TAG=v0.4.8) is historical context for the v0.5.0 release notes and does not require updating.

sdk/apis/kubebind/v1alpha2/register.go (1)

62-62: LGTM!

The type registration correctly reflects the rename from BindingResponse to BindingResourceResponse, aligning with the API refactoring changes in this PR.

web/embed.go (2)

30-38: LGTM!

The panic on fs.Sub failure is appropriate for this initialization function. If the embedded filesystem doesn't contain the expected dist directory, it indicates a critical build configuration error that should fail fast at startup rather than at runtime.


27-28: Build orchestration is correctly implemented; original concern is incorrect.

The codebase has proper build sequencing in place:

  • CI Workflow (.github/workflows/ci.yaml): The go-test-e2e job explicitly runs make build-web (which executes npm run build) before make test-e2e, ensuring the dist/ directory is populated before Go tests execute.
  • Docker Build: The Dockerfile uses multi-stage builds with the Node stage (ui-build-env) executing npm run build before the Go stage copies the compiled dist/ directory.
  • Repository State: The web/dist/ directory exists and is tracked in the repository (verified via git status).
  • Makefile: Provides explicit build-web target for consistent frontend compilation.

The static analysis warning about unmatched files is a development-time concern that occurs before the frontend is built locally, but does not prevent CI/CD or Docker builds from succeeding.

web/.eslintrc.cjs (1)

14-16: No changes required—component naming is safe.

The verification identified four single-word component names (App, BindingResult, Resources, TemplateBindingModal). None conflict with native HTML elements or reserved custom element names. The vue/multi-word-component-names rule disable is acceptable for this project's naming conventions.

cli/pkg/kubectl/bind-login/cmd/cmd.go (1)

43-79: Command wiring looks solid.

The RunE flow cleanly composes logging, Complete/Validate, and Run on the shared options, so this command will integrate consistently with the other plugin verbs. Nicely done.

contrib/kcp/test/e2e/binding.go (1)

19-68: End-to-end helper reads well.

The helper stitches together BindableResources, binder execution, and CRD polling cleanly, which should keep the happy-path e2e concise.

web/src/components/TemplateBindingModal.vue (1)

1-198: Modal UX and validation look great.

Thanks for enforcing RFC1123-style names, auto-suggesting defaults, and rendering the template details in one place; this should make the binding flow far clearer for users.

backend/auth/handler.go (2)

171-211: Validate redirect_url before redirecting.

authCode.RedirectURL is taken straight from the inbound request and later used (with access_token appended) as the redirect target. There is no allowlist or same-origin check, so an attacker can point it at an arbitrary domain and receive the JWT/session data. Please validate the redirect against a strict allowlist (or require a relative path) before calling url.Parse/http.Redirect.


199-201: Mark the session cookie as Secure.

secure := false causes the auth session cookie to be sent over plain HTTP if any endpoint is reachable that way, exposing credentials. Derive the value from the request (e.g., r.TLS != nil or trusted proto headers) so it is always Secure under HTTPS.

Comment thread backend/auth/jwt.go
Comment thread backend/client/client.go Outdated
Comment thread backend/http/handler.go
Comment thread cli/pkg/client/client.go Outdated
Comment thread cli/pkg/client/client.go Outdated
Comment thread cli/pkg/kubectl/bind-login/plugin/login.go Outdated
Comment thread cli/pkg/kubectl/bind/plugin/bind.go Outdated
Comment thread contrib/kcp/test/e2e/kcp.go
Comment thread test/e2e/bind/happy-case_test.go
Comment thread web/src/views/Resources.vue

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
contrib/kcp/test/e2e/kcp_test.go (1)

21-22: Switch to filepath.Join for filesystem paths.

path.Join is URL-focused and leaves Windows drive letters mixed with forward slashes. Using filepath.Join keeps the generated kubeconfig location valid across OSes that might run these tests. Please also swap the import accordingly.

-	"path"
+	"path/filepath"
-	kubeBindConfig := path.Join(framework.WorkDir, "kube-bind-config-kcp.yaml")
+	kubeBindConfig := filepath.Join(framework.WorkDir, "kube-bind-config-kcp.yaml")

Also applies to: 168-176

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d5d903 and a1c89d7.

📒 Files selected for processing (1)
  • contrib/kcp/test/e2e/kcp_test.go (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-01T15:55:45.035Z
Learnt from: ntnn
Repo: kube-bind/kube-bind PR: 333
File: contrib/kcp/test/e2e/kcp_test.go:270-279
Timestamp: 2025-10-01T15:55:45.035Z
Learning: In KCP, when constructing kubeconfig server URLs for workspaces, the correct pattern is to append ":" + workspace.Base() to an existing server URL that already targets the root workspace, rather than using "/clusters/" + workspace.String(). This colon-separated syntax is KCP's valid format for workspace targeting.

Applied to files:

  • contrib/kcp/test/e2e/kcp_test.go
📚 Learning: 2025-09-19T06:28:44.853Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: pkg/konnector/controllers/cluster/serviceexport/serviceexport_reconcile.go:148-150
Timestamp: 2025-09-19T06:28:44.853Z
Learning: In kube-bind, permission claims must all have the same scope (either all cluster-scoped or all namespace-scoped). Mixed scopes are not allowed, which means the isClusterScoped flag can be safely determined from any processed schema in the export.

Applied to files:

  • contrib/kcp/test/e2e/kcp_test.go
🧬 Code graph analysis (1)
contrib/kcp/test/e2e/kcp_test.go (7)
test/e2e/framework/kubeconfig.go (1)
  • ClientConfig (30-38)
test/e2e/framework/kcp.go (3)
  • RandomString (73-78)
  • NewWorkspace (101-162)
  • WithStaticName (66-71)
contrib/kcp/deploy/bootstrap.go (1)
  • Examples (40-40)
test/e2e/framework/apply.go (1)
  • ApplyManifest (72-126)
test/e2e/framework/env.go (1)
  • WorkDir (26-26)
test/e2e/framework/browser.go (1)
  • SimulateBrowser (41-48)
test/e2e/framework/bind.go (1)
  • Login (42-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: go-test
  • GitHub Check: lint
  • GitHub Check: go-test-e2e
  • GitHub Check: goreleaser

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/http/handler.go (1)

199-210: Derive request scheme from TLS context.

Line 203 hardcodes http:// when constructing the authorize URL, which will fail for HTTPS deployments or those behind TLS-terminating proxies. The scheme should be derived from r.TLS or the X-Forwarded-Proto header.

Apply this diff:

 func (h *handler) handleServiceExport(w http.ResponseWriter, r *http.Request) {
 	logger := klog.FromContext(r.Context()).WithValues("method", r.Method, "url", r.URL.String())
 	params := client.GetQueryParams(r)
 
-	oidcAuthorizeURL := params.WithParams(fmt.Sprintf("http://%s/api/authorize", r.Host))
+	scheme := "http"
+	if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
+		scheme = strings.Split(proto, ",")[0]
+	} else if r.TLS != nil {
+		scheme = "https"
+	}
+	oidcAuthorizeURL := params.WithParams(fmt.Sprintf("%s://%s/api/authorize", scheme, r.Host))
🧹 Nitpick comments (3)
cli/pkg/kubectl/bind-templates/plugin/templates.go (3)

83-86: Add validation logic or document why validation is not required.

The Validate method returns nil without performing any validation. If no validation is needed at this stage, add a comment explaining this. Otherwise, consider adding appropriate validation (e.g., checking if required options are set).


107-116: Consider simplifying the output format check.

The condition on line 109 can be simplified. The print flags typically handle empty string checks internally.

Consider this refactor:

-	if o.Print.OutputFormat != nil && *o.Print.OutputFormat != "" {
+	if o.Print.OutputFormat != nil && *o.Print.OutputFormat != "" && *o.Print.OutputFormat != "wide" {
 		templates.SetGroupVersionKind(kubebindv1alpha2.SchemeGroupVersion.WithKind("APIServiceExportTemplateList"))
 		return o.printer.PrintObj(templates, o.IOStreams.Out)
 	}

Or check with the print flags to confirm that empty string always means table output is expected.


118-145: Consider handling empty templates list and potential write errors.

The table display implementation could be improved:

  1. Consider displaying a user-friendly message when the templates list is empty
  2. While w.Flush() error is properly returned, intermediate fmt.Fprintf calls (lines 122, 141) have their errors silently ignored, which could mask write failures

Consider this enhancement:

 func (o *TemplatesOptions) displayTemplatesTable(templates *kubebindv1alpha2.APIServiceExportTemplateList) error {
+	if len(templates.Items) == 0 {
+		fmt.Fprintln(o.Options.IOStreams.Out, "No templates found")
+		return nil
+	}
+
 	w := tabwriter.NewWriter(o.Options.IOStreams.Out, 0, 0, 3, ' ', 0)
 	fmt.Fprintf(w, "NAME\tRESOURCES\tPERMISSIONCLAIMS\tAGE\n")
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1c89d7 and 6e7b07c.

⛔ Files ignored due to path filters (1)
  • cli/go.sum is excluded by !**/*.sum
📒 Files selected for processing (18)
  • backend/auth/handler.go (1 hunks)
  • backend/auth/types.go (2 hunks)
  • backend/client/client.go (1 hunks)
  • backend/http/handler.go (8 hunks)
  • backend/session/session.go (1 hunks)
  • cli/go.mod (7 hunks)
  • cli/pkg/client/client.go (1 hunks)
  • cli/pkg/config/config.go (1 hunks)
  • cli/pkg/kubectl/base/client.go (1 hunks)
  • cli/pkg/kubectl/base/options.go (3 hunks)
  • cli/pkg/kubectl/bind-apiservice/plugin/bind.go (5 hunks)
  • cli/pkg/kubectl/bind-collections/plugin/collections.go (1 hunks)
  • cli/pkg/kubectl/bind-login/plugin/login.go (1 hunks)
  • cli/pkg/kubectl/bind-templates/plugin/templates.go (1 hunks)
  • cli/pkg/kubectl/bind/plugin/bind.go (5 hunks)
  • contrib/kcp/test/e2e/binding.go (1 hunks)
  • contrib/kcp/test/e2e/kcp_test.go (3 hunks)
  • test/e2e/framework/bind.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/client/client.go
  • cli/pkg/kubectl/bind-login/plugin/login.go
🧰 Additional context used
🧠 Learnings (10)
📚 Learning: 2025-09-19T05:56:35.969Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: backend/controllers/servicenamespace/servicenamespace_reconcile.go:81-98
Timestamp: 2025-09-19T05:56:35.969Z
Learning: In kube-bind, RBAC permissions for PermissionClaims use "*" verbs intentionally. This is a design decision based on: 1) permissions are scoped to consumer-owned provider namespaces, limiting blast radius, 2) bidirectional resource flow requires broad permissions for operations like initial resource creation from consumer side, 3) kube-bind's architecture prioritizes operational simplicity over granular RBAC within the namespace security boundary.

Applied to files:

  • backend/auth/types.go
  • backend/http/handler.go
📚 Learning: 2025-09-19T06:28:44.853Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: pkg/konnector/controllers/cluster/serviceexport/serviceexport_reconcile.go:148-150
Timestamp: 2025-09-19T06:28:44.853Z
Learning: In kube-bind, permission claims must all have the same scope (either all cluster-scoped or all namespace-scoped). Mixed scopes are not allowed, which means the isClusterScoped flag can be safely determined from any processed schema in the export.

Applied to files:

  • backend/auth/types.go
  • contrib/kcp/test/e2e/kcp_test.go
  • backend/http/handler.go
📚 Learning: 2025-10-22T06:41:37.475Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 342
File: docs/content/usage/migration.md:34-37
Timestamp: 2025-10-22T06:41:37.475Z
Learning: In the kube-bind repository documentation and examples, "mangodb" (with group "mangodb.com" and resource "mangodbs") is an intentional fictional example name chosen to avoid referencing real products like MongoDB. It should not be treated as a typo.

Applied to files:

  • backend/auth/types.go
📚 Learning: 2025-09-12T09:05:29.762Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 295
File: sdk/client/listers/kubebind/v1alpha2/boundschema.go:46-48
Timestamp: 2025-09-12T09:05:29.762Z
Learning: In the kube-bind project, lister-gen is generating BoundSchema listers with singular resource names ("boundschema") instead of plural ("boundschemas"), which breaks client-go conventions and can cause cache lookup issues. This is identified as a generator issue that needs upstream investigation rather than manual code fixes.

Applied to files:

  • backend/auth/types.go
  • backend/http/handler.go
📚 Learning: 2025-09-22T13:20:49.952Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: sdk/apis/kubebind/v1alpha2/boundchema_types.go:49-0
Timestamp: 2025-09-22T13:20:49.952Z
Learning: In kube-bind, BoundSchema.ResourceGroupName() is only used for CRDs (Custom Resource Definitions), and CRDs must always have non-empty API groups. Therefore, handling empty groups (core API group "") is not necessary in this context, unlike general Kubernetes GroupResource handling.

Applied to files:

  • backend/auth/types.go
  • backend/http/handler.go
📚 Learning: 2025-09-12T08:55:41.860Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 295
File: sdk/apis/kubebind/v1alpha2/helpers/boundschema.go:115-123
Timestamp: 2025-09-12T08:55:41.860Z
Learning: In BoundSchemasSpecHash function in sdk/apis/kubebind/v1alpha2/helpers/boundschema.go, silent error handling during JSON encoding (continuing on encoding errors) is acceptable to mjudeikis for the current implementation, even though it could potentially lead to incorrect hash values.

Applied to files:

  • backend/auth/types.go
  • backend/http/handler.go
📚 Learning: 2025-10-01T15:55:45.035Z
Learnt from: ntnn
Repo: kube-bind/kube-bind PR: 333
File: contrib/kcp/test/e2e/kcp_test.go:270-279
Timestamp: 2025-10-01T15:55:45.035Z
Learning: In KCP, when constructing kubeconfig server URLs for workspaces, the correct pattern is to append ":" + workspace.Base() to an existing server URL that already targets the root workspace, rather than using "/clusters/" + workspace.String(). This colon-separated syntax is KCP's valid format for workspace targeting.

Applied to files:

  • contrib/kcp/test/e2e/kcp_test.go
📚 Learning: 2025-09-12T08:40:15.290Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 295
File: backend/controllers/serviceexportrequest/serviceexportrequest_reconcile.go:140-143
Timestamp: 2025-09-12T08:40:15.290Z
Learning: APIServiceExportRequest resources in kube-bind are short-lived and automatically deleted after 10 minutes, so they should not be used as owner references for longer-lived resources like BoundSchema. The proper lifecycle management for BoundSchema resources created by APIServiceExportRequest is tracked in issue #297.

Applied to files:

  • backend/http/handler.go
📚 Learning: 2025-09-22T13:32:29.499Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: pkg/konnector/controllers/cluster/claimedresources/claimedresources_controller.go:255-263
Timestamp: 2025-09-22T13:32:29.499Z
Learning: In kube-bind's claimedresources controller (pkg/konnector/controllers/cluster/claimedresources/claimedresources_controller.go), the controller does not run if apiServiceExport is not set. This means nil-checks for c.apiServiceExport are unnecessary since the controller lifecycle ensures it's always non-nil when active.

Applied to files:

  • backend/http/handler.go
  • cli/pkg/kubectl/bind-apiservice/plugin/bind.go
📚 Learning: 2025-09-22T13:20:49.952Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: sdk/apis/kubebind/v1alpha2/boundchema_types.go:49-0
Timestamp: 2025-09-22T13:20:49.952Z
Learning: In kube-bind, there are two different ResourceGroupName() methods: BoundSchema.ResourceGroupName() for CRDs (always non-empty groups) uses simple fmt.Sprintf formatting, while APIServiceExportRequestResource.ResourceGroupName() for export requests handles empty groups by converting to "core". BoundSchema is exclusively for CRDs which cannot have empty API groups per Kubernetes validation.

Applied to files:

  • backend/http/handler.go
🧬 Code graph analysis (13)
backend/auth/types.go (2)
backend/auth/middleware.go (1)
  • ClientType (37-37)
backend/session/session.go (1)
  • State (29-34)
cli/pkg/kubectl/bind-collections/plugin/collections.go (2)
cli/pkg/kubectl/base/options.go (2)
  • Options (34-59)
  • NewOptions (62-67)
sdk/apis/kubebind/v1alpha2/collection_types.go (1)
  • CollectionList (89-94)
cli/pkg/kubectl/bind-templates/plugin/templates.go (2)
cli/pkg/kubectl/base/options.go (2)
  • Options (34-59)
  • NewOptions (62-67)
sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1)
  • APIServiceExportTemplateList (98-103)
backend/auth/handler.go (6)
backend/auth/types.go (3)
  • OIDCServiceProvider (61-69)
  • AuthorizeRequest (46-52)
  • AuthResponse (27-33)
backend/auth/jwt.go (1)
  • JWTService (28-32)
backend/client/client.go (2)
  • GetQueryParams (20-30)
  • ClientParameters (8-17)
backend/auth/middleware.go (2)
  • ClientType (37-37)
  • ClientTypeCLI (41-41)
backend/session/cookie.go (1)
  • MakeCookie (24-35)
backend/session/session.go (2)
  • State (29-34)
  • TokenInfo (36-39)
cli/pkg/kubectl/bind/plugin/bind.go (4)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
cli/pkg/kubectl/base/options.go (1)
  • Options (34-59)
cli/pkg/kubectl/base/browser.go (1)
  • OpenBrowser (9-23)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
contrib/kcp/test/e2e/binding.go (4)
test/e2e/framework/bind.go (1)
  • GetKubeBindRestClient (32-46)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • APIServiceExportTemplateRef (92-98)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
test/e2e/framework/clients.go (1)
  • ApiextensionsClient (45-49)
contrib/kcp/test/e2e/kcp_test.go (9)
test/e2e/framework/dex.go (1)
  • StartDex (53-93)
test/e2e/framework/kubeconfig.go (1)
  • ClientConfig (30-38)
test/e2e/framework/kcp.go (3)
  • RandomString (73-78)
  • NewWorkspace (101-162)
  • WithStaticName (66-71)
test/e2e/framework/konnector.go (1)
  • StartKonnector (35-75)
contrib/kcp/deploy/bootstrap.go (1)
  • Examples (40-40)
test/e2e/framework/apply.go (1)
  • ApplyManifest (72-126)
test/e2e/framework/env.go (1)
  • WorkDir (26-26)
test/e2e/framework/browser.go (1)
  • SimulateBrowser (41-48)
test/e2e/framework/bind.go (1)
  • Login (48-74)
backend/http/handler.go (10)
backend/auth/types.go (1)
  • OIDCServiceProvider (61-69)
backend/auth/handler.go (2)
  • AuthHandler (37-42)
  • NewAuthHandler (44-51)
backend/auth/middleware.go (4)
  • AuthMiddleware (50-54)
  • NewAuthMiddleware (56-62)
  • RequireAuth (127-145)
  • GetAuthContext (120-125)
backend/kubernetes/manager.go (1)
  • Manager (39-49)
backend/auth/jwt.go (1)
  • NewJWTService (43-54)
backend/spaserver/spaserver.go (2)
  • NewSPAReverseProxyServer (103-112)
  • NewSPAFileServer (33-38)
web/embed.go (1)
  • GetFileSystem (31-38)
backend/client/client.go (1)
  • GetQueryParams (20-30)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
sdk/apis/kubebind/v1alpha2/claimable_apis.go (1)
  • ClaimableAPIs (38-81)
cli/pkg/kubectl/base/options.go (1)
cli/pkg/config/config.go (5)
  • Server (40-47)
  • Config (29-37)
  • GetDefaultConfigFilePath (64-70)
  • NewConfig (50-61)
  • LoadConfigFromFile (78-101)
cli/pkg/kubectl/bind-apiservice/plugin/bind.go (5)
cli/pkg/kubectl/base/options.go (1)
  • Options (34-59)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
cli/pkg/kubectl/base/kubeconfig.go (3)
  • ParseRemoteKubeconfig (32-46)
  • FindRemoteKubeconfig (48-79)
  • EnsureKubeconfigSecret (86-139)
cli/pkg/client/client.go (1)
  • Client (36-44)
cli/pkg/kubectl/base/client.go (2)
cli/pkg/kubectl/base/options.go (1)
  • Options (34-59)
cli/pkg/client/client.go (3)
  • Client (36-44)
  • NewClient (65-87)
  • WithInsecure (58-62)
cli/pkg/client/client.go (4)
sdk/apis/kubebind/v1alpha2/apiserviceexporttemplate_types.go (1)
  • APIServiceExportTemplateList (98-103)
sdk/apis/kubebind/v1alpha2/collection_types.go (1)
  • CollectionList (89-94)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
cli/pkg/config/config.go (2)
  • Server (40-47)
  • Config (29-37)
test/e2e/framework/bind.go (3)
cli/pkg/client/client.go (3)
  • Client (36-44)
  • NewClient (65-87)
  • WithInsecure (58-62)
cli/pkg/config/config.go (1)
  • LoadConfigFromFile (78-101)
cli/pkg/kubectl/bind-login/plugin/login.go (1)
  • NewLoginOptions (77-85)
🪛 ast-grep (0.39.7)
cli/pkg/client/client.go

[warning] 79-81: MinVersionis missing from this TLS configuration. By default, TLS 1.2 is currently used as the minimum when acting as a client, and TLS 1.0 when acting as a server. General purpose web applications should default to TLS 1.3 with all other protocols disabled. Only where it is known that a web server must support legacy clients with unsupported an insecure browsers (such as Internet Explorer 10), it may be necessary to enable TLS 1.0 to provide support. AddMinVersion: tls.VersionTLS13' to the TLS configuration to bump the minimum version to TLS 1.3.
Context: tls.Config{
InsecureSkipVerify: true,
}
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
https://owasp.org/Top10/A02_2021-Cryptographic_Failures

(missing-ssl-minversion-go)

🪛 golangci-lint (2.5.0)
backend/http/handler.go

[error] 40-40: could not import github.com/kube-bind/kube-bind/web (web/embed.go:27:12: pattern dist/*: no matching files found)

(typecheck)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: goreleaser
  • GitHub Check: lint
  • GitHub Check: go-test
  • GitHub Check: go-test-e2e
🔇 Additional comments (29)
cli/go.mod (1)

1-138: Clarification: Dependencies are in root go.mod, not cli/go.mod.

The AI summary's claims about adding JWT and web dependencies are accurate but apply to the root go.mod, not the cli/go.mod file under review. The root go.mod contains:

  • JWT dependency: github.com/golang-jwt/jwt/v5 v5.3.0
  • Web module replace directive: github.com/kube-bind/kube-bind/web => ./web
  • Web module require: github.com/kube-bind/kube-bind/web v0.0.0-...

The cli/go.mod does not include these as direct requirements because it inherits them transitively through its direct requirement on the root module (github.com/kube-bind/kube-bind). The marked changes in cli/go.mod are version updates to indirect dependencies, consistent with a go mod tidy operation.

This structure is correct if the CLI package does not directly use JWT or web functionality.

cli/pkg/kubectl/bind-collections/plugin/collections.go (3)

37-60: LGTM!

The struct definition and initialization follow standard kubectl plugin patterns. The use of empty default output appropriately triggers table format for the default case.


62-85: LGTM!

The lifecycle methods properly handle argument processing, option completion, and validation with appropriate error handling.


87-102: LGTM!

The execution flow is clear with proper error handling and appropriate error wrapping for debugging context.

cli/pkg/kubectl/bind-templates/plugin/templates.go (2)

53-67: LGTM!

The constructor and flag binding logic follow standard kubectl plugin patterns and properly initialize all required fields.


88-104: Based on the verification, the code in cli/pkg/kubectl/bind-templates/plugin/templates.go lines 88-104 follows the standard Go error handling pattern correctly.

If an error occurs, the error will be set with implementation of the error interface, otherwise it will be set to nil. The implementation of GetTemplates() confirms this pattern: it returns nil pointer only when an error is non-nil, and returns a valid pointer when error is nil.

The Run method's error checks with early returns provide adequate nil safety:

  1. If GetAuthenticatedClient() fails, the method returns before using client
  2. If GetTemplates() fails, the method returns before calling displayTemplates(templates)

This is the idiomatic Go approach and aligns with standard library practices.

Likely an incorrect or invalid review comment.

cli/pkg/kubectl/bind-apiservice/plugin/bind.go (8)

26-27: LGTM!

The import additions support the new template-based binding flow and namespace management functionality.

Also applies to: 31-31, 39-39


100-112: LGTM!

The printer initialization is correct and error handling is appropriate.


116-132: LGTM!

The validation logic correctly handles the new template-based binding flow and maintains appropriate constraints for remote kubeconfig parameters.


138-171: LGTM!

The refactored Run method provides clear separation of concerns between obtaining the binding response and processing it through the Binder. The error handling and flow are appropriate.


173-225: LGTM!

The method correctly handles multiple kubeconfig sources with appropriate fallthrough behavior when explicit namespace/name flags are provided.


227-249: LGTM!

The namespace creation logic is straightforward with appropriate error handling and user feedback.


313-325: LGTM!

The function correctly constructs a binding request with a template reference and delegates to the client.


279-281: Code is correct; no changes required.

Verification confirms that OAuth2CodeGrant is the only supported authentication method in the binding flow. The BindingResponseAuthentication struct contains only a single field—OAuth2CodeGrant—and the AuthenticationMethod.Method enum is restricted to "OAuth2CodeGrant" only. The nil check at lines 279-281 appropriately handles this single supported method.

contrib/kcp/test/e2e/binding.go (1)

19-69: LGTM!

The test helper function is well-structured. It properly sequences the binding workflow: creating the REST client, sending the binding request, using the binder to process the response, and polling for CRD creation. The use of testify/require for assertions and wait.Eventually for polling is appropriate for test code.

backend/auth/types.go (1)

17-95: LGTM!

The authentication types are well-defined with appropriate JSON/form bindings. The use of time.Time for ExpiresAt provides proper type safety for token expiration handling, and the struct definitions align with the authentication flows implemented in the handler and middleware.

test/e2e/framework/bind.go (2)

32-46: LGTM!

The helper function correctly loads configuration, retrieves the current server, creates an authenticated client with insecure mode enabled, and provides proper error handling with descriptive assertions.


48-74: LGTM!

The Login helper appropriately constructs CLI flags, parses them, and executes the login flow with proper context and error handling. The conditional flag construction for cluster and config file is correct.

backend/auth/handler.go (2)

53-92: LGTM!

The HandleAuthorize method correctly handles both POST (JSON) and GET (query params) requests, validates required parameters, encodes the authorization request state, and redirects to the OIDC provider.


214-277: LGTM!

The helper methods provide appropriate functionality: respondWithError differentiates between CLI (JSON) and UI (plain text) responses, generateCookieName namespaces cookies by cluster ID, createSessionState extracts token information from OIDC responses, and unwrapJWT correctly parses JWT structure.

backend/http/handler.go (5)

71-108: LGTM!

The NewHandler constructor correctly initializes JWT service, auth handler, and auth middleware with appropriate error handling. The wiring of authentication components is clean and follows the expected initialization pattern.


110-145: LGTM!

The AddRoutes method properly organizes routes into public API endpoints, authentication endpoints, and protected API routes with authentication middleware. The SPA serving logic correctly handles both development proxy mode and production static file serving, and returning an error instead of panicking on SPA server creation is the correct approach.


242-285: LGTM!

Both handleTemplates and handleCollections correctly retrieve query parameters, list resources from the backend, and encode JSON responses with appropriate error handling.


287-374: LGTM!

The handleBind method correctly retrieves authentication context from middleware, handles resources, enforces request body size limits (preventing DoS), parses and validates the bind request, retrieves the template, constructs the response, and returns JSON. The request body size limit and proper error handling for MaxBytesError address previous concerns.


376-421: LGTM!

The helper functions (listTemplates, listCollections, handleBindableResources, prepareNoCache, getLogger) are straightforward and provide appropriate functionality with proper error handling.

cli/pkg/client/client.go (4)

89-127: LGTM!

The GetTemplates and GetCollections methods correctly construct endpoint URLs, perform authenticated GET requests, and decode JSON responses with proper error handling and resource cleanup.


129-152: LGTM!

The Bind method correctly marshals the request, performs an authenticated POST, and decodes the response with appropriate error wrapping and resource cleanup.


154-190: LGTM!

The Get, Post, and Do methods correctly construct HTTP requests, add authentication headers, set appropriate content types, and delegate to the underlying HTTP client.


192-255: LGTM!

The helper methods provide appropriate functionality: addAuthHeaders validates token presence and expiration with helpful error messages, isTokenValid checks token validity with a 5-minute buffer, buildEndpointURL constructs proper API URLs with cluster context, and getAuthorizationHeader formats the authorization header correctly.

Comment thread backend/auth/handler.go
Comment thread backend/session/session.go
Comment thread cli/pkg/client/client.go
Comment thread cli/pkg/config/config.go
Comment thread cli/pkg/kubectl/base/client.go
Comment thread cli/pkg/kubectl/bind-apiservice/plugin/bind.go
Comment thread cli/pkg/kubectl/bind-apiservice/plugin/bind.go Outdated
Comment thread cli/pkg/kubectl/bind-collections/plugin/collections.go
Comment thread cli/pkg/kubectl/bind-templates/plugin/templates.go
Comment thread cli/pkg/kubectl/bind/plugin/bind.go
@mjudeikis mjudeikis force-pushed the mjudeikis/api.refactor branch from 0a414a1 to fa967cf Compare November 4, 2025 10:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/content/setup/quickstart.md (1)

42-49: Update dex configuration callback URL to match backend changes.

The dex config example at line 46 specifies http://127.0.0.1:8080/callback, but the backend is configured at line 89 with http://127.0.0.1:8080/api/callback. These must match for OIDC authentication to function.

Apply this diff to align the dex configuration with the backend callback URL:

  staticClients:
  - id: kube-bind
    redirectURIs:
-   - 'http://127.0.0.1:8080/callback'
+   - 'http://127.0.0.1:8080/api/callback'
    name: 'Kube Bind'
    secret: ZXhhbXBsZS1hcHAtc2VjcmV0
♻️ Duplicate comments (5)
cli/pkg/kubectl/base/options.go (1)

128-133: Fix parsing of --server values containing @.

This issue was previously flagged but remains unresolved. The stored config format is cluster@serverURL, but this code treats the input as server@cluster, causing incorrect lookups when users provide the stored format. The previous review suggested detecting which part is the URL to handle both orders correctly.

Apply the fix suggested in the previous review:

-	case o.Server != "" && strings.Contains(o.Server, "@"):
-		// Server specified in server@cluster format
-		parts := strings.SplitN(o.Server, "@", 2)
-		o.Server = parts[0]
-		o.Cluster = parts[1]
+	case o.Server != "" && strings.Contains(o.Server, "@"):
+		parts := strings.SplitN(o.Server, "@", 2)
+		// Config keys are stored as cluster@server. If the second segment looks
+		// like a URL, treat the input as that stored format; otherwise fall back
+		// to server@cluster for backward compatibility.
+		if strings.Contains(parts[1], "://") {
+			o.Cluster = parts[0]
+			o.Server = parts[1]
+		} else {
+			o.Server = parts[0]
+			o.Cluster = parts[1]
+		}
cli/pkg/kubectl/bind-login/plugin/login.go (1)

245-247: Preserve base path when appending /api/exports (previously flagged).

This issue was identified in a previous review but remains unaddressed. Overwriting url.Path with /api/exports drops any existing prefix (e.g., /backend), breaking login for installations served from a subpath.

Apply this diff to preserve the original base path:

+import "path"
+
 func (o *LoginOptions) getProvider(ctx context.Context) (*kubebindv1alpha2.BindingProvider, error) {
     url, err := url.Parse(o.Options.Server)
     // ... existing code ...
 
     if !strings.Contains(url.Path, "/api/exports") {
-        url.Path = "/api/exports"
+        basePath := strings.TrimSuffix(url.Path, "/")
+        url.Path = path.Join(basePath, "/api/exports")
     }
cli/pkg/kubectl/bind/plugin/bind.go (1)

282-288: Fix callback listener security issue and race condition.

This issue was previously flagged but remains unresolved. After selecting a loopback port with net.Listen("127.0.0.1:0") at Line 282, the code closes that listener at Line 288 and restarts the server at Line 356 with Addr: fmt.Sprintf(":%d", port), which rebinds on all network interfaces. This exposes the callback endpoint to the network and introduces a race where another process can steal the port between Lines 288 and 361.

Keep the original loopback listener open and serve it directly with server.Serve(listener) to maintain local-only binding and eliminate the race condition.

Apply this diff:

 	listener, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
 		return nil, 0, fmt.Errorf("failed to find available port: %w", err)
 	}
 
 	port := listener.Addr().(*net.TCPAddr).Port
-	listener.Close()
 
 	// Setup HTTP handler
 	mux := http.NewServeMux()
 	mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
@@
 	})
 
 	server := &http.Server{
 		ReadTimeout: time.Minute * 5,
-		Addr:        fmt.Sprintf(":%d", port),
 		Handler:     mux,
 	}
 
 	go func() {
-		if err := server.ListenAndServe(); err != http.ErrServerClosed {
+		if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
 			select {
 			case errCh <- err:
 			default:
 			}
 		}
 	}()

Also applies to: 354-367

cli/pkg/kubectl/bind-apiservice/plugin/bind.go (2)

64-64: DryRun flag is not wired to the implementation.

The DryRun field is exposed via the CLI flag, but it is never passed to BinderOptions during binder initialization (lines 158-167). The BinderOptions struct includes a DryRun field, but it remains unset. Users who specify --dry-run will see no effect.

Apply this diff to wire the flag:

 binderOpts := &BinderOptions{
 	IOStreams:                 b.Options.IOStreams,
 	SkipKonnector:             b.SkipKonnector,
 	KonnectorImageOverride:    b.KonnectorImageOverride,
 	DowngradeKonnector:        b.DowngradeKonnector,
 	RemoteKubeconfigFile:      b.remoteKubeconfigFile,
 	RemoteKubeconfigNamespace: b.remoteKubeconfigNamespace,
 	RemoteKubeconfigName:      b.remoteKubeconfigName,
 	RemoteNamespace:           b.remoteNamespace,
+	DryRun:                    b.DryRun,
 }

Additionally, ensure the Binder implementation respects the DryRun option by checking it before performing mutations and logging planned actions instead.

Also applies to: 93-93, 158-167


87-87: Fix the default value reference in the flag help text.

The flag is bound to b.remoteKubeconfigName but incorrectly references b.remoteKubeconfigNamespace as the default value.

Apply this diff:

-	cmd.Flags().StringVar(&b.remoteKubeconfigName, "remote-kubeconfig-name", b.remoteKubeconfigNamespace, "The name of the remote kubeconfig secret to read from")
+	cmd.Flags().StringVar(&b.remoteKubeconfigName, "remote-kubeconfig-name", b.remoteKubeconfigName, "The name of the remote kubeconfig secret to read from")
🧹 Nitpick comments (5)
docs/content/setup/kcp-setup.md (1)

92-92: Document the purpose of --consumer-scope=cluster.

The new --consumer-scope=cluster flag is added to the backend startup command but lacks explanation. Clarify what this flag controls and why it is necessary for this setup.

cli/pkg/kubectl/base/options.go (2)

57-57: Consider clarifying the comment.

The comment states "Server is the loaded kube-bind server" but the field is named server (lowercase). Consider updating to "server is the loaded kube-bind server" for consistency.


200-202: Consider documenting preconditions.

The GetConfig() method returns o.config, which is only populated after Complete() executes successfully. Consider adding a comment noting that this method should only be called after Complete() to avoid returning nil.

cli/pkg/kubectl/bind-login/plugin/login.go (1)

338-357: Consider extracting HTML template.

The inline HTML string is verbose and reduces readability. Consider extracting it to a constant or using a template.

Example refactor:

const callbackHTMLTemplate = `<!DOCTYPE html>
<html>
<head>
    <title>Kube-Bind Authentication</title>
    <style>
        body { font-family: Arial, sans-serif; text-align: center; margin-top: 100px; }
        .success { color: green; }
        .error { color: red; }
    </style>
</head>
<body>
    <h1>Kube-Bind Authentication</h1>
    <div class="%s">
        <h2>%s</h2>
        <p>You can now close this window and return to the CLI.</p>
    </div>
</body>
</html>`

// In handler:
w.Header().Set("Content-Type", "text/html")
statusClass := "success"
message := "Authentication Successful!"
if token.Error != "" {
    statusClass = "error"
    message = "Authentication Failed"
}
fmt.Fprintf(w, callbackHTMLTemplate, statusClass, message)
cli/pkg/kubectl/bind/plugin/bind.go (1)

142-145: Consider removing redundant client config retrieval.

The client config retrieved at Line 142 is not used until Line 229, where it is retrieved again. Since this early retrieval serves no immediate purpose, consider removing Lines 142-145 to simplify the code.

Apply this diff:

 func (b *BindOptions) runWithCallback(ctx context.Context, _ chan<- string) error {
-	_, err := b.Options.ClientConfig.ClientConfig()
-	if err != nil {
-		return fmt.Errorf("failed to get client config: %w", err)
-	}
-
 	// Generate session ID. It is used to verify callback.
-	sessionID, err := generateRandomString(32)
+	sessionID, err := generateRandomString(32)
 	if err != nil {
 		return fmt.Errorf("failed to generate session ID: %w", err)
 	}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e7b07c and 0c6c234.

⛔ Files ignored due to path filters (7)
  • cli/go.sum is excluded by !**/*.sum
  • contrib/kcp/go.sum is excluded by !**/*.sum
  • docs/generators/cli-doc/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • web/dist/assets/index.33c2e7b8.css is excluded by !**/dist/**
  • web/dist/assets/index.4caa6f6c.js is excluded by !**/dist/**
  • web/dist/index.html is excluded by !**/dist/**
📒 Files selected for processing (11)
  • .gitignore (1 hunks)
  • cli/go.mod (1 hunks)
  • cli/pkg/kubectl/base/options.go (3 hunks)
  • cli/pkg/kubectl/bind-apiservice/plugin/bind.go (5 hunks)
  • cli/pkg/kubectl/bind-login/plugin/login.go (1 hunks)
  • cli/pkg/kubectl/bind/plugin/bind.go (3 hunks)
  • contrib/kcp/go.mod (6 hunks)
  • docs/content/setup/kcp-setup.md (3 hunks)
  • docs/content/setup/quickstart.md (2 hunks)
  • docs/generators/cli-doc/go.mod (0 hunks)
  • go.mod (3 hunks)
💤 Files with no reviewable changes (1)
  • docs/generators/cli-doc/go.mod
🚧 Files skipped from review as they are similar to previous changes (2)
  • cli/go.mod
  • .gitignore
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-12T08:40:15.290Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 295
File: backend/controllers/serviceexportrequest/serviceexportrequest_reconcile.go:140-143
Timestamp: 2025-09-12T08:40:15.290Z
Learning: APIServiceExportRequest resources in kube-bind are short-lived and automatically deleted after 10 minutes, so they should not be used as owner references for longer-lived resources like BoundSchema. The proper lifecycle management for BoundSchema resources created by APIServiceExportRequest is tracked in issue #297.

Applied to files:

  • docs/content/setup/quickstart.md
📚 Learning: 2025-10-01T15:55:45.035Z
Learnt from: ntnn
Repo: kube-bind/kube-bind PR: 333
File: contrib/kcp/test/e2e/kcp_test.go:270-279
Timestamp: 2025-10-01T15:55:45.035Z
Learning: In KCP, when constructing kubeconfig server URLs for workspaces, the correct pattern is to append ":" + workspace.Base() to an existing server URL that already targets the root workspace, rather than using "/clusters/" + workspace.String(). This colon-separated syntax is KCP's valid format for workspace targeting.

Applied to files:

  • docs/content/setup/kcp-setup.md
📚 Learning: 2025-09-22T13:32:29.499Z
Learnt from: mjudeikis
Repo: kube-bind/kube-bind PR: 304
File: pkg/konnector/controllers/cluster/claimedresources/claimedresources_controller.go:255-263
Timestamp: 2025-09-22T13:32:29.499Z
Learning: In kube-bind's claimedresources controller (pkg/konnector/controllers/cluster/claimedresources/claimedresources_controller.go), the controller does not run if apiServiceExport is not set. This means nil-checks for c.apiServiceExport are unnecessary since the controller lifecycle ensures it's always non-nil when active.

Applied to files:

  • cli/pkg/kubectl/bind-apiservice/plugin/bind.go
🧬 Code graph analysis (4)
cli/pkg/kubectl/base/options.go (1)
cli/pkg/config/config.go (5)
  • Server (40-47)
  • Config (29-37)
  • GetDefaultConfigFilePath (64-70)
  • NewConfig (50-61)
  • LoadConfigFromFile (78-101)
cli/pkg/kubectl/bind/plugin/bind.go (4)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • BindingResourceResponse (37-58)
cli/pkg/kubectl/base/options.go (1)
  • Options (34-59)
cli/pkg/kubectl/base/browser.go (1)
  • OpenBrowser (9-23)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
cli/pkg/kubectl/bind-login/plugin/login.go (5)
cli/pkg/kubectl/base/options.go (2)
  • Options (34-59)
  • NewOptions (62-67)
cli/pkg/config/config.go (1)
  • Server (40-47)
backend/auth/types.go (1)
  • TokenResponse (35-44)
cli/pkg/kubectl/base/browser.go (1)
  • OpenBrowser (9-23)
sdk/apis/kubebind/v1alpha1/bindingprovider_types.go (2)
  • BindingProvider (27-52)
  • OAuth2CodeGrant (73-81)
cli/pkg/kubectl/bind-apiservice/plugin/bind.go (3)
cli/pkg/kubectl/base/options.go (1)
  • Options (34-59)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
cli/pkg/kubectl/base/kubeconfig.go (3)
  • ParseRemoteKubeconfig (32-46)
  • FindRemoteKubeconfig (48-79)
  • EnsureKubeconfigSecret (86-139)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: verify
  • GitHub Check: goreleaser
  • GitHub Check: lint
  • GitHub Check: go-test
  • GitHub Check: go-test-e2e
🔇 Additional comments (13)
contrib/kcp/go.mod (3)

23-23: Verify the necessity of direct require on cli module.

The cli module is added to the require block with a pseudo-version v0.0.0-20250515145715-d9f20e7c840d, but a replace directive already exists at line 7 that overrides this version (github.com/kube-bind/kube-bind/cli => ../../cli). Please confirm that this direct require is necessary or if it should be removed from the require block, allowing the replace directive alone to manage the dependency.


9-9: Web module integration is appropriately configured.

The new replace directive for the web module aligns with the PR's API-first backend and dedicated frontend objective. The pseudo-version v0.0.0-00010101000000-000000000000 is the standard marker for local module replacements and is correct.


66-66: JWT dependency addition is appropriate.

The inclusion of github.com/golang-jwt/jwt/v5 v5.3.0 as an indirect dependency aligns with the authentication flow refactoring to support OIDC + JWT mentioned in the PR summary.

go.mod (4)

27-27: Confirm web module is directly imported in root backend code.

The PR adds github.com/kube-bind/kube-bind/web as a direct require with replace directive github.com/kube-bind/kube-bind/web => ./web. Please verify that the root backend package directly imports and uses types, functions, or assets from the web module. If the web module is only consumed transitively (e.g., only by CLI or other packages), it should be moved to an indirect dependency to keep the require block clean.


9-9: Web module integration configuration is correct.

The replace directive for the web module points to the local ./web directory, which aligns with the monorepo structure and enables asset embedding into the Go binary as described in the PR objectives.


16-16: JWT dependency addition aligns with authentication refactoring.

The addition of github.com/golang-jwt/jwt/v5 v5.3.0 supports the PR's backend authentication rework for OIDC + JWT flows serving both UI (cookie-based) and CLI (token-based) authentication paths.


56-57: Dependency version updates are consistent across modules.

The updates to github.com/PuerkitoBio/goquery (v1.10.3) and github.com/andybalholm/cascadia (v1.3.3) are aligned between the root go.mod and contrib/kcp/go.mod, maintaining consistency across the dependency graph.

docs/content/setup/kcp-setup.md (2)

137-139: All referenced example files are present in the repository.

The verification confirms that all three example files referenced in the documentation exist and are properly checked in:

  • contrib/kcp/deploy/examples/template-cowboys.yaml
  • contrib/kcp/deploy/examples/template-sheriffs.yaml
  • contrib/kcp/deploy/examples/collection-wildwest.yaml

No further action is required.


165-169: Update stale comment and clarify binding workflow and placeholder value.

The comment on line 165 describes the old flow but the code implements a login-based workflow. Additionally, the --skip-konnector flag on line 169 is used without explanation, and the <logical-cluster-id> placeholder lacks clarification regarding whether to use the NAME column value (e.g., "cluster"), the full URL from the URL column, or the cluster ID extracted from the URL path (e.g., "2xh2v3gzjhn4tmve").

Update the comment and add clarification:

-Generate the APIServiceExport YAML:
+Perform login-based binding:

Add inline documentation explaining that --skip-konnector defers konnector startup (see section "3. Start Konnector" below), and specify that <logical-cluster-id> should be replaced with the cluster ID from the URL column (the alphanumeric path segment following /clusters/).

docs/content/setup/quickstart.md (1)

118-121: Verify endpoint change accuracy and impact on existing users.

Unable to locate the kubectl-bind CLI implementation in the codebase to verify whether the command at line 121 is correct. While the enriched summary indicates this reflects a "shift to a more general API surface," the documentation change from http://127.0.0.1:8080/exports to http://127.0.0.1:8080 removes a path segment without explanation.

Manually verify:

  1. Whether the current CLI implementation accepts the base URL without /exports path segment
  2. Whether this is a breaking change requiring user migration documentation
  3. Whether a note should explain the endpoint structure change to users upgrading from previous versions
cli/pkg/kubectl/base/options.go (2)

89-93: LGTM!

The new flag bindings are clear and correctly wired to the struct fields.


108-123: LGTM!

The config file initialization logic correctly creates the directory structure with appropriate permissions (0700) and initializes an empty config file when needed.

cli/pkg/kubectl/bind-login/plugin/login.go (1)

383-389: LGTM!

Proper use of crypto/rand for security-sensitive random generation. The implementation is correct.

Comment thread cli/pkg/kubectl/base/options.go
Comment thread cli/pkg/kubectl/base/options.go Outdated
Comment thread cli/pkg/kubectl/bind-apiservice/plugin/bind.go
Comment thread cli/pkg/kubectl/bind-apiservice/plugin/bind.go
Comment thread cli/pkg/kubectl/bind-apiservice/plugin/bind.go
Comment on lines +128 to +131
sessionID, err := generateRandomString(32)
if err != nil {
return fmt.Errorf("failed to generate session ID: %w", err)
}

@coderabbitai coderabbitai Bot Nov 4, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Verify session ID in the callback handler.

The session ID is generated but never validated when the callback is received. This allows an attacker to craft a callback URL without a matching session ID, potentially enabling CSRF attacks against the local callback server.

Consider adding session ID verification in the callback handler. Store the expected session ID and validate it matches the session_id query parameter in the callback:

 func (o *LoginOptions) startCallbackServerWithRandomPort(tokenCh chan<- *TokenResponse, errCh chan<- error) (*http.Server, string, error) {
+    expectedSessionID := "" // Store the session ID
     // ... existing code ...
     mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
+        receivedSessionID := r.URL.Query().Get("session_id")
+        if receivedSessionID != expectedSessionID {
+            http.Error(w, "Invalid session", http.StatusBadRequest)
+            return
+        }
         // ... rest of handler ...

Alternatively, pass the session ID as a parameter to startCallbackServerWithRandomPort.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In cli/pkg/kubectl/bind-login/plugin/login.go around lines 128 to 131, the
generated sessionID is not validated on OAuth callback which permits forged
callbacks; persist the expected sessionID and validate the incoming callback's
session_id query parameter against it (or pass the sessionID into
startCallbackServerWithRandomPort and have the server compare the request
parameter to the expected value), and on mismatch return an error/HTTP 400 and
reject the flow; ensure any storage used is thread-safe and the validation
happens before exchanging the code for tokens.

✅ Addressed in commits d4feaec to 30a80e5

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sessionID, err := generateRandomString(32)
if err != nil {
return fmt.Errorf("failed to generate session ID: %w", err)
}
sessionID := rand.Text()

rand.Text should be enough for things like this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for rand.Text usage in the codebase
rg -n 'rand\.Text\(\)' --type=go

# Also search for rand package imports to understand usage patterns
rg -n 'k8s\.io/.*rand' --type=go -A 1

# Search for the util/rand package specifically
rg -n '"k8s.io/apimachinery/pkg/util/rand"' --type=go

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check the imports in the file that uses rand.Text()
head -50 test/e2e/framework/dex.go | rg 'import' -A 20

# Also check if there are other usages of rand.Text in the broader codebase
rg 'rand\.Text' --type=go -B 2 -A 2

# Search for the specific import of the rand package
rg '"k8s.io/apimachinery/pkg/util/rand"' -B 5 -A 5

Length of output: 602


🏁 Script executed:

#!/bin/bash
# Get more context around the rand.Text usage to see what package it's from
cat test/e2e/framework/dex.go | head -30

# Search for any custom Text() method definitions on rand
ast-grep --pattern 'func $_(rand $_) Text() $_ { $$$ }'

# Check if there's a util/rand import anywhere that might be aliased
rg 'util/rand' --type=go

# Look for the full function signature and more context
rg 'func.*Text\(\)' --type=go -A 3

Length of output: 713


🏁 Script executed:

#!/bin/bash
# Get the full dex.go file around the rand.Text usage
sed -n '95,115p' test/e2e/framework/dex.go

# Check if there's a local rand package or helper in the framework
fd -e go . test/e2e/framework/ --exec grep -l 'func Text'

# Look for any rand helper or wrapper in the test framework
rg 'package.*rand' --type=go test/e2e/

# Check the exact line with more context
sed -n '100,110p' test/e2e/framework/dex.go

Length of output: 1043


🏁 Script executed:

#!/bin/bash
# Search for the Text function definition in the framework or test directories
rg 'func Text\(\)' --type=go

# Check if there's a rand.go file in the test framework
fd 'rand.go' test/

# Look for any file that defines a Text function that returns a string
ast-grep --pattern 'func Text() string { $$$ }'

# Check for any custom wrapper around crypto/rand
rg 'crypto/rand' test/e2e/framework/ -A 10 | head -50

Length of output: 927


🏁 Script executed:

#!/bin/bash
# Look for all go files in test/e2e/framework that might define Text
rg 'func.*Text' test/e2e/framework/ --type=go

# Check if rand is imported with an alias or from a different package
rg 'import.*rand' test/e2e/framework/dex.go -B 5 -A 5

# Look for the complete imports block in dex.go to see if there's a util/rand
sed -n '18,35p' test/e2e/framework/dex.go

# Check if there's a rand subdirectory or package
fd -t d 'rand' test/

Length of output: 238


🏁 Script executed:

#!/bin/bash
# Check if this is in the diff (new code)
git diff origin/main test/e2e/framework/dex.go 2>/dev/null | grep -A 5 -B 5 'rand.Text' || echo "Not in diff or git not available"

# Look for any rand utility in the entire codebase that has Text
rg 'func.*Text.*string' --type=go | grep -i rand

# Check the base36 import - maybe that's related
rg 'base36' test/e2e/framework/ -A 3 -B 3

# Check if there's a vendor directory with a custom rand package
fd 'rand' vendor/ 2>/dev/null || echo "No vendor directory"

Length of output: 1823


🏁 Script executed:

#!/bin/bash
# Get the complete dex.go file to see the actual usage context
cat test/e2e/framework/dex.go | grep -A 2 -B 2 'rand.Text'

# Check the kcp.go file for the RandomString function definition
rg 'func RandomString' test/e2e/framework/kcp.go -A 5

# Check if maybe it's referring to a method that should be added
# Look for similar patterns in the framework
rg 'rand\.' test/e2e/framework/ --type=go

# See if there's documentation or TODOs about rand.Text
rg 'TODO.*rand|rand.*TODO' --type=go

Length of output: 520


🏁 Script executed:

#!/bin/bash
# Check if rand.Text is a proposed helper or if it exists elsewhere
# Look for any rand package in the project
fd 'rand.go' --type=f

# Check if there's a k8s apimachinery util/rand that might be intended
rg 'apimachinery.*rand' --type=go

# Check what the actual implementation would look like if it exists
# Look for String() methods on rand
rg 'func.*String.*rand' --type=go -A 3

# Check if there's any reference in go.mod to a rand utility
grep 'rand' go.mod

Length of output: 40


🏁 Script executed:

#!/bin/bash
# Check if the code actually compiles - look for any rand helper in framework package
cat test/e2e/framework/dex.go | head -200 | tail -100

# Look for any method extensions or helpers for rand in test utilities
rg 'type.*rand' test/e2e/ --type=go

# Check if there might be a rand.go helper file that extends crypto/rand
ls -la test/e2e/framework/*.go | grep -i rand

# Try to understand the complete context - get the full function with rand.Text
sed -n '90,120p' test/e2e/framework/dex.go

Length of output: 3706


🌐 Web query:

k8s.io/apimachinery/pkg/util/rand String function

💡 Result:

k8s.io/apimachinery/pkg/util/rand.String(n int)

  • What it does: returns a pseudo‑random alphanumeric string of length n that omits vowels (to reduce accidental real words). It panics if n < 0. [1]

  • How it works (implementation summary): it generates random int63 values and extracts multiple character indices from each int63 by bit‑shifting and masking; any out‑of‑range indices are skipped. This yields the requested number of characters. [1][2]

  • Related utility: SafeEncodeString(s string) encodes a given string using the same character set as String (useful for stable, “safe” IDs). [1]

References:
[1] pkg.go.dev — k8s.io/apimachinery/pkg/util/rand (rand.String docs).
[2] Source (rand.go) — Kubernetes apimachinery implementation.


@ntnn, I appreciate the suggestion to simplify the code. However, I cannot find a rand.Text() function in the codebase or in k8s.io/apimachinery/pkg/util/rand.

The k8s.io/apimachinery/pkg/util/rand package does provide rand.String(n int), but there's an important security consideration here:

Security concern: The k8s.io/apimachinery/pkg/util/rand.String() function uses math/rand internally, which generates pseudo-random (not cryptographically secure) values. For OAuth session IDs that protect against CSRF and session fixation attacks, cryptographically secure random values are required.

The current implementation using crypto/rand is appropriate for this security-sensitive use case. Session IDs in OAuth flows should be unpredictable to prevent attackers from guessing or brute-forcing them.

If simplification is desired while maintaining security, the function could be shortened slightly, but crypto/rand should be retained:

func generateRandomString(length int) (string, error) {
    bytes := make([]byte, length)
    if _, err := rand.Read(bytes); err != nil {
        return "", err
    }
    return base64.URLEncoding.EncodeToString(bytes)[:length], nil
}

Note: I did notice rand.Text() is used in test/e2e/framework/dex.go:105, but that function doesn't appear to be defined anywhere in the codebase. If you're aware of where rand.Text() is defined or if it's a proposed helper, please let me know.


🧬 Code Graph Analysis Results

cli/pkg/kubectl/bind-login/plugin/login.go

  • generateRandomString(length int) (string, error)

    func generateRandomString(length int) (string, error) {
    	bytes := make([]byte, length)
    	if _, err := rand.Read(bytes); err != nil {
    		return "", err
    	}
    	return base64.URLEncoding.EncodeToString(bytes)[:length], nil
    }
    
  • Additionally, the session ID generation usage (context for the above function):

    • In Run: sessionID, err := generateRandomString(32)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rand.Text from crypto/rand, not the k8s rand.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for any usage of rand.Text where rand is from crypto/rand
rg 'crypto/rand' --type=go -A 10 | grep -B 5 -A 5 'Text'

# Search for any Text function definitions that might be extensions of crypto/rand
rg 'func.*Text.*string' --type=go | head -20

# Check if there's any rand.Text usage in the entire codebase
rg '\.Text\(\)' --type=go | grep rand

# Look for the specific import pattern and usage
ast-grep --pattern 'import "crypto/rand"'

Length of output: 48


🌐 Web query:

Go crypto/rand Text function

💡 Result:

The crypto/rand package gained Text() in Go 1.24. rand.Text() returns a cryptographically-random string encoded with the RFC‑4648 base32 alphabet, containing at least 128 bits of randomness (suitable for tokens/secrets). Example:

import "crypto/rand"
func main() {
    s := rand.Text()
    fmt.Println(s) // base32 string, safe to display
}

References: [Go package doc — crypto/rand Text()][1], [package source/docs summary][2].

[1] https://pkg.go.dev/crypto/rand
[2] https://docs.go101.org/std/pkg/crypto/rand.html


@ntnn, thank you for the clarification! You're absolutely right. I apologize for the confusion.

crypto/rand.Text() was added in Go 1.24, and since this project uses Go 1.24.0, it's available and is indeed a much better choice here. The function returns a cryptographically-random base32-encoded string with at least 128 bits of randomness, which is perfect for session IDs.

Your suggestion to simplify the code is excellent:

sessionID := rand.Text()

This is:

  • Simpler - no custom function needed
  • Secure - cryptographically random with 128+ bits of entropy
  • Standard - uses the built-in Go 1.24 function (already used elsewhere in the codebase at test/e2e/framework/dex.go:105)
  • Cleaner - no error handling required

The custom generateRandomString function can be removed entirely since rand.Text() provides everything needed for session IDs.


🧬 Code Graph Analysis Results

[cli/pkg/kubectl/bind-login/plugin/login.go: generateRandomString function]

  • Summary (full snippet):
    func generateRandomString(length int) (string, error) {
    bytes := make([]byte, length)
    if _, err := rand.Read(bytes); err != nil {
    return "", err
    }
    return base64.URLEncoding.EncodeToString(bytes)[:length], nil
    }

Note: This function uses crypto/rand (imported as rand) to generate a cryptographically secure random string, then base64-encodes and truncates it to the requested length. This directly pertains to the user’s note about using rand.Text from crypto/rand rather than a Kubernetes-provided rand.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjudeikis what do you think? I think we can just use rand.Text here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didnt knew this existed :) will test.

Comment thread cli/pkg/kubectl/bind-login/plugin/login.go Outdated
Comment thread cli/pkg/kubectl/bind-login/plugin/login.go
Comment thread cli/pkg/kubectl/bind/plugin/bind.go
Comment thread cli/pkg/kubectl/bind/plugin/bind.go
@mjudeikis mjudeikis force-pushed the mjudeikis/api.refactor branch from 0c6c234 to 12c4793 Compare November 4, 2025 11:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
hack/run-frontend.sh (1)

19-31: Update misleading messages to accurately reflect script behavior.

Lines 19 and 30 contain misleading messaging. The script does not build the frontend; it runs a development server. The echo message on line 19 and the comment on line 30 should reflect this accurately.

Apply this diff to correct the messaging:

-echo "Building frontend..."
+echo "Starting frontend development server..."
 
 # Navigate to web directory
 cd web
@@ -27,7 +27,7 @@ if [ ! -d "node_modules" ]; then
     npm install
 fi
 
-# Build the frontend
+# Start the frontend development server
 echo "Running dev application..."
 npm run dev
backend/client/client.go (1)

62-64: Fix redirect parameter serialization (unresolved from past review).

This issue was previously identified but remains unresolved. The current code writes ClientSideRedirectURL to the redirect_url parameter and completely omits the RedirectURL field, breaking round-trip serialization. Both fields must be preserved with correct parameter names.

Apply this diff to fix the parameter mapping:

-	if r.ClientSideRedirectURL != "" {
-		query.Set("redirect_url", r.ClientSideRedirectURL)
+	if r.RedirectURL != "" {
+		query.Set("redirect_url", r.RedirectURL)
+	}
+	if r.ClientSideRedirectURL != "" {
+		query.Set("client_side_redirect_url", r.ClientSideRedirectURL)
 	}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c6c234 and 12c4793.

⛔ Files ignored due to path filters (3)
  • web/dist/assets/index.33c2e7b8.css is excluded by !**/dist/**
  • web/dist/assets/index.4caa6f6c.js is excluded by !**/dist/**
  • web/dist/index.html is excluded by !**/dist/**
📒 Files selected for processing (5)
  • .gitignore (1 hunks)
  • backend/client/client.go (1 hunks)
  • cli/pkg/kubectl/base/browser.go (1 hunks)
  • contrib/kcp/test/e2e/binding.go (1 hunks)
  • hack/run-frontend.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .gitignore
  • cli/pkg/kubectl/base/browser.go
🧰 Additional context used
🧬 Code graph analysis (2)
contrib/kcp/test/e2e/binding.go (4)
test/e2e/framework/bind.go (1)
  • GetKubeBindRestClient (32-46)
sdk/apis/kubebind/v1alpha2/bindingresponse_types.go (1)
  • APIServiceExportTemplateRef (92-98)
cli/pkg/kubectl/bind-apiservice/plugin/binder.go (2)
  • BinderOptions (34-44)
  • NewBinder (53-58)
test/e2e/framework/clients.go (1)
  • ApiextensionsClient (45-49)
backend/client/client.go (1)
backend/auth/middleware.go (1)
  • ClientType (37-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: verify
  • GitHub Check: lint
  • GitHub Check: go-test-e2e
  • GitHub Check: go-test
  • GitHub Check: goreleaser
🔇 Additional comments (4)
backend/client/client.go (1)

35-46: LGTM!

The function correctly extracts and maps query parameters to the struct fields, and the derived IsClusterScoped flag is a reasonable convenience.

contrib/kcp/test/e2e/binding.go (3)

35-41: Function signature is well-defined.

The function parameters are appropriate for orchestrating the binding workflow, with clear names and necessary types.


60-69: Binder setup and execution logic is correct.

The binder is properly configured with SkipKonnector for e2e testing, and the assertions validate the binding result appropriately.


84-84: Remove the cleanup requirement from this helper function.

The performBinding() function in binding.go is a test helper, not a test function itself. Helper functions do not own cleanup responsibility—that belongs to the test functions that invoke them. The codebase already follows this pattern: test functions in test/e2e/bind/happy-case_test.go and framework files use t.Cleanup() callbacks to handle resource lifecycle. Any cleanup needed for resources created by performBinding() should be managed by the test that calls this helper, not within the helper itself.

Likely an incorrect or invalid review comment.

Comment thread contrib/kcp/test/e2e/binding.go
Comment thread contrib/kcp/test/e2e/binding.go
Comment thread contrib/kcp/test/e2e/binding.go
@mjudeikis mjudeikis force-pushed the mjudeikis/api.refactor branch from 12c4793 to d4feaec Compare November 4, 2025 11:47
@mjudeikis mjudeikis force-pushed the mjudeikis/api.refactor branch from 461c63c to 7bc77fa Compare November 4, 2025 14:46
Comment thread .github/workflows/image.yaml Outdated
Comment on lines +19 to +31
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: v1.24.0
check-latest: true

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: web/package-lock.json

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need setup-go and setup-node? Doesn't Go and JS run inside of the container during the build?

Comment thread Dockerfile.konnector Outdated
Comment thread backend/auth/handler.go Outdated
Comment thread backend/auth/handler.go Outdated
Comment thread backend/server.go Outdated
Comment thread cli/pkg/client/client.go Outdated
Comment thread cli/pkg/client/client.go Outdated
Comment thread cli/pkg/kubectl/base/browser.go Outdated
Comment thread cli/pkg/kubectl/base/options.go Outdated
Comment thread cli/pkg/kubectl/bind-apiservice/plugin/binder.go
Comment on lines +128 to +131
sessionID, err := generateRandomString(32)
if err != nil {
return fmt.Errorf("failed to generate session ID: %w", err)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sessionID, err := generateRandomString(32)
if err != nil {
return fmt.Errorf("failed to generate session ID: %w", err)
}
sessionID := rand.Text()

rand.Text should be enough for things like this.

Comment thread contrib/kcp/test/e2e/kcp_test.go
ntnn
ntnn previously approved these changes Nov 5, 2025
@mjudeikis mjudeikis enabled auto-merge (rebase) November 5, 2025 15:12
@mjudeikis mjudeikis merged commit 640871f into kbind-dev:main Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants