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>
136 lines
4.0 KiB
Go
136 lines
4.0 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package attribute // import "go.opentelemetry.io/otel/attribute"
|
|
|
|
import (
|
|
"bytes"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
type (
|
|
// Encoder is a mechanism for serializing an attribute set into a specific
|
|
// string representation that supports caching, to avoid repeated
|
|
// serialization. An example could be an exporter encoding the attribute
|
|
// set into a wire representation.
|
|
Encoder interface {
|
|
// Encode returns the serialized encoding of the attribute set using
|
|
// its Iterator. This result may be cached by a attribute.Set.
|
|
Encode(iterator Iterator) string
|
|
|
|
// ID returns a value that is unique for each class of attribute
|
|
// encoder. Attribute encoders allocate these using `NewEncoderID`.
|
|
ID() EncoderID
|
|
}
|
|
|
|
// EncoderID is used to identify distinct Encoder
|
|
// implementations, for caching encoded results.
|
|
EncoderID struct {
|
|
value uint64
|
|
}
|
|
|
|
// defaultAttrEncoder uses a sync.Pool of buffers to reduce the number of
|
|
// allocations used in encoding attributes. This implementation encodes a
|
|
// comma-separated list of key=value, with '/'-escaping of '=', ',', and
|
|
// '\'.
|
|
defaultAttrEncoder struct {
|
|
// pool is a pool of attribute set builders. The buffers in this pool
|
|
// grow to a size that most attribute encodings will not allocate new
|
|
// memory.
|
|
pool sync.Pool // *bytes.Buffer
|
|
}
|
|
)
|
|
|
|
// escapeChar is used to ensure uniqueness of the attribute encoding where
|
|
// keys or values contain either '=' or ','. Since there is no parser needed
|
|
// for this encoding and its only requirement is to be unique, this choice is
|
|
// arbitrary. Users will see these in some exporters (e.g., stdout), so the
|
|
// backslash ('\') is used as a conventional choice.
|
|
const escapeChar = '\\'
|
|
|
|
var (
|
|
_ Encoder = &defaultAttrEncoder{}
|
|
|
|
// encoderIDCounter is for generating IDs for other attribute encoders.
|
|
encoderIDCounter uint64
|
|
|
|
defaultEncoderOnce sync.Once
|
|
defaultEncoderID = NewEncoderID()
|
|
defaultEncoderInstance *defaultAttrEncoder
|
|
)
|
|
|
|
// NewEncoderID returns a unique attribute encoder ID. It should be called
|
|
// once per each type of attribute encoder. Preferably in init() or in var
|
|
// definition.
|
|
func NewEncoderID() EncoderID {
|
|
return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
|
|
}
|
|
|
|
// DefaultEncoder returns an attribute encoder that encodes attributes in such
|
|
// a way that each escaped attribute's key is followed by an equal sign and
|
|
// then by an escaped attribute's value. All key-value pairs are separated by
|
|
// a comma.
|
|
//
|
|
// Escaping is done by prepending a backslash before either a backslash, equal
|
|
// sign or a comma.
|
|
func DefaultEncoder() Encoder {
|
|
defaultEncoderOnce.Do(func() {
|
|
defaultEncoderInstance = &defaultAttrEncoder{
|
|
pool: sync.Pool{
|
|
New: func() any {
|
|
return &bytes.Buffer{}
|
|
},
|
|
},
|
|
}
|
|
})
|
|
return defaultEncoderInstance
|
|
}
|
|
|
|
// Encode is a part of an implementation of the AttributeEncoder interface.
|
|
func (d *defaultAttrEncoder) Encode(iter Iterator) string {
|
|
buf := d.pool.Get().(*bytes.Buffer)
|
|
defer d.pool.Put(buf)
|
|
buf.Reset()
|
|
|
|
for iter.Next() {
|
|
i, keyValue := iter.IndexedAttribute()
|
|
if i > 0 {
|
|
_ = buf.WriteByte(',')
|
|
}
|
|
copyAndEscape(buf, string(keyValue.Key))
|
|
|
|
_ = buf.WriteByte('=')
|
|
|
|
if keyValue.Value.Type() == STRING {
|
|
copyAndEscape(buf, keyValue.Value.AsString())
|
|
} else {
|
|
_, _ = buf.WriteString(keyValue.Value.Emit())
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
// ID is a part of an implementation of the AttributeEncoder interface.
|
|
func (*defaultAttrEncoder) ID() EncoderID {
|
|
return defaultEncoderID
|
|
}
|
|
|
|
// copyAndEscape escapes `=`, `,` and its own escape character (`\`),
|
|
// making the default encoding unique.
|
|
func copyAndEscape(buf *bytes.Buffer, val string) {
|
|
for _, ch := range val {
|
|
switch ch {
|
|
case '=', ',', escapeChar:
|
|
_ = buf.WriteByte(escapeChar)
|
|
}
|
|
_, _ = buf.WriteRune(ch)
|
|
}
|
|
}
|
|
|
|
// Valid reports whether this encoder ID was allocated by
|
|
// [NewEncoderID]. Invalid encoder IDs will not be cached.
|
|
func (id EncoderID) Valid() bool {
|
|
return id.value != 0
|
|
}
|