 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>
		
			
				
	
	
		
			377 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			377 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright The OpenTelemetry Authors
 | |
| // SPDX-License-Identifier: Apache-2.0
 | |
| 
 | |
| package metric // import "go.opentelemetry.io/otel/metric"
 | |
| 
 | |
| import "go.opentelemetry.io/otel/attribute"
 | |
| 
 | |
| // Observable is used as a grouping mechanism for all instruments that are
 | |
| // updated within a Callback.
 | |
| type Observable interface {
 | |
| 	observable()
 | |
| }
 | |
| 
 | |
| // InstrumentOption applies options to all instruments.
 | |
| type InstrumentOption interface {
 | |
| 	Int64CounterOption
 | |
| 	Int64UpDownCounterOption
 | |
| 	Int64HistogramOption
 | |
| 	Int64GaugeOption
 | |
| 	Int64ObservableCounterOption
 | |
| 	Int64ObservableUpDownCounterOption
 | |
| 	Int64ObservableGaugeOption
 | |
| 
 | |
| 	Float64CounterOption
 | |
| 	Float64UpDownCounterOption
 | |
| 	Float64HistogramOption
 | |
| 	Float64GaugeOption
 | |
| 	Float64ObservableCounterOption
 | |
| 	Float64ObservableUpDownCounterOption
 | |
| 	Float64ObservableGaugeOption
 | |
| }
 | |
| 
 | |
| // HistogramOption applies options to histogram instruments.
 | |
| type HistogramOption interface {
 | |
| 	Int64HistogramOption
 | |
| 	Float64HistogramOption
 | |
| }
 | |
| 
 | |
| type descOpt string
 | |
| 
 | |
| func (o descOpt) applyFloat64Counter(c Float64CounterConfig) Float64CounterConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyFloat64UpDownCounter(c Float64UpDownCounterConfig) Float64UpDownCounterConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64HistogramConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyFloat64ObservableUpDownCounter(
 | |
| 	c Float64ObservableUpDownCounterConfig,
 | |
| ) Float64ObservableUpDownCounterConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyFloat64ObservableGauge(c Float64ObservableGaugeConfig) Float64ObservableGaugeConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyInt64Counter(c Int64CounterConfig) Int64CounterConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyInt64UpDownCounter(c Int64UpDownCounterConfig) Int64UpDownCounterConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyInt64ObservableUpDownCounter(
 | |
| 	c Int64ObservableUpDownCounterConfig,
 | |
| ) Int64ObservableUpDownCounterConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o descOpt) applyInt64ObservableGauge(c Int64ObservableGaugeConfig) Int64ObservableGaugeConfig {
 | |
| 	c.description = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| // WithDescription sets the instrument description.
 | |
| func WithDescription(desc string) InstrumentOption { return descOpt(desc) }
 | |
| 
 | |
| type unitOpt string
 | |
| 
 | |
| func (o unitOpt) applyFloat64Counter(c Float64CounterConfig) Float64CounterConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyFloat64UpDownCounter(c Float64UpDownCounterConfig) Float64UpDownCounterConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64HistogramConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyFloat64ObservableUpDownCounter(
 | |
| 	c Float64ObservableUpDownCounterConfig,
 | |
| ) Float64ObservableUpDownCounterConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyFloat64ObservableGauge(c Float64ObservableGaugeConfig) Float64ObservableGaugeConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyInt64Counter(c Int64CounterConfig) Int64CounterConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyInt64UpDownCounter(c Int64UpDownCounterConfig) Int64UpDownCounterConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyInt64ObservableUpDownCounter(
 | |
| 	c Int64ObservableUpDownCounterConfig,
 | |
| ) Int64ObservableUpDownCounterConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o unitOpt) applyInt64ObservableGauge(c Int64ObservableGaugeConfig) Int64ObservableGaugeConfig {
 | |
| 	c.unit = string(o)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| // WithUnit sets the instrument unit.
 | |
| //
 | |
| // The unit u should be defined using the appropriate [UCUM](https://ucum.org) case-sensitive code.
 | |
| func WithUnit(u string) InstrumentOption { return unitOpt(u) }
 | |
| 
 | |
| // WithExplicitBucketBoundaries sets the instrument explicit bucket boundaries.
 | |
| //
 | |
| // This option is considered "advisory", and may be ignored by API implementations.
 | |
| func WithExplicitBucketBoundaries(bounds ...float64) HistogramOption { return bucketOpt(bounds) }
 | |
| 
 | |
| type bucketOpt []float64
 | |
| 
 | |
| func (o bucketOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64HistogramConfig {
 | |
| 	c.explicitBucketBoundaries = o
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o bucketOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfig {
 | |
| 	c.explicitBucketBoundaries = o
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| // AddOption applies options to an addition measurement. See
 | |
| // [MeasurementOption] for other options that can be used as an AddOption.
 | |
| type AddOption interface {
 | |
| 	applyAdd(AddConfig) AddConfig
 | |
| }
 | |
| 
 | |
| // AddConfig contains options for an addition measurement.
 | |
| type AddConfig struct {
 | |
| 	attrs attribute.Set
 | |
| }
 | |
| 
 | |
| // NewAddConfig returns a new [AddConfig] with all opts applied.
 | |
| func NewAddConfig(opts []AddOption) AddConfig {
 | |
| 	config := AddConfig{attrs: *attribute.EmptySet()}
 | |
| 	for _, o := range opts {
 | |
| 		config = o.applyAdd(config)
 | |
| 	}
 | |
| 	return config
 | |
| }
 | |
| 
 | |
| // Attributes returns the configured attribute set.
 | |
| func (c AddConfig) Attributes() attribute.Set {
 | |
| 	return c.attrs
 | |
| }
 | |
| 
 | |
| // RecordOption applies options to an addition measurement. See
 | |
| // [MeasurementOption] for other options that can be used as a RecordOption.
 | |
| type RecordOption interface {
 | |
| 	applyRecord(RecordConfig) RecordConfig
 | |
| }
 | |
| 
 | |
| // RecordConfig contains options for a recorded measurement.
 | |
| type RecordConfig struct {
 | |
| 	attrs attribute.Set
 | |
| }
 | |
| 
 | |
| // NewRecordConfig returns a new [RecordConfig] with all opts applied.
 | |
| func NewRecordConfig(opts []RecordOption) RecordConfig {
 | |
| 	config := RecordConfig{attrs: *attribute.EmptySet()}
 | |
| 	for _, o := range opts {
 | |
| 		config = o.applyRecord(config)
 | |
| 	}
 | |
| 	return config
 | |
| }
 | |
| 
 | |
| // Attributes returns the configured attribute set.
 | |
| func (c RecordConfig) Attributes() attribute.Set {
 | |
| 	return c.attrs
 | |
| }
 | |
| 
 | |
| // ObserveOption applies options to an addition measurement. See
 | |
| // [MeasurementOption] for other options that can be used as a ObserveOption.
 | |
| type ObserveOption interface {
 | |
| 	applyObserve(ObserveConfig) ObserveConfig
 | |
| }
 | |
| 
 | |
| // ObserveConfig contains options for an observed measurement.
 | |
| type ObserveConfig struct {
 | |
| 	attrs attribute.Set
 | |
| }
 | |
| 
 | |
| // NewObserveConfig returns a new [ObserveConfig] with all opts applied.
 | |
| func NewObserveConfig(opts []ObserveOption) ObserveConfig {
 | |
| 	config := ObserveConfig{attrs: *attribute.EmptySet()}
 | |
| 	for _, o := range opts {
 | |
| 		config = o.applyObserve(config)
 | |
| 	}
 | |
| 	return config
 | |
| }
 | |
| 
 | |
| // Attributes returns the configured attribute set.
 | |
| func (c ObserveConfig) Attributes() attribute.Set {
 | |
| 	return c.attrs
 | |
| }
 | |
| 
 | |
| // MeasurementOption applies options to all instrument measurement.
 | |
| type MeasurementOption interface {
 | |
| 	AddOption
 | |
| 	RecordOption
 | |
| 	ObserveOption
 | |
| }
 | |
| 
 | |
| type attrOpt struct {
 | |
| 	set attribute.Set
 | |
| }
 | |
| 
 | |
| // mergeSets returns the union of keys between a and b. Any duplicate keys will
 | |
| // use the value associated with b.
 | |
| func mergeSets(a, b attribute.Set) attribute.Set {
 | |
| 	// NewMergeIterator uses the first value for any duplicates.
 | |
| 	iter := attribute.NewMergeIterator(&b, &a)
 | |
| 	merged := make([]attribute.KeyValue, 0, a.Len()+b.Len())
 | |
| 	for iter.Next() {
 | |
| 		merged = append(merged, iter.Attribute())
 | |
| 	}
 | |
| 	return attribute.NewSet(merged...)
 | |
| }
 | |
| 
 | |
| func (o attrOpt) applyAdd(c AddConfig) AddConfig {
 | |
| 	switch {
 | |
| 	case o.set.Len() == 0:
 | |
| 	case c.attrs.Len() == 0:
 | |
| 		c.attrs = o.set
 | |
| 	default:
 | |
| 		c.attrs = mergeSets(c.attrs, o.set)
 | |
| 	}
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o attrOpt) applyRecord(c RecordConfig) RecordConfig {
 | |
| 	switch {
 | |
| 	case o.set.Len() == 0:
 | |
| 	case c.attrs.Len() == 0:
 | |
| 		c.attrs = o.set
 | |
| 	default:
 | |
| 		c.attrs = mergeSets(c.attrs, o.set)
 | |
| 	}
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (o attrOpt) applyObserve(c ObserveConfig) ObserveConfig {
 | |
| 	switch {
 | |
| 	case o.set.Len() == 0:
 | |
| 	case c.attrs.Len() == 0:
 | |
| 		c.attrs = o.set
 | |
| 	default:
 | |
| 		c.attrs = mergeSets(c.attrs, o.set)
 | |
| 	}
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| // WithAttributeSet sets the attribute Set associated with a measurement is
 | |
| // made with.
 | |
| //
 | |
| // If multiple WithAttributeSet or WithAttributes options are passed the
 | |
| // attributes will be merged together in the order they are passed. Attributes
 | |
| // with duplicate keys will use the last value passed.
 | |
| func WithAttributeSet(attributes attribute.Set) MeasurementOption {
 | |
| 	return attrOpt{set: attributes}
 | |
| }
 | |
| 
 | |
| // WithAttributes converts attributes into an attribute Set and sets the Set to
 | |
| // be associated with a measurement. This is shorthand for:
 | |
| //
 | |
| //	cp := make([]attribute.KeyValue, len(attributes))
 | |
| //	copy(cp, attributes)
 | |
| //	WithAttributeSet(attribute.NewSet(cp...))
 | |
| //
 | |
| // [attribute.NewSet] may modify the passed attributes so this will make a copy
 | |
| // of attributes before creating a set in order to ensure this function is
 | |
| // concurrent safe. This makes this option function less optimized in
 | |
| // comparison to [WithAttributeSet]. Therefore, [WithAttributeSet] should be
 | |
| // preferred for performance sensitive code.
 | |
| //
 | |
| // See [WithAttributeSet] for information about how multiple WithAttributes are
 | |
| // merged.
 | |
| func WithAttributes(attributes ...attribute.KeyValue) MeasurementOption {
 | |
| 	cp := make([]attribute.KeyValue, len(attributes))
 | |
| 	copy(cp, attributes)
 | |
| 	return attrOpt{set: attribute.NewSet(cp...)}
 | |
| }
 |