Skip to content

feat: demo_mode should work also when no graph api token is set#2185

Merged
alepane21 merged 5 commits intomainfrom
ale/eng-8042-demo-mode-is-broken
Sep 5, 2025
Merged

feat: demo_mode should work also when no graph api token is set#2185
alepane21 merged 5 commits intomainfrom
ale/eng-8042-demo-mode-is-broken

Conversation

@alepane21
Copy link
Copy Markdown
Contributor

@alepane21 alepane21 commented Sep 2, 2025

Summary by CodeRabbit

  • New Features

    • Demo mode can run without a graph token and falls back to a default demo configuration.
  • Improvements

    • Config polling is automatically disabled in demo mode.
    • Poller initialization now accepts an optional primary client for safer startup.
    • Clearer demo-mode warnings and logging.
  • Bug Fixes

    • Prevents nil-related crashes when tokens or clients are missing.
    • Handles “config not found” gracefully in demo mode.

Checklist

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 2, 2025

Walkthrough

Adds demo-mode-aware handling to config client resolution and polling: allows starting without a graph token or primary client in demo mode, constructs the config poller via options (conditionally including a primary client), and returns a default demo config when no config source or config is found in demo mode.

Changes

Cohort / File(s) Summary of Changes
Poller initialization & client resolution
router/core/init_config_poller.go
getConfigClient returns nil, nil when no graph token and r.demoMode == true; InitializeConfigPoller proceeds in demo mode even if primary client is nil; poller constructed via an opts []configpoller.Option (WithLogger, WithPolling, WithFallbackClient, WithDemoMode) and conditionally WithClient(*primaryClient) only when primary client exists; instantiation changed to configpoller.New(r.graphApiToken, opts...).
Config fetcher demo-mode behavior
router/pkg/controlplane/configpoller/config_poller.go
getRouterConfig treats a nil config client as acceptable in demo mode (logs warning, returns default demo config); when primary fetch yields ErrConfigNotFound and demoMode == true (with or without fallback), returns default demo config; preserves fallback attempt flow and non-demo error propagation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions Bot added the router label Sep 2, 2025
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Sep 2, 2025

Router-nonroot image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-25135fd914e60b4bd41e46816ffbec6753b9430f-nonroot

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 (4)
router/pkg/controlplane/configpoller/config_poller.go (3)

127-135: Avoid log spam in demo mode when no primary client is configured

This warning will fire on every poll. Gate it to warn only once (first fetch) and use debug thereafter.

Apply this diff:

-	if c.configClient == nil && c.demoMode {
-		c.logger.Warn("The router is running in demo mode and no config client has been found. Router is using a demo execution config that can be used for testing purposes.")
-		return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
-	}
+	if c.configClient == nil && c.demoMode {
+		if c.latestRouterConfigVersion == "" {
+			c.logger.Warn("The router is running in demo mode and no config client has been found. Using default demo execution config.")
+		} else {
+			c.logger.Debug("Demo mode: no config client; using default demo execution config")
+		}
+		return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
+	}

145-147: Same here: warn once, then downgrade to debug

Prevents repeated warnings on every poll when upstream returns not found in demo mode without fallback.

-	if c.demoMode && c.fallbackConfigClient == nil && errors.Is(err, ErrConfigNotFound) {
-		c.logger.Warn("The router is running in demo mode and no config has been found. Router is using a demo execution config that can be used for testing purposes.")
-		return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
-	}
+	if c.demoMode && c.fallbackConfigClient == nil && errors.Is(err, ErrConfigNotFound) {
+		if c.latestRouterConfigVersion == "" {
+			c.logger.Warn("Demo mode: no execution config found. Using default demo config.")
+		} else {
+			c.logger.Debug("Demo mode: no execution config found. Using default demo config.")
+		}
+		return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
+	}

157-159: Log fallback-not-found path for observability

Visibility helps explain why the router is serving the demo config from fallback.

-	if c.demoMode && errors.Is(err, ErrConfigNotFound) {
-		return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
-	}
+	if c.demoMode && errors.Is(err, ErrConfigNotFound) {
+		if c.latestRouterConfigVersion == "" {
+			c.logger.Warn("Demo mode: fallback execution config not found. Using default demo config.")
+		} else {
+			c.logger.Debug("Demo mode: fallback execution config not found. Using default demo config.")
+		}
+		return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
+	}
router/core/init_config_poller.go (1)

160-161: Constructor token appears unused in poller

configpoller.New takes a token but config_poller.go does not use graphApiToken. Consider removing the parameter in a follow-up to reduce confusion.

Happy to draft a scoped follow-up PR plan if you want.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0f15d1e and 934d180.

📒 Files selected for processing (2)
  • router/core/init_config_poller.go (3 hunks)
  • router/pkg/controlplane/configpoller/config_poller.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
router/pkg/controlplane/configpoller/config_poller.go (2)
router/pkg/routerconfig/client.go (2)
  • Response (11-14)
  • GetDefaultConfig (27-59)
router/pkg/config/config.go (1)
  • Config (985-1059)
router/core/init_config_poller.go (1)
router/pkg/controlplane/configpoller/config_poller.go (7)
  • Option (15-15)
  • WithLogger (177-181)
  • WithPolling (183-188)
  • WithFallbackClient (196-200)
  • WithDemoMode (202-206)
  • WithClient (190-194)
  • New (46-62)
⏰ 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). (11)
  • GitHub Check: build-router
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: build_push_image
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./events)
  • GitHub Check: image_scan
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_test
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (4)
router/pkg/controlplane/configpoller/config_poller.go (1)

17-19: No action required: ErrConfigNotFound compatibility verified Both the S3 client (returns configpoller.ErrConfigNotFound on “NoSuchKey”) and the CDN client convert their internal 404 sentinel to configpoller.ErrConfigNotFound, so all errors.Is(err, ErrConfigNotFound) checks will succeed.

router/core/init_config_poller.go (3)

91-97: Good: allow demo mode without a token

Returning (nil, nil) here matches the “run without polling” intent.


156-158: Conditional WithClient looks correct

Only attach the primary client when present; avoids nil derefs.


150-156: Verify polling behavior in demo mode without a primary client
Ensure that configPoller.Subscribe actually skips or disables polling when demoMode==true and primaryClient==nil; if it doesn’t, apply the suggested change to set interval and jitter to 0 in that case.

Comment thread router/pkg/controlplane/configpoller/config_poller.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
router/pkg/controlplane/configpoller/config_poller.go (1)

132-134: Prefer generic wording; optionally attempt fallback when primary is nil

  • Message: "config client" is internal (echoing prior feedback). Suggest "configuration source".
  • Optional: If a fallback is configured, consider using it when the primary client is nil (non-demo mode) instead of failing immediately.

Apply:

- if c.configClient == nil {
-     return nil, errors.New("no config client found")
- }
+ if c.configClient == nil {
+     if c.fallbackConfigClient != nil {
+         c.logger.Warn("No primary configuration source configured. Attempting fallback storage")
+         return (*c.fallbackConfigClient).RouterConfig(ctx, c.latestRouterConfigVersion, c.latestRouterConfigDate)
+     }
+     return nil, errors.New("no configuration source configured")
+ }
🧹 Nitpick comments (2)
router/pkg/controlplane/configpoller/config_poller.go (2)

127-130: Fix log grammar; confirm intended precedence over fallback in demo mode

  • Grammar: "a execution" → "an execution".
  • Behavior: When demoMode is true and configClient is nil, you return the default config without checking a configured fallback. Is that intentional? If a fallback is provided, should we prefer it over the hardcoded demo config?

Apply:

- c.logger.Warn("The router is running in demo mode without a execution configuration source. Using a default demo execution config for testing purposes.")
+ c.logger.Warn("The router is running in demo mode without an execution configuration source. Using a default demo execution config for testing purposes.")

154-159: Include error details when falling back; log when returning demo config from fallback path

  • Add zap.Error(err) to the fallback attempt log.
  • When demo mode uses the default config after fallback returns ErrConfigNotFound, log that decision for parity with the primary path.

Apply:

- c.logger.Warn("Failed to retrieve execution config. Attempting with fallback storage")
+ c.logger.Warn("Failed to retrieve execution config. Attempting with fallback storage", zap.Error(err))

 config, err = (*c.fallbackConfigClient).RouterConfig(ctx, c.latestRouterConfigVersion, c.latestRouterConfigDate)
- if c.demoMode && errors.Is(err, ErrConfigNotFound) {
-     return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
- }
+ if c.demoMode && errors.Is(err, ErrConfigNotFound) {
+     c.logger.Warn("Fallback storage returned no execution config in demo mode. Using a demo execution config for testing purposes.")
+     return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
+ }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 934d180 and 6204bd0.

📒 Files selected for processing (1)
  • router/pkg/controlplane/configpoller/config_poller.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
router/pkg/controlplane/configpoller/config_poller.go (2)
router/pkg/routerconfig/client.go (2)
  • Response (11-14)
  • GetDefaultConfig (27-59)
router/pkg/config/config.go (1)
  • Config (985-1059)
⏰ 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). (11)
  • GitHub Check: build-router
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: Analyze (go)
  • GitHub Check: build_push_image
  • GitHub Check: image_scan
  • GitHub Check: integration_test (./events)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: build_test
🔇 Additional comments (1)
router/pkg/controlplane/configpoller/config_poller.go (1)

145-147: Fix log grammar
Change the warning message to past tense and simplify. Apply:

- c.logger.Warn("The router is running in demo mode and no execution config has been found. Router is using a demo execution config that can be used for testing purposes.")
+ c.logger.Warn("The router is running in demo mode and no execution config was found. Using a demo execution config for testing purposes.")

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
router/pkg/controlplane/configpoller/config_poller.go (1)

41-44: Fix pointer-to-interface: replace with plain interface to avoid panics and simplify usage.

Using *routerconfig.Client (pointer to interface) is a Go anti-pattern and can panic when dereferenced if the underlying interface is nil. Store the interface by value instead and adjust the option and call sites.

@@
 type configPoller struct {
   graphApiToken             string
   logger                    *zap.Logger
   latestRouterConfigVersion string
   latestRouterConfigDate    time.Time
   poller                    controlplane.Poller
   pollInterval              time.Duration
   pollJitter                time.Duration
   configClient              routerconfig.Client
-  fallbackConfigClient      *routerconfig.Client
+  fallbackConfigClient      routerconfig.Client
   demoMode                  bool
 }
@@
-func WithFallbackClient(client *routerconfig.Client) Option {
+func WithFallbackClient(client routerconfig.Client) Option {
   return func(s *configPoller) {
     s.fallbackConfigClient = client
   }
 }
@@
-  c.logger.Warn("Failed to retrieve execution config. Attempting with fallback storage")
-
-  config, err = (*c.fallbackConfigClient).RouterConfig(ctx, c.latestRouterConfigVersion, c.latestRouterConfigDate)
+  c.logger.Warn("Failed to retrieve execution config. Attempting with fallback storage")
+  config, err = c.fallbackConfigClient.RouterConfig(ctx, c.latestRouterConfigVersion, c.latestRouterConfigDate)

Also applies to: 196-200, 150-157

🧹 Nitpick comments (2)
router/pkg/controlplane/configpoller/config_poller.go (2)

127-135: Try fallback storage before defaulting to demo config when no primary client.

If a fallback client is configured, prefer it over the demo default. This keeps demo mode useful while still honoring a valid fallback source.

- if c.configClient == nil && c.demoMode {
-   c.logger.Warn("The router is running in demo mode without an execution configuration source, using a demo execution config for testing purposes.")
-   return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
- }
-
- if c.configClient == nil {
-   return nil, errors.New("no execution configuration source found")
- }
+ if c.configClient == nil {
+   // Prefer fallback storage if available
+   if c.fallbackConfigClient != nil {
+     c.logger.Warn("No primary execution configuration source configured; attempting fallback storage.")
+     cfg, ferr := c.fallbackConfigClient.RouterConfig(ctx, c.latestRouterConfigVersion, c.latestRouterConfigDate)
+     if ferr == nil {
+       return cfg, nil
+     }
+     if c.demoMode && errors.Is(ferr, ErrConfigNotFound) {
+       c.logger.Warn("The router is running in demo mode and no execution config has been found, using a demo execution config for testing purposes.")
+       return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
+     }
+     if !c.demoMode {
+       return nil, ferr
+     }
+   }
+   if c.demoMode {
+     c.logger.Warn("The router is running in demo mode without an execution configuration source, using a demo execution config for testing purposes.")
+     return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
+   }
+   return nil, errors.New("no execution configuration source found")
+ }

146-148: Add a matching warning log for the fallback ErrConfigNotFound path.

You warn on the primary path (Line 146) but not when the fallback yields ErrConfigNotFound. Log for parity and easier troubleshooting.

   config, err = c.fallbackConfigClient.RouterConfig(ctx, c.latestRouterConfigVersion, c.latestRouterConfigDate)
-  if c.demoMode && errors.Is(err, ErrConfigNotFound) {
-    return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
-  }
+  if c.demoMode && errors.Is(err, ErrConfigNotFound) {
+    c.logger.Warn("The router is running in demo mode and no execution config has been found in fallback storage, using a demo execution config for testing purposes.")
+    return &routerconfig.Response{Config: routerconfig.GetDefaultConfig()}, nil
+  }

Also applies to: 156-159

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6204bd0 and 825ef0d.

📒 Files selected for processing (1)
  • router/pkg/controlplane/configpoller/config_poller.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
router/pkg/controlplane/configpoller/config_poller.go (2)
router/pkg/routerconfig/client.go (2)
  • Response (11-14)
  • GetDefaultConfig (27-59)
router/pkg/config/config.go (1)
  • Config (985-1059)
⏰ 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). (11)
  • GitHub Check: build-router
  • GitHub Check: integration_test (./events)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: build_push_image
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_test
  • GitHub Check: image_scan
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
router/pkg/controlplane/configpoller/config_poller.go (1)

141-145: No action required: all routerconfig clients return the configpoller error sentinels directly. The S3 and CDN clients both import and return configpoller.ErrConfigNotModified and configpoller.ErrConfigNotFound, so errors.Is(err, ErrConfig…) will match as intended.

Copy link
Copy Markdown
Contributor

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

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

LGTM

@alepane21 alepane21 merged commit cad81fc into main Sep 5, 2025
27 checks passed
@alepane21 alepane21 deleted the ale/eng-8042-demo-mode-is-broken branch September 5, 2025 14:57
@Noroth Noroth mentioned this pull request Sep 30, 2025
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants