 8d9b62daf3
			
		
	
	8d9b62daf3
	
	
	
		
			
			This commit implements Phase 2 of the CHORUS Task Execution Engine development plan, providing a comprehensive execution environment abstraction layer with Docker container sandboxing support. ## New Features ### Core Sandbox Interface - Comprehensive ExecutionSandbox interface with isolated task execution - Support for command execution, file I/O, environment management - Resource usage monitoring and sandbox lifecycle management - Standardized error handling with SandboxError types and categories ### Docker Container Sandbox Implementation - Full Docker API integration with secure container creation - Transparent repository mounting with configurable read/write access - Advanced security policies with capability dropping and privilege controls - Comprehensive resource limits (CPU, memory, disk, processes, file handles) - Support for tmpfs mounts, masked paths, and read-only bind mounts - Container lifecycle management with proper cleanup and health monitoring ### Security & Resource Management - Configurable security policies with SELinux, AppArmor, and Seccomp support - Fine-grained capability management with secure defaults - Network isolation options with configurable DNS and proxy settings - Resource monitoring with real-time CPU, memory, and network usage tracking - Comprehensive ulimits configuration for process and file handle limits ### Repository Integration - Seamless repository mounting from local paths to container workspaces - Git configuration support with user credentials and global settings - File inclusion/exclusion patterns for selective repository access - Configurable permissions and ownership for mounted repositories ### Testing Infrastructure - Comprehensive test suite with 60+ test cases covering all functionality - Docker integration tests with Alpine Linux containers (skipped in short mode) - Mock sandbox implementation for unit testing without Docker dependencies - Security policy validation tests with read-only filesystem enforcement - Resource usage monitoring and cleanup verification tests ## Technical Details ### Dependencies Added - github.com/docker/docker v28.4.0+incompatible - Docker API client - github.com/docker/go-connections v0.6.0 - Docker connection utilities - github.com/docker/go-units v0.5.0 - Docker units and formatting - Associated Docker API dependencies for complete container management ### Architecture - Interface-driven design enabling multiple sandbox implementations - Comprehensive configuration structures for all sandbox aspects - Resource usage tracking with detailed metrics collection - Error handling with retryable error classification - Proper cleanup and resource management throughout sandbox lifecycle ### Compatibility - Maintains backward compatibility with existing CHORUS architecture - Designed for future integration with Phase 3 Core Task Execution Engine - Extensible design supporting additional sandbox implementations (VM, process) This Phase 2 implementation provides the foundation for secure, isolated task execution that will be integrated with the AI model providers from Phase 1 in the upcoming Phase 3 development. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			211 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright The OpenTelemetry Authors
 | |
| // SPDX-License-Identifier: Apache-2.0
 | |
| 
 | |
| package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"net/http"
 | |
| 	"net/http/httptrace"
 | |
| 
 | |
| 	"go.opentelemetry.io/otel"
 | |
| 	"go.opentelemetry.io/otel/attribute"
 | |
| 	"go.opentelemetry.io/otel/metric"
 | |
| 	"go.opentelemetry.io/otel/propagation"
 | |
| 	"go.opentelemetry.io/otel/trace"
 | |
| )
 | |
| 
 | |
| // ScopeName is the instrumentation scope name.
 | |
| const ScopeName = "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
 | |
| 
 | |
| // config represents the configuration options available for the http.Handler
 | |
| // and http.Transport types.
 | |
| type config struct {
 | |
| 	ServerName        string
 | |
| 	Tracer            trace.Tracer
 | |
| 	Meter             metric.Meter
 | |
| 	Propagators       propagation.TextMapPropagator
 | |
| 	SpanStartOptions  []trace.SpanStartOption
 | |
| 	PublicEndpoint    bool
 | |
| 	PublicEndpointFn  func(*http.Request) bool
 | |
| 	ReadEvent         bool
 | |
| 	WriteEvent        bool
 | |
| 	Filters           []Filter
 | |
| 	SpanNameFormatter func(string, *http.Request) string
 | |
| 	ClientTrace       func(context.Context) *httptrace.ClientTrace
 | |
| 
 | |
| 	TracerProvider     trace.TracerProvider
 | |
| 	MeterProvider      metric.MeterProvider
 | |
| 	MetricAttributesFn func(*http.Request) []attribute.KeyValue
 | |
| }
 | |
| 
 | |
| // Option interface used for setting optional config properties.
 | |
| type Option interface {
 | |
| 	apply(*config)
 | |
| }
 | |
| 
 | |
| type optionFunc func(*config)
 | |
| 
 | |
| func (o optionFunc) apply(c *config) {
 | |
| 	o(c)
 | |
| }
 | |
| 
 | |
| // newConfig creates a new config struct and applies opts to it.
 | |
| func newConfig(opts ...Option) *config {
 | |
| 	c := &config{
 | |
| 		Propagators:   otel.GetTextMapPropagator(),
 | |
| 		MeterProvider: otel.GetMeterProvider(),
 | |
| 	}
 | |
| 	for _, opt := range opts {
 | |
| 		opt.apply(c)
 | |
| 	}
 | |
| 
 | |
| 	// Tracer is only initialized if manually specified. Otherwise, can be passed with the tracing context.
 | |
| 	if c.TracerProvider != nil {
 | |
| 		c.Tracer = newTracer(c.TracerProvider)
 | |
| 	}
 | |
| 
 | |
| 	c.Meter = c.MeterProvider.Meter(
 | |
| 		ScopeName,
 | |
| 		metric.WithInstrumentationVersion(Version()),
 | |
| 	)
 | |
| 
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| // WithTracerProvider specifies a tracer provider to use for creating a tracer.
 | |
| // If none is specified, the global provider is used.
 | |
| func WithTracerProvider(provider trace.TracerProvider) Option {
 | |
| 	return optionFunc(func(cfg *config) {
 | |
| 		if provider != nil {
 | |
| 			cfg.TracerProvider = provider
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithMeterProvider specifies a meter provider to use for creating a meter.
 | |
| // If none is specified, the global provider is used.
 | |
| func WithMeterProvider(provider metric.MeterProvider) Option {
 | |
| 	return optionFunc(func(cfg *config) {
 | |
| 		if provider != nil {
 | |
| 			cfg.MeterProvider = provider
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithPublicEndpoint configures the Handler to link the span with an incoming
 | |
| // span context. If this option is not provided, then the association is a child
 | |
| // association instead of a link.
 | |
| func WithPublicEndpoint() Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		c.PublicEndpoint = true
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithPublicEndpointFn runs with every request, and allows conditionally
 | |
| // configuring the Handler to link the span with an incoming span context. If
 | |
| // this option is not provided or returns false, then the association is a
 | |
| // child association instead of a link.
 | |
| // Note: WithPublicEndpoint takes precedence over WithPublicEndpointFn.
 | |
| func WithPublicEndpointFn(fn func(*http.Request) bool) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		c.PublicEndpointFn = fn
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithPropagators configures specific propagators. If this
 | |
| // option isn't specified, then the global TextMapPropagator is used.
 | |
| func WithPropagators(ps propagation.TextMapPropagator) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		if ps != nil {
 | |
| 			c.Propagators = ps
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithSpanOptions configures an additional set of
 | |
| // trace.SpanOptions, which are applied to each new span.
 | |
| func WithSpanOptions(opts ...trace.SpanStartOption) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		c.SpanStartOptions = append(c.SpanStartOptions, opts...)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithFilter adds a filter to the list of filters used by the handler.
 | |
| // If any filter indicates to exclude a request then the request will not be
 | |
| // traced. All filters must allow a request to be traced for a Span to be created.
 | |
| // If no filters are provided then all requests are traced.
 | |
| // Filters will be invoked for each processed request, it is advised to make them
 | |
| // simple and fast.
 | |
| func WithFilter(f Filter) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		c.Filters = append(c.Filters, f)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| type event int
 | |
| 
 | |
| // Different types of events that can be recorded, see WithMessageEvents.
 | |
| const (
 | |
| 	ReadEvents event = iota
 | |
| 	WriteEvents
 | |
| )
 | |
| 
 | |
| // WithMessageEvents configures the Handler to record the specified events
 | |
| // (span.AddEvent) on spans. By default only summary attributes are added at the
 | |
| // end of the request.
 | |
| //
 | |
| // Valid events are:
 | |
| //   - ReadEvents: Record the number of bytes read after every http.Request.Body.Read
 | |
| //     using the ReadBytesKey
 | |
| //   - WriteEvents: Record the number of bytes written after every http.ResponeWriter.Write
 | |
| //     using the WriteBytesKey
 | |
| func WithMessageEvents(events ...event) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		for _, e := range events {
 | |
| 			switch e {
 | |
| 			case ReadEvents:
 | |
| 				c.ReadEvent = true
 | |
| 			case WriteEvents:
 | |
| 				c.WriteEvent = true
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithSpanNameFormatter takes a function that will be called on every
 | |
| // request and the returned string will become the Span Name.
 | |
| //
 | |
| // When using [http.ServeMux] (or any middleware that sets the Pattern of [http.Request]),
 | |
| // the span name formatter will run twice. Once when the span is created, and
 | |
| // second time after the middleware, so the pattern can be used.
 | |
| func WithSpanNameFormatter(f func(operation string, r *http.Request) string) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		c.SpanNameFormatter = f
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithClientTrace takes a function that returns client trace instance that will be
 | |
| // applied to the requests sent through the otelhttp Transport.
 | |
| func WithClientTrace(f func(context.Context) *httptrace.ClientTrace) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		c.ClientTrace = f
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithServerName returns an Option that sets the name of the (virtual) server
 | |
| // handling requests.
 | |
| func WithServerName(server string) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		c.ServerName = server
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // WithMetricAttributesFn returns an Option to set a function that maps an HTTP request to a slice of attribute.KeyValue.
 | |
| // These attributes will be included in metrics for every request.
 | |
| func WithMetricAttributesFn(metricAttributesFn func(r *http.Request) []attribute.KeyValue) Option {
 | |
| 	return optionFunc(func(c *config) {
 | |
| 		c.MetricAttributesFn = metricAttributesFn
 | |
| 	})
 | |
| }
 |