diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4d1c4d6..72a6e43 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -67,6 +67,12 @@ jobs: go-version: "1.24" cache: true + - name: Create k8s Kind Cluster + uses: helm/kind-action@v1 + with: + cluster_name: kagent + config: scripts/kind/kind-config.yaml + - name: Run cmd/main.go tests working-directory: . run: | diff --git a/.github/workflows/tag.yaml b/.github/workflows/tag.yaml index 025642b..a1fceee 100644 --- a/.github/workflows/tag.yaml +++ b/.github/workflows/tag.yaml @@ -50,6 +50,7 @@ jobs: export VERSION=$(echo "$GITHUB_REF" | cut -c12-) fi make docker-build + make helm-publish release: # Only run release after images and helm chart are pushed # In the future we can take the chart from the helm action, diff --git a/.gitignore b/.gitignore index 2496f99..6bd4b64 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ bin/ /kagent-tools /*.out *.html +/helm/kagent-tools/Chart.yaml +/reports/tools-cve.csv diff --git a/Makefile b/Makefile index 1d9fdd1..dea88a8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,14 @@ DOCKER_REGISTRY ?= ghcr.io BASE_IMAGE_REGISTRY ?= cgr.dev + DOCKER_REPO ?= kagent-dev/kagent + +HELM_REPO ?= oci://ghcr.io/kagent-dev +HELM_ACTION=upgrade --install + KIND_CLUSTER_NAME ?= kagent +KIND_IMAGE_VERSION ?= 1.33.1 +KIND_CREATE_CMD ?= "kind create cluster --name $(KIND_CLUSTER_NAME) --image kindest/node:v$(KIND_IMAGE_VERSION) --config ./scripts/kind/kind-config.yaml" BUILD_DATE := $(shell date -u '+%Y-%m-%d') GIT_COMMIT := $(shell git rev-parse --short HEAD || echo "unknown") @@ -12,6 +19,7 @@ LDFLAGS := -X github.com/kagent-dev/tools/internal/version.Version=$(VERSION) -X ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin +HELM_DIST_FOLDER ?= $(shell pwd)/dist .PHONY: clean clean: @@ -55,8 +63,8 @@ test-only: ## Run tests only (without build/lint for faster iteration) go test -tags=test -v -cover ./pkg/... ./internal/... .PHONY: e2e -e2e: test docker-build - go test -tags=test -v -cover ./e2e/... +e2e: test retag + go test -v -tags=test -cover ./test/e2e/ -timeout 5m bin/kagent-tools-linux-amd64: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/kagent-tools-linux-amd64 ./cmd @@ -89,7 +97,7 @@ bin/kagent-tools-windows-amd64.exe.sha256: bin/kagent-tools-windows-amd64.exe sha256sum bin/kagent-tools-windows-amd64.exe > bin/kagent-tools-windows-amd64.exe.sha256 .PHONY: build -build: $(LOCALBIN) bin/kagent-tools-linux-amd64.sha256 bin/kagent-tools-linux-arm64.sha256 bin/kagent-tools-darwin-amd64.sha256 bin/kagent-tools-darwin-arm64.sha256 bin/kagent-tools-windows-amd64.exe.sha256 +build: $(LOCALBIN) clean bin/kagent-tools-linux-amd64.sha256 bin/kagent-tools-linux-arm64.sha256 bin/kagent-tools-darwin-amd64.sha256 bin/kagent-tools-darwin-arm64.sha256 bin/kagent-tools-windows-amd64.exe.sha256 build: @echo "Build complete. Binaries are available in the bin/ directory." ls -lt bin/kagent-tools-* @@ -100,8 +108,10 @@ run: docker-build @echo "Use: npx @modelcontextprotocol/inspector to connect to the tool server" @docker run --rm --net=host -p 8084:8084 -e OPENAI_API_KEY=$(OPENAI_API_KEY) -v $(HOME)/.kube:/home/nonroot/.kube -e KAGENT_TOOLS_PORT=8084 $(TOOLS_IMG) -- --kubeconfig /root/.kube/config -PHONY: retag -retag: docker-build +.PHONY: retag +retag: docker-build helm-version + @echo "Check Kind cluster $(KIND_CLUSTER_NAME) exists" + kind get clusters | grep -q $(KIND_CLUSTER_NAME) || bash -c $(KIND_CREATE_CMD) @echo "Retagging tools image to $(RETAGGED_TOOLS_IMG)" docker tag $(TOOLS_IMG) $(RETAGGED_TOOLS_IMG) kind load docker-image --name $(KIND_CLUSTER_NAME) $(RETAGGED_TOOLS_IMG) @@ -127,7 +137,7 @@ DOCKER_BUILD_ARGS ?= --pull --load --platform linux/$(LOCALARCH) --builder $(BUI TOOLS_ISTIO_VERSION ?= 1.26.2 TOOLS_ARGO_ROLLOUTS_VERSION ?= 1.8.3 TOOLS_KUBECTL_VERSION ?= 1.33.2 -TOOLS_HELM_VERSION ?= 3.18.3 +TOOLS_HELM_VERSION ?= 3.18.4 TOOLS_CILIUM_VERSION ?= 0.18.5 # build args @@ -155,11 +165,55 @@ docker-build-all: DOCKER_BUILD_ARGS = --progress=plain --builder $(BUILDX_BUILDE docker-build-all: $(DOCKER_BUILDER) build $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -f Dockerfile ./ +.PHONY: helm-version +helm-version: + VERSION=$(VERSION) envsubst < helm/kagent-tools/Chart-template.yaml > helm/kagent-tools/Chart.yaml + mkdir -p $(HELM_DIST_FOLDER) + helm package -d $(HELM_DIST_FOLDER) helm/kagent-tools + +.PHONY: helm-uninstall +helm-uninstall: + helm uninstall kagent --namespace kagent --kube-context kind-$(KIND_CLUSTER_NAME) --wait + +.PHONY: helm-install +helm-install: helm-version + helm $(HELM_ACTION) kagent-tools ./helm/kagent-tools \ + --kube-context kind-$(KIND_CLUSTER_NAME) \ + --namespace kagent \ + --create-namespace \ + --history-max 2 \ + --timeout 5m \ + -f ./scripts/kind/test-values.yaml \ + --set tools.image.registry=$(RETAGGED_DOCKER_REGISTRY) \ + --wait + +.PHONY: helm-publish +helm-publish: helm-version + helm push ./$(HELM_DIST_FOLDER)/kagent-tools-$(VERSION).tgz $(HELM_REPO)/tools/helm + +.PHONY: create-kind-cluster +create-kind-cluster: + docker pull kindest/node:v$(KIND_IMAGE_VERSION) || true + bash -c $(KIND_CREATE_CMD) + +.PHONY: delete-kind-cluster +delete-kind-cluster: + kind delete cluster --name $(KIND_CLUSTER_NAME) + .PHONY: kind-update-kagent -kind-update-kagent: docker-build - kind get clusters | grep -q $(KIND_CLUSTER_NAME) || kind create cluster --name $(KIND_CLUSTER_NAME) - kind load docker-image --name $(KIND_CLUSTER_NAME) $(TOOLS_IMG) - kubectl patch --namespace kagent deployment/kagent --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/3/image", "value": "$(TOOLS_IMG)"}]' +kind-update-kagent: retag + kubectl patch --namespace kagent deployment/kagent --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/3/image", "value": "$(RETAGGED_TOOLS_IMG)"}]' + +.PHONY: otel-local +otel-local: + docker rm -f jaeger-desktop || true + docker run -d --name jaeger-desktop --restart=always -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/jaeger:2.7.0 + open http://localhost:16686/ + +.PHONY: report/image-cve +report/image-cve: docker-build govulncheck + echo "Running CVE scan :: CVE -> CSV ... reports/$(SEMVER)/" + grype docker:$(TOOLS_IMG) -o template -t reports/cve-report.tmpl --file reports/$(SEMVER)/tools-cve.csv ## Tool Binaries ## Location to install dependencies t diff --git a/README.md b/README.md index 56ab7c6..f26ccf9 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,21 @@ This directory contains the Go implementation of all KAgent tools, migrated from the original Python implementation. The tools are designed to work with the Model Context Protocol (MCP) server and provide comprehensive Kubernetes, cloud-native, and observability functionality. +## Installation + +- **Bash:** + +`curl -sL https://github.com/kagent-dev/tools/blob/main/scripts/install.sh | bash` + +- **Docker:** + +`docker run -it --rm ghcr.io/kagent-dev/kagent/tools:` + +- **Kubernetes** + +`helm upgrade -i kagent-tools --version oci://ghcr.io/kagent-dev/tools/helm/` + + ## Architecture The Go tools are implemented as a single MCP server that exposes all available tools through the MCP protocol. diff --git a/cmd/main.go b/cmd/main.go index 6ea40ae..03a2665 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -33,10 +33,11 @@ import ( ) var ( - port int - stdio bool - tools []string - kubeconfig *string + port int + stdio bool + tools []string + kubeconfig *string + showVersion bool // These variables should be set during build time using -ldflags Name = "kagent-tools-server" @@ -55,6 +56,7 @@ func init() { rootCmd.Flags().IntVarP(&port, "port", "p", 8084, "Port to run the server on") rootCmd.Flags().BoolVar(&stdio, "stdio", false, "Use stdio for communication instead of HTTP") rootCmd.Flags().StringSliceVar(&tools, "tools", []string{}, "List of tools to register. If empty, all tools are registered.") + rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "Show version information and exit") kubeconfig = rootCmd.Flags().String("kubeconfig", "", "kubeconfig file path (optional, defaults to in-cluster config)") // if found .env file, load it @@ -70,7 +72,23 @@ func main() { } } +// printVersion displays version information in a formatted way +func printVersion() { + fmt.Printf("%s\n", Name) + fmt.Printf("Version: %s\n", Version) + fmt.Printf("Git Commit: %s\n", GitCommit) + fmt.Printf("Build Date: %s\n", BuildDate) + fmt.Printf("Go Version: %s\n", runtime.Version()) + fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH) +} + func run(cmd *cobra.Command, args []string) { + // Handle version flag early, before any initialization + if showVersion { + printVersion() + return + } + logger.Init() defer logger.Sync() @@ -130,7 +148,9 @@ func run(cmd *cobra.Command, args []string) { runStdioServer(ctx, mcp) }() } else { - sseServer := server.NewStreamableHTTPServer(mcp) + sseServer := server.NewStreamableHTTPServer(mcp, + server.WithHeartbeatInterval(30*time.Second), + ) // Create a mux to handle different routes mux := http.NewServeMux() diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go deleted file mode 100644 index ec04751..0000000 --- a/e2e/e2e_test.go +++ /dev/null @@ -1,1005 +0,0 @@ -package e2e - -import ( - "bufio" - "context" - "fmt" - "io" - "net/http" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// getBinaryName returns the platform-specific binary name -func getBinaryName() string { - osName := runtime.GOOS - archName := runtime.GOARCH - return fmt.Sprintf("kagent-tools-%s-%s", osName, archName) -} - -// TestServerConfig holds configuration for server tests -type TestServerConfig struct { - Port int - Tools []string - Kubeconfig string - Stdio bool - Timeout time.Duration -} - -// ServerTestResult holds the result of a server test -type ServerTestResult struct { - Output string - Error error - Duration time.Duration -} - -// TestServer represents a test server instance -type TestServer struct { - cmd *exec.Cmd - port int - stdio bool - cancel context.CancelFunc - done chan struct{} - output strings.Builder - mu sync.RWMutex -} - -// NewTestServer creates a new test server instance -func NewTestServer(config TestServerConfig) *TestServer { - return &TestServer{ - port: config.Port, - stdio: config.Stdio, - done: make(chan struct{}), - } -} - -// Start starts the test server -func (ts *TestServer) Start(ctx context.Context, config TestServerConfig) error { - ts.mu.Lock() - defer ts.mu.Unlock() - - // Build command arguments - args := []string{} - if config.Stdio { - args = append(args, "--stdio") - } else { - args = append(args, "--port", fmt.Sprintf("%d", config.Port)) - } - - if len(config.Tools) > 0 { - args = append(args, "--tools", strings.Join(config.Tools, ",")) - } - - if config.Kubeconfig != "" { - args = append(args, "--kubeconfig", config.Kubeconfig) - } - - // Create context with cancellation - ctx, cancel := context.WithCancel(ctx) - ts.cancel = cancel - - // Start server process - binaryName := getBinaryName() - ts.cmd = exec.CommandContext(ctx, fmt.Sprintf("../bin/%s", binaryName), args...) - ts.cmd.Env = append(os.Environ(), "LOG_LEVEL=debug") - - // Set up output capture - stdout, err := ts.cmd.StdoutPipe() - if err != nil { - return fmt.Errorf("failed to create stdout pipe: %w", err) - } - - stderr, err := ts.cmd.StderrPipe() - if err != nil { - return fmt.Errorf("failed to create stderr pipe: %w", err) - } - - // Start the command - if err := ts.cmd.Start(); err != nil { - return fmt.Errorf("failed to start server: %w", err) - } - - // Start goroutines to capture output - go ts.captureOutput(stdout, "STDOUT") - go ts.captureOutput(stderr, "STDERR") - - // Wait for server to start - if !config.Stdio { - return ts.waitForHTTPServer(ctx, config.Timeout) - } - - return nil -} - -// Stop stops the test server -func (ts *TestServer) Stop() error { - ts.mu.Lock() - defer ts.mu.Unlock() - - if ts.cancel != nil { - ts.cancel() - } - - if ts.cmd != nil && ts.cmd.Process != nil { - // Send interrupt signal for graceful shutdown - if err := ts.cmd.Process.Signal(os.Interrupt); err != nil { - // If interrupt fails, kill the process - _ = ts.cmd.Process.Kill() - } - - // Wait for process to exit with timeout - done := make(chan error, 1) - go func() { - done <- ts.cmd.Wait() - }() - - select { - case <-done: - // Process exited - case <-time.After(5 * time.Second): - // Timeout, force kill - _ = ts.cmd.Process.Kill() - } - } - - close(ts.done) - return nil -} - -// GetOutput returns the captured output -func (ts *TestServer) GetOutput() string { - ts.mu.RLock() - defer ts.mu.RUnlock() - return ts.output.String() -} - -// captureOutput captures output from the server -func (ts *TestServer) captureOutput(reader io.Reader, prefix string) { - scanner := bufio.NewScanner(reader) - for scanner.Scan() { - line := scanner.Text() - ts.mu.Lock() - ts.output.WriteString(fmt.Sprintf("[%s] %s\n", prefix, line)) - ts.mu.Unlock() - } -} - -// waitForHTTPServer waits for the HTTP server to become available -func (ts *TestServer) waitForHTTPServer(ctx context.Context, timeout time.Duration) error { - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - url := fmt.Sprintf("http://localhost:%d/health", ts.port) - ticker := time.NewTicker(100 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return fmt.Errorf("timeout waiting for server to start") - case <-ticker.C: - resp, err := http.Get(url) - if err == nil { - resp.Body.Close() - if resp.StatusCode == http.StatusOK { - return nil - } - } - } - } -} - -// TestHTTPServerStartup tests basic HTTP server startup and shutdown -func TestHTTPServerStartup(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8085, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait a bit for server to be fully ready - time.Sleep(3 * time.Second) - - // Test health endpoint - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) - require.NoError(t, err, "Health endpoint should be accessible") - assert.Equal(t, http.StatusOK, resp.StatusCode) - resp.Body.Close() - - // Check server output - output := server.GetOutput() - assert.Contains(t, output, "Running KAgent Tools Server") - assert.Contains(t, output, fmt.Sprintf(":%d", config.Port)) - - // Stop server - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") - - // Verify server is stopped - time.Sleep(1 * time.Second) - _, err = http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) - assert.Error(t, err, "Server should not be accessible after stop") -} - -// TestHTTPServerWithSpecificTools tests server with specific tools enabled -func TestHTTPServerWithSpecificTools(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8086, - Tools: []string{"utils", "k8s"}, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output for tool registration - output := server.GetOutput() - assert.Contains(t, output, "RegisterTools initialized", "Should register specified tools") - assert.Contains(t, output, "utils", "Should register utils tools") - assert.Contains(t, output, "k8s", "Should register k8s tools") - - // Stop server - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestHTTPServerWithAllTools tests server with all tools enabled (default) -func TestHTTPServerWithAllTools(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8087, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output for all tools registration - output := server.GetOutput() - assert.Contains(t, output, "RegisterTools initialized", "Should initialize RegisterTools") - - // Verify server is running (tools are implicitly registered when no specific tools are provided) - assert.Contains(t, output, "Running KAgent Tools Server", "Should be running with all tools") - - // Stop server - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestHTTPServerWithKubeconfig tests server with kubeconfig parameter -func TestHTTPServerWithKubeconfig(t *testing.T) { - ctx := context.Background() - - // Create a temporary kubeconfig file - tempDir := t.TempDir() - kubeconfigPath := filepath.Join(tempDir, "kubeconfig") - - kubeconfigContent := `apiVersion: v1 -kind: Config -clusters: -- cluster: - server: https://test-cluster - name: test-cluster -contexts: -- context: - cluster: test-cluster - user: test-user - name: test-context -current-context: test-context -users: -- name: test-user - user: - token: test-token -` - - err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644) - require.NoError(t, err, "Should create temporary kubeconfig file") - - config := TestServerConfig{ - Port: 8088, - Kubeconfig: kubeconfigPath, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err = server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output for kubeconfig setting - output := server.GetOutput() - assert.Contains(t, output, "RegisterTools initialized", "Should initialize RegisterTools") - assert.Contains(t, output, "Running KAgent Tools Server", "Should be running with kubeconfig") - - // Stop server - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestStdioServer tests STDIO server mode -func TestStdioServer(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Stdio: true, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output for STDIO mode - output := server.GetOutput() - assert.Contains(t, output, "Running KAgent Tools Server STDIO") - - // Stop server - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestServerGracefulShutdown tests graceful shutdown behavior -func TestServerGracefulShutdown(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8100, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Stop server and measure shutdown time - start := time.Now() - err = server.Stop() - duration := time.Since(start) - - require.NoError(t, err, "Server should stop gracefully") - assert.Less(t, duration, 10*time.Second, "Shutdown should complete within reasonable time") - - // Wait a bit for shutdown logs to be captured - time.Sleep(3 * time.Second) - - // Check server output for graceful shutdown - output := server.GetOutput() - // The main test is that the server started successfully and stopped without error - assert.Contains(t, output, "Running KAgent Tools Server", "Server should have started successfully") - - // Try to verify the server is actually stopped by attempting to connect - _, err = http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) - assert.Error(t, err, "Server should not be accessible after stop") -} - -// TestServerWithInvalidTool tests server behavior with invalid tool names -func TestServerWithInvalidTool(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8090, - Tools: []string{"invalid-tool", "utils"}, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start even with invalid tools") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output for error about invalid tool - output := server.GetOutput() - assert.Contains(t, output, "Unknown tool specified") - assert.Contains(t, output, "invalid-tool") - - // Valid tools should still be registered - assert.Contains(t, output, "RegisterTools initialized") - assert.Contains(t, output, "utils") - - // Stop server - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestServerVersionAndBuildInfo tests server version and build information -func TestServerVersionAndBuildInfo(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8091, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output for version information - output := server.GetOutput() - assert.Contains(t, output, "Starting kagent-tools-server") - assert.Contains(t, output, "version") - - // Stop server - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestConcurrentServerInstances tests running multiple server instances -func TestConcurrentServerInstances(t *testing.T) { - ctx := context.Background() - - var wg sync.WaitGroup - numServers := 3 - servers := make([]*TestServer, numServers) - - // Start multiple servers on different ports - for i := 0; i < numServers; i++ { - wg.Add(1) - go func(index int) { - defer wg.Done() - - config := TestServerConfig{ - Port: 8092 + index, - Tools: []string{"utils"}, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - servers[index] = server - - err := server.Start(ctx, config) - assert.NoError(t, err, fmt.Sprintf("Server %d should start successfully", index)) - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Test health endpoint - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) - assert.NoError(t, err, fmt.Sprintf("Health endpoint should be accessible for server %d", index)) - if resp != nil { - resp.Body.Close() - } - }(i) - } - - wg.Wait() - - // Stop all servers - for i, server := range servers { - if server != nil { - err := server.Stop() - assert.NoError(t, err, fmt.Sprintf("Server %d should stop gracefully", i)) - } - } -} - -// TestServerEnvironmentVariables tests server with environment variables -func TestServerEnvironmentVariables(t *testing.T) { - ctx := context.Background() - - // Set environment variables - originalEnv := os.Environ() - defer func() { - os.Clearenv() - for _, env := range originalEnv { - parts := strings.SplitN(env, "=", 2) - if len(parts) == 2 { - os.Setenv(parts[0], parts[1]) - } - } - }() - - os.Setenv("LOG_LEVEL", "info") - os.Setenv("OTEL_SERVICE_NAME", "test-kagent-tools") - - config := TestServerConfig{ - Port: 8095, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - // Start server - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output - output := server.GetOutput() - assert.Contains(t, output, "Starting kagent-tools-server") - - // Stop server - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestServerBuildAndExecution tests that the server binary exists and is executable -func TestServerBuildAndExecution(t *testing.T) { - // Check if server binary exists - binaryName := getBinaryName() - binaryPath := fmt.Sprintf("../bin/%s", binaryName) - _, err := os.Stat(binaryPath) - if os.IsNotExist(err) { - t.Skip("Server binary not found, skipping test. Run 'make build' first.") - } - require.NoError(t, err, "Server binary should exist") - - // Test --help flag - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, binaryPath, "--help") - output, err := cmd.CombinedOutput() - require.NoError(t, err, "Server should respond to --help flag") - - outputStr := string(output) - assert.Contains(t, outputStr, "KAgent tool server") - assert.Contains(t, outputStr, "--port") - assert.Contains(t, outputStr, "--stdio") - assert.Contains(t, outputStr, "--tools") - assert.Contains(t, outputStr, "--kubeconfig") -} - -// Benchmark tests -func BenchmarkServerStartup(b *testing.B) { - ctx := context.Background() - - for i := 0; i < b.N; i++ { - config := TestServerConfig{ - Port: 8096 + i, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - - start := time.Now() - err := server.Start(ctx, config) - if err != nil { - b.Fatalf("Server startup failed: %v", err) - } - - // Wait for server to be ready - time.Sleep(1 * time.Second) - - duration := time.Since(start) - b.ReportMetric(float64(duration.Nanoseconds()), "startup_time_ns") - - // Stop server - _ = server.Stop() - } -} - -// Helper functions for test setup -func init() { - // Ensure the binary exists before running tests - binaryName := getBinaryName() - binaryPath := fmt.Sprintf("../bin/%s", binaryName) - if _, err := os.Stat(binaryPath); os.IsNotExist(err) { - // Try to build the binary - cmd := exec.Command("make", "build") - cmd.Dir = ".." - if err := cmd.Run(); err != nil { - panic(fmt.Sprintf("Failed to build server binary: %v", err)) - } - } -} - -// TestToolRegistrationValidation tests that tool registration works correctly -func TestToolRegistrationValidation(t *testing.T) { - ctx := context.Background() - - testCases := []struct { - name string - config TestServerConfig - expectedTools []string - shouldFail bool - }{ - { - name: "Register single tool", - config: TestServerConfig{ - Port: 8087, - Tools: []string{"k8s"}, - Timeout: 30 * time.Second, - }, - expectedTools: []string{"k8s"}, - shouldFail: false, - }, - { - name: "Register multiple tools", - config: TestServerConfig{ - Port: 8088, - Tools: []string{"k8s", "prometheus", "utils"}, - Timeout: 30 * time.Second, - }, - expectedTools: []string{"k8s", "prometheus", "utils"}, - shouldFail: false, - }, - { - name: "Register invalid tool", - config: TestServerConfig{ - Port: 8089, - Tools: []string{"invalid-tool"}, - Timeout: 30 * time.Second, - }, - shouldFail: false, - }, - { - name: "Register all tools implicitly", - config: TestServerConfig{ - Port: 8090, - Tools: []string{}, - Timeout: 30 * time.Second, - }, - expectedTools: []string{"utils", "k8s", "prometheus", "helm", "istio", "argo", "cilium"}, - shouldFail: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - server := NewTestServer(tc.config) - err := server.Start(ctx, tc.config) - - if tc.shouldFail { - require.Error(t, err, "Server should fail to start with invalid configuration") - return - } - - require.NoError(t, err, "Server should start successfully") - defer func() { - if err := server.Stop(); err != nil { - t.Errorf("Failed to stop server: %v", err) - } - }() - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Verify registered tools - output := server.GetOutput() - - // Special handling for invalid tool test case - if tc.name == "Register invalid tool" { - assert.Contains(t, output, "Unknown tool specified", "Should warn about invalid tool") - assert.Contains(t, output, "invalid-tool", "Should mention the invalid tool name") - } else { - if tc.name == "Register all tools implicitly" { - // For implicit all tools registration, check for RegisterTools initialized - assert.Contains(t, output, "RegisterTools initialized", "Should initialize RegisterTools") - // Don't check for individual tool names as they're not logged individually - assert.Contains(t, output, "Running KAgent Tools Server", "Should be running with all tools") - } else { - // For specific tools, check for Running server message and tool names - assert.Contains(t, output, "Running KAgent Tools Server", "Should be running server") - for _, tool := range tc.expectedTools { - assert.Contains(t, output, tool, fmt.Sprintf("Should register %s tool", tool)) - } - } - } - - // Test health endpoint - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", tc.config.Port)) - require.NoError(t, err, "Health endpoint should be accessible") - assert.Equal(t, http.StatusOK, resp.StatusCode) - resp.Body.Close() - }) - } -} - -// TestToolExecutionFlow tests the complete flow of tool execution -func TestToolExecutionFlow(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8091, - Tools: []string{"utils"}, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - defer func() { - if err := server.Stop(); err != nil { - t.Errorf("Failed to stop server: %v", err) - } - }() - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Test health endpoint (MCP server doesn't have REST endpoints for tool execution) - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) - require.NoError(t, err, "Should execute request successfully") - defer resp.Body.Close() - - // Check response - assert.Equal(t, http.StatusOK, resp.StatusCode, "Should return OK status") - - // Read response body - body, err := io.ReadAll(resp.Body) - require.NoError(t, err, "Should read response body") - - // Response should contain "OK" - assert.Equal(t, "OK", string(body), "Should return OK response") -} - -// TestServerTelemetry tests that telemetry is properly initialized and working -func TestServerTelemetry(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8092, - Tools: []string{"utils"}, - Timeout: 30 * time.Second, - } - - // Set test environment variables for telemetry - os.Setenv("OTEL_SERVICE_NAME", "kagent-tools-test") - os.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost:4317") - defer os.Unsetenv("OTEL_SERVICE_NAME") - defer os.Unsetenv("OTEL_EXPORTER_OTLP_ENDPOINT") - - server := NewTestServer(config) - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - defer func() { - if err := server.Stop(); err != nil { - t.Errorf("Failed to stop server: %v", err) - } - }() - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output for telemetry initialization - output := server.GetOutput() - assert.Contains(t, output, "Starting kagent-tools-server", "Server should start with telemetry") - - // Make a request to generate telemetry - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) - require.NoError(t, err, "Health endpoint should be accessible") - assert.Equal(t, http.StatusOK, resp.StatusCode) - resp.Body.Close() - - // Check server output for successful startup (telemetry is initialized internally) - output = server.GetOutput() - assert.Contains(t, output, "Running KAgent Tools Server", "Server should be running with telemetry enabled") -} - -// TestToolRegistrationWithInvalidNames tests server behavior with invalid tool names -func TestToolRegistrationWithInvalidNames(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8087, - Tools: []string{"invalid-tool", "not-exists", "k8s"}, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully despite invalid tools") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Check server output for warning messages about invalid tools - output := server.GetOutput() - assert.Contains(t, output, "Unknown tool specified") - assert.Contains(t, output, "invalid-tool") - assert.Contains(t, output, "not-exists") - - // Verify that valid tools were still registered - assert.Contains(t, output, "Running KAgent Tools Server") - assert.Contains(t, output, "k8s") - - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestConcurrentToolExecution tests concurrent tool execution -func TestConcurrentToolExecution(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8088, - Tools: []string{"utils", "k8s"}, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Create multiple concurrent requests - var wg sync.WaitGroup - for i := 0; i < 10; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) - require.NoError(t, err, "Concurrent request %d should succeed", id) - assert.Equal(t, http.StatusOK, resp.StatusCode) - resp.Body.Close() - }(i) - } - - wg.Wait() - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestServerErrorHandling tests server's error handling capabilities -func TestServerErrorHandling(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8089, - Tools: []string{"utils"}, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Test malformed request - req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/nonexistent", config.Port), strings.NewReader("invalid json")) - require.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - client := &http.Client{} - resp, err := client.Do(req) - require.NoError(t, err) - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - resp.Body.Close() - - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestServerMetricsEndpoint tests the metrics endpoint functionality -func TestServerMetricsEndpoint(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8090, - Tools: []string{"utils"}, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Test metrics endpoint - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/metrics", config.Port)) - require.NoError(t, err, "Metrics endpoint should be accessible") - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Read and verify metrics content - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - resp.Body.Close() - - metricsContent := string(body) - assert.Contains(t, metricsContent, "go_") - assert.Contains(t, metricsContent, "process_") - - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} - -// TestToolSpecificFunctionality tests specific functionality of registered tools -func TestToolSpecificFunctionality(t *testing.T) { - ctx := context.Background() - - config := TestServerConfig{ - Port: 8091, - Tools: []string{"utils", "k8s"}, - Stdio: false, - Timeout: 30 * time.Second, - } - - server := NewTestServer(config) - err := server.Start(ctx, config) - require.NoError(t, err, "Server should start successfully") - - // Wait for server to be ready - time.Sleep(3 * time.Second) - - // Test utils tool endpoint - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) - require.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - resp.Body.Close() - - // Verify response format matches expected OK response - assert.Equal(t, "OK", string(body), "Should return OK response") - - err = server.Stop() - require.NoError(t, err, "Server should stop gracefully") -} diff --git a/go.mod b/go.mod index 220ea7f..cde53c8 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,16 @@ module github.com/kagent-dev/tools go 1.24.5 require ( - github.com/go-logr/logr v1.4.3 - github.com/go-logr/stdr v1.2.2 github.com/joho/godotenv v1.5.1 github.com/mark3labs/mcp-go v0.32.0 + github.com/onsi/ginkgo/v2 v2.23.4 + github.com/onsi/gomega v1.37.0 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 + github.com/testcontainers/testcontainers-go v0.38.0 github.com/tmc/langchaingo v0.1.13 go.opentelemetry.io/otel v1.37.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0 go.opentelemetry.io/otel/metric v1.37.0 @@ -19,25 +21,69 @@ require ( ) require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect + github.com/docker/docker v28.2.2+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.8.4 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.1.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pkoukk/tiktoken-go v0.1.6 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shirou/gopsutil/v4 v4.25.5 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cast v1.9.2 // indirect github.com/spf13/pflag v1.0.6 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + golang.org/x/crypto v0.39.0 // indirect golang.org/x/net v0.41.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.33.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/grpc v1.73.0 // indirect diff --git a/go.sum b/go.sum index 3a1cf49..9966e3e 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,46 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= +github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -14,11 +48,20 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= @@ -27,33 +70,94 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mark3labs/mcp-go v0.32.0 h1:fgwmbfL2gbd67obg57OfV2Dnrhs1HtSdlY/i5fn7MU8= github.com/mark3labs/mcp-go v0.32.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw= github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= +github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw= +github.com/testcontainers/testcontainers-go v0.38.0/go.mod h1:C52c9MoHpWO+C4aqmgSU+hxlR5jlEayWtgYrb8Pzz1w= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmc/langchaingo v0.1.13 h1:rcpMWBIi2y3B90XxfE4Ao8dhCQPVDMaNPnN5cGB1CaA= github.com/tmc/langchaingo v0.1.13/go.mod h1:vpQ5NOIhpzxDfTZK9B6tf2GM/MoaHewPWM5KXXGh7hg= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= @@ -74,14 +178,55 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= @@ -93,7 +238,10 @@ google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/helm/kagent-tools/Chart-template.yaml b/helm/kagent-tools/Chart-template.yaml new file mode 100644 index 0000000..6fb1a68 --- /dev/null +++ b/helm/kagent-tools/Chart-template.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: kagent-tools +description: A Helm chart for kagent-tools, +type: application +version: ${VERSION} diff --git a/helm/kagent-tools/templates/NOTES.txt b/helm/kagent-tools/templates/NOTES.txt new file mode 100644 index 0000000..a5be260 --- /dev/null +++ b/helm/kagent-tools/templates/NOTES.txt @@ -0,0 +1,10 @@ +################################ To open kagent UI: ########################################### +# +# This is a Helm chart for Kagent Tools +# +# 1. Forward application port by running these commands in the terminal: +# kubectl -n {{ include "kagent.namespace" . }} port-forward service/{{ .Release.Name }} {{.Values.service.ports.tools.targetPort}}:{{.Values.service.ports.tools.port}} & +# +# 2. Then visit http://127.0.0.1:{{.Values.service.ports.tools.port}}/mcp to use MCP +# +############################################################################################### \ No newline at end of file diff --git a/helm/kagent-tools/templates/_helpers.tpl b/helm/kagent-tools/templates/_helpers.tpl new file mode 100644 index 0000000..7922df0 --- /dev/null +++ b/helm/kagent-tools/templates/_helpers.tpl @@ -0,0 +1,75 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "kagent.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "kagent.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- if not .Values.nameOverride }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "kagent.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "kagent.labels" -}} +helm.sh/chart: {{ include "kagent.chart" . }} +{{ include "kagent.selectorLabels" . }} +{{- if .Chart.Version }} +app.kubernetes.io/version: {{ .Chart.Version | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "kagent.selectorLabels" -}} +app.kubernetes.io/name: {{ include "kagent.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/*Default provider name*/}} +{{- define "kagent.defaultProviderName" -}} +{{ .Values.providers.default | default "openAI" | lower}} +{{- end }} + +{{/*Default model name*/}} +{{- define "kagent.defaultModelConfigName" -}} +default-model-config +{{- end }} + +{{/* +Expand the namespace of the release. +Allows overriding it for multi-namespace deployments in combined charts. +*/}} +{{- define "kagent.namespace" -}} +{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Watch namespaces - transforms list of namespaces cached by the controller into comma-separated string +Removes duplicates +*/}} +{{- define "kagent.watchNamespaces" -}} +{{- $nsSet := dict }} +{{- .Values.controller.watchNamespaces | default list | uniq | join "," }} +{{- end -}} diff --git a/helm/kagent-tools/templates/clusterrole.yaml b/helm/kagent-tools/templates/clusterrole.yaml new file mode 100644 index 0000000..cde9f45 --- /dev/null +++ b/helm/kagent-tools/templates/clusterrole.yaml @@ -0,0 +1,29 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kagent.fullname" . }}-cluster-admin-role + labels: + {{- include "kagent.labels" . | nindent 4 }} +rules: +- apiGroups: ["*"] + resources: ["*"] + verbs: ["*"] +- nonResourceURLs: ["*"] + verbs: ["*"] +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kagent.fullname" . }}-read-role + labels: + {{- include "kagent.labels" . | nindent 4 }} +rules: + - apiGroups: ["*"] + resources: ["*"] + verbs: ["*"] + - nonResourceURLs: ["*"] + verbs: + - get + - list + - watch \ No newline at end of file diff --git a/helm/kagent-tools/templates/clusterrolebinding.yaml b/helm/kagent-tools/templates/clusterrolebinding.yaml new file mode 100644 index 0000000..ee7d67e --- /dev/null +++ b/helm/kagent-tools/templates/clusterrolebinding.yaml @@ -0,0 +1,44 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kagent.fullname" . }}-cluster-admin-rolebinding + labels: + {{- include "kagent.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kagent.fullname" . }}-cluster-admin-role +subjects: +- kind: ServiceAccount + name: {{ include "kagent.fullname" . }} + namespace: {{ include "kagent.namespace" . }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kagent.fullname" . }}-getter-rolebinding + labels: + {{- include "kagent.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kagent.fullname" . }}-getter-role +subjects: +- kind: ServiceAccount + name: {{ include "kagent.fullname" . }} + namespace: {{ include "kagent.namespace" . }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "kagent.fullname" . }}-writer-rolebinding + labels: + {{- include "kagent.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kagent.fullname" . }}-writer-role +subjects: +- kind: ServiceAccount + name: {{ include "kagent.fullname" . }} + namespace: {{ include "kagent.namespace" . }} \ No newline at end of file diff --git a/helm/kagent-tools/templates/deployment.yaml b/helm/kagent-tools/templates/deployment.yaml new file mode 100644 index 0000000..48ac0d7 --- /dev/null +++ b/helm/kagent-tools/templates/deployment.yaml @@ -0,0 +1,74 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "kagent.fullname" . }} + namespace: {{ include "kagent.namespace" . }} + labels: + {{- include "kagent.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "kagent.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "kagent.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + serviceAccountName: {{ include "kagent.fullname" . }} + containers: + - name: tools + command: + - /tool-server + args: + - "--port" + - "{{ .Values.service.ports.tools.targetPort }}" + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.tools.image.registry }}/{{ .Values.tools.image.repository }}:{{ coalesce .Values.global.tag .Values.tools.image.tag .Chart.Version }}" + imagePullPolicy: {{ .Values.tools.image.pullPolicy }} + resources: + {{- toYaml .Values.tools.resources | nindent 12 }} + env: + - name: KAGENT_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "kagent.fullname" . }}-openai + key: OPENAI_API_KEY + optional: true # if the secret is not found, the tool will not be available + - name: OTEL_TRACING_ENABLED + value: {{ .Values.otel.tracing.enabled | quote }} + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: {{ .Values.otel.tracing.exporter.otlp.endpoint | quote }} + - name: OTEL_EXPORTER_OTLP_TRACES_TIMEOUT + value: {{ .Values.otel.tracing.exporter.otlp.timeout | quote }} + - name: OTEL_EXPORTER_OTLP_TRACES_INSECURE + value: {{ .Values.otel.tracing.exporter.otlp.insecure | quote }} + {{- with .Values.tools.env }} + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http-tools + containerPort: {{ .Values.service.ports.tools.targetPort }} + protocol: TCP + readinessProbe: + tcpSocket: + port: http-tools + initialDelaySeconds: 15 + periodSeconds: 15 + diff --git a/helm/kagent-tools/templates/service.yaml b/helm/kagent-tools/templates/service.yaml new file mode 100644 index 0000000..55c7fd2 --- /dev/null +++ b/helm/kagent-tools/templates/service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "kagent.fullname" . }} + namespace: {{ include "kagent.namespace" . }} + labels: + {{- include "kagent.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.ports.tools.port }} + targetPort: {{ .Values.service.ports.tools.targetPort }} + {{- if eq .Values.service.type "NodePort" }} # Only used if service.type is NodePort + {{- if .Values.service.ports.tools.nodePort }} + nodePort: {{ .Values.service.ports.tools.nodePort | default "" }} + {{- end }} + {{- end }} + protocol: TCP + name: tools + selector: + {{- include "kagent.selectorLabels" . | nindent 4 }} diff --git a/helm/kagent-tools/templates/serviceaccount.yaml b/helm/kagent-tools/templates/serviceaccount.yaml new file mode 100644 index 0000000..b0b4c03 --- /dev/null +++ b/helm/kagent-tools/templates/serviceaccount.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "kagent.fullname" . }} + namespace: {{ include "kagent.namespace" . }} + labels: + {{- include "kagent.labels" . | nindent 4 }} \ No newline at end of file diff --git a/helm/kagent-tools/values.yaml b/helm/kagent-tools/values.yaml new file mode 100644 index 0000000..6d68f55 --- /dev/null +++ b/helm/kagent-tools/values.yaml @@ -0,0 +1,61 @@ +# Default values for kagent +replicaCount: 1 + +global: + tag: "" + +tools: + loglevel: "debug" + image: + registry: ghcr.io + repository: kagent-dev/kagent/tools + tag: "" + pullPolicy: IfNotPresent + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 1000m + memory: 512Mi + prometheus: + url: "prometheus.kagent.svc.cluster.local:9090" + username: "" + password: "" + grafana: # kubectl port-forward svc/grafana 3000:3000 + url: "http://grafana.kagent.svc.cluster.local:3000" + apiKey: "" + +service: + type: ClusterIP + ports: + tools: + port: 8084 + targetPort: 8084 + nodePort: # Only used if service.type is NodePort + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +otel: + tracing: + enabled: false + exporter: + otlp: + endpoint: http://host.docker.internal:4317 + timeout: 15 + insecure: true diff --git a/internal/commands/builder.go b/internal/commands/builder.go index a6bd8e6..f3e75ce 100644 --- a/internal/commands/builder.go +++ b/internal/commands/builder.go @@ -26,6 +26,7 @@ type CommandBuilder struct { labels map[string]string annotations map[string]string timeout time.Duration + useTimeout bool dryRun bool force bool wait bool @@ -42,9 +43,10 @@ func NewCommandBuilder(command string) *CommandBuilder { args: make([]string, 0), labels: make(map[string]string), annotations: make(map[string]string), - timeout: 30 * time.Second, + timeout: 60 * time.Second, + useTimeout: false, // Only enable timeout when explicitly requested validate: true, - cacheTTL: 5 * time.Minute, + cacheTTL: 1 * time.Minute, } } @@ -101,11 +103,13 @@ func (cb *CommandBuilder) WithContext(context string) *CommandBuilder { // WithKubeconfig sets the kubeconfig file func (cb *CommandBuilder) WithKubeconfig(kubeconfig string) *CommandBuilder { - if err := security.ValidateFilePath(kubeconfig); err != nil { - logger.Get().Error("Invalid kubeconfig path", "kubeconfig", kubeconfig, "error", err) - return cb + if kubeconfig != "" { + if err := security.ValidateFilePath(kubeconfig); err != nil { + logger.Get().Error("Invalid kubeconfig path", "kubeconfig", kubeconfig, "error", err) + return cb + } + cb.kubeconfig = kubeconfig } - cb.kubeconfig = kubeconfig return cb } @@ -160,6 +164,7 @@ func (cb *CommandBuilder) WithAnnotation(key, value string) *CommandBuilder { // WithTimeout sets the command timeout func (cb *CommandBuilder) WithTimeout(timeout time.Duration) *CommandBuilder { + cb.useTimeout = true cb.timeout = timeout return cb } @@ -248,11 +253,9 @@ func (cb *CommandBuilder) Build() (string, []string, error) { } } - // Add timeout only for commands that support it - if cb.timeout > 0 { - if cb.supportsTimeout() { - args = append(args, "--timeout", cb.timeout.String()) - } + // Add timeout when explicitly requested + if cb.timeout > 0 && cb.useTimeout { + args = append(args, "--timeout", cb.timeout.String()) } // Add dry run @@ -278,56 +281,6 @@ func (cb *CommandBuilder) Build() (string, []string, error) { return cb.command, args, nil } -// supportsTimeout checks if the command supports the --timeout flag -func (cb *CommandBuilder) supportsTimeout() bool { - // For kubectl, many commands support --timeout - if cb.command == "kubectl" { - if len(cb.args) == 0 { - return false - } - - // Check the first argument (subcommand) - subcommand := cb.args[0] - switch subcommand { - case "wait": - return true - case "delete": - // kubectl delete supports --timeout when waiting for deletion - return true - case "rollout": - // kubectl rollout status supports --timeout - if len(cb.args) > 1 && cb.args[1] == "status" { - return true - } - return false - case "apply": - // kubectl apply supports --timeout when used with --wait - return cb.wait - case "annotate", "label": - // kubectl annotate and label support --timeout - return true - case "create": - // kubectl create supports --timeout - return true - case "argo": - // kubectl argo rollouts commands support --timeout - if len(cb.args) > 1 && cb.args[1] == "rollouts" { - return true - } - return false - case "get": - // kubectl get supports --timeout for some operations - return false // Most get operations don't need timeout, they're read-only - default: - return false - } - } - - // For other commands (helm, istioctl, cilium), assume they support timeout - // unless we find specific cases where they don't - return true -} - // Execute runs the command func (cb *CommandBuilder) Execute(ctx context.Context) (string, error) { log := logger.WithContext(ctx) @@ -465,8 +418,7 @@ func (cb *CommandBuilder) executeCommand(ctx context.Context, command string, ar default: toolError = errors.NewCommandError(command, err) } - - return "", toolError + return string(output), toolError } return string(output), nil diff --git a/internal/commands/builder_test.go b/internal/commands/builder_test.go index e326a76..52dc0ec 100644 --- a/internal/commands/builder_test.go +++ b/internal/commands/builder_test.go @@ -19,8 +19,8 @@ func TestNewCommandBuilder(t *testing.T) { assert.Empty(t, cb.output) assert.NotNil(t, cb.labels) assert.NotNil(t, cb.annotations) - assert.Equal(t, 30*time.Second, cb.timeout) - assert.Equal(t, 5*time.Minute, cb.cacheTTL) + assert.Equal(t, 60*time.Second, cb.timeout) + assert.Equal(t, 1*time.Minute, cb.cacheTTL) assert.True(t, cb.validate) assert.False(t, cb.cached) assert.False(t, cb.dryRun) @@ -562,8 +562,6 @@ func TestCommandBuilderExecuteWithoutCache(t *testing.T) { assert.Equal(t, "echo", command) assert.Contains(t, args, "hello") assert.Contains(t, args, "world") - assert.Contains(t, args, "--timeout") - assert.Contains(t, args, "30s") } func TestCommandBuilderExecuteWithCache(t *testing.T) { @@ -579,7 +577,5 @@ func TestCommandBuilderExecuteWithCache(t *testing.T) { assert.Equal(t, "echo", command) assert.Contains(t, args, "hello") assert.Contains(t, args, "world") - assert.Contains(t, args, "--timeout") - assert.Contains(t, args, "30s") assert.True(t, cb.cached) } diff --git a/internal/security/validation.go b/internal/security/validation.go index 6aadc38..f06bf47 100644 --- a/internal/security/validation.go +++ b/internal/security/validation.go @@ -102,10 +102,6 @@ func ValidateContainerImage(image string) error { // ValidateFilePath validates a file path for security func ValidateFilePath(path string) error { - if path == "" { - return ValidationError{Field: "path", Message: "cannot be empty"} - } - if len(path) > 4096 { return ValidationError{Field: "path", Message: "path too long"} } diff --git a/pkg/argo/argo.go b/pkg/argo/argo.go index a24e978..7edea84 100644 --- a/pkg/argo/argo.go +++ b/pkg/argo/argo.go @@ -354,6 +354,27 @@ func handleCheckPluginLogs(ctx context.Context, request mcp.CallToolRequest) (*m return mcp.NewToolResultText(status.String()), nil } +func handleListRollouts(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + ns := mcp.ParseString(request, "namespace", "argo-rollouts") + tt := mcp.ParseString(request, "type", "rollouts") + + cmd := []string{"argo", "rollouts", "list", tt} + if ns != "" { + cmd = append(cmd, "-n", ns) + } + + output, err := runArgoRolloutCommand(ctx, cmd) + if err != nil { + return mcp.NewToolResultError("Error listing rollouts: " + err.Error()), nil + } + + if strings.HasPrefix(output, "Error") { + return mcp.NewToolResultText(output), nil + } + + return mcp.NewToolResultText(output), nil +} + func RegisterTools(s *server.MCPServer) { s.AddTool(mcp.NewTool("argo_verify_argo_rollouts_controller_install", mcp.WithDescription("Verify that the Argo Rollouts controller is installed and running"), @@ -365,6 +386,12 @@ func RegisterTools(s *server.MCPServer) { mcp.WithDescription("Verify that the kubectl Argo Rollouts plugin is installed"), ), telemetry.AdaptToolHandler(telemetry.WithTracing("argo_verify_kubectl_plugin_install", handleVerifyKubectlPluginInstall))) + s.AddTool(mcp.NewTool("argo_rollouts_list", + mcp.WithDescription("List rollouts or experiments"), + mcp.WithString("namespace", mcp.Description("The namespace of the rollout")), + mcp.WithString("type", mcp.Description("What to list: rollouts or experiments"), mcp.DefaultString("rollouts")), + ), telemetry.AdaptToolHandler(telemetry.WithTracing("argo_rollouts_list", handleListRollouts))) + s.AddTool(mcp.NewTool("argo_promote_rollout", mcp.WithDescription("Promote a paused rollout to the next step"), mcp.WithString("rollout_name", mcp.Description("The name of the rollout to promote"), mcp.Required()), diff --git a/pkg/argo/argo_test.go b/pkg/argo/argo_test.go index 0f90c39..3af620f 100644 --- a/pkg/argo/argo_test.go +++ b/pkg/argo/argo_test.go @@ -30,7 +30,7 @@ func TestHandlePromoteRollout(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `rollout "myapp" promoted` - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "promote", "myapp", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "promote", "myapp"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -43,23 +43,20 @@ func TestHandlePromoteRollout(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, result) assert.False(t, result.IsError) - - // Verify the expected output - content := getResultText(result) - assert.Contains(t, content, "promoted") + assert.Contains(t, getResultText(result), "promoted") // Verify the correct command was called callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "kubectl", callLog[0].Command) - assert.Equal(t, []string{"argo", "rollouts", "promote", "myapp", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"argo", "rollouts", "promote", "myapp"}, callLog[0].Args) }) t.Run("promote rollout with namespace", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `rollout "myapp" promoted` - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "promote", "-n", "production", "myapp", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "promote", "-n", "production", "myapp"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -77,14 +74,14 @@ func TestHandlePromoteRollout(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "kubectl", callLog[0].Command) - assert.Equal(t, []string{"argo", "rollouts", "promote", "-n", "production", "myapp", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"argo", "rollouts", "promote", "-n", "production", "myapp"}, callLog[0].Args) }) t.Run("promote rollout with full flag", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `rollout "myapp" fully promoted` - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "promote", "myapp", "--full", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "promote", "myapp", "--full"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -98,11 +95,11 @@ func TestHandlePromoteRollout(t *testing.T) { assert.NoError(t, err) assert.False(t, result.IsError) - // Verify the correct command was called with --full flag + // Verify the correct command was called with full flag callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "kubectl", callLog[0].Command) - assert.Equal(t, []string{"argo", "rollouts", "promote", "myapp", "--full", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"argo", "rollouts", "promote", "myapp", "--full"}, callLog[0].Args) }) t.Run("missing required parameters", func(t *testing.T) { @@ -126,7 +123,7 @@ func TestHandlePromoteRollout(t *testing.T) { t.Run("kubectl command failure", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "promote", "myapp", "--timeout", "30s"}, "", assert.AnError) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "promote", "myapp"}, "", assert.AnError) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -148,7 +145,7 @@ func TestHandlePauseRollout(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `rollout "myapp" paused` - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "pause", "myapp", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "pause", "myapp"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -170,14 +167,14 @@ func TestHandlePauseRollout(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "kubectl", callLog[0].Command) - assert.Equal(t, []string{"argo", "rollouts", "pause", "myapp", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"argo", "rollouts", "pause", "myapp"}, callLog[0].Args) }) t.Run("pause rollout with namespace", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `rollout "myapp" paused` - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "pause", "-n", "production", "myapp", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "pause", "-n", "production", "myapp"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -195,7 +192,7 @@ func TestHandlePauseRollout(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "kubectl", callLog[0].Command) - assert.Equal(t, []string{"argo", "rollouts", "pause", "-n", "production", "myapp", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"argo", "rollouts", "pause", "-n", "production", "myapp"}, callLog[0].Args) }) t.Run("missing required parameters", func(t *testing.T) { @@ -224,7 +221,7 @@ func TestHandleSetRolloutImage(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `rollout "myapp" image updated` - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "set", "image", "myapp", "nginx:latest", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "set", "image", "myapp", "nginx:latest"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -247,14 +244,14 @@ func TestHandleSetRolloutImage(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "kubectl", callLog[0].Command) - assert.Equal(t, []string{"argo", "rollouts", "set", "image", "myapp", "nginx:latest", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"argo", "rollouts", "set", "image", "myapp", "nginx:latest"}, callLog[0].Args) }) t.Run("set rollout image with namespace", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `rollout "myapp" image updated` - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "set", "image", "myapp", "nginx:1.20", "-n", "production", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "set", "image", "myapp", "nginx:1.20", "-n", "production"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -273,7 +270,7 @@ func TestHandleSetRolloutImage(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "kubectl", callLog[0].Command) - assert.Equal(t, []string{"argo", "rollouts", "set", "image", "myapp", "nginx:1.20", "-n", "production", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"argo", "rollouts", "set", "image", "myapp", "nginx:1.20", "-n", "production"}, callLog[0].Args) }) t.Run("missing rollout_name parameter", func(t *testing.T) { @@ -370,7 +367,7 @@ func TestHandleVerifyGatewayPlugin(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `gateway-api-plugin not found` - mock.AddCommandString("kubectl", []string{"get", "configmap", "argo-rollouts-config", "-n", "argo-rollouts", "-o", "yaml", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"get", "configmap", "argo-rollouts-config", "-n", "argo-rollouts", "-o", "yaml"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -397,7 +394,7 @@ func TestHandleVerifyGatewayPlugin(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `gateway-api-plugin-abc123` - mock.AddCommandString("kubectl", []string{"get", "configmap", "argo-rollouts-config", "-n", "custom-namespace", "-o", "yaml", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"get", "configmap", "argo-rollouts-config", "-n", "custom-namespace", "-o", "yaml"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -426,7 +423,7 @@ func TestHandleVerifyArgoRolloutsControllerInstall(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `argo-rollouts-controller-manager-abc123` - mock.AddCommandString("kubectl", []string{"get", "pods", "-l", "app.kubernetes.io/name=argo-rollouts", "-n", "argo-rollouts", "-o", "jsonpath={.items[*].metadata.name}", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"get", "pods", "-l", "app.kubernetes.io/name=argo-rollouts", "-n", "argo-rollouts", "-o", "jsonpath={.items[*].metadata.name}"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -447,7 +444,7 @@ func TestHandleVerifyArgoRolloutsControllerInstall(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `argo-rollouts-controller-manager-abc123` - mock.AddCommandString("kubectl", []string{"get", "pods", "-l", "app.kubernetes.io/name=argo-rollouts", "-n", "custom-argo", "-o", "jsonpath={.items[*].metadata.name}", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"get", "pods", "-l", "app.kubernetes.io/name=argo-rollouts", "-n", "custom-argo", "-o", "jsonpath={.items[*].metadata.name}"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -472,7 +469,7 @@ func TestHandleVerifyArgoRolloutsControllerInstall(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `argo-rollouts-controller-manager-abc123` - mock.AddCommandString("kubectl", []string{"get", "pods", "-l", "app=custom-rollouts", "-n", "argo-rollouts", "-o", "jsonpath={.items[*].metadata.name}", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"get", "pods", "-l", "app=custom-rollouts", "-n", "argo-rollouts", "-o", "jsonpath={.items[*].metadata.name}"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -500,25 +497,25 @@ func TestHandleVerifyKubectlPluginInstall(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `kubectl-argo-rollouts` - mock.AddCommandString("kubectl", []string{"argo", "rollouts", "version", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"argo", "rollouts", "version"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} result, err := handleVerifyKubectlPluginInstall(ctx, request) assert.NoError(t, err) - assert.NotNil(t, result) + assert.False(t, result.IsError) - // Verify kubectl command was called + // Verify the correct command was called callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "kubectl", callLog[0].Command) - assert.Equal(t, []string{"argo", "rollouts", "version", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"argo", "rollouts", "version"}, callLog[0].Args) }) t.Run("kubectl plugin command failure", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("kubectl", []string{"plugin", "list", "--timeout", "30s"}, "", assert.AnError) + mock.AddCommandString("kubectl", []string{"plugin", "list"}, "", assert.AnError) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} diff --git a/pkg/cilium/cilium_test.go b/pkg/cilium/cilium_test.go index 866de5d..b7827de 100644 --- a/pkg/cilium/cilium_test.go +++ b/pkg/cilium/cilium_test.go @@ -23,8 +23,8 @@ func TestRegisterCiliumTools(t *testing.T) { func TestHandleCiliumStatusAndVersion(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"status", "--timeout", "30s"}, "Cilium status: OK", nil) - mock.AddCommandString("cilium", []string{"version", "--timeout", "30s"}, "cilium version 1.14.0", nil) + mock.AddCommandString("cilium", []string{"status"}, "Cilium status: OK", nil) + mock.AddCommandString("cilium", []string{"version"}, "cilium version 1.14.0", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -49,8 +49,8 @@ func TestHandleCiliumStatusAndVersion(t *testing.T) { func TestHandleCiliumStatusAndVersionError(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"status", "--timeout", "30s"}, "", errors.New("command failed")) - mock.AddCommandString("cilium", []string{"version", "--timeout", "30s"}, "cilium version 1.14.0", nil) + mock.AddCommandString("cilium", []string{"status"}, "", errors.New("command failed")) + mock.AddCommandString("cilium", []string{"version"}, "cilium version 1.14.0", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -64,7 +64,7 @@ func TestHandleCiliumStatusAndVersionError(t *testing.T) { func TestHandleInstallCilium(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"install", "--timeout", "30s"}, "✓ Cilium was successfully installed!", nil) + mock.AddCommandString("cilium", []string{"install"}, "✓ Cilium was successfully installed!", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -78,7 +78,7 @@ func TestHandleInstallCilium(t *testing.T) { func TestHandleUninstallCilium(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"uninstall", "--timeout", "30s"}, "✓ Cilium was successfully uninstalled!", nil) + mock.AddCommandString("cilium", []string{"uninstall"}, "✓ Cilium was successfully uninstalled!", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -92,7 +92,7 @@ func TestHandleUninstallCilium(t *testing.T) { func TestHandleUpgradeCilium(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"upgrade", "--timeout", "30s"}, "✓ Cilium was successfully upgraded!", nil) + mock.AddCommandString("cilium", []string{"upgrade"}, "✓ Cilium was successfully upgraded!", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -108,7 +108,7 @@ func TestHandleConnectToRemoteCluster(t *testing.T) { t.Run("success", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"clustermesh", "connect", "--destination-cluster", "my-cluster", "--timeout", "30s"}, "✓ Connected to cluster my-cluster!", nil) + mock.AddCommandString("cilium", []string{"clustermesh", "connect", "--destination-cluster", "my-cluster"}, "✓ Connected to cluster my-cluster!", nil) ctx = cmd.WithShellExecutor(ctx, mock) req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ @@ -144,7 +144,7 @@ func TestHandleDisconnectFromRemoteCluster(t *testing.T) { t.Run("success", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"clustermesh", "disconnect", "--destination-cluster", "my-cluster", "--timeout", "30s"}, "✓ Disconnected from cluster my-cluster!", nil) + mock.AddCommandString("cilium", []string{"clustermesh", "disconnect", "--destination-cluster", "my-cluster"}, "✓ Disconnected from cluster my-cluster!", nil) ctx = cmd.WithShellExecutor(ctx, mock) req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ @@ -178,7 +178,7 @@ func TestHandleDisconnectFromRemoteCluster(t *testing.T) { func TestHandleEnableHubble(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"hubble", "enable", "--timeout", "30s"}, "✓ Hubble was successfully enabled!", nil) + mock.AddCommandString("cilium", []string{"hubble", "enable"}, "✓ Hubble was successfully enabled!", nil) ctx = cmd.WithShellExecutor(ctx, mock) req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ @@ -198,7 +198,7 @@ func TestHandleEnableHubble(t *testing.T) { func TestHandleDisableHubble(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"hubble", "disable", "--timeout", "30s"}, "✓ Hubble was successfully disabled!", nil) + mock.AddCommandString("cilium", []string{"hubble", "disable"}, "✓ Hubble was successfully disabled!", nil) ctx = cmd.WithShellExecutor(ctx, mock) req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ @@ -217,7 +217,7 @@ func TestHandleDisableHubble(t *testing.T) { func TestHandleListBGPPeers(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"bgp", "peers", "--timeout", "30s"}, "listing BGP peers", nil) + mock.AddCommandString("cilium", []string{"bgp", "peers"}, "listing BGP peers", nil) ctx = cmd.WithShellExecutor(ctx, mock) result, err := handleListBGPPeers(ctx, mcp.CallToolRequest{}) require.NoError(t, err) @@ -229,7 +229,7 @@ func TestHandleListBGPPeers(t *testing.T) { func TestHandleListBGPRoutes(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"bgp", "routes", "--timeout", "30s"}, "listing BGP routes", nil) + mock.AddCommandString("cilium", []string{"bgp", "routes"}, "listing BGP routes", nil) ctx = cmd.WithShellExecutor(ctx, mock) result, err := handleListBGPRoutes(ctx, mcp.CallToolRequest{}) require.NoError(t, err) @@ -242,7 +242,7 @@ func TestRunCiliumCliWithContext(t *testing.T) { ctx := context.Background() t.Run("success", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"test", "--timeout", "30s"}, "success", nil) + mock.AddCommandString("cilium", []string{"test"}, "success", nil) ctx = cmd.WithShellExecutor(ctx, mock) result, err := runCiliumCliWithContext(ctx, "test") require.NoError(t, err) @@ -250,7 +250,7 @@ func TestRunCiliumCliWithContext(t *testing.T) { }) t.Run("error", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("cilium", []string{"test", "--timeout", "30s"}, "", fmt.Errorf("test error")) + mock.AddCommandString("cilium", []string{"test"}, "", fmt.Errorf("test error")) ctx = cmd.WithShellExecutor(ctx, mock) _, err := runCiliumCliWithContext(ctx, "test") require.Error(t, err) diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go index 06a9ac8..0bf1a4c 100644 --- a/pkg/helm/helm.go +++ b/pkg/helm/helm.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/kagent-dev/tools/internal/commands" "github.com/kagent-dev/tools/internal/errors" @@ -88,10 +89,18 @@ func handleHelmListReleases(ctx context.Context, request mcp.CallToolRequest) (* func runHelmCommand(ctx context.Context, args []string) (string, error) { kubeconfigPath := utils.GetKubeconfig() - result, err := commands.NewCommandBuilder("helm"). + + // Add timeout for helm upgrade commands + cmdBuilder := commands.NewCommandBuilder("helm"). WithArgs(args...). - WithKubeconfig(kubeconfigPath). - Execute(ctx) + WithKubeconfig(kubeconfigPath) + + // Only add timeout for upgrade commands + if len(args) > 0 && args[0] == "upgrade" { + cmdBuilder = cmdBuilder.WithTimeout(30 * time.Second) + } + + result, err := cmdBuilder.Execute(ctx) if err != nil { if toolErr, ok := err.(*errors.ToolError); ok { diff --git a/pkg/helm/helm_test.go b/pkg/helm/helm_test.go index 3848de2..28dca31 100644 --- a/pkg/helm/helm_test.go +++ b/pkg/helm/helm_test.go @@ -18,106 +18,105 @@ func TestRegisterTools(t *testing.T) { // Test Helm List Releases func TestHandleHelmListReleases(t *testing.T) { - t.Run("basic list releases", func(t *testing.T) { - mock := cmd.NewMockShellExecutor() - expectedOutput := `NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -app1 default 1 2023-01-01 12:00:00.000000000 +0000 UTC deployed myapp-1.0.0 1.0.0 -app2 kube-system 2 2023-01-02 12:00:00.000000000 +0000 UTC deployed system-2.0.0 2.0.0` - - mock.AddCommandString("helm", []string{"list", "--timeout", "30s"}, expectedOutput, nil) - ctx := cmd.WithShellExecutor(context.Background(), mock) - - request := mcp.CallToolRequest{} - result, err := handleHelmListReleases(ctx, request) - - assert.NoError(t, err) - assert.NotNil(t, result) - assert.False(t, result.IsError) - - // Verify the expected output - content := getResultText(result) - assert.Contains(t, content, "app1") - assert.Contains(t, content, "app2") - - // Verify the correct command was called - callLog := mock.GetCallLog() - require.Len(t, callLog, 1) - assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"list", "--timeout", "30s"}, callLog[0].Args) - }) - - t.Run("list releases with namespace", func(t *testing.T) { - mock := cmd.NewMockShellExecutor() - mock.AddCommandString("helm", []string{"list", "-n", "production", "--timeout", "30s"}, "production releases", nil) - ctx := cmd.WithShellExecutor(context.Background(), mock) - - request := mcp.CallToolRequest{} - request.Params.Arguments = map[string]interface{}{ - "namespace": "production", - } - - result, err := handleHelmListReleases(ctx, request) - - assert.NoError(t, err) - assert.False(t, result.IsError) - - // Verify the correct command was called with namespace - callLog := mock.GetCallLog() - require.Len(t, callLog, 1) - assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"list", "-n", "production", "--timeout", "30s"}, callLog[0].Args) - }) - - t.Run("list releases with all namespaces", func(t *testing.T) { - mock := cmd.NewMockShellExecutor() - mock.AddCommandString("helm", []string{"list", "-A", "--timeout", "30s"}, "all namespaces releases", nil) - ctx := cmd.WithShellExecutor(context.Background(), mock) - - request := mcp.CallToolRequest{} - request.Params.Arguments = map[string]interface{}{ - "all_namespaces": "true", - } - - result, err := handleHelmListReleases(ctx, request) - - assert.NoError(t, err) - assert.False(t, result.IsError) - - // Verify the correct command was called with -A flag - callLog := mock.GetCallLog() - require.Len(t, callLog, 1) - assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"list", "-A", "--timeout", "30s"}, callLog[0].Args) - }) - - t.Run("list releases with multiple flags", func(t *testing.T) { - mock := cmd.NewMockShellExecutor() - mock.AddCommandString("helm", []string{"list", "-A", "-a", "--failed", "-o", "json", "--timeout", "30s"}, `[{"name":"failed-app","status":"failed"}]`, nil) - ctx := cmd.WithShellExecutor(context.Background(), mock) - - request := mcp.CallToolRequest{} - request.Params.Arguments = map[string]interface{}{ - "all_namespaces": "true", - "all": "true", - "failed": "true", - "output": "json", - } - - result, err := handleHelmListReleases(ctx, request) - - assert.NoError(t, err) - assert.False(t, result.IsError) + tests := []struct { + name string + args map[string]interface{} + expectedArgs []string + expectedOutput string + expectError bool + }{ + { + name: "basic_list_releases", + args: map[string]interface{}{}, + expectedArgs: []string{"list"}, + expectedOutput: `NAME NAMESPACE REVISION STATUS CHART +app1 default 1 deployed my-chart-1.0.0 +app2 default 2 deployed my-chart-2.0.0`, + expectError: false, + }, + { + name: "list_releases_with_namespace", + args: map[string]interface{}{ + "namespace": "production", + }, + expectedArgs: []string{"list", "-n", "production"}, + expectedOutput: `NAME NAMESPACE REVISION STATUS CHART +prod-app production 1 deployed my-chart-1.0.0`, + expectError: false, + }, + { + name: "list_releases_with_all_namespaces", + args: map[string]interface{}{ + "all_namespaces": "true", + }, + expectedArgs: []string{"list", "-A"}, + expectedOutput: `NAME NAMESPACE REVISION STATUS CHART +app1 default 1 deployed my-chart-1.0.0 +prod-app production 1 deployed my-chart-1.0.0`, + expectError: false, + }, + { + name: "list_releases_with_multiple_flags", + args: map[string]interface{}{ + "all_namespaces": "true", + "all": "true", + "failed": "true", + "output": "json", + }, + expectedArgs: []string{"list", "-A", "-a", "--failed", "-o", "json"}, + expectedOutput: `[ + { + "name": "app1", + "namespace": "default", + "revision": "1", + "status": "deployed" + } +]`, + expectError: false, + }, + } - // Verify the correct command was called with multiple flags - callLog := mock.GetCallLog() - require.Len(t, callLog, 1) - assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"list", "-A", "-a", "--failed", "-o", "json", "--timeout", "30s"}, callLog[0].Args) - }) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mock := cmd.NewMockShellExecutor() + mock.AddCommandString("helm", tt.expectedArgs, tt.expectedOutput, nil) + ctx := cmd.WithShellExecutor(context.Background(), mock) + + request := mcp.CallToolRequest{} + request.Params.Arguments = tt.args + + result, err := handleHelmListReleases(ctx, request) + + assert.NoError(t, err) + assert.False(t, result.IsError) + + // Verify the expected output + content := getResultText(result) + if tt.name == "basic_list_releases" { + assert.Contains(t, content, "app1") + assert.Contains(t, content, "app2") + } else if tt.name == "list_releases_with_namespace" { + assert.Contains(t, content, "prod-app") + assert.Contains(t, content, "production") + } else if tt.name == "list_releases_with_all_namespaces" { + assert.Contains(t, content, "app1") + assert.Contains(t, content, "prod-app") + } else if tt.name == "list_releases_with_multiple_flags" { + assert.Contains(t, content, "app1") + assert.Contains(t, content, "default") + } + + // Verify the correct command was called + callLog := mock.GetCallLog() + require.Len(t, callLog, 1) + assert.Equal(t, "helm", callLog[0].Command) + assert.Equal(t, tt.expectedArgs, callLog[0].Args) + }) + } t.Run("helm command failure", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("helm", []string{"list", "--timeout", "30s"}, "", assert.AnError) + mock.AddCommandString("helm", []string{"list"}, "", assert.AnError) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -139,7 +138,7 @@ CHART: myapp-1.0.0 VALUES: replicaCount: 3` - mock.AddCommandString("helm", []string{"get", "all", "myapp", "-n", "default", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("helm", []string{"get", "all", "myapp", "-n", "default"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -158,12 +157,12 @@ replicaCount: 3` callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"get", "all", "myapp", "-n", "default", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"get", "all", "myapp", "-n", "default"}, callLog[0].Args) }) t.Run("get release values only", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("helm", []string{"get", "values", "myapp", "-n", "default", "--timeout", "30s"}, "replicaCount: 3", nil) + mock.AddCommandString("helm", []string{"get", "values", "myapp", "-n", "default"}, "replicaCount: 3", nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -182,7 +181,7 @@ replicaCount: 3` callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"get", "values", "myapp", "-n", "default", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"get", "values", "myapp", "-n", "default"}, callLog[0].Args) }) t.Run("missing required parameters", func(t *testing.T) { @@ -318,7 +317,7 @@ func TestHandleHelmUninstall(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `release "myapp" uninstalled` - mock.AddCommandString("helm", []string{"uninstall", "myapp", "-n", "default", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("helm", []string{"uninstall", "myapp", "-n", "default"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -330,6 +329,7 @@ func TestHandleHelmUninstall(t *testing.T) { result, err := handleHelmUninstall(ctx, request) assert.NoError(t, err) + assert.NotNil(t, result) assert.False(t, result.IsError) assert.Contains(t, getResultText(result), "uninstalled") @@ -337,13 +337,14 @@ func TestHandleHelmUninstall(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"uninstall", "myapp", "-n", "default", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"uninstall", "myapp", "-n", "default"}, callLog[0].Args) }) t.Run("uninstall with options", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - expectedArgs := []string{"uninstall", "myapp", "-n", "production", "--dry-run", "--wait", "--timeout", "30s"} - mock.AddCommandString("helm", expectedArgs, "dry run uninstall", nil) + expectedOutput := `release "myapp" uninstalled` + + mock.AddCommandString("helm", []string{"uninstall", "myapp", "-n", "production", "--dry-run", "--wait"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -363,7 +364,7 @@ func TestHandleHelmUninstall(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, expectedArgs, callLog[0].Args) + assert.Equal(t, []string{"uninstall", "myapp", "-n", "production", "--dry-run", "--wait"}, callLog[0].Args) }) t.Run("missing required parameters for uninstall", func(t *testing.T) { @@ -403,7 +404,7 @@ func TestHandleHelmRepoAdd(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `"my-repo" has been added to your repositories` - mock.AddCommandString("helm", []string{"repo", "add", "my-repo", "https://charts.example.com/", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("helm", []string{"repo", "add", "my-repo", "https://charts.example.com/"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -422,7 +423,7 @@ func TestHandleHelmRepoAdd(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"repo", "add", "my-repo", "https://charts.example.com/", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"repo", "add", "my-repo", "https://charts.example.com/"}, callLog[0].Args) }) t.Run("missing required parameters for repo add", func(t *testing.T) { @@ -451,9 +452,10 @@ func TestHandleHelmRepoUpdate(t *testing.T) { t.Run("basic repo update", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `Hang tight while we grab the latest from your chart repositories... -...Successfully got an update from the "my-repo" chart repository` +...Successfully got an update from the "stable" chart repository +Update Complete. ⎈Happy Helming!⎈` - mock.AddCommandString("helm", []string{"repo", "update", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("helm", []string{"repo", "update"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(context.Background(), mock) request := mcp.CallToolRequest{} @@ -467,7 +469,7 @@ func TestHandleHelmRepoUpdate(t *testing.T) { callLog := mock.GetCallLog() require.Len(t, callLog, 1) assert.Equal(t, "helm", callLog[0].Command) - assert.Equal(t, []string{"repo", "update", "--timeout", "30s"}, callLog[0].Args) + assert.Equal(t, []string{"repo", "update"}, callLog[0].Args) }) } diff --git a/pkg/istio/istio_test.go b/pkg/istio/istio_test.go index 02efbee..d2503c9 100644 --- a/pkg/istio/istio_test.go +++ b/pkg/istio/istio_test.go @@ -21,7 +21,7 @@ func TestHandleIstioProxyStatus(t *testing.T) { t.Run("basic proxy status", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"proxy-status", "--timeout", "30s"}, "Proxy status output", nil) + mock.AddCommandString("istioctl", []string{"proxy-status"}, "Proxy status output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -34,7 +34,7 @@ func TestHandleIstioProxyStatus(t *testing.T) { t.Run("proxy status with namespace", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"proxy-status", "-n", "istio-system", "--timeout", "30s"}, "Proxy status output", nil) + mock.AddCommandString("istioctl", []string{"proxy-status", "-n", "istio-system"}, "Proxy status output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -52,7 +52,7 @@ func TestHandleIstioProxyStatus(t *testing.T) { t.Run("proxy status with pod name", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"proxy-status", "-n", "default", "test-pod", "--timeout", "30s"}, "Proxy status output", nil) + mock.AddCommandString("istioctl", []string{"proxy-status", "-n", "default", "test-pod"}, "Proxy status output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -83,7 +83,7 @@ func TestHandleIstioProxyConfig(t *testing.T) { t.Run("proxy config with pod name", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"proxy-config", "all", "test-pod", "--timeout", "30s"}, "Proxy config output", nil) + mock.AddCommandString("istioctl", []string{"proxy-config", "all", "test-pod"}, "Proxy config output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -101,7 +101,7 @@ func TestHandleIstioProxyConfig(t *testing.T) { t.Run("proxy config with namespace", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"proxy-config", "cluster", "test-pod.default", "--timeout", "30s"}, "Proxy config output", nil) + mock.AddCommandString("istioctl", []string{"proxy-config", "cluster", "test-pod.default"}, "Proxy config output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -125,7 +125,7 @@ func TestHandleIstioInstall(t *testing.T) { t.Run("install with default profile", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"install", "--set", "profile=default", "-y", "--timeout", "30s"}, "Install completed", nil) + mock.AddCommandString("istioctl", []string{"install", "--set", "profile=default", "-y"}, "Install completed", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -138,7 +138,7 @@ func TestHandleIstioInstall(t *testing.T) { t.Run("install with custom profile", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"install", "--set", "profile=demo", "-y", "--timeout", "30s"}, "Install completed", nil) + mock.AddCommandString("istioctl", []string{"install", "--set", "profile=demo", "-y"}, "Install completed", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -159,7 +159,7 @@ func TestHandleIstioGenerateManifest(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"manifest", "generate", "--set", "profile=minimal", "--timeout", "30s"}, "Generated manifest", nil) + mock.AddCommandString("istioctl", []string{"manifest", "generate", "--set", "profile=minimal"}, "Generated manifest", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -180,7 +180,7 @@ func TestHandleIstioAnalyzeClusterConfiguration(t *testing.T) { t.Run("analyze all namespaces", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"analyze", "-A", "--timeout", "30s"}, "Analysis output", nil) + mock.AddCommandString("istioctl", []string{"analyze", "-A"}, "Analysis output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -198,7 +198,7 @@ func TestHandleIstioAnalyzeClusterConfiguration(t *testing.T) { t.Run("analyze specific namespace", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"analyze", "-n", "default", "--timeout", "30s"}, "Analysis output", nil) + mock.AddCommandString("istioctl", []string{"analyze", "-n", "default"}, "Analysis output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -220,7 +220,7 @@ func TestHandleIstioVersion(t *testing.T) { t.Run("version full", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"version", "--timeout", "30s"}, "Version output", nil) + mock.AddCommandString("istioctl", []string{"version"}, "Version output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -233,7 +233,7 @@ func TestHandleIstioVersion(t *testing.T) { t.Run("version short", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"version", "--short", "--timeout", "30s"}, "1.18.0", nil) + mock.AddCommandString("istioctl", []string{"version", "--short"}, "1.18.0", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -254,7 +254,7 @@ func TestHandleIstioRemoteClusters(t *testing.T) { ctx := context.Background() mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"remote-clusters", "--timeout", "30s"}, "Remote clusters output", nil) + mock.AddCommandString("istioctl", []string{"remote-clusters"}, "Remote clusters output", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -270,7 +270,7 @@ func TestHandleWaypointList(t *testing.T) { t.Run("list waypoints in all namespaces", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"waypoint", "list", "-A", "--timeout", "30s"}, "Waypoints list", nil) + mock.AddCommandString("istioctl", []string{"waypoint", "list", "-A"}, "Waypoints list", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -288,7 +288,7 @@ func TestHandleWaypointList(t *testing.T) { t.Run("list waypoints in a specific namespace", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"waypoint", "list", "-n", "default", "--timeout", "30s"}, "Waypoints list", nil) + mock.AddCommandString("istioctl", []string{"waypoint", "list", "-n", "default"}, "Waypoints list", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -310,7 +310,7 @@ func TestHandleWaypointGenerate(t *testing.T) { t.Run("generate waypoint with namespace", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"waypoint", "generate", "waypoint", "-n", "default", "--for", "all", "--timeout", "30s"}, "Generated waypoint", nil) + mock.AddCommandString("istioctl", []string{"waypoint", "generate", "waypoint", "-n", "default", "--for", "all"}, "Generated waypoint", nil) ctx = cmd.WithShellExecutor(ctx, mock) @@ -332,7 +332,7 @@ func TestHandleWaypointGenerate(t *testing.T) { func TestRunIstioCtl(t *testing.T) { t.Run("run istioctl with context", func(t *testing.T) { mock := cmd.NewMockShellExecutor() - mock.AddCommandString("istioctl", []string{"version", "--timeout", "30s"}, "1.18.0", nil) + mock.AddCommandString("istioctl", []string{"version"}, "1.18.0", nil) ctx := cmd.WithShellExecutor(context.Background(), mock) result, err := runIstioCtl(ctx, []string{"version"}) diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go index 6c29c9c..e474df6 100644 --- a/pkg/k8s/k8s.go +++ b/pkg/k8s/k8s.go @@ -9,6 +9,7 @@ import ( "os" "slices" "strings" + "time" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" @@ -236,7 +237,7 @@ func (k *K8sTool) handleCheckServiceConnectivity(ctx context.Context, request mc } // Wait for pod to be ready - _, err = k.runKubectlCommand(ctx, "wait", "--for=condition=ready", "pod/"+podName, "-n", namespace, "--timeout=60s") + _, err = k.runKubectlCommandWithTimeout(ctx, 60*time.Second, "wait", "--for=condition=ready", "pod/"+podName, "-n", namespace) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to wait for curl pod: %v", err)), nil } @@ -541,6 +542,21 @@ func (k *K8sTool) runKubectlCommand(ctx context.Context, args ...string) (*mcp.C return mcp.NewToolResultText(output), nil } +// runKubectlCommandWithTimeout is a helper function to execute kubectl commands with a timeout +func (k *K8sTool) runKubectlCommandWithTimeout(ctx context.Context, timeout time.Duration, args ...string) (*mcp.CallToolResult, error) { + output, err := commands.NewCommandBuilder("kubectl"). + WithArgs(args...). + WithKubeconfig(k.kubeconfig). + WithTimeout(timeout). + Execute(ctx) + + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } + + return mcp.NewToolResultText(output), nil +} + // RegisterK8sTools registers all k8s tools with the MCP server func RegisterTools(s *server.MCPServer, llm llms.Model, kubeconfig string) { k8sTool := NewK8sToolWithConfig(kubeconfig, llm) diff --git a/pkg/k8s/k8s_test.go b/pkg/k8s/k8s_test.go index a71e10f..f3c53c4 100644 --- a/pkg/k8s/k8s_test.go +++ b/pkg/k8s/k8s_test.go @@ -267,7 +267,7 @@ func TestHandleDeleteResource(t *testing.T) { t.Run("valid parameters", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `deployment.apps/test-deployment deleted` - mock.AddCommandString("kubectl", []string{"delete", "deployment", "test-deployment", "-n", "default", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"delete", "deployment", "test-deployment", "-n", "default"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(ctx, mock) k8sTool := newTestK8sTool() @@ -315,9 +315,9 @@ func TestHandleCheckServiceConnectivity(t *testing.T) { // Mock the pod creation, wait, and exec commands using partial matchers mock.AddPartialMatcherString("kubectl", []string{"run", "*", "--image=curlimages/curl", "-n", "default", "--restart=Never", "--", "sleep", "3600"}, "pod/curl-test-123 created", nil) - mock.AddPartialMatcherString("kubectl", []string{"wait", "--for=condition=ready", "*", "-n", "default", "--timeout=60s", "--timeout", "30s"}, "pod/curl-test-123 condition met", nil) + mock.AddPartialMatcherString("kubectl", []string{"wait", "--for=condition=ready", "*", "-n", "default", "--timeout=60s"}, "pod/curl-test-123 condition met", nil) mock.AddPartialMatcherString("kubectl", []string{"exec", "*", "-n", "default", "--", "curl", "-s", "test-service.default.svc.cluster.local:80"}, "Connection successful", nil) - mock.AddPartialMatcherString("kubectl", []string{"delete", "pod", "*", "-n", "default", "--ignore-not-found", "--timeout", "30s"}, "pod deleted", nil) + mock.AddPartialMatcherString("kubectl", []string{"delete", "pod", "*", "-n", "default", "--ignore-not-found"}, "pod deleted", nil) ctx := cmd.WithShellExecutor(ctx, mock) @@ -774,7 +774,7 @@ func TestHandleAnnotateResource(t *testing.T) { t.Run("success", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `deployment.apps/test-deployment annotated` - mock.AddCommandString("kubectl", []string{"annotate", "deployment", "test-deployment", "key1=value1", "key2=value2", "-n", "default", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"annotate", "deployment", "test-deployment", "key1=value1", "key2=value2", "-n", "default"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(ctx, mock) k8sTool := newTestK8sTool() @@ -826,7 +826,7 @@ func TestHandleLabelResource(t *testing.T) { t.Run("success", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `deployment.apps/test-deployment labeled` - mock.AddCommandString("kubectl", []string{"label", "deployment", "test-deployment", "env=prod", "version=1.0", "-n", "default", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"label", "deployment", "test-deployment", "env=prod", "version=1.0", "-n", "default"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(ctx, mock) k8sTool := newTestK8sTool() @@ -878,7 +878,7 @@ func TestHandleRemoveAnnotation(t *testing.T) { t.Run("success", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `deployment.apps/test-deployment annotated` - mock.AddCommandString("kubectl", []string{"annotate", "deployment", "test-deployment", "key1-", "-n", "default", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"annotate", "deployment", "test-deployment", "key1-", "-n", "default"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(ctx, mock) k8sTool := newTestK8sTool() @@ -930,7 +930,7 @@ func TestHandleRemoveLabel(t *testing.T) { t.Run("success", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `deployment.apps/test-deployment labeled` - mock.AddCommandString("kubectl", []string{"label", "deployment", "test-deployment", "env-", "-n", "default", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"label", "deployment", "test-deployment", "env-", "-n", "default"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(ctx, mock) k8sTool := newTestK8sTool() @@ -982,7 +982,7 @@ func TestHandleCreateResourceFromURL(t *testing.T) { t.Run("success", func(t *testing.T) { mock := cmd.NewMockShellExecutor() expectedOutput := `deployment.apps/test-deployment created` - mock.AddCommandString("kubectl", []string{"create", "-f", "https://example.com/manifest.yaml", "-n", "default", "--timeout", "30s"}, expectedOutput, nil) + mock.AddCommandString("kubectl", []string{"create", "-f", "https://example.com/manifest.yaml", "-n", "default"}, expectedOutput, nil) ctx := cmd.WithShellExecutor(ctx, mock) k8sTool := newTestK8sTool() diff --git a/reports/cve-report.tmpl b/reports/cve-report.tmpl new file mode 100644 index 0000000..4518291 --- /dev/null +++ b/reports/cve-report.tmpl @@ -0,0 +1,4 @@ +"Package","Version Installed","Vulnerability ID","Severity","Location","Version Fixed" +{{- range .Matches}} +"{{.Artifact.Name}}","{{.Artifact.Version}}","{{.Vulnerability.ID}}","{{.Vulnerability.Severity}}","{{ (index .Artifact.Locations 0).RealPath }}","{{.Vulnerability.Fix.Versions | join ".."}}" +{{- end}} \ No newline at end of file diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..81c5788 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,252 @@ +#!/bin/bash + +# exit on error +set -e + +#do not output commands +set +x + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +# Configuration +GITHUB_REPO="kagent-dev/tools" +BINARY_NAME="kagent-tools" +INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}" + +# Helper functions +log_info() { + echo -e "${BLUE}ℹ️ ${NC}$1" >&2 +} + +log_success() { + echo -e "${GREEN}✅ ${NC}$1" >&2 +} + +log_step() { + echo -e "${CYAN}🔄 ${NC}$1" >&2 +} + +log_step_complete() { + # Move cursor up one line and clear it, then print the completed message + echo -e "\033[1A\033[2K${GREEN}✅ ${NC}$1" >&2 +} + +log_warn() { + echo -e "${YELLOW}⚠️ ${NC}$1" >&2 +} + +log_error() { + echo -e "${RED}❌ ${NC}$1" >&2 +} + +log_header() { + echo -e "\n${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" >&2 + echo -e "${BOLD}${CYAN} 🚀 kagent-tools Installer${NC}" >&2 + echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" >&2 +} + +# Check if command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Detect OS and architecture +detect_platform() { + local os + local arch + + # Detect OS + case "$(uname -s)" in + Darwin) + os="darwin" + ;; + Linux) + os="linux" + ;; + CYGWIN*|MINGW32*|MSYS*|MINGW*) + os="windows" + ;; + *) + log_error "Unsupported operating system: $(uname -s)" + exit 1 + ;; + esac + + # Detect architecture + case "$(uname -m)" in + x86_64|amd64) + arch="amd64" + ;; + arm64|aarch64) + arch="arm64" + ;; + armv7l) + arch="arm" + ;; + *) + log_error "Unsupported architecture: $(uname -m)" + exit 1 + ;; + esac + + echo "${os}-${arch}" +} + +# Get latest release version from GitHub +get_latest_version() { + log_step "Fetching latest version from GitHub..." + + if command_exists curl; then + local response=$(curl -s "https://api.github.com/repos/${GITHUB_REPO}/releases/latest") + elif command_exists wget; then + local response=$(wget -qO- "https://api.github.com/repos/${GITHUB_REPO}/releases/latest") + else + log_error "Neither curl nor wget is available. Please install one of them." + exit 1 + fi + + # Extract version using basic tools (avoiding jq dependency) + local version=$(echo "$response" | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') + + if [ -z "$version" ]; then + echo "" >&2 # Add newline before error + log_error "Failed to get latest version from GitHub API" + exit 1 + fi + + echo "$version" +} + +# Download binary +download_binary() { + local version="$1" + local platform="$2" + local download_url="https://github.com/${GITHUB_REPO}/releases/download/${version}/${BINARY_NAME}-${platform}" + local temp_file="/tmp/${BINARY_NAME}" + rm -rf "/tmp/${BINARY_NAME}" + + log_step "Downloading ${BINARY_NAME} ${BOLD}${version}${NC} for ${BOLD}${platform}${NC} from GitHub..." + + if command_exists curl; then + curl -sL -o "$temp_file" "$download_url" + elif command_exists wget; then + wget -q -O "$temp_file" "$download_url" + else + log_error "Neither curl nor wget is available. Please install one of them." + exit 1 + fi + + if [ ! -f "$temp_file" ]; then + echo "" >&2 # Add newline before error + log_error "Failed to download binary from $download_url" + exit 1 + fi + + echo "$temp_file" +} + +# Install binary +install_binary() { + local temp_file="$1" + local install_path="${INSTALL_DIR}/${BINARY_NAME}" + + log_step "Installing ${BINARY_NAME} to ${BOLD}${install_path}${NC}..." + + # Create install directory if it doesn't exist + if [ ! -d "$INSTALL_DIR" ]; then + echo -e "\n${BLUE}ℹ️ ${NC}📁 Creating install directory: ${INSTALL_DIR}" >&2 + mkdir -p "$INSTALL_DIR" + fi + + mv "$temp_file" "$install_path" + chmod +x "$install_path" + + # Cleanup + rm -f "$temp_file" +} + +# Verify installation +verify_installation() { + log_step "Verifying installation..." + + if command_exists "$BINARY_NAME"; then + local version=$("$BINARY_NAME" --version 2>/dev/null || echo "unknown") + log_step_complete "Installation verified! ${BINARY_NAME} is available in PATH" + echo -e "${BLUE}ℹ️ ${NC}🏷️ Version: ${BOLD}${version}${NC}" >&2 + else + echo "" >&2 # Add newline before warning + log_warn "${BINARY_NAME} is installed but not in PATH" + log_info "💡 Add this line to your shell profile (~/.bashrc, ~/.zshrc, etc.):" + echo -e "${CYAN} export PATH=\"${INSTALL_DIR}:\$PATH\"${NC}" >&2 + fi +} + +# Main installation function +main() { + log_header + + # Check prerequisites + log_step "Checking prerequisites..." + if ! command_exists curl && ! command_exists wget; then + echo "" >&2 # Add newline before error + log_error "This script requires either curl or wget to be installed." + exit 1 + fi + log_step_complete "Prerequisites check passed" + + # Detect platform + log_step "Detecting platform..." + local platform=$(detect_platform) + log_step_complete "Platform detected: ${BOLD}${platform}${NC}" + + # Get latest version + local version=$(get_latest_version) + log_step_complete "Found latest version: ${BOLD}${version}${NC}" + + # Download binary + local temp_file=$(download_binary "$version" "$platform") + log_step_complete "Binary downloaded successfully" + + # Install binary + install_binary "$temp_file" + log_step_complete "Binary installed successfully" + + # Verify installation + verify_installation + + echo -e "\n${BOLD}${GREEN}🎉 Installation completed successfully!${NC}" >&2 + echo -e "${GREEN} You can now use '${BOLD}kagent-tools${NC}${GREEN}' command.${NC}\n" >&2 +} + +# Handle command line arguments +case "${1:-}" in + -h|--help) + echo -e "${BOLD}${CYAN}🚀 kagent-tools Installer${NC}" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "\n${BOLD}USAGE:${NC}" + echo -e " $0 [OPTIONS]" + echo -e "\n${BOLD}DESCRIPTION:${NC}" + echo -e " Install kagent-tools from GitHub releases" + echo -e "\n${BOLD}OPTIONS:${NC}" + echo -e " -h, --help Show this help message" + echo -e "\n${BOLD}ENVIRONMENT VARIABLES:${NC}" + echo -e " INSTALL_DIR Installation directory (default: \$HOME/.local/bin)" + echo -e "\n${BOLD}EXAMPLES:${NC}" + echo -e " ${GREEN}$0${NC} # Install to \$HOME/.local/bin" + echo -e " ${GREEN}INSTALL_DIR=~/bin $0${NC} # Install to ~/bin" + echo "" + exit 0 + ;; + *) + main "$@" + ;; +esac + + diff --git a/scripts/kind/crd-argo.yaml b/scripts/kind/crd-argo.yaml new file mode 100644 index 0000000..23dff63 --- /dev/null +++ b/scripts/kind/crd-argo.yaml @@ -0,0 +1,16470 @@ +# This is an auto-generated file. DO NOT EDIT +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: analysisruns.argoproj.io +spec: + group: argoproj.io + names: + kind: AnalysisRun + listKind: AnalysisRunList + plural: analysisruns + shortNames: + - ar + singular: analysisrun + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: AnalysisRun status + jsonPath: .status.phase + name: Status + type: string + - description: Time since resource was created + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + measurementRetention: + items: + properties: + limit: + format: int32 + type: integer + metricName: + type: string + required: + - limit + - metricName + type: object + type: array + metrics: + items: + properties: + consecutiveErrorLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + consecutiveSuccessLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + failureCondition: + type: string + failureLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + inconclusiveLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + initialDelay: + type: string + interval: + type: string + name: + type: string + provider: + properties: + cloudWatch: + properties: + interval: + type: string + metricDataQueries: + items: + properties: + expression: + type: string + id: + type: string + label: + type: string + metricStat: + properties: + metric: + properties: + dimensions: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + metricName: + type: string + namespace: + type: string + type: object + period: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + stat: + type: string + unit: + type: string + type: object + period: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + returnData: + type: boolean + type: object + type: array + required: + - metricDataQueries + type: object + datadog: + properties: + aggregator: + enum: + - avg + - min + - max + - sum + - last + - percentile + - mean + - l2norm + - area + type: string + apiVersion: + default: v1 + enum: + - v1 + - v2 + type: string + formula: + type: string + interval: + default: 5m + type: string + queries: + additionalProperties: + type: string + type: object + query: + type: string + secretRef: + properties: + name: + type: string + namespaced: + type: boolean + type: object + type: object + graphite: + properties: + address: + type: string + query: + type: string + type: object + influxdb: + properties: + profile: + type: string + query: + type: string + type: object + job: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + backoffLimit: + format: int32 + type: integer + backoffLimitPerIndex: + format: int32 + type: integer + completionMode: + type: string + completions: + format: int32 + type: integer + manualSelector: + type: boolean + maxFailedIndexes: + format: int32 + type: integer + parallelism: + format: int32 + type: integer + podFailurePolicy: + properties: + rules: + items: + properties: + action: + type: string + onExitCodes: + properties: + containerName: + type: string + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + - values + type: object + onPodConditions: + items: + properties: + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-type: atomic + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + required: + - rules + type: object + podReplacementPolicy: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + suspend: + type: boolean + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + searches: + items: + type: string + type: array + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + ip: + type: string + type: object + type: array + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + type: array + initContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + resourceClaims: + items: + properties: + name: + type: string + source: + properties: + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + x-kubernetes-preserve-unknown-fields: true + required: + - containers + type: object + type: object + ttlSecondsAfterFinished: + format: int32 + type: integer + required: + - template + type: object + required: + - spec + type: object + kayenta: + properties: + address: + type: string + application: + type: string + canaryConfigName: + type: string + configurationAccountName: + type: string + metricsAccountName: + type: string + scopes: + items: + properties: + controlScope: + properties: + end: + type: string + region: + type: string + scope: + type: string + start: + type: string + step: + format: int64 + type: integer + required: + - end + - region + - scope + - start + - step + type: object + experimentScope: + properties: + end: + type: string + region: + type: string + scope: + type: string + start: + type: string + step: + format: int64 + type: integer + required: + - end + - region + - scope + - start + - step + type: object + name: + type: string + required: + - controlScope + - experimentScope + - name + type: object + type: array + storageAccountName: + type: string + threshold: + properties: + marginal: + format: int64 + type: integer + pass: + format: int64 + type: integer + required: + - marginal + - pass + type: object + required: + - address + - application + - canaryConfigName + - configurationAccountName + - metricsAccountName + - scopes + - storageAccountName + - threshold + type: object + newRelic: + properties: + profile: + type: string + query: + type: string + timeout: + format: int64 + type: integer + required: + - query + type: object + plugin: + type: object + x-kubernetes-preserve-unknown-fields: true + prometheus: + properties: + address: + type: string + authentication: + properties: + oauth2: + properties: + clientId: + type: string + clientSecret: + type: string + scopes: + items: + type: string + type: array + tokenUrl: + type: string + type: object + sigv4: + properties: + profile: + type: string + region: + type: string + roleArn: + type: string + type: object + type: object + headers: + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + insecure: + type: boolean + query: + type: string + rangeQuery: + properties: + end: + type: string + start: + type: string + step: + type: string + type: object + timeout: + format: int64 + type: integer + type: object + skywalking: + properties: + address: + type: string + interval: + type: string + query: + type: string + type: object + wavefront: + properties: + address: + type: string + query: + type: string + type: object + web: + properties: + authentication: + properties: + oauth2: + properties: + clientId: + type: string + clientSecret: + type: string + scopes: + items: + type: string + type: array + tokenUrl: + type: string + type: object + sigv4: + properties: + profile: + type: string + region: + type: string + roleArn: + type: string + type: object + type: object + body: + type: string + headers: + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + insecure: + type: boolean + jsonBody: + type: object + x-kubernetes-preserve-unknown-fields: true + jsonPath: + type: string + method: + type: string + timeoutSeconds: + format: int64 + type: integer + url: + type: string + required: + - url + type: object + type: object + successCondition: + type: string + required: + - name + - provider + type: object + type: array + terminate: + type: boolean + ttlStrategy: + properties: + secondsAfterCompletion: + format: int32 + type: integer + secondsAfterFailure: + format: int32 + type: integer + secondsAfterSuccess: + format: int32 + type: integer + type: object + required: + - metrics + type: object + status: + properties: + completedAt: + format: date-time + type: string + dryRunSummary: + properties: + count: + format: int32 + type: integer + error: + format: int32 + type: integer + failed: + format: int32 + type: integer + inconclusive: + format: int32 + type: integer + successful: + format: int32 + type: integer + type: object + message: + type: string + metricResults: + items: + properties: + consecutiveError: + format: int32 + type: integer + consecutiveSuccess: + format: int32 + type: integer + count: + format: int32 + type: integer + dryRun: + type: boolean + error: + format: int32 + type: integer + failed: + format: int32 + type: integer + inconclusive: + format: int32 + type: integer + measurements: + items: + properties: + finishedAt: + format: date-time + type: string + message: + type: string + metadata: + additionalProperties: + type: string + type: object + phase: + type: string + resumeAt: + format: date-time + type: string + startedAt: + format: date-time + type: string + value: + type: string + required: + - phase + type: object + type: array + message: + type: string + metadata: + additionalProperties: + type: string + type: object + name: + type: string + phase: + type: string + successful: + format: int32 + type: integer + required: + - name + - phase + type: object + type: array + phase: + type: string + runSummary: + properties: + count: + format: int32 + type: integer + error: + format: int32 + type: integer + failed: + format: int32 + type: integer + inconclusive: + format: int32 + type: integer + successful: + format: int32 + type: integer + type: object + startedAt: + format: date-time + type: string + required: + - phase + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: analysistemplates.argoproj.io +spec: + group: argoproj.io + names: + kind: AnalysisTemplate + listKind: AnalysisTemplateList + plural: analysistemplates + shortNames: + - at + singular: analysistemplate + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Time since resource was created + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + measurementRetention: + items: + properties: + limit: + format: int32 + type: integer + metricName: + type: string + required: + - limit + - metricName + type: object + type: array + metrics: + items: + properties: + consecutiveErrorLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + consecutiveSuccessLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + failureCondition: + type: string + failureLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + inconclusiveLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + initialDelay: + type: string + interval: + type: string + name: + type: string + provider: + properties: + cloudWatch: + properties: + interval: + type: string + metricDataQueries: + items: + properties: + expression: + type: string + id: + type: string + label: + type: string + metricStat: + properties: + metric: + properties: + dimensions: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + metricName: + type: string + namespace: + type: string + type: object + period: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + stat: + type: string + unit: + type: string + type: object + period: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + returnData: + type: boolean + type: object + type: array + required: + - metricDataQueries + type: object + datadog: + properties: + aggregator: + enum: + - avg + - min + - max + - sum + - last + - percentile + - mean + - l2norm + - area + type: string + apiVersion: + default: v1 + enum: + - v1 + - v2 + type: string + formula: + type: string + interval: + default: 5m + type: string + queries: + additionalProperties: + type: string + type: object + query: + type: string + secretRef: + properties: + name: + type: string + namespaced: + type: boolean + type: object + type: object + graphite: + properties: + address: + type: string + query: + type: string + type: object + influxdb: + properties: + profile: + type: string + query: + type: string + type: object + job: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + backoffLimit: + format: int32 + type: integer + backoffLimitPerIndex: + format: int32 + type: integer + completionMode: + type: string + completions: + format: int32 + type: integer + manualSelector: + type: boolean + maxFailedIndexes: + format: int32 + type: integer + parallelism: + format: int32 + type: integer + podFailurePolicy: + properties: + rules: + items: + properties: + action: + type: string + onExitCodes: + properties: + containerName: + type: string + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + - values + type: object + onPodConditions: + items: + properties: + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-type: atomic + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + required: + - rules + type: object + podReplacementPolicy: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + suspend: + type: boolean + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + searches: + items: + type: string + type: array + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + ip: + type: string + type: object + type: array + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + type: array + initContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + resourceClaims: + items: + properties: + name: + type: string + source: + properties: + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + x-kubernetes-preserve-unknown-fields: true + required: + - containers + type: object + type: object + ttlSecondsAfterFinished: + format: int32 + type: integer + required: + - template + type: object + required: + - spec + type: object + kayenta: + properties: + address: + type: string + application: + type: string + canaryConfigName: + type: string + configurationAccountName: + type: string + metricsAccountName: + type: string + scopes: + items: + properties: + controlScope: + properties: + end: + type: string + region: + type: string + scope: + type: string + start: + type: string + step: + format: int64 + type: integer + required: + - end + - region + - scope + - start + - step + type: object + experimentScope: + properties: + end: + type: string + region: + type: string + scope: + type: string + start: + type: string + step: + format: int64 + type: integer + required: + - end + - region + - scope + - start + - step + type: object + name: + type: string + required: + - controlScope + - experimentScope + - name + type: object + type: array + storageAccountName: + type: string + threshold: + properties: + marginal: + format: int64 + type: integer + pass: + format: int64 + type: integer + required: + - marginal + - pass + type: object + required: + - address + - application + - canaryConfigName + - configurationAccountName + - metricsAccountName + - scopes + - storageAccountName + - threshold + type: object + newRelic: + properties: + profile: + type: string + query: + type: string + timeout: + format: int64 + type: integer + required: + - query + type: object + plugin: + type: object + x-kubernetes-preserve-unknown-fields: true + prometheus: + properties: + address: + type: string + authentication: + properties: + oauth2: + properties: + clientId: + type: string + clientSecret: + type: string + scopes: + items: + type: string + type: array + tokenUrl: + type: string + type: object + sigv4: + properties: + profile: + type: string + region: + type: string + roleArn: + type: string + type: object + type: object + headers: + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + insecure: + type: boolean + query: + type: string + rangeQuery: + properties: + end: + type: string + start: + type: string + step: + type: string + type: object + timeout: + format: int64 + type: integer + type: object + skywalking: + properties: + address: + type: string + interval: + type: string + query: + type: string + type: object + wavefront: + properties: + address: + type: string + query: + type: string + type: object + web: + properties: + authentication: + properties: + oauth2: + properties: + clientId: + type: string + clientSecret: + type: string + scopes: + items: + type: string + type: array + tokenUrl: + type: string + type: object + sigv4: + properties: + profile: + type: string + region: + type: string + roleArn: + type: string + type: object + type: object + body: + type: string + headers: + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + insecure: + type: boolean + jsonBody: + type: object + x-kubernetes-preserve-unknown-fields: true + jsonPath: + type: string + method: + type: string + timeoutSeconds: + format: int64 + type: integer + url: + type: string + required: + - url + type: object + type: object + successCondition: + type: string + required: + - name + - provider + type: object + type: array + templates: + items: + properties: + clusterScope: + type: boolean + templateName: + type: string + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: clusteranalysistemplates.argoproj.io +spec: + group: argoproj.io + names: + kind: ClusterAnalysisTemplate + listKind: ClusterAnalysisTemplateList + plural: clusteranalysistemplates + shortNames: + - cat + singular: clusteranalysistemplate + preserveUnknownFields: false + scope: Cluster + versions: + - additionalPrinterColumns: + - description: Time since resource was created + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + measurementRetention: + items: + properties: + limit: + format: int32 + type: integer + metricName: + type: string + required: + - limit + - metricName + type: object + type: array + metrics: + items: + properties: + consecutiveErrorLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + consecutiveSuccessLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + failureCondition: + type: string + failureLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + inconclusiveLimit: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + initialDelay: + type: string + interval: + type: string + name: + type: string + provider: + properties: + cloudWatch: + properties: + interval: + type: string + metricDataQueries: + items: + properties: + expression: + type: string + id: + type: string + label: + type: string + metricStat: + properties: + metric: + properties: + dimensions: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + metricName: + type: string + namespace: + type: string + type: object + period: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + stat: + type: string + unit: + type: string + type: object + period: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + returnData: + type: boolean + type: object + type: array + required: + - metricDataQueries + type: object + datadog: + properties: + aggregator: + enum: + - avg + - min + - max + - sum + - last + - percentile + - mean + - l2norm + - area + type: string + apiVersion: + default: v1 + enum: + - v1 + - v2 + type: string + formula: + type: string + interval: + default: 5m + type: string + queries: + additionalProperties: + type: string + type: object + query: + type: string + secretRef: + properties: + name: + type: string + namespaced: + type: boolean + type: object + type: object + graphite: + properties: + address: + type: string + query: + type: string + type: object + influxdb: + properties: + profile: + type: string + query: + type: string + type: object + job: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + backoffLimit: + format: int32 + type: integer + backoffLimitPerIndex: + format: int32 + type: integer + completionMode: + type: string + completions: + format: int32 + type: integer + manualSelector: + type: boolean + maxFailedIndexes: + format: int32 + type: integer + parallelism: + format: int32 + type: integer + podFailurePolicy: + properties: + rules: + items: + properties: + action: + type: string + onExitCodes: + properties: + containerName: + type: string + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + - values + type: object + onPodConditions: + items: + properties: + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-type: atomic + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + required: + - rules + type: object + podReplacementPolicy: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + suspend: + type: boolean + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + searches: + items: + type: string + type: array + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + ip: + type: string + type: object + type: array + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + type: array + initContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + resourceClaims: + items: + properties: + name: + type: string + source: + properties: + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + x-kubernetes-preserve-unknown-fields: true + required: + - containers + type: object + type: object + ttlSecondsAfterFinished: + format: int32 + type: integer + required: + - template + type: object + required: + - spec + type: object + kayenta: + properties: + address: + type: string + application: + type: string + canaryConfigName: + type: string + configurationAccountName: + type: string + metricsAccountName: + type: string + scopes: + items: + properties: + controlScope: + properties: + end: + type: string + region: + type: string + scope: + type: string + start: + type: string + step: + format: int64 + type: integer + required: + - end + - region + - scope + - start + - step + type: object + experimentScope: + properties: + end: + type: string + region: + type: string + scope: + type: string + start: + type: string + step: + format: int64 + type: integer + required: + - end + - region + - scope + - start + - step + type: object + name: + type: string + required: + - controlScope + - experimentScope + - name + type: object + type: array + storageAccountName: + type: string + threshold: + properties: + marginal: + format: int64 + type: integer + pass: + format: int64 + type: integer + required: + - marginal + - pass + type: object + required: + - address + - application + - canaryConfigName + - configurationAccountName + - metricsAccountName + - scopes + - storageAccountName + - threshold + type: object + newRelic: + properties: + profile: + type: string + query: + type: string + timeout: + format: int64 + type: integer + required: + - query + type: object + plugin: + type: object + x-kubernetes-preserve-unknown-fields: true + prometheus: + properties: + address: + type: string + authentication: + properties: + oauth2: + properties: + clientId: + type: string + clientSecret: + type: string + scopes: + items: + type: string + type: array + tokenUrl: + type: string + type: object + sigv4: + properties: + profile: + type: string + region: + type: string + roleArn: + type: string + type: object + type: object + headers: + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + insecure: + type: boolean + query: + type: string + rangeQuery: + properties: + end: + type: string + start: + type: string + step: + type: string + type: object + timeout: + format: int64 + type: integer + type: object + skywalking: + properties: + address: + type: string + interval: + type: string + query: + type: string + type: object + wavefront: + properties: + address: + type: string + query: + type: string + type: object + web: + properties: + authentication: + properties: + oauth2: + properties: + clientId: + type: string + clientSecret: + type: string + scopes: + items: + type: string + type: array + tokenUrl: + type: string + type: object + sigv4: + properties: + profile: + type: string + region: + type: string + roleArn: + type: string + type: object + type: object + body: + type: string + headers: + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + insecure: + type: boolean + jsonBody: + type: object + x-kubernetes-preserve-unknown-fields: true + jsonPath: + type: string + method: + type: string + timeoutSeconds: + format: int64 + type: integer + url: + type: string + required: + - url + type: object + type: object + successCondition: + type: string + required: + - name + - provider + type: object + type: array + templates: + items: + properties: + clusterScope: + type: boolean + templateName: + type: string + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: experiments.argoproj.io +spec: + group: argoproj.io + names: + kind: Experiment + listKind: ExperimentList + plural: experiments + shortNames: + - exp + singular: experiment + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Experiment status + jsonPath: .status.phase + name: Status + type: string + - description: Time since resource was created + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + analyses: + items: + properties: + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + clusterScope: + type: boolean + name: + type: string + requiredForCompletion: + type: boolean + templateName: + type: string + required: + - name + - templateName + type: object + type: array + analysisRunMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + duration: + type: string + measurementRetention: + items: + properties: + limit: + format: int32 + type: integer + metricName: + type: string + required: + - limit + - metricName + type: object + type: array + progressDeadlineSeconds: + format: int32 + type: integer + scaleDownDelaySeconds: + format: int32 + type: integer + templates: + items: + properties: + minReadySeconds: + format: int32 + type: integer + name: + type: string + replicas: + format: int32 + type: integer + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + service: + properties: + name: + type: string + type: object + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + searches: + items: + type: string + type: array + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + ip: + type: string + type: object + type: array + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + type: array + initContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + resourceClaims: + items: + properties: + name: + type: string + source: + properties: + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + x-kubernetes-preserve-unknown-fields: true + required: + - containers + type: object + type: object + required: + - name + - selector + - template + type: object + type: array + terminate: + type: boolean + required: + - templates + type: object + status: + properties: + analysisRuns: + items: + properties: + analysisRun: + type: string + message: + type: string + name: + type: string + phase: + type: string + required: + - analysisRun + - name + - phase + type: object + type: array + availableAt: + format: date-time + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + lastUpdateTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - lastTransitionTime + - lastUpdateTime + - message + - reason + - status + - type + type: object + type: array + message: + type: string + phase: + type: string + templateStatuses: + items: + properties: + availableReplicas: + format: int32 + type: integer + collisionCount: + format: int32 + type: integer + lastTransitionTime: + format: date-time + type: string + message: + type: string + name: + type: string + podTemplateHash: + type: string + readyReplicas: + format: int32 + type: integer + replicas: + format: int32 + type: integer + serviceName: + type: string + status: + type: string + updatedReplicas: + format: int32 + type: integer + required: + - availableReplicas + - name + - readyReplicas + - replicas + - updatedReplicas + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: rollouts.argoproj.io +spec: + group: argoproj.io + names: + kind: Rollout + listKind: RolloutList + plural: rollouts + shortNames: + - ro + singular: rollout + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Number of desired pods + jsonPath: .spec.replicas + name: Desired + type: integer + - description: Total number of non-terminated pods targeted by this rollout + jsonPath: .status.replicas + name: Current + type: integer + - description: Total number of non-terminated pods targeted by this rollout that + have the desired template spec + jsonPath: .status.updatedReplicas + name: Up-to-date + type: integer + - description: Total number of available pods (ready for at least minReadySeconds) + targeted by this rollout + jsonPath: .status.availableReplicas + name: Available + type: integer + - description: Time since resource was created + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + analysis: + properties: + successfulRunHistoryLimit: + format: int32 + type: integer + unsuccessfulRunHistoryLimit: + format: int32 + type: integer + type: object + minReadySeconds: + format: int32 + type: integer + paused: + type: boolean + progressDeadlineAbort: + type: boolean + progressDeadlineSeconds: + format: int32 + type: integer + replicas: + format: int32 + type: integer + restartAt: + format: date-time + type: string + revisionHistoryLimit: + format: int32 + type: integer + rollbackWindow: + properties: + revisions: + format: int32 + type: integer + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + strategy: + properties: + blueGreen: + properties: + abortScaleDownDelaySeconds: + format: int32 + type: integer + activeMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + activeService: + type: string + antiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + properties: + weight: + format: int32 + type: integer + required: + - weight + type: object + requiredDuringSchedulingIgnoredDuringExecution: + type: object + type: object + autoPromotionEnabled: + type: boolean + autoPromotionSeconds: + format: int32 + type: integer + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + postPromotionAnalysis: + properties: + analysisRunMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + podTemplateHashValue: + type: string + type: object + required: + - name + type: object + type: array + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + measurementRetention: + items: + properties: + limit: + format: int32 + type: integer + metricName: + type: string + required: + - limit + - metricName + type: object + type: array + templates: + items: + properties: + clusterScope: + type: boolean + templateName: + type: string + type: object + type: array + type: object + prePromotionAnalysis: + properties: + analysisRunMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + podTemplateHashValue: + type: string + type: object + required: + - name + type: object + type: array + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + measurementRetention: + items: + properties: + limit: + format: int32 + type: integer + metricName: + type: string + required: + - limit + - metricName + type: object + type: array + templates: + items: + properties: + clusterScope: + type: boolean + templateName: + type: string + type: object + type: array + type: object + previewMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + previewReplicaCount: + format: int32 + type: integer + previewService: + type: string + scaleDownDelayRevisionLimit: + format: int32 + type: integer + scaleDownDelaySeconds: + format: int32 + type: integer + required: + - activeService + type: object + canary: + properties: + abortScaleDownDelaySeconds: + format: int32 + type: integer + analysis: + properties: + analysisRunMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + podTemplateHashValue: + type: string + type: object + required: + - name + type: object + type: array + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + measurementRetention: + items: + properties: + limit: + format: int32 + type: integer + metricName: + type: string + required: + - limit + - metricName + type: object + type: array + startingStep: + format: int32 + type: integer + templates: + items: + properties: + clusterScope: + type: boolean + templateName: + type: string + type: object + type: array + type: object + antiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + properties: + weight: + format: int32 + type: integer + required: + - weight + type: object + requiredDuringSchedulingIgnoredDuringExecution: + type: object + type: object + canaryMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + canaryService: + type: string + dynamicStableScale: + type: boolean + maxSurge: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + minPodsPerReplicaSet: + format: int32 + type: integer + pingPong: + properties: + pingService: + type: string + pongService: + type: string + required: + - pingService + - pongService + type: object + scaleDownDelayRevisionLimit: + format: int32 + type: integer + scaleDownDelaySeconds: + format: int32 + type: integer + stableMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + stableService: + type: string + steps: + items: + properties: + analysis: + properties: + analysisRunMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + podTemplateHashValue: + type: string + type: object + required: + - name + type: object + type: array + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + measurementRetention: + items: + properties: + limit: + format: int32 + type: integer + metricName: + type: string + required: + - limit + - metricName + type: object + type: array + templates: + items: + properties: + clusterScope: + type: boolean + templateName: + type: string + type: object + type: array + type: object + experiment: + properties: + analyses: + items: + properties: + args: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + fieldRef: + properties: + fieldPath: + type: string + required: + - fieldPath + type: object + podTemplateHashValue: + type: string + type: object + required: + - name + type: object + type: array + clusterScope: + type: boolean + name: + type: string + requiredForCompletion: + type: boolean + templateName: + type: string + required: + - name + - templateName + type: object + type: array + analysisRunMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + dryRun: + items: + properties: + metricName: + type: string + required: + - metricName + type: object + type: array + duration: + type: string + templates: + items: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + name: + type: string + replicas: + format: int32 + type: integer + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + service: + properties: + name: + type: string + type: object + specRef: + type: string + weight: + format: int32 + type: integer + required: + - name + - specRef + type: object + type: array + required: + - templates + type: object + pause: + properties: + duration: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: object + plugin: + properties: + config: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + required: + - name + type: object + setCanaryScale: + properties: + matchTrafficWeight: + type: boolean + replicas: + format: int32 + type: integer + weight: + format: int32 + type: integer + type: object + setHeaderRoute: + properties: + match: + items: + properties: + headerName: + type: string + headerValue: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + required: + - headerName + - headerValue + type: object + type: array + name: + type: string + type: object + setMirrorRoute: + properties: + match: + items: + properties: + headers: + additionalProperties: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + type: object + method: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + path: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + type: object + type: array + name: + type: string + percentage: + format: int32 + type: integer + required: + - name + type: object + setWeight: + format: int32 + type: integer + type: object + type: array + trafficRouting: + properties: + alb: + properties: + annotationPrefix: + type: string + ingress: + type: string + ingresses: + items: + type: string + type: array + rootService: + type: string + servicePort: + format: int32 + type: integer + stickinessConfig: + properties: + durationSeconds: + format: int64 + type: integer + enabled: + type: boolean + required: + - durationSeconds + - enabled + type: object + required: + - servicePort + type: object + ambassador: + properties: + mappings: + items: + type: string + type: array + required: + - mappings + type: object + apisix: + properties: + route: + properties: + name: + type: string + rules: + items: + type: string + type: array + required: + - name + type: object + type: object + appMesh: + properties: + virtualNodeGroup: + properties: + canaryVirtualNodeRef: + properties: + name: + type: string + required: + - name + type: object + stableVirtualNodeRef: + properties: + name: + type: string + required: + - name + type: object + required: + - canaryVirtualNodeRef + - stableVirtualNodeRef + type: object + virtualService: + properties: + name: + type: string + routes: + items: + type: string + type: array + required: + - name + type: object + type: object + istio: + properties: + destinationRule: + properties: + canarySubsetName: + type: string + name: + type: string + stableSubsetName: + type: string + required: + - canarySubsetName + - name + - stableSubsetName + type: object + virtualService: + properties: + name: + type: string + routes: + items: + type: string + type: array + tcpRoutes: + items: + properties: + port: + format: int64 + type: integer + type: object + type: array + tlsRoutes: + items: + properties: + port: + format: int64 + type: integer + sniHosts: + items: + type: string + type: array + type: object + type: array + required: + - name + type: object + virtualServices: + items: + properties: + name: + type: string + routes: + items: + type: string + type: array + tcpRoutes: + items: + properties: + port: + format: int64 + type: integer + type: object + type: array + tlsRoutes: + items: + properties: + port: + format: int64 + type: integer + sniHosts: + items: + type: string + type: array + type: object + type: array + required: + - name + type: object + type: array + type: object + managedRoutes: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + maxTrafficWeight: + format: int32 + type: integer + nginx: + properties: + additionalIngressAnnotations: + additionalProperties: + type: string + type: object + annotationPrefix: + type: string + canaryIngressAnnotations: + additionalProperties: + type: string + type: object + stableIngress: + type: string + stableIngresses: + items: + type: string + type: array + type: object + plugins: + type: object + x-kubernetes-preserve-unknown-fields: true + smi: + properties: + rootService: + type: string + trafficSplitName: + type: string + type: object + traefik: + properties: + weightedTraefikServiceName: + type: string + required: + - weightedTraefikServiceName + type: object + type: object + type: object + type: object + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + searches: + items: + type: string + type: array + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + ip: + type: string + type: object + type: array + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + type: array + initContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + x-kubernetes-preserve-unknown-fields: true + requests: + x-kubernetes-preserve-unknown-fields: true + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + resourceClaims: + items: + properties: + name: + type: string + source: + properties: + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + x-kubernetes-preserve-unknown-fields: true + type: array + required: + - containers + type: object + type: object + workloadRef: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + scaleDown: + type: string + type: object + type: object + status: + properties: + HPAReplicas: + format: int32 + type: integer + abort: + type: boolean + abortedAt: + format: date-time + type: string + alb: + properties: + canaryTargetGroup: + properties: + arn: + type: string + fullName: + type: string + name: + type: string + required: + - arn + - name + type: object + ingress: + type: string + loadBalancer: + properties: + arn: + type: string + fullName: + type: string + name: + type: string + required: + - arn + - name + type: object + stableTargetGroup: + properties: + arn: + type: string + fullName: + type: string + name: + type: string + required: + - arn + - name + type: object + type: object + albs: + items: + properties: + canaryTargetGroup: + properties: + arn: + type: string + fullName: + type: string + name: + type: string + required: + - arn + - name + type: object + ingress: + type: string + loadBalancer: + properties: + arn: + type: string + fullName: + type: string + name: + type: string + required: + - arn + - name + type: object + stableTargetGroup: + properties: + arn: + type: string + fullName: + type: string + name: + type: string + required: + - arn + - name + type: object + type: object + type: array + availableReplicas: + format: int32 + type: integer + blueGreen: + properties: + activeSelector: + type: string + postPromotionAnalysisRunStatus: + properties: + message: + type: string + name: + type: string + status: + type: string + required: + - name + - status + type: object + prePromotionAnalysisRunStatus: + properties: + message: + type: string + name: + type: string + status: + type: string + required: + - name + - status + type: object + previewSelector: + type: string + scaleUpPreviewCheckPoint: + type: boolean + type: object + canary: + properties: + currentBackgroundAnalysisRunStatus: + properties: + message: + type: string + name: + type: string + status: + type: string + required: + - name + - status + type: object + currentExperiment: + type: string + currentStepAnalysisRunStatus: + properties: + message: + type: string + name: + type: string + status: + type: string + required: + - name + - status + type: object + stablePingPong: + type: string + stepPluginStatuses: + items: + properties: + backoff: + type: string + disabled: + type: boolean + executions: + format: int32 + type: integer + finishedAt: + format: date-time + type: string + index: + format: int32 + type: integer + message: + type: string + name: + type: string + operation: + type: string + phase: + type: string + startedAt: + format: date-time + type: string + status: + type: object + x-kubernetes-preserve-unknown-fields: true + updatedAt: + format: date-time + type: string + required: + - index + - name + - operation + type: object + type: array + weights: + properties: + additional: + items: + properties: + podTemplateHash: + type: string + serviceName: + type: string + weight: + format: int32 + type: integer + required: + - weight + type: object + type: array + canary: + properties: + podTemplateHash: + type: string + serviceName: + type: string + weight: + format: int32 + type: integer + required: + - weight + type: object + stable: + properties: + podTemplateHash: + type: string + serviceName: + type: string + weight: + format: int32 + type: integer + required: + - weight + type: object + verified: + type: boolean + required: + - canary + - stable + type: object + type: object + collisionCount: + format: int32 + type: integer + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + lastUpdateTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - lastTransitionTime + - lastUpdateTime + - message + - reason + - status + - type + type: object + type: array + controllerPause: + type: boolean + currentPodHash: + type: string + currentStepHash: + type: string + currentStepIndex: + format: int32 + type: integer + message: + type: string + observedGeneration: + type: string + pauseConditions: + items: + properties: + reason: + type: string + startTime: + format: date-time + type: string + required: + - reason + - startTime + type: object + type: array + phase: + type: string + promoteFull: + type: boolean + readyReplicas: + format: int32 + type: integer + replicas: + format: int32 + type: integer + restartedAt: + format: date-time + type: string + selector: + type: string + stableRS: + type: string + updatedReplicas: + format: int32 + type: integer + workloadObservedGeneration: + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.HPAReplicas + status: {} diff --git a/scripts/kind/kind-config.yaml b/scripts/kind/kind-config.yaml new file mode 100644 index 0000000..8afdaab --- /dev/null +++ b/scripts/kind/kind-config.yaml @@ -0,0 +1,39 @@ +######################################################################## +# https://kind.sigs.k8s.io/docs/user/configuration/ +######################################################################## +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +name: kagent + +# network configuration +networking: + # WARNING: It is _strongly_ recommended that you keep this the default + # (127.0.0.1) for security reasons. However, it is possible to change this. + apiServerAddress: "127.0.0.1" + # By default, the API server listens on a random open port. + # You may choose a specific port but probably don't need to in most cases. + # Using a random port makes it easier to spin up multiple clusters. + # apiServerPort: 6443 + +# this may be used to e.g. disable beta / alpha APIs. +runtimeConfig: + "api/alpha": "false" + +# add to the apiServer certSANs the name of the docker (dind) service in order to be able to reach the cluster through it +kubeadmConfigPatchesJSON6902: + - group: kubeadm.k8s.io + version: v1beta2 + kind: ClusterConfiguration + patch: | + - op: add + path: /apiServer/certSANs/- + value: docker + +# this is the default configuration for nodes +nodes: + - role: control-plane + extraPortMappings: + - containerPort: 30884 + hostPort: 30884 + - containerPort: 30885 + hostPort: 30885 \ No newline at end of file diff --git a/scripts/kind/test-values-e2e.yaml b/scripts/kind/test-values-e2e.yaml new file mode 100644 index 0000000..9460dde --- /dev/null +++ b/scripts/kind/test-values-e2e.yaml @@ -0,0 +1,18 @@ +service: + type: NodePort + ports: + tools: + nodePort: 30885 + +tools: + image: + registry: cr.kagent.dev + +otel: + tracing: + enabled: true + exporter: + otlp: + endpoint: http://host.docker.internal:4317 + timeout: 15 + insecure: true \ No newline at end of file diff --git a/scripts/kind/test-values.yaml b/scripts/kind/test-values.yaml new file mode 100644 index 0000000..f06a7cc --- /dev/null +++ b/scripts/kind/test-values.yaml @@ -0,0 +1,18 @@ +service: + type: NodePort + ports: + tools: + nodePort: 30884 + +tools: + image: + registry: cr.kagent.dev + +otel: + tracing: + enabled: true + exporter: + otlp: + endpoint: http://host.docker.internal:4317 + timeout: 15 + insecure: true \ No newline at end of file diff --git a/test/e2e/cli_test.go b/test/e2e/cli_test.go new file mode 100644 index 0000000..61285b0 --- /dev/null +++ b/test/e2e/cli_test.go @@ -0,0 +1,633 @@ +package e2e + +import ( + "context" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// Test suite setup +var _ = Describe("KAgent Tools E2E Tests", func() { + var ( + ctx context.Context + cancel context.CancelFunc + ) + + BeforeEach(func() { + ctx, cancel = context.WithTimeout(context.Background(), 60*time.Second) + + // Set OTEL environment variables for testing + os.Setenv("OTEL_SERVICE_NAME", "kagent-tools-e2e-test") + os.Setenv("LOG_LEVEL", "debug") + }) + + AfterEach(func() { + if cancel != nil { + cancel() + } + os.Unsetenv("OTEL_SERVICE_NAME") + os.Unsetenv("LOG_LEVEL") + }) + + Describe("HTTP Server Tests", func() { + It("should start and stop HTTP server successfully", func() { + config := TestServerConfig{ + Port: 8085, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + + // Start server + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Test health endpoint + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Health endpoint should be accessible") + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + + // Check server output + output := server.GetOutput() + Expect(output).To(ContainSubstring("Running KAgent Tools Server")) + Expect(output).To(ContainSubstring(fmt.Sprintf(":%d", config.Port))) + + // Stop server + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + + // Wait for server to fully shutdown + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) + defer shutdownCancel() + + err = server.waitForShutdown(shutdownCtx, config.Port) + Expect(err).NotTo(HaveOccurred(), "Server should shut down completely") + }) + + It("should start server with specific tools", func() { + config := TestServerConfig{ + Port: 8086, + Tools: []string{"utils", "k8s"}, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + + // Start server + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Check server output for tool registration + output := server.GetOutput() + Expect(output).To(ContainSubstring("RegisterTools initialized")) + Expect(output).To(ContainSubstring("utils")) + Expect(output).To(ContainSubstring("k8s")) + + // Stop server + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + + It("should start server with all tools enabled", func() { + config := TestServerConfig{ + Port: 8087, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + + // Start server + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Check server output for all tools registration + output := server.GetOutput() + Expect(output).To(ContainSubstring("RegisterTools initialized")) + Expect(output).To(ContainSubstring("Running KAgent Tools Server")) + + // Stop server + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + + It("should start server with kubeconfig parameter", func() { + // Create a temporary kubeconfig file + tempDir := GinkgoT().TempDir() + kubeconfigPath := filepath.Join(tempDir, "kubeconfig") + + kubeconfigContent := `apiVersion: v1 +kind: Config +clusters: +- cluster: + server: https://test-cluster + name: test-cluster +contexts: +- context: + cluster: test-cluster + user: test-user + name: test-context +current-context: test-context +users: +- name: test-user + user: + token: test-token +` + + err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644) + Expect(err).NotTo(HaveOccurred(), "Should create temporary kubeconfig file") + + config := TestServerConfig{ + Port: 8088, + Kubeconfig: kubeconfigPath, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + + // Start server + err = server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Check server output for kubeconfig setting + output := server.GetOutput() + Expect(output).To(ContainSubstring("RegisterTools initialized")) + Expect(output).To(ContainSubstring("Running KAgent Tools Server")) + + // Stop server + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + + It("should handle invalid tool names gracefully", func() { + config := TestServerConfig{ + Port: 18190, + Tools: []string{"invalid-tool", "utils"}, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + + // Start server + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start even with invalid tools") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Check server output for error about invalid tool + output := server.GetOutput() + Expect(output).To(ContainSubstring("Unknown tool specified")) + Expect(output).To(ContainSubstring("invalid-tool")) + + // Valid tools should still be registered + Expect(output).To(ContainSubstring("RegisterTools initialized")) + Expect(output).To(ContainSubstring("utils")) + + // Stop server + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + + It("should handle graceful shutdown", func() { + config := TestServerConfig{ + Port: 8100, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + + // Start server + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Test health endpoint to ensure server is fully ready + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Health endpoint should be accessible") + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + + // Stop server and measure shutdown time + start := time.Now() + err = server.Stop() + duration := time.Since(start) + + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + Expect(duration).To(BeNumerically("<", 10*time.Second), "Shutdown should complete within reasonable time") + + // Check server output for graceful shutdown + output := server.GetOutput() + Expect(output).To(ContainSubstring("Running KAgent Tools Server")) + + // Wait for server to fully shutdown + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) + defer shutdownCancel() + + err = server.waitForShutdown(shutdownCtx, config.Port) + Expect(err).NotTo(HaveOccurred(), "Server should shut down completely") + }) + + It("should handle concurrent requests", func() { + config := TestServerConfig{ + Port: 8088, + Tools: []string{"utils", "k8s"}, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Create multiple concurrent requests + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Concurrent request %d should succeed", id) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + }(i) + } + + wg.Wait() + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + + It("should expose metrics endpoint", func() { + config := TestServerConfig{ + Port: 18190, + Tools: []string{"utils"}, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Test metrics endpoint + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/metrics", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Metrics endpoint should be accessible") + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + + // Read and verify metrics content + body, err := io.ReadAll(resp.Body) + Expect(err).NotTo(HaveOccurred()) + resp.Body.Close() + + metricsContent := string(body) + Expect(metricsContent).To(ContainSubstring("go_")) + Expect(metricsContent).To(ContainSubstring("process_")) + + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + }) + + Describe("STDIO Server Tests", func() { + It("should start STDIO server successfully", func() { + config := TestServerConfig{ + Stdio: true, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + + // Start server + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Check server output for STDIO mode + output := server.GetOutput() + Expect(output).To(ContainSubstring("Running KAgent Tools Server STDIO")) + + // Stop server + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + }) + + Describe("Tool Registration Tests", func() { + It("should register single tool correctly", func() { + config := TestServerConfig{ + Port: 8087, + Tools: []string{"k8s"}, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Verify registered tools + output := server.GetOutput() + Expect(output).To(ContainSubstring("Running KAgent Tools Server")) + Expect(output).To(ContainSubstring("k8s")) + + // Test health endpoint + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Health endpoint should be accessible") + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + + It("should register multiple tools correctly", func() { + config := TestServerConfig{ + Port: 8088, + Tools: []string{"k8s", "prometheus", "utils"}, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Verify registered tools + output := server.GetOutput() + Expect(output).To(ContainSubstring("Running KAgent Tools Server")) + for _, tool := range []string{"k8s", "prometheus", "utils"} { + Expect(output).To(ContainSubstring(tool)) + } + + // Test health endpoint + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Health endpoint should be accessible") + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + + It("should register all tools implicitly", func() { + config := TestServerConfig{ + Port: 18190, + Tools: []string{}, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Verify all tools are registered + output := server.GetOutput() + Expect(output).To(ContainSubstring("RegisterTools initialized")) + Expect(output).To(ContainSubstring("Running KAgent Tools Server")) + + // Test health endpoint + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Health endpoint should be accessible") + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + }) + + Describe("Error Handling Tests", func() { + It("should handle malformed requests gracefully", func() { + config := TestServerConfig{ + Port: 8089, + Tools: []string{"utils"}, + Stdio: false, + Timeout: 10 * time.Second, + } + + server := NewTestServer(config) + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Test malformed request + req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/nonexistent", config.Port), strings.NewReader("invalid json")) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) + resp.Body.Close() + + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + }) + + Describe("Environment and Configuration Tests", func() { + It("should handle environment variables correctly", func() { + // Set environment variables + originalEnv := os.Environ() + defer func() { + os.Clearenv() + for _, env := range originalEnv { + parts := strings.SplitN(env, "=", 2) + if len(parts) == 2 { + os.Setenv(parts[0], parts[1]) + } + } + }() + + os.Setenv("LOG_LEVEL", "info") + os.Setenv("OTEL_SERVICE_NAME", "test-kagent-tools") + + config := TestServerConfig{ + Port: 18195, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + + // Start server + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Check server output + output := server.GetOutput() + Expect(output).To(ContainSubstring("Starting kagent-tools-server")) + + // Stop server + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + + It("should validate server binary exists and is executable", func() { + // Check if server binary exists + binaryName := getBinaryName() + binaryPath := fmt.Sprintf("../../bin/%s", binaryName) + _, err := os.Stat(binaryPath) + if os.IsNotExist(err) { + Skip("Server binary not found, skipping test. Run 'make build' first.") + } + Expect(err).NotTo(HaveOccurred(), "Server binary should exist") + + // Test --help flag + helpCtx, helpCancel := context.WithTimeout(context.Background(), 10*time.Second) + defer helpCancel() + + cmd := exec.CommandContext(helpCtx, binaryPath, "--help") + output, err := cmd.CombinedOutput() + Expect(err).NotTo(HaveOccurred(), "Server should respond to --help flag") + + outputStr := string(output) + Expect(outputStr).To(ContainSubstring("KAgent tool server")) + Expect(outputStr).To(ContainSubstring("--port")) + Expect(outputStr).To(ContainSubstring("--stdio")) + Expect(outputStr).To(ContainSubstring("--tools")) + Expect(outputStr).To(ContainSubstring("--kubeconfig")) + }) + }) + + Describe("Concurrent Server Instances", func() { + It("should run multiple server instances concurrently", func() { + var wg sync.WaitGroup + numServers := 3 + servers := make([]*TestServer, numServers) + + // Start multiple servers on different ports + for i := 0; i < numServers; i++ { + wg.Add(1) + go func(index int) { + defer wg.Done() + + config := TestServerConfig{ + Port: 18092 + index, + Tools: []string{"utils"}, + Stdio: false, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + servers[index] = server + + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server %d should start successfully", index) + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Test health endpoint + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Health endpoint should be accessible for server %d", index) + if resp != nil { + resp.Body.Close() + } + }(i) + } + + wg.Wait() + + // Stop all servers + for i, server := range servers { + if server != nil { + err := server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server %d should stop gracefully", i) + } + } + }) + }) + + Describe("Telemetry Tests", func() { + It("should initialize telemetry correctly", func() { + config := TestServerConfig{ + Port: 18092, + Tools: []string{"utils"}, + Timeout: 30 * time.Second, + } + + server := NewTestServer(config) + err := server.Start(ctx, config) + Expect(err).NotTo(HaveOccurred(), "Server should start successfully") + + // Wait for server to be ready + time.Sleep(3 * time.Second) + + // Check server output for telemetry initialization + output := server.GetOutput() + Expect(output).To(ContainSubstring("Starting kagent-tools-server")) + + // Make a request to generate telemetry + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/health", config.Port)) + Expect(err).NotTo(HaveOccurred(), "Health endpoint should be accessible") + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + + // Check server output for successful startup + output = server.GetOutput() + Expect(output).To(ContainSubstring("Running KAgent Tools Server")) + + err = server.Stop() + Expect(err).NotTo(HaveOccurred(), "Server should stop gracefully") + }) + }) +}) + +// Helper functions for test setup +func init() { + // Ensure the binary exists before running tests + binaryName := getBinaryName() + binaryPath := fmt.Sprintf("../../bin/%s", binaryName) + if _, err := os.Stat(binaryPath); os.IsNotExist(err) { + // Try to build the binary + cmd := exec.Command("make", "build") + cmd.Dir = "../.." + if err := cmd.Run(); err != nil { + panic(fmt.Sprintf("Failed to build server binary: %v", err)) + } + } +} diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go new file mode 100644 index 0000000..1f95792 --- /dev/null +++ b/test/e2e/e2e_test.go @@ -0,0 +1,13 @@ +package e2e + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "testing" +) + +// TestE2EK8s is the main test runner for Kubernetes E2E tests +func TestE2EK8s(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Tools E2E Suite") +} diff --git a/test/e2e/helpers_test.go b/test/e2e/helpers_test.go new file mode 100644 index 0000000..59008d8 --- /dev/null +++ b/test/e2e/helpers_test.go @@ -0,0 +1,606 @@ +package e2e + +import ( + "bufio" + "context" + "fmt" + "io" + "log/slog" + "net/http" + "os" + "os/exec" + "runtime" + "strings" + "sync" + "time" + + "github.com/kagent-dev/tools/internal/commands" + "github.com/mark3labs/mcp-go/client" + "github.com/mark3labs/mcp-go/client/transport" + "github.com/mark3labs/mcp-go/mcp" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// getBinaryName returns the platform-specific binary name +func getBinaryName() string { + osName := runtime.GOOS + archName := runtime.GOARCH + return fmt.Sprintf("kagent-tools-%s-%s", osName, archName) +} + +// TestServerConfig holds configuration for server tests +type TestServerConfig struct { + Port int + Tools []string + Kubeconfig string + Stdio bool + Timeout time.Duration +} + +// TestServer represents a test server instance +type TestServer struct { + cmd *exec.Cmd + port int + stdio bool + cancel context.CancelFunc + done chan struct{} + output strings.Builder + mu sync.RWMutex +} + +// NewTestServer creates a new test server instance +func NewTestServer(config TestServerConfig) *TestServer { + return &TestServer{ + port: config.Port, + stdio: config.Stdio, + done: make(chan struct{}), + } +} + +// Start starts the test server +func (ts *TestServer) Start(ctx context.Context, config TestServerConfig) error { + ts.mu.Lock() + defer ts.mu.Unlock() + + // Build command arguments + args := []string{} + if config.Stdio { + args = append(args, "--stdio") + } else { + args = append(args, "--port", fmt.Sprintf("%d", config.Port)) + } + + if len(config.Tools) > 0 { + args = append(args, "--tools", strings.Join(config.Tools, ",")) + } + + if config.Kubeconfig != "" { + args = append(args, "--kubeconfig", config.Kubeconfig) + } + + // Create context with cancellation + ctx, cancel := context.WithCancel(ctx) + ts.cancel = cancel + + // Start server process + binaryName := getBinaryName() + ts.cmd = exec.CommandContext(ctx, fmt.Sprintf("../../bin/%s", binaryName), args...) + ts.cmd.Env = append(os.Environ(), "LOG_LEVEL=debug") + + // Set up output capture + stdout, err := ts.cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("failed to create stdout pipe: %w", err) + } + + stderr, err := ts.cmd.StderrPipe() + if err != nil { + return fmt.Errorf("failed to create stderr pipe: %w", err) + } + + // Start the command + if err := ts.cmd.Start(); err != nil { + return fmt.Errorf("failed to start server: %w", err) + } + + // Start goroutines to capture output + go ts.captureOutput(stdout, "STDOUT") + go ts.captureOutput(stderr, "STDERR") + + // Wait for server to start + if !config.Stdio { + return ts.waitForHTTPServer(ctx, config.Timeout) + } + + return nil +} + +// Stop stops the test server +func (ts *TestServer) Stop() error { + ts.mu.Lock() + defer ts.mu.Unlock() + + if ts.cancel != nil { + ts.cancel() + } + + if ts.cmd != nil && ts.cmd.Process != nil { + // Send interrupt signal for graceful shutdown + if err := ts.cmd.Process.Signal(os.Interrupt); err != nil { + // If interrupt fails, kill the process + _ = ts.cmd.Process.Kill() + } + + // Wait for process to exit with timeout + done := make(chan error, 1) + go func() { + done <- ts.cmd.Wait() + }() + + select { + case <-done: + // Process exited + case <-time.After(8 * time.Second): // Increased timeout + // Timeout, force kill + _ = ts.cmd.Process.Kill() + // Wait a bit more for force kill to complete + select { + case <-done: + case <-time.After(2 * time.Second): + // Force kill timeout, continue anyway + } + } + } + + // Signal done and wait for goroutines to exit + if ts.done != nil { + close(ts.done) + } + + // Give goroutines time to exit + time.Sleep(100 * time.Millisecond) + + return nil +} + +// MCPClient represents a client for communicating with the MCP server using the official mcp-go client +type MCPClient struct { + client *client.Client + log *slog.Logger +} + +// InstallKAgentTools installs KAgent Tools using helm in the specified namespace +func InstallKAgentTools(namespace string, releaseName string) { + // Use longer timeout for helm installation as it can take time to pull images + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + + log := slog.Default() + By("Installing KAgent Tools in namespace " + namespace) + log.Info("Installing KAgent Tools", "namespace", namespace) + + // First, try to uninstall any existing release to clean up + log.Info("Cleaning up any existing release", "release", releaseName, "namespace", namespace) + _, _ = commands.NewCommandBuilder("helm"). + WithArgs("uninstall", releaseName). + WithArgs("--namespace", namespace). + WithArgs("--ignore-not-found"). + WithCache(false). + Execute(ctx) + + // install crd scripts/kind/crd-argo.yaml + By("Installing CRDs for KAgent Tools") + _, err := commands.NewCommandBuilder("kubectl"). + WithArgs("apply", "-f", "../../scripts/kind/crd-argo.yaml"). + WithArgs("--namespace", namespace). + WithCache(false). // Don't cache CRD installation + Execute(ctx) + Expect(err).ToNot(HaveOccurred(), "Failed to install CRDs: %v", err) + + // Install KAgent Tools using helm with unique release name + // Use absolute path from project root + output, err := commands.NewCommandBuilder("helm"). + WithArgs("install", releaseName, "../../helm/kagent-tools"). + WithArgs("--namespace", namespace). + WithArgs("-f"). + WithArgs("../../scripts/kind/test-values-e2e.yaml"). + WithArgs("--create-namespace"). + WithArgs("--debug"). + WithArgs("--wait"). + WithArgs("--timeout=1m"). + WithCache(false). // Don't cache helm installation + Execute(ctx) + + Expect(err).ToNot(HaveOccurred(), "Failed to install KAgent Tools: %v %v", err, output) + log.Info("KAgent Tools installation completed", "namespace", namespace, "output", output) + + // Verify the installation by checking if pods are running + By("Verifying KAgent Tools pods are running") + log.Info("Verifying KAgent Tools pods", "namespace", namespace) + + Eventually(func() bool { + ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) + defer cancel() + + output, err := commands.NewCommandBuilder("kubectl"). + WithArgs("get", "pods", "-n", namespace, "-l", "app.kubernetes.io/name=kagent-tools", "-o", "jsonpath={.items[*].status.phase}"). + Execute(ctx) + + if err != nil { + log.Error("Failed to get pod status", "error", err) + return false + } + + log.Info("Pod status check", "namespace", namespace, "output", output) + // Check if all pods are in Running state + return output == "Running" || (len(output) > 0 && !contains(output, "Pending") && !contains(output, "Failed")) + }, 60*time.Second, 5*time.Second).Should(BeTrue(), "KAgent Tools pods should be running") + + log.Info("KAgent Tools pods are running", "namespace", namespace) + //validate service nodePort == 30885 + By("Validating KAgent Tools service is accessible") + nodePort, err := commands.NewCommandBuilder("kubectl"). + WithArgs("get", "svc", "-n", namespace, "-o", "jsonpath={.items[0].spec.ports[0].nodePort}"). + Execute(ctx) + Expect(err).ToNot(HaveOccurred(), "Failed to get service nodePort: %v", err) + Expect(nodePort).To(Equal("30885")) +} + +// GetMCPClient creates a new MCP client configured for the e2e test environment using the official mcp-go client +func GetMCPClient() (*MCPClient, error) { + // Create HTTP transport for the MCP server + httpTransport, err := transport.NewStreamableHTTP("http://127.0.0.1:30885/mcp", transport.WithHTTPTimeout(15*time.Second)) + if err != nil { + return nil, fmt.Errorf("failed to create HTTP transport: %w", err) + } + + // Create the official MCP client + mcpClient := client.NewClient(httpTransport) + + // Start the client + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + if err := mcpClient.Start(ctx); err != nil { + return nil, fmt.Errorf("failed to start MCP client: %w", err) + } + + // Initialize the client + initRequest := mcp.InitializeRequest{} + initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION + initRequest.Params.ClientInfo = mcp.Implementation{ + Name: "e2e-test-client", + Version: "1.0.0", + } + initRequest.Params.Capabilities = mcp.ClientCapabilities{} + + _, err = mcpClient.Initialize(ctx, initRequest) + if err != nil { + return nil, fmt.Errorf("failed to initialize MCP client: %w", err) + } + + mcpHelper := &MCPClient{ + client: mcpClient, + log: slog.Default(), + } + + // Validate connection by listing tools + tools, err := mcpHelper.listTools() + if len(tools) == 0 { + return nil, fmt.Errorf("no tools found in MCP server: %w", err) + } + slog.Default().Info("MCP Client created", "baseURL", "http://127.0.0.1:30885/mcp", "tools", len(tools)) + return mcpHelper, err +} + +// listTools calls the tools/list method to get available tools +func (c *MCPClient) listTools() ([]interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + request := mcp.ListToolsRequest{} + result, err := c.client.ListTools(ctx, request) + if err != nil { + return nil, err + } + + // Convert tools to interface{} slice for compatibility + tools := make([]interface{}, len(result.Tools)) + for i, tool := range result.Tools { + tools[i] = tool + } + + return tools, nil +} + +// k8sListResources calls the k8s_get_resources tool +func (c *MCPClient) k8sListResources(resourceType string) (interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + type K8sArgs struct { + ResourceType string `json:"resource_type"` + Output string `json:"output"` + } + + arguments := K8sArgs{ + ResourceType: resourceType, + Output: "json", + } + + request := mcp.CallToolRequest{ + Params: mcp.CallToolParams{ + Name: "k8s_get_resources", + Arguments: arguments, + }, + } + + result, err := c.client.CallTool(ctx, request) + if err != nil { + return nil, err + } + if result.IsError { + return nil, fmt.Errorf("tool call failed: %s", result.Content) + } + return result, nil +} + +// helmListReleases calls the helm_list_releases tool +func (c *MCPClient) helmListReleases() (interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + type HelmArgs struct { + AllNamespaces string `json:"all_namespaces"` + Output string `json:"output"` + } + + arguments := HelmArgs{ + AllNamespaces: "true", + Output: "json", + } + + request := mcp.CallToolRequest{ + Params: mcp.CallToolParams{ + Name: "helm_list_releases", + Arguments: arguments, + }, + } + + result, err := c.client.CallTool(ctx, request) + if err != nil { + return nil, err + } + if result.IsError { + return nil, fmt.Errorf("tool call failed: %s", result.Content) + } + return result, nil +} + +// istioInstall calls the istio_install_istio tool +func (c *MCPClient) istioInstall(profile string) (interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) // Istio install can take time + defer cancel() + + type IstioArgs struct { + Profile string `json:"profile"` + } + + arguments := IstioArgs{ + Profile: profile, + } + + request := mcp.CallToolRequest{ + Params: mcp.CallToolParams{ + Name: "istio_install_istio", + Arguments: arguments, + }, + } + + result, err := c.client.CallTool(ctx, request) + if err != nil { + return nil, err + } + if result.IsError { + return nil, fmt.Errorf("tool call failed: %s", result.Content) + } + return result, nil +} + +// argoRolloutsList calls the argo_rollouts_get tool to list rollouts +func (c *MCPClient) argoRolloutsList(namespace string) (interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + type ArgoArgs struct { + Namespace string `json:"namespace"` + Output string `json:"output"` + } + + arguments := ArgoArgs{ + Namespace: namespace, + Output: "json", + } + + request := mcp.CallToolRequest{ + Params: mcp.CallToolParams{ + Name: "argo_rollouts_list", + Arguments: arguments, + }, + } + + result, err := c.client.CallTool(ctx, request) + if err != nil { + return nil, err + } + if result.IsError { + return nil, fmt.Errorf("tool call failed: %s", result.Content) + } + return result, nil +} + +// ciliumStatus calls the cilium_status_and_version tool +func (c *MCPClient) ciliumStatus() (interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + request := mcp.CallToolRequest{ + Params: mcp.CallToolParams{ + Name: "cilium_status_and_version", + Arguments: nil, + }, + } + + result, err := c.client.CallTool(ctx, request) + if err != nil { + return nil, err + } + return result, nil +} + +// Constants for default test values +const ( + DefaultReleaseName = "kagent-tools-e2e" + DefaultTestNamespace = "kagent-tools-e2e" + DefaultTimeout = 60 * time.Second // Increased for more realistic timeouts +) + +// CreateNamespace creates a new Kubernetes namespace +func CreateNamespace(namespace string) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + log := slog.Default() + By("Creating namespace " + namespace) + log.Info("Creating namespace", "namespace", namespace) + + // First, check if the namespace already exists + _, err := commands.NewCommandBuilder("kubectl"). + WithArgs("get", "namespace", namespace). + WithCache(false). + Execute(ctx) + + if err == nil { + log.Info("Namespace already exists, skipping creation", "namespace", namespace) + return + } + + // Create the namespace using kubectl + output, err := commands.NewCommandBuilder("kubectl"). + WithArgs("create", "namespace", namespace). + WithCache(false). // Don't cache namespace creation + Execute(ctx) + + // If it's an AlreadyExists error, that's fine - treat it as success + if err != nil && strings.Contains(err.Error(), "AlreadyExists") { + log.Info("Namespace already exists, continuing", "namespace", namespace) + return + } + + Expect(err).ToNot(HaveOccurred(), "Failed to create namespace: %v", err) + log.Info("Namespace creation completed", "namespace", namespace, "output", output) +} + +// DeleteNamespace deletes a Kubernetes namespace +func DeleteNamespace(namespace string) { + // Use longer timeout for namespace deletion as it can take more time + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + log := slog.Default() + By("Deleting namespace " + namespace) + log.Info("Deleting namespace", "namespace", namespace) + + // Delete the namespace using kubectl + output, err := commands.NewCommandBuilder("kubectl"). + WithArgs("delete", "namespace", namespace, "--ignore-not-found=true", "--wait=false"). + WithCache(false). // Don't cache namespace deletion + Execute(ctx) + + Expect(err).ToNot(HaveOccurred(), "Failed to delete namespace: %v", err) + log.Info("Namespace deletion completed", "namespace", namespace, "output", output) +} + +// contains checks if a string contains a substring +func contains(s, substr string) bool { + return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || containsHelper(s, substr))) +} + +func containsHelper(s, substr string) bool { + for i := 0; i <= len(s)-len(substr); i++ { + if s[i:i+len(substr)] == substr { + return true + } + } + return false +} + +// waitForHTTPServer waits for the HTTP server to become available +func (ts *TestServer) waitForHTTPServer(ctx context.Context, timeout time.Duration) error { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + url := fmt.Sprintf("http://localhost:%d/health", ts.port) + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return fmt.Errorf("timeout waiting for server to start") + case <-ticker.C: + resp, err := http.Get(url) + if err == nil { + _ = resp.Body.Close() + if resp.StatusCode == http.StatusOK { + return nil + } + } + } + } +} + +// waitForShutdown waits for the HTTP server to become unavailable +func (ts *TestServer) waitForShutdown(ctx context.Context, port int) error { + url := fmt.Sprintf("http://localhost:%d/health", port) + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return fmt.Errorf("timeout waiting for server to shutdown") + case <-ticker.C: + _, err := http.Get(url) + if err != nil { + // Server is not accessible, shutdown complete + return nil + } + } + } +} + +// GetOutput returns the captured output +func (ts *TestServer) GetOutput() string { + ts.mu.RLock() + defer ts.mu.RUnlock() + return ts.output.String() +} + +// captureOutput captures output from the server +func (ts *TestServer) captureOutput(reader io.Reader, prefix string) { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + select { + case <-ts.done: + // Shutdown signal received, exit goroutine + return + default: + line := scanner.Text() + ts.mu.Lock() + ts.output.WriteString(fmt.Sprintf("[%s] %s\n", prefix, line)) + ts.mu.Unlock() + } + } +} diff --git a/test/e2e/k8s_test.go b/test/e2e/k8s_test.go new file mode 100644 index 0000000..65ad66a --- /dev/null +++ b/test/e2e/k8s_test.go @@ -0,0 +1,151 @@ +package e2e + +import ( + "context" + "fmt" + "github.com/kagent-dev/tools/internal/commands" + "github.com/kagent-dev/tools/internal/logger" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +/* +K8s E2E Tests +These tests are used to test the Kubernetes integration of the KAgent Tools. +They are run in a Kubernetes cluster and have working in-cluster resources. + +They test the following: +- KAgent Tools can be installed in a Kubernetes cluster +- KAgent Tools k8s can list all resources in the cluster +- KAgent Tools helm can list all releases in the cluster +- KAgent Tools istioctl can install istio in the cluster +- KAgent Tools cillium can install cillium in the cluster +*/ + +var _ = Describe("KAgent Tools Kubernetes E2E Tests", Ordered, func() { + + var err error + var client *MCPClient + var log = logger.Get() + var namespace = DefaultTestNamespace + var releaseName = DefaultReleaseName + + BeforeAll(func() { + log.Info("Starting KAgent Tools E2E tests") + // Create new namespace + CreateNamespace(namespace) + // Install kagent tools + InstallKAgentTools(namespace, releaseName) + + client, err = GetMCPClient() + Expect(err).ToNot(HaveOccurred(), "Failed to get MCP client: %v", err) + }) + + AfterAll(func() { + log.Info("Cleaning up KAgent Tools E2E tests", "namespace", namespace) + // Delete namespace + if namespace != "" { + DeleteNamespace(namespace) + } + }) + + Describe("KAgent Tools Deployment", func() { + It("should have kagent-tools pods running", func() { + ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) + defer cancel() + + log.Info("Checking if kagent-tools pods are running", "namespace", namespace) + output, err := commands.NewCommandBuilder("kubectl"). + WithArgs("get", "pods", "-n", namespace, "-l", "app.kubernetes.io/name=kagent-tools", "-o", "json"). + Execute(ctx) + + Expect(err).ToNot(HaveOccurred()) + Expect(output).ToNot(BeEmpty()) + log.Info("Successfully verified kagent-tools pods", "namespace", namespace) + }) + + It("should have kagent-tools service accessible", func() { + ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) + defer cancel() + + log.Info("Checking if kagent-tools service is accessible", "namespace", namespace) + output, err := commands.NewCommandBuilder("kubectl"). + WithArgs("get", "svc", "-n", namespace, "-l", "app.kubernetes.io/name=kagent-tools", "-o", "json"). + Execute(ctx) + + Expect(err).ToNot(HaveOccurred()) + Expect(output).ToNot(BeEmpty()) + log.Info("Successfully verified kagent-tools service", "namespace", namespace, "output", output) + }) + }) + + Describe("KAgent Tools K8s Operations", func() { + It("should be able to list namespace in the cluster", func() { + log.Info("Testing MCP client connectivity and k8s operations", "namespace", namespace) + + // Test k8s list resources functionality + log.Info("Testing k8s list resources via MCP") + response, err := client.k8sListResources("namespace") + Expect(err).ToNot(HaveOccurred(), "Failed to list k8s resources via MCP: %v", err) + Expect(response).ToNot(BeNil()) + + log.Info("Successfully tested k8s operations via MCP", "namespace", namespace) + }) + }) + + Describe("KAgent Tools Helm Operations", func() { + It("should be able to list all helm releases", func() { + log.Info("Testing helm operations via MCP", "namespace", namespace) + + // Test helm list releases functionality + log.Info("Testing helm list releases via MCP") + response, err := client.helmListReleases() + if err != nil { + log.Info("Helm list releases failed (may be normal)", "error", err) + Skip(fmt.Sprintf("Helm operations not available: %v", err)) + return + } + Expect(response).ToNot(BeNil()) + log.Info("Successfully tested helm operations via MCP", "namespace", namespace) + }) + }) + + Describe("KAgent Tools Istio Operations", func() { + It("should be able to install istio in the cluster", func() { + log.Info("Testing istio operations via MCP", "namespace", namespace) + + // If we get here, MCP is accessible, test istio operations + response, err := client.istioInstall("default") + Expect(err).ToNot(HaveOccurred(), "Failed to install istio via MCP: %v", err) + Expect(response).ToNot(BeNil()) + + log.Info("Successfully tested istio operations via MCP", "namespace", namespace, "response", response) + }) + }) + + Describe("KAgent Tools Cilium Operations", func() { + It("should be able to install cilium in the cluster", func() { + log.Info("Testing cilium operations via MCP", "namespace", namespace) + + // If we get here, MCP is accessible, test cilium operations + response, err := client.ciliumStatus() + Expect(err).ToNot(HaveOccurred(), "Failed to get cilium status via MCP: %v", err) + Expect(response).ToNot(BeNil()) + + log.Info("Successfully tested cilium operations via MCP", "namespace", namespace) + }) + }) + + Describe("KAgent Tools Argo Operations", func() { + It("should be able to list Argo rollouts in the cluster", func() { + log.Info("Testing Argo operations via MCP", "namespace", namespace) + + // If we get here, MCP is accessible, test cilium operations + response, err := client.argoRolloutsList(namespace) + Expect(err).ToNot(HaveOccurred(), "Failed to list argo rollouts via MCP: %v", err) + Expect(response).ToNot(BeNil()) + + log.Info("Successfully tested argo rollouts via MCP", "namespace", namespace) + }) + }) +})