Перейти к основному содержимому

Pod Execution

Implementation Reference

Source Files:

  • packages/core/src/adapters/kubernetes-adapter.ts - Main Kubernetes adapter
  • packages/core/src/utils/kubernetes-api.ts - K8s API utilities and pod instances
  • packages/core/src/core/command.ts - KubernetesAdapterOptions interface

Key Functions:

  • KubernetesAdapter.execute() - Main command execution in pods
  • KubernetesAdapter.buildKubectlExecArgs() - Build kubectl exec arguments
  • createK8sPod() - Create pod instance with execution methods
  • K8sPod.exec() - Execute commands in specific pod
  • K8sPod.raw() - Raw command execution without shell

Overview

Xec provides powerful capabilities for executing commands inside Kubernetes pods through the kubectl exec interface. The execution engine handles pod selection, container targeting, and command execution with proper error handling and stream management.

Basic Pod Execution

Direct Pod Execution

Execute commands in pods using the Kubernetes adapter:

import { $ } from '@xec-sh/core';

// Execute in specific pod
const result = await $.k8s({
pod: 'web-server-abc123',
namespace: 'production'
})`ps aux`;

console.log(result.stdout);

Using Pod Instance

Get a pod instance for multiple operations:

const k8s = $.k8s({ namespace: 'default' });
const pod = k8s.pod('my-app-pod');

// Execute multiple commands
const hostname = await pod.exec`hostname`;
const processes = await pod.exec`ps aux | grep node`;
const diskUsage = await pod.exec`df -h`;

console.log(`Pod: ${hostname.stdout.trim()}`);
console.log(`Node processes: ${processes.stdout}`);

Container Selection

Multi-Container Pods

Target specific containers in multi-container pods:

// Execute in specific container
const appResult = await $.k8s({
pod: 'multi-container-pod',
container: 'app',
namespace: 'production'
})`cat /app/version.txt`;

// Execute in sidecar container
const sidecarResult = await $.k8s({
pod: 'multi-container-pod',
container: 'nginx',
namespace: 'production'
})`nginx -t`;

Container Methods

Use pod instance methods for container-specific operations:

const pod = k8s.pod('multi-container-pod');

// Different containers in same pod
const appStatus = await pod.exec`curl localhost:3000/health`;
const nginxConfig = await pod.exec`nginx -T`; // Uses default container

// Override container per command
const specificContainer = await $.k8s({
pod: 'multi-container-pod',
container: 'sidecar'
})`tail -f /var/log/sidecar.log`;

Execution Options

TTY and Interactive Mode

Control TTY and interactive options:

// Enable TTY for interactive commands
const interactive = await $.k8s({
pod: 'debug-pod',
tty: true,
stdin: true
})`top -b -n 1`;

// Non-interactive mode (default)
const batch = await $.k8s({
pod: 'worker-pod',
tty: false
})`batch-process --config /app/config.json`;

Custom kubectl Flags

Pass additional flags to kubectl exec:

const result = await $.k8s({
pod: 'my-pod',
execFlags: ['--quiet', '--request-timeout=30s']
})`long-running-command`;

Shell vs Raw Execution

Shell Execution (Default)

Commands are executed through shell by default:

// Shell command with pipes and redirects
const result = await pod.exec`ps aux | grep node | wc -l`;

// Environment variable expansion
const path = await pod.exec`echo $PATH`;

// Complex shell operations
const cleanup = await pod.exec`find /tmp -name "*.log" -mtime +7 -delete`;

Raw Execution

Execute commands directly without shell interpretation:

// Raw command execution
const direct = await pod.raw`ls -la /app`;

// Safer for commands with special characters
const literal = await pod.raw`echo "Hello | World"`;

Pod Selection Patterns

Exact Pod Names

Target pods by exact name:

// Full pod name
await $.k8s({
pod: 'web-deployment-abc123-xyz',
namespace: 'production'
})`uptime`;

Label Selectors

Select pods using Kubernetes label selectors:

// Using label selector syntax
await $.k8s({
pod: '-l app=web,env=production',
namespace: 'production'
})`systemctl status nginx`;

// The adapter automatically resolves to first matching pod

Pattern Matching

Use patterns for pod selection:

// Regex pattern (handled by kubectl)
const webPods = await $.k8s({
pod: 'web-.*',
namespace: 'production'
})`curl localhost:8080/health`;

Environment and Working Directory

Environment Variables

Set environment variables for pod execution:

const configured = $.k8s({
pod: 'my-pod'
}).env({
DATABASE_URL: 'postgres://localhost:5432/mydb',
LOG_LEVEL: 'debug'
});

await configured`echo "DB: $DATABASE_URL"`;

Working Directory

Execute commands in specific directories:

const app = $.k8s({
pod: 'app-pod'
}).cd('/app');

// All commands run in /app directory
await app`npm test`;
await app`ls -la package.json`;

Error Handling

Command Failures

Handle pod execution failures:

try {
await $.k8s({
pod: 'worker-pod'
})`failing-command`;
} catch (error) {
if (error.code === 'KUBERNETES_ERROR') {
console.log('Kubectl failed:', error.message);
console.log('Stderr:', error.stderr);
}
}

Non-throwing Execution

Use .nothrow() to handle failures gracefully:

const result = await $.k8s({
pod: 'test-pod'
})`risky-operation`.nothrow();

if (result.ok) {
console.log('Success:', result.stdout);
} else {
console.log('Failed with code:', result.exitCode);
console.log('Error:', result.stderr);
}

Pod Availability

Check pod readiness before execution:

const adapter = $.getAdapter('kubernetes');

// Check if pod is ready
const isReady = await adapter.isPodReady('my-pod', 'default');
if (!isReady) {
console.log('Pod not ready, waiting...');
// Implementation would wait or retry
}

Advanced Execution

Timeout Configuration

Set execution timeouts:

const longRunning = $.k8s({
pod: 'batch-processor'
}).timeout(300000); // 5 minutes

await longRunning`large-batch-job --input /data/large-file.csv`;

Retry Logic

Implement retry for transient failures:

const resilient = $.k8s({
pod: 'api-pod'
}).retry({
attempts: 3,
delay: 1000
});

await resilient`curl -f http://external-api/data`;

Streaming Output

Stream command output in real-time:

const stream = $.k8s({
pod: 'log-processor'
})`tail -f /var/log/app.log`;

// Process streaming output
stream.stdout.on('data', (chunk) => {
process.stdout.write(`[${pod}] ${chunk}`);
});

await stream;

Common Patterns

Health Checks

Implement pod health checking:

async function checkPodHealth(podName: string) {
const pod = $.k8s({ pod: podName, namespace: 'production' });

const health = await pod.exec`curl -f http://localhost:8080/health`.nothrow();

if (health.ok) {
const status = JSON.parse(health.stdout);
return { healthy: status.status === 'ok', details: status };
}

return { healthy: false, error: health.stderr };
}

Log Collection

Collect logs from application files:

async function collectApplicationLogs(pods: string[]) {
const results = await Promise.all(
pods.map(async (podName) => {
const pod = $.k8s({ pod: podName, namespace: 'production' });
const logs = await pod.exec`tail -n 100 /var/log/app.log`.nothrow();

return {
pod: podName,
logs: logs.ok ? logs.stdout : `Error: ${logs.stderr}`
};
})
);

return results;
}

Configuration Validation

Validate configuration in pods:

async function validateConfig(podName: string) {
const pod = $.k8s({ pod: podName, namespace: 'staging' });

// Check config file exists
const configCheck = await pod.exec`test -f /app/config.json`.nothrow();
if (!configCheck.ok) {
throw new Error('Configuration file missing');
}

// Validate JSON syntax
const jsonCheck = await pod.exec`python -m json.tool /app/config.json`.nothrow();
if (!jsonCheck.ok) {
throw new Error('Invalid JSON configuration');
}

// Application-specific validation
const appCheck = await pod.exec`/app/bin/validate-config`.nothrow();
if (!appCheck.ok) {
throw new Error(`Config validation failed: ${appCheck.stderr}`);
}

return true;
}

Performance Considerations

Command Efficiency

  • Batch Commands: Combine multiple operations into single commands
  • Shell Pipelines: Use shell features to reduce round trips
  • Local Processing: Process data locally when possible

Resource Usage

  • Memory: Each kubectl exec creates a new process
  • Network: Commands go through Kubernetes API server
  • Timing: Allow 200-500ms overhead per command

Best Practices

// Good: Single command with pipeline
await pod.exec`ps aux | grep node | awk '{print $2}' | head -5`;

// Less efficient: Multiple separate commands
const ps = await pod.exec`ps aux`;
const filtered = await pod.exec`echo "${ps.stdout}" | grep node`;
const pids = await pod.exec`echo "${filtered.stdout}" | awk '{print $2}'`;