Skip to content

Conversation

@Tharsanan1
Copy link
Contributor

@Tharsanan1 Tharsanan1 commented Dec 22, 2025

Purpose

$subject

Summary by CodeRabbit

  • New Features

    • Per-scenario gateway configuration switching using selectable profiles.
    • Distributed tracing support with an OpenTelemetry collector and steps to validate traces.
  • Tests / Reliability

    • Improved test infra: background log streaming, health/readiness checks, controlled service restarts with env overrides, and log-based trace assertions.
  • Chores

    • Sample backend port updated from 5000 to 5050.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Walkthrough

Adds per-scenario gateway configuration profiles and runtime controls: a ConfigProfile registry and GatewayConfigManager to switch profiles via scenario tags, ComposeManager methods to restart/health-check gateway components, OpenTelemetry collector integration, BDD tracing steps, and a tracing feature to assert traces in collector logs.

Changes

Cohort / File(s) Summary
Configuration profiles & manager
gateway/it/config_profiles.go, gateway/it/config_manager.go
Adds ConfigProfile and ConfigProfileRegistry with preset profiles (default, debug, memory, tracing). Adds GatewayConfigManager (thread-safe) with NewGatewayConfigManager and EnsureConfig that reads @config- tags, validates profiles, and triggers gateway restarts with profile env vars.
Compose / runtime helpers
gateway/it/setup.go
Extends ComposeManager with RestartGatewayController(ctx, envVars), WaitForGatewayControllerHealthy(ctx), WaitForRouterReady(ctx), StreamLogs(), CheckLogsForText(ctx, containerName, text) and GetContainerLogs(ctx, containerName). Start() now streams logs and PrintServiceStatus added; sample backend port check updated to 5050 and compose project name fixed.
OpenTelemetry collector & config
gateway/it/docker-compose.test.yaml, gateway/it/otel-config.yaml
Adds otel-collector service to the test docker-compose and provides otel-config.yaml with OTLP grpc/http receivers and a logging exporter; collector exposes OTLP ports and attaches to the test network. Also adds GATEWAY_TRACING_ENABLED env vars and adjusts port mappings.
Test integration & tracing steps
gateway/it/suite_test.go, gateway/it/tracing_steps.go, gateway/it/features/tracing.feature
Wires GatewayConfigManager into suite init and per-scenario hooks; registers tracing BDD steps and a scenario that enables tracing, issues requests, and asserts a trace appears in collector logs. verifyTraceInLogs polls collector logs with timeout and retries.
Test infra changes & configs
gateway/it/Makefile, gateway/it/configs/config.yaml, gateway/it/state.go
Test target forced unbuffered output and -count=1. Adds comprehensive gateway config YAML including tracing and policy_engine settings. Updates SampleBackendURL to use port 5050.

Sequence Diagram

sequenceDiagram
    actor TestSuite as Test Suite
    participant GCM as GatewayConfigManager
    participant CM as ComposeManager
    participant GC as Gateway Controller
    participant OC as OTel Collector

    TestSuite->>GCM: EnsureConfig(ctx, scenario)
    activate GCM
    GCM->>GCM: extractConfigTag -> profile name
    GCM->>GCM: registry.Get(profile)
    alt profile found & differs
        GCM->>CM: RestartGatewayController(ctx, profile.EnvVars)
        activate CM
        CM->>GC: stop & remove container(s)
        CM->>GC: start container(s) with env vars
        CM->>CM: WaitForGatewayControllerHealthy(ctx)
        CM->>CM: WaitForRouterReady(ctx)
        GC-->>CM: healthy / ready
        deactivate CM
        GCM->>GCM: set currentProfile
    else already on profile
        GCM-->>TestSuite: no-op
    else profile not found
        GCM-->>TestSuite: error
    end
    deactivate GCM

    TestSuite->>GC: execute scenario requests
    GC->>OC: export trace via OTLP
    OC->>OC: receive & log trace

    TestSuite->>CM: CheckLogsForText(ctx,"it-otel-collector", text)
    activate CM
    CM->>OC: fetch container logs (poll)
    OC-->>CM: logs contain trace text
    CM-->>TestSuite: found -> pass
    deactivate CM
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • gateway/it/config_manager.go — verify mutex usage, tag parsing, and error messages.
  • gateway/it/setup.go — review restart sequence, env var injection, health/readiness loops, timeouts, and cleanup logic.
  • gateway/it/tracing_steps.go & tracing feature — check polling logic, retry/backoff, and log retrieval.
  • docker-compose.test.yaml & otel-config.yaml — confirm ports, network, and OTLP receiver/exporter configs.
  • suite_test.go — ensure correct init ordering and per-scenario hook behavior.

Possibly related PRs

Suggested reviewers

  • Krishanx92

Poem

🐰 I hopped through code with nimble paws,
I stitched env flags and tracked the cause.
OTEL listens, spans arrive,
Tests restart so traces thrive.
— a celebratory rabbit 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete, containing only a Purpose section with a placeholder '$subject' and missing all other required template sections including Goals, Approach, User stories, Documentation, Automation tests, Security checks, Samples, Related PRs, and Test environment. Complete the description by filling in all required sections: Goals, Approach, User stories, Documentation, Automation tests, Security checks, Samples, Related PRs, and Test environment. The Purpose section should replace the '$subject' placeholder with actual information.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Tag based gateway deployment' accurately summarizes the main feature: enabling dynamic gateway configuration switching via scenario tags for testing.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
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: 5

🧹 Nitpick comments (5)
gateway/it/docker-compose.test.yaml (1)

64-75: Consider adding a healthcheck for the otel-collector service.

The otel-collector service lacks a healthcheck, which could lead to timing issues if tests try to verify traces before the collector is ready. The OTEL collector exposes health endpoints that can be used.

🔎 Suggested healthcheck configuration
   otel-collector:
     image: otel/opentelemetry-collector-contrib:0.88.0
     container_name: it-otel-collector
     command: ["--config=/etc/otel-collector-config.yaml"]
     volumes:
       - ./otel-config.yaml:/etc/otel-collector-config.yaml:ro
     ports:
       - "4317:4317" # OTLP gRPC receiver
       - "4318:4318" # OTLP HTTP receiver
+    healthcheck:
+      test: ["CMD", "wget", "--quiet", "--tries=1", "-O", "-", "http://localhost:13133/"]
+      interval: 5s
+      timeout: 3s
+      retries: 5
+      start_period: 5s
     networks:
       - it-gateway-network

Note: The health extension needs to be enabled in otel-config.yaml:

extensions:
  health_check:

service:
  extensions: [health_check]
  pipelines:
    # ...existing config
gateway/it/tracing_steps.go (1)

32-46: Consider checking logs immediately before first ticker interval.

The current implementation waits 1 second before the first log check. If traces are already present, this adds unnecessary delay. Checking immediately on entry would improve responsiveness.

🔎 Suggested improvement
 func verifyTraceInLogs(cm *ComposeManager, text string) error {
 	// Retry for a few seconds as logs might be delayed
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()

 	ticker := time.NewTicker(1 * time.Second)
 	defer ticker.Stop()

+	// Check immediately first
+	if found, err := cm.CheckLogsForText(ctx, "it-otel-collector", text); err == nil && found {
+		return nil
+	}
+
 	for {
 		select {
 		case <-ctx.Done():
 			return fmt.Errorf("timed out waiting for trace logs containing '%s'", text)
 		case <-ticker.C:
 			found, err := cm.CheckLogsForText(ctx, "it-otel-collector", text)
 			if err != nil {
-				// Don't fail immediately on log retrieval error (container might be starting?)
 				continue
 			}
 			if found {
 				return nil
 			}
 		}
 	}
 }
gateway/it/config_profiles.go (2)

10-14: The defaultProfile field appears unused.

The defaultProfile field is set to "default" but there's no method to retrieve or use it. Consider adding a GetDefault() method or removing the field if not needed.

🔎 Suggested addition
// GetDefault returns the default profile
func (r *ConfigProfileRegistry) GetDefault() (*ConfigProfile, bool) {
	return r.Get(r.defaultProfile)
}

65-68: Consider adding nil check in Register method.

If a nil profile is passed, accessing profile.Name will cause a panic. While this may be internal code, a defensive check could prevent debugging headaches.

🔎 Suggested fix
 // Register adds a profile to the registry
 func (r *ConfigProfileRegistry) Register(profile *ConfigProfile) {
+	if profile == nil {
+		return
+	}
 	r.profiles[profile.Name] = profile
 }
gateway/it/config_manager.go (1)

63-71: Consider validating for multiple config tags.

The current implementation returns the first matching config tag when multiple are present. While this works, it could lead to confusion if test authors accidentally apply multiple config tags to a scenario.

Consider either:

  1. Returning an error if multiple config tags are detected
  2. Adding a comment documenting the "first match wins" behavior
🔎 Proposed enhancement to detect multiple config tags
 // extractConfigTag finds the first tag starting with @config- and returns the suffix
 func (m *GatewayConfigManager) extractConfigTag(sc *godog.Scenario) string {
+	var found string
 	for _, tag := range sc.Tags {
 		if strings.HasPrefix(tag.Name, ConfigTagPrefix) {
-			return strings.TrimPrefix(tag.Name, ConfigTagPrefix)
+			if found != "" {
+				log.Printf("Warning: Multiple config tags found in scenario '%s'. Using first: %s", sc.Name, found)
+				return found
+			}
+			found = strings.TrimPrefix(tag.Name, ConfigTagPrefix)
 		}
 	}
-	return ""
+	return found
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 258f348 and 77ba8f6.

📒 Files selected for processing (8)
  • gateway/it/config_manager.go
  • gateway/it/config_profiles.go
  • gateway/it/docker-compose.test.yaml
  • gateway/it/features/tracing.feature
  • gateway/it/otel-config.yaml
  • gateway/it/setup.go
  • gateway/it/suite_test.go
  • gateway/it/tracing_steps.go
🧰 Additional context used
🧬 Code graph analysis (3)
gateway/it/tracing_steps.go (1)
gateway/it/setup.go (1)
  • ComposeManager (68-78)
gateway/it/suite_test.go (2)
gateway/it/config_manager.go (2)
  • GatewayConfigManager (16-21)
  • NewGatewayConfigManager (24-30)
gateway/it/tracing_steps.go (1)
  • RegisterTracingSteps (12-22)
gateway/it/config_manager.go (2)
gateway/it/config_profiles.go (2)
  • ConfigProfileRegistry (11-14)
  • NewConfigProfileRegistry (17-63)
gateway/it/setup.go (1)
  • ComposeManager (68-78)
🔇 Additional comments (15)
gateway/it/features/tracing.feature (1)

1-12: Feature file structure looks good.

The BDD scenario is well-defined with clear intent. The @config-tracing tag correctly triggers the tracing profile configuration before the scenario runs.

gateway/it/tracing_steps.go (1)

11-22: LGTM!

Clean step registration. The no-op step with explanatory comment is a reasonable pattern when the actual setup is handled via scenario tags.

gateway/it/config_profiles.go (1)

51-59: Tracing profile configuration looks correct.

The tracing profile sets GATEWAY_TRACING_ENABLED=true which will be applied when scenarios with @config-tracing tag run.

gateway/it/suite_test.go (4)

92-93: Setting env var at runtime may not affect already-started containers.

os.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") is set in BeforeSuite, which runs after the test process starts. For testcontainers to pick this up, it should be set before any testcontainers code executes. The current placement in BeforeSuite before NewComposeManager should work, but consider moving to init() for guaranteed early execution.


172-180: Configuration switch happens per-scenario; verify gateway restart timing.

The EnsureConfig call before each scenario will restart the gateway-controller if the config profile changes. This is a potentially slow operation (30s health timeout per WaitForGatewayControllerHealthy). Ensure scenarios with the same config tag are grouped to minimize restarts.


215-218: Tracing steps registration is correctly conditional.

The guard if composeManager != nil ensures tracing steps are only registered when compose infrastructure is available.


56-63: Godog options initialization follows standard patterns.

Using BindCommandLineFlags in init() allows test customization via command-line flags.

gateway/it/setup.go (3)

213-242: Health check implementation is correct.

The polling pattern with context timeout, ticker, and proper cleanup is well-implemented. The 30-second timeout is reasonable for container startup.


344-358: StreamLogs implementation is clean.

Running in a goroutine with proper context cancellation handling. The error check for cm.ctx.Err() == nil correctly suppresses expected errors during shutdown.


360-372: CheckLogsForText implementation is straightforward and effective.

Simple log retrieval with string search. Works well for the trace verification use case.

gateway/it/config_manager.go (5)

1-11: LGTM!

The package declaration and imports are appropriate for the file's functionality.


13-13: LGTM!

The constant is well-named and follows Godog tag conventions.


15-21: LGTM!

The struct is well-designed with appropriate fields for configuration management and includes a mutex for thread safety.


32-61: Code logic is sound; method signature verification recommended but not blocking.

The mutex protection, early return optimization, and error handling are all correctly implemented. Signature appears correct based on usage context (ctx and profile.EnvVars mapping), but final confirmation of RestartGatewayController(ctx context.Context, envVars map[string]string) error would be ideal to ensure env var updates are properly handled.


24-29: No action needed. The gateway initializes with environment variables (GATEWAY_STORAGE_TYPE=sqlite, GATEWAY_LOGGING_LEVEL=info) that exactly match the "default" profile definition. The assumption is valid and will not cause test flakiness.

Likely an incorrect or invalid review comment.

Scenario: API invocation generates a trace
Given the Gateway is running with tracing enabled
And I have a valid API Key for the "Sales API"
When I send a GET request to "http://localhost:9090/api/v1/sales/orders"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine docker-compose.test.yaml
find . -name "docker-compose.test.yaml" -o -name "docker-compose*.yaml" | head -20

Repository: wso2/api-platform

Length of output: 265


🏁 Script executed:

#!/bin/bash
# Find the tracing.feature file and examine it
find . -name "tracing.feature" -type f | head -20

Repository: wso2/api-platform

Length of output: 98


🏁 Script executed:

#!/bin/bash
# Search for port definitions in key files
rg -n "9090|8080" --max-count=20

Repository: wso2/api-platform

Length of output: 50375


🏁 Script executed:

#!/bin/bash
# Read docker-compose.test.yaml to check port mappings
cat gateway/it/docker-compose.test.yaml | grep -A5 -B5 "ports:"

Repository: wso2/api-platform

Length of output: 1616


🏁 Script executed:

#!/bin/bash
# Read tracing.feature to see line 10 and surrounding context
head -20 gateway/it/features/tracing.feature | cat -n

Repository: wso2/api-platform

Length of output: 657


🏁 Script executed:

#!/bin/bash
# Look for port constants in setup.go or similar
find gateway -name "setup.go" -o -name "config*.go" | head -5

Repository: wso2/api-platform

Length of output: 235


🏁 Script executed:

#!/bin/bash
# Check setup.go to understand how the test harness is configured
cat gateway/it/setup.go | grep -A5 -B5 "9090\|8080" | head -50

Repository: wso2/api-platform

Length of output: 795


Port 9090 is for the gateway-controller REST API, not for API requests.

The request should use http://localhost:8080/api/v1/sales/orders instead. The router service exposes HTTP traffic on port 8080, while port 9090 is reserved for the gateway-controller management API.

🤖 Prompt for AI Agents
gateway/it/features/tracing.feature around line 10: the test sends the GET
request to http://localhost:9090/api/v1/sales/orders but port 9090 is for the
gateway-controller management API; update the request URL to use port 8080
(http://localhost:8080/api/v1/sales/orders) so the test targets the router
service HTTP endpoint.

Comment on lines +7 to +9
exporters:
logging:
loglevel: debug
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace deprecated loglevel with verbosity configuration.

The loglevel configuration key is deprecated in favor of verbosity. Update the logging exporter configuration to use verbosity: detailed instead of loglevel: debug.

🤖 Prompt for AI Agents
In gateway/it/otel-config.yaml around lines 7 to 9, the logging exporter uses
the deprecated key `loglevel: debug`; replace that key with `verbosity:
detailed` so the exporter configuration reads the new `verbosity` setting
(ensure indentation and YAML formatting remain unchanged and no other keys are
modified).

Comment on lines 193 to 207
// Start with new env vars
args := []string{"compose", "-f", cm.composeFile, "-p", cm.projectName, "up", "-d", "gateway-controller"}
cmd := execCommandContext(ctx, "docker", args...)

// Copy existing env and append new ones
cmd.Env = os.Environ()
for k, v := range envVars {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
log.Printf("Setting env: %s=%s", k, v)
}

output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to start gateway-controller: %w\nOutput: %s", err, string(output))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Environment variables set via cmd.Env will not propagate to the container.

Setting environment variables on the docker compose up command only affects the Docker CLI process, not the container itself. Docker Compose reads environment variables for the container from the compose file's environment section or from .env files.

To dynamically set container environment variables, you need to either:

  1. Use docker compose run with -e flags
  2. Modify the compose file dynamically
  3. Use an .env file that gets updated
  4. Use docker run directly with -e flags
🔎 Possible fix using docker run

One approach is to use docker run directly instead of docker compose up:

// Build args with environment variables
args := []string{"run", "-d", "--name", "it-gateway-controller",
    "--network", "gateway-it_it-gateway-network",
    // Add other necessary mounts and ports from compose file
}
for k, v := range envVars {
    args = append(args, "-e", fmt.Sprintf("%s=%s", k, v))
}
args = append(args, "ghcr.io/wso2/api-platform/gateway-controller-coverage:0.1.0-SNAPSHOT")

cmd := execCommandContext(ctx, "docker", args...)

However, this requires duplicating the container configuration. A better approach might be to use a templated compose file or environment file.

🤖 Prompt for AI Agents
In gateway/it/setup.go around lines 193-207, environment variables appended to
cmd.Env won't be passed into containers by "docker compose up"; instead create a
temporary .env file containing each "KEY=VALUE" from envVars, update the docker
compose command args to include "--env-file", "-f", cm.composeFile, "-p",
cm.projectName, "up", "-d", "gateway-controller" (removing the manual cmd.Env
population), write the envVars to that file before running the command, ensure
the file is closed and removed afterward (or reused appropriately), and keep
cmd.Env minimal (e.g., os.Environ()) so the Docker CLI inherits normal
environment.

EnvoyAdminPort, // 9901
"9002", // Policy engine
"5000", // Sample backend
"5050", // Sample backend
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Port check mismatch with docker-compose.test.yaml.

The port check uses 5050 but docker-compose.test.yaml shows sample-backend on port 5000:5000. This inconsistency will cause false port conflict detection.

🔎 Suggested fix
-		"5050",                // Sample backend
+		"5000",                // Sample backend
📝 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
"5050", // Sample backend
"5000", // Sample backend
🤖 Prompt for AI Agents
In gateway/it/setup.go around line 396, the hard-coded sample backend port is
set to "5050" but docker-compose.test.yaml maps the sample-backend to 5000:5000;
update the port check to use "5000" (or better, derive the expected port from
the docker-compose/test config or an environment variable) so the test's port
conflict detection matches docker-compose.test.yaml.

Copy link
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 (2)
gateway/it/setup.go (2)

396-396: Port check mismatch with docker-compose.test.yaml.

The sample backend port is set to "5050", but according to previous review comments, docker-compose.test.yaml maps the sample-backend to 5000:5000. This inconsistency will cause false port conflict detection.

🔎 Suggested fix
-		"5050",                // Sample backend
+		"5000",                // Sample backend

Based on past review comments, this issue was previously identified but remains unresolved.


193-207: Environment variables won't propagate to the container.

Setting cmd.Env only affects the docker compose CLI process environment, not the container's runtime environment. Docker Compose reads container environment variables from the compose file's environment section, .env files, or variable substitution (${VAR}).

Since the purpose of this method is to dynamically restart the gateway-controller with profile-specific environment variables, the current implementation will fail to apply those variables.

🔎 Recommended fix using temporary .env file
 // Start with new env vars
+// Create temporary .env file for docker compose
+envFile, err := os.CreateTemp("", "gateway-it-*.env")
+if err != nil {
+	return fmt.Errorf("failed to create temp env file: %w", err)
+}
+defer os.Remove(envFile.Name())
+defer envFile.Close()
+
+for k, v := range envVars {
+	if _, err := fmt.Fprintf(envFile, "%s=%s\n", k, v); err != nil {
+		return fmt.Errorf("failed to write env file: %w", err)
+	}
+	log.Printf("Setting env: %s=%s", k, v)
+}
+if err := envFile.Close(); err != nil {
+	return fmt.Errorf("failed to close env file: %w", err)
+}
+
-args := []string{"compose", "-f", cm.composeFile, "-p", cm.projectName, "up", "-d", "gateway-controller"}
+args := []string{"compose", "-f", cm.composeFile, "--env-file", envFile.Name(), "-p", cm.projectName, "up", "-d", "gateway-controller"}
 cmd := execCommandContext(ctx, "docker", args...)
-
-// Copy existing env and append new ones
-cmd.Env = os.Environ()
-for k, v := range envVars {
-	cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
-	log.Printf("Setting env: %s=%s", k, v)
-}

 output, err := cmd.CombinedOutput()

Note: This approach requires the compose file's environment section to reference the variables (e.g., MY_VAR: ${MY_VAR}), or use env_file directive. Verify the compose file is configured accordingly.

Based on past review comments, this issue was previously identified but remains unresolved.

🧹 Nitpick comments (1)
gateway/it/setup.go (1)

344-358: Consider logging successful streaming start.

The implementation correctly handles context cancellation and error suppression during shutdown. For enhanced observability, consider logging when streaming successfully starts, not just when it stops.

🔎 Suggested enhancement
 func (cm *ComposeManager) StreamLogs() {
 	go func() {
 		log.Println("Streaming logs from containers...")
 		cmd := execCommandContext(cm.ctx, "docker", "compose", "-f", cm.composeFile, "-p", cm.projectName, "logs", "-f")
 		cmd.Stdout = os.Stdout
 		cmd.Stderr = os.Stderr
+		log.Println("Log streaming started successfully")
 		if err := cmd.Run(); err != nil {
 			// Don't log error on context cancellation (standard shutdown)
 			if cm.ctx.Err() == nil {
 				log.Printf("Background log streaming stopped: %v", err)
 			}
 		}
 	}()
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77ba8f6 and 805d74d.

📒 Files selected for processing (1)
  • gateway/it/setup.go
🔇 Additional comments (5)
gateway/it/setup.go (5)

31-31: LGTM!

The strings import is appropriately added to support the strings.Contains call in the new CheckLogsForText method.


143-144: LGTM!

Starting log streaming immediately after bringing up services provides valuable observability for debugging test failures.


213-242: LGTM!

The health check implementation correctly uses a ticker-based polling loop with context timeout and proper resource cleanup.


335-342: LGTM!

The cleanup method properly uses RemoveOrphans and RemoveImagesLocal options with an appropriate timeout to ensure thorough resource cleanup.


360-372: LGTM!

The log inspection method correctly retrieves container logs and performs text matching. Note that strings.Contains is case-sensitive, which should be acceptable for trace verification use cases.

Copy link
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: 1

♻️ Duplicate comments (1)
gateway/it/setup.go (1)

513-513: Port check now correctly uses 5050.

This aligns with the updated docker-compose.test.yaml sample-backend port mapping. The earlier review concern is resolved.

🧹 Nitpick comments (4)
gateway/it/docker-compose.test.yaml (1)

67-77: Consider adding a healthcheck for otel-collector.

The otel-collector service lacks a healthcheck, which could cause flaky tests if dependent services send traces before the collector is ready. The collector exposes a health endpoint that can be used.

🔎 Suggested healthcheck configuration
   otel-collector:
     image: otel/opentelemetry-collector-contrib:0.88.0
     container_name: it-otel-collector
     command: ["--config=/etc/otel-collector-config.yaml"]
     volumes:
       - ./otel-config.yaml:/etc/otel-collector-config.yaml:ro
     ports:
       - "4317:4317" # OTLP gRPC receiver
       - "4318:4318" # OTLP HTTP receiver
+    healthcheck:
+      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:13133/"]
+      interval: 5s
+      timeout: 3s
+      retries: 5
+      start_period: 5s
     networks:
       - it-gateway-network

Note: This requires enabling the health_check extension in otel-config.yaml.

gateway/it/configs/config.yaml (2)

371-384: Consider using placeholder values for KeyManager configurations.

The JWKS and issuer URLs reference real Asgardeo tenant names (tharsanan1995, testorgforsecurity). While these are public OAuth endpoints, using real tenant identifiers in test configurations could:

  1. Cause test failures if these tenants are modified/deleted
  2. Expose organization identifiers in the codebase

Consider using placeholder URLs or mock endpoints for test configurations.


316-317: Placeholder token needs clear documentation.

The <MOESIF_APPLICATION_ID> placeholder is good, but consider adding a comment indicating this must be replaced or that analytics will fail gracefully when unconfigured.

gateway/it/setup.go (1)

263-265: Consider making xDS propagation delay configurable.

The hardcoded 3-second sleep is a magic number. Consider extracting this as a constant or making it configurable for environments with different performance characteristics.

🔎 Suggested refactor
+const (
+	// XDSPropagationDelay is the time to wait for xDS configuration to propagate
+	XDSPropagationDelay = 3 * time.Second
+)

 // Additional wait for xDS configuration to propagate
 log.Println("Waiting for xDS configuration to propagate...")
-time.Sleep(3 * time.Second)
+time.Sleep(XDSPropagationDelay)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 805d74d and a1ced6f.

📒 Files selected for processing (8)
  • gateway/it/Makefile
  • gateway/it/configs/config.yaml
  • gateway/it/docker-compose.test.yaml
  • gateway/it/features/tracing.feature
  • gateway/it/setup.go
  • gateway/it/state.go
  • gateway/it/suite_test.go
  • gateway/it/tracing_steps.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • gateway/it/features/tracing.feature
  • gateway/it/tracing_steps.go
  • gateway/it/suite_test.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-18T08:49:15.812Z
Learnt from: RakhithaRR
Repo: wso2/api-platform PR: 504
File: gateway/policies/mcp-auth/v0.1.0/policy-definition.yaml:118-191
Timestamp: 2025-12-18T08:49:15.812Z
Learning: The policy engine in the gateway validates configuration paths (e.g., `${config.policy_configurations.jwtauth_v010.*}`) in a case-insensitive manner, so references using lowercase (jwtauth_v010) will correctly match configuration keys using mixed case (JWTAuth_v010) in the Helm values or config files.

Applied to files:

  • gateway/it/configs/config.yaml
📚 Learning: 2025-12-19T06:57:38.504Z
Learnt from: tharindu1st
Repo: wso2/api-platform PR: 514
File: gateway/gateway-controller/config/config.yaml:0-0
Timestamp: 2025-12-19T06:57:38.504Z
Learning: In gateway/gateway-controller/config/config.yaml, the default admin user with plaintext password "admin" is intentionally provided for testing purposes only and must be changed to secure hashed passwords for production deployments.

Applied to files:

  • gateway/it/configs/config.yaml
🔇 Additional comments (12)
gateway/it/state.go (1)

50-50: LGTM!

The SampleBackendURL port update to 5050 correctly aligns with the docker-compose.test.yaml port mapping change (5050:5000).

gateway/it/docker-compose.test.yaml (3)

27-34: Port and tracing configuration looks correct.

The gateway-controller port mapping change to 9099:9090 aligns with the GatewayControllerPort constant in setup.go. The GATEWAY_TRACING_ENABLED environment variable with a default of false follows a good pattern for optional feature toggling.


57-58: LGTM!

The GATEWAY_TRACING_ENABLED environment variable is correctly propagated to the policy-engine service with the same default value pattern.


106-106: Port mapping change is consistent.

The sample-backend port mapping update to 5050:5000 correctly aligns with the SampleBackendURL in state.go.

gateway/it/configs/config.yaml (2)

203-206: Plain text admin credentials are acceptable for testing.

Based on learnings, the default admin user with plaintext password "admin" is intentionally provided for testing purposes only. This should not be used in production deployments.


252-254: Overly permissive allowed_ips configuration.

The allowed_ips list includes "*" which permits access from any IP. While acceptable for local integration tests, ensure this doesn't accidentally propagate to production configurations.

gateway/it/setup.go (6)

50-50: Port constant correctly updated.

GatewayControllerPort now matches the docker-compose.test.yaml mapping (9099:9090).


95-111: Good use of explicit project name for network consistency.

Using tc.StackIdentifier("gateway-it") ensures all containers share the same network, which is essential for RestartGatewayController to reconnect services properly.


213-224: Environment variable propagation is correct for compose variable substitution.

Setting cmd.Env with the environment variables works correctly here because docker compose reads shell-style variable substitutions (like ${GATEWAY_TRACING_ENABLED:-false}) from its parent process environment. This addresses the earlier review concern — the pattern is valid for variable substitution in the compose file.


271-332: Health check methods follow good patterns.

Both WaitForGatewayControllerHealthy and WaitForRouterReady properly use context timeouts, tickers, and resource cleanup. The 30-second timeout and 500ms polling interval are reasonable defaults.

Minor observation: These methods share similar structure and could be consolidated into a generic helper, but the current implementation is clear and maintainable.


425-447: Enhanced cleanup handles edge cases well.

The dual cleanup approach (testcontainers-go down + direct docker compose down) ensures containers restarted via RestartGatewayController are properly cleaned up. Explicit volume removal is a good defensive measure.


467-478: Log inspection methods are straightforward and correct.

CheckLogsForText properly handles the container name (using explicit container_name from compose) and returns useful error context.

Comment on lines +23 to +25
# Uses script to force unbuffered output on macOS
test:
go test -v ./...
script -q /dev/null go test -v -count=1 ./...
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "Makefile" -path "*/gateway/it/*" -type f | head -5

Repository: wso2/api-platform

Length of output: 82


🏁 Script executed:

if [ -f "gateway/it/Makefile" ]; then
  cat -n "gateway/it/Makefile" | head -50
else
  find . -name "Makefile" | grep -E "(gateway|it)" | head -10
fi

Repository: wso2/api-platform

Length of output: 2154


🏁 Script executed:

ls -la gateway/it/ 2>/dev/null || find . -type d -name "it" | head -5

Repository: wso2/api-platform

Length of output: 1571


🏁 Script executed:

find . -path "*/gateway/it/Makefile" -type f 2>/dev/null && cat -n ./gateway/it/Makefile | sed -n '20,40p'

Repository: wso2/api-platform

Length of output: 790


Portability issue: script -q /dev/null syntax is macOS/BSD-specific and will fail on Linux.

The test target uses BSD-style script -q /dev/null syntax, which is incompatible with GNU script on Linux systems. This breaks cross-platform development and CI/CD pipelines running on Linux.

Additionally, the test-verbose target (line 36) runs tests without the script wrapper, creating inconsistent behavior between the two targets.

The -count=1 flag is correct for disabling test caching in integration tests. However, go test -v already provides line-buffered streaming output; the script wrapper may be unnecessary. Consider either:

  • Removing the wrapper and relying on go test -v output buffering
  • Using OS detection to apply the wrapper only on macOS (if unbuffered output is critical)
🤖 Prompt for AI Agents
In gateway/it/Makefile lines 23-25 the test target uses macOS/BSD-specific
`script -q /dev/null` which fails on Linux; remove the script wrapper and run
`go test -v -count=1 ./...` directly (or add OS detection to apply the wrapper
only on macOS if truly required), and make the test-verbose target consistent
with the change so both targets behave the same across platforms.

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.

1 participant