Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
package main

import (
"context"
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"log/slog"
Expand Down Expand Up @@ -286,6 +288,14 @@ func main() {
os.Exit(1)
}

// Non-fatal: a cluster that blocks the discovery endpoint should still start.
// The reconciler surfaces a not-ready condition rather than asserting an
// issuer it never verified.
if err := RegisterServiceAccountIssuer(context.Background()); err != nil {
setupLog.Error(err, "failed to discover the cluster service-account issuer; "+
"internal service auth will not reconcile until spec.wandb.internalServiceAuth.oidcIssuer is set")
}
Comment on lines +291 to +297

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a deadline to issuer discovery.

context.Background() at Line 294 has no deadline. If the API server stalls, DoRaw blocks before the manager starts. The non-fatal error path does not run.

Use context.WithTimeout for this startup lookup.

Proposed fix
+	"time"
+
-	if err := RegisterServiceAccountIssuer(context.Background()); err != nil {
+	issuerCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	defer cancel()
+	if err := RegisterServiceAccountIssuer(issuerCtx); err != nil {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Non-fatal: a cluster that blocks the discovery endpoint should still start.
// The reconciler surfaces a not-ready condition rather than asserting an
// issuer it never verified.
if err := RegisterServiceAccountIssuer(context.Background()); err != nil {
setupLog.Error(err, "failed to discover the cluster service-account issuer; "+
"internal service auth will not reconcile until spec.wandb.internalServiceAuth.oidcIssuer is set")
}
// Non-fatal: a cluster that blocks the discovery endpoint should still start.
// The reconciler surfaces a not-ready condition rather than asserting an
// issuer it never verified.
issuerCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := RegisterServiceAccountIssuer(issuerCtx); err != nil {
setupLog.Error(err, "failed to discover the cluster service-account issuer; "+
"internal service auth will not reconcile until spec.wandb.internalServiceAuth.oidcIssuer is set")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/manager/main.go` around lines 291 - 297, Update the startup
issuer-discovery call around RegisterServiceAccountIssuer to use a
context.WithTimeout with an appropriate bounded duration instead of
context.Background(), and ensure the derived context is canceled. Preserve the
existing non-fatal error logging and manager startup behavior when discovery
times out or fails.

Source: Coding guidelines


mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Cache: cacheOptions,
Expand Down Expand Up @@ -445,6 +455,47 @@ func detectRuntimeNamespace() (string, error) {
return namespace, nil
}

// serviceAccountIssuerDiscoveryPath is the API server's OIDC discovery document.
// Its `issuer` field is the value the API server stamps as `iss` on projected
// ServiceAccount tokens.
const serviceAccountIssuerDiscoveryPath = "/.well-known/openid-configuration"

// RegisterServiceAccountIssuer discovers the cluster's service-account issuer so
// W&B services can be told the exact issuer their projected tokens carry. The
// issuer is a fixed property of the API server's --service-account-issuer flag,
// so one lookup at start-up is enough; granting the operator access after the
// fact requires a restart.
func RegisterServiceAccountIssuer(ctx context.Context) error {
cfg, err := config.GetConfig()
if err != nil {
return err
}

discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil {
return err
}

raw, err := discoveryClient.RESTClient().Get().AbsPath(serviceAccountIssuerDiscoveryPath).DoRaw(ctx)
if err != nil {
return fmt.Errorf("get %s: %w", serviceAccountIssuerDiscoveryPath, err)
}

var doc struct {
Issuer string `json:"issuer"`
}
if err := json.Unmarshal(raw, &doc); err != nil {
return fmt.Errorf("decode %s: %w", serviceAccountIssuerDiscoveryPath, err)
}
if doc.Issuer == "" {
return fmt.Errorf("%s returned no issuer field", serviceAccountIssuerDiscoveryPath)
}

setupLog.Info("discovered cluster service-account issuer", "issuer", doc.Issuer)
utils.SetServiceAccountIssuer(doc.Issuer)
return nil
}

func RegisterServerResources() error {
cfg, err := config.GetConfig()
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ metadata:
name: manager-role
rules:
- nonResourceURLs:
- /.well-known/openid-configuration
- /metrics
verbs:
- get
Expand Down
7 changes: 7 additions & 0 deletions deploy/operator/templates/wandb-operator-wandb-role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ rules:
- patch
- update
- watch
# Read the cluster's service-account issuer. Internal service auth must be told
# the API server's exact --service-account-issuer, or W&B services reject each
# other's projected tokens with a 401.
- nonResourceURLs:
- /.well-known/openid-configuration
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
Expand Down
10 changes: 4 additions & 6 deletions internal/controller/reconciler/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,10 @@
}
components = append(components, fmt.Sprintf("%s%s:%d%s", proto, svcHost, selectedPort, src.Path))
case "jwt-issuer-map":
if wandb.Spec.Wandb.InternalServiceAuth.Enabled != nil &&
*wandb.Spec.Wandb.InternalServiceAuth.Enabled {
// TODO Get real OIDC Issuer
issuer := "https://kubernetes.default.svc.cluster.local"
if wandb.Spec.Wandb.InternalServiceAuth.OIDCIssuer != "" {
issuer = wandb.Spec.Wandb.InternalServiceAuth.OIDCIssuer
if internalServiceAuthEnabled(wandb) {

Check failure on line 413 in internal/controller/reconciler/pods.go

View workflow job for this annotation

GitHub Actions / Test

undefined: internalServiceAuthEnabled

Check failure on line 413 in internal/controller/reconciler/pods.go

View workflow job for this annotation

GitHub Actions / Test

undefined: internalServiceAuthEnabled

Check failure on line 413 in internal/controller/reconciler/pods.go

View workflow job for this annotation

GitHub Actions / Build

undefined: internalServiceAuthEnabled

Check failure on line 413 in internal/controller/reconciler/pods.go

View workflow job for this annotation

GitHub Actions / Build

undefined: internalServiceAuthEnabled
issuer := resolveInternalServiceAuthIssuer(wandb)

Check failure on line 414 in internal/controller/reconciler/pods.go

View workflow job for this annotation

GitHub Actions / Test

undefined: resolveInternalServiceAuthIssuer

Check failure on line 414 in internal/controller/reconciler/pods.go

View workflow job for this annotation

GitHub Actions / Test

undefined: resolveInternalServiceAuthIssuer

Check failure on line 414 in internal/controller/reconciler/pods.go

View workflow job for this annotation

GitHub Actions / Build

undefined: resolveInternalServiceAuthIssuer

Check failure on line 414 in internal/controller/reconciler/pods.go

View workflow job for this annotation

GitHub Actions / Build

undefined: resolveInternalServiceAuthIssuer
if issuer == "" {
continue
}
components = append(
components,
Expand Down
12 changes: 12 additions & 0 deletions internal/controller/reconciler/reconcile_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,18 @@
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
}

// Refuse to reconcile applications with an unknown issuer rather than emitting
// a guessed one: W&B services reject a mismatched `iss` with a 401 and the API
// panics on that path, so no value is safer than a wrong value.
if internalServiceAuthEnabled(wandb) && resolveInternalServiceAuthIssuer(wandb) == "" {

Check failure on line 527 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Test

undefined: resolveInternalServiceAuthIssuer

Check failure on line 527 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Test

undefined: internalServiceAuthEnabled

Check failure on line 527 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Test

undefined: resolveInternalServiceAuthIssuer

Check failure on line 527 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Test

undefined: internalServiceAuthEnabled

Check failure on line 527 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Build

undefined: resolveInternalServiceAuthIssuer

Check failure on line 527 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Build

undefined: internalServiceAuthEnabled

Check failure on line 527 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Build

undefined: resolveInternalServiceAuthIssuer

Check failure on line 527 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Build

undefined: internalServiceAuthEnabled
logger.Info("Cluster service-account issuer unknown; not reconciling applications")
if err := updateReadyStatus(ctx, client, wandb, statusBefore, false,
serviceAccountIssuerUnknownReason, serviceAccountIssuerUnknownMessage); err != nil {

Check failure on line 530 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Test

undefined: serviceAccountIssuerUnknownMessage

Check failure on line 530 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Test

undefined: serviceAccountIssuerUnknownReason

Check failure on line 530 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Build

undefined: serviceAccountIssuerUnknownMessage

Check failure on line 530 in internal/controller/reconciler/reconcile_v2.go

View workflow job for this annotation

GitHub Actions / Build

undefined: serviceAccountIssuerUnknownReason
Comment on lines +527 to +530

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Restore the shared issuer-resolution declarations.

internalServiceAuthEnabled, resolveInternalServiceAuthIssuer, serviceAccountIssuerUnknownReason, and serviceAccountIssuerUnknownMessage are undefined in package reconciler. The golangci-lint typecheck phase fails before the operator can build.

  • internal/controller/reconciler/reconcile_v2.go#L527-L530: add or reference the shared helper functions and readiness constants.
  • internal/controller/reconciler/pods.go#L413-L416: use the same declared helper functions.

After the fix, run make lint and make test. As per coding guidelines, “Run both make lint and make test before considering a task complete.”

📍 Affects 2 files
  • internal/controller/reconciler/reconcile_v2.go#L527-L530 (this comment)
  • internal/controller/reconciler/pods.go#L413-L416
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/reconciler/reconcile_v2.go` around lines 527 - 530,
Restore shared declarations for internalServiceAuthEnabled,
resolveInternalServiceAuthIssuer, serviceAccountIssuerUnknownReason, and
serviceAccountIssuerUnknownMessage, then reference those declarations in
internal/controller/reconciler/reconcile_v2.go lines 527-530 and
internal/controller/reconciler/pods.go lines 413-416. Ensure both call sites use
the same helpers and readiness constants, then run make lint and make test.

Sources: Coding guidelines, Linters/SAST tools

return ctrl.Result{}, err
}
return ctrl.Result{RequeueAfter: time.Minute}, nil
}

result, err = reconcileApplications(ctx, client, wandb, manifest, telemetryConfig)
if err != nil {
return result, err
Expand Down
3 changes: 3 additions & 0 deletions internal/controller/weightsandbiases_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ type WeightsAndBiasesReconciler struct {
//+kubebuilder:rbac:groups=redis.redis.opstreelabs.in,resources=redis/status,verbs=get
//+kubebuilder:rbac:groups=security.openshift.io,resources=securitycontextconstraints,resourceNames=nonroot-v2,verbs=use
//+kubebuilder:rbac:urls=/metrics,verbs=get
// Read the cluster's service-account issuer, which internal service auth must
// match exactly (see RegisterServiceAccountIssuer).
//+kubebuilder:rbac:urls=/.well-known/openid-configuration,verbs=get

// Deprecated/Erroneously required RBAC rules
//+kubebuilder:rbac:groups=extensions,resources=daemonsets;deployments;replicasets;ingresses;ingresses/status,verbs=get;list;watch
Expand Down
8 changes: 5 additions & 3 deletions internal/webhook/v2/weightsandbiases_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ func (d *WeightsAndBiasesCustomDefaulter) Default(ctx context.Context, obj runti
wandb.Spec.Wandb.InternalServiceAuth.Enabled = ptr.To(true)
}

if wandb.Spec.Wandb.InternalServiceAuth.OIDCIssuer == "" {
wandb.Spec.Wandb.InternalServiceAuth.OIDCIssuer = "https://kubernetes.default.svc.cluster.local"
}
// OIDCIssuer is deliberately not defaulted: it must match the API server's
// --service-account-issuer, which only kubeadm sets to
// kubernetes.default.svc.cluster.local. Defaulting it here would mask the
// issuer discovered from the cluster and pin every install to a value that
// fails token validation on EKS/GKE/AKS.

if wandb.Spec.Wandb.ServiceAccount.Create == nil {
wandb.Spec.Wandb.ServiceAccount.Create = ptr.To(true)
Expand Down
3 changes: 2 additions & 1 deletion internal/webhook/v2/weightsandbiases_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ var _ = Describe("WeightsAndBiases Webhook", func() {
Expect(obj.Spec.Wandb.ManifestRepository).To(Equal("oci://example.com/wandb/server-manifest"))
Expect(obj.Spec.Wandb.InternalServiceAuth.Enabled).ToNot(BeNil())
Expect(*obj.Spec.Wandb.InternalServiceAuth.Enabled).To(BeTrue())
Expect(obj.Spec.Wandb.InternalServiceAuth.OIDCIssuer).To(Equal("https://kubernetes.default.svc.cluster.local"))
Expect(obj.Spec.Wandb.InternalServiceAuth.OIDCIssuer).To(BeEmpty(),
"oidcIssuer must not be defaulted; it has to match the cluster's --service-account-issuer")
Expect(obj.Spec.Wandb.ServiceAccount.Create).ToNot(BeNil())
Expect(*obj.Spec.Wandb.ServiceAccount.Create).To(BeTrue())
Expect(obj.Spec.Wandb.ServiceAccount.ServiceAccountName).To(Equal("wandb-app"))
Expand Down
Loading