Phase 2: Implement Execution Environment Abstraction (v0.3.0)
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>
This commit is contained in:
		
							
								
								
									
										64
									
								
								vendor/google.golang.org/protobuf/internal/impl/codec_field.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										64
									
								
								vendor/google.golang.org/protobuf/internal/impl/codec_field.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -233,9 +233,15 @@ func sizeMessageInfo(p pointer, f *coderFieldInfo, opts marshalOptions) int { | ||||
| } | ||||
|  | ||||
| func appendMessageInfo(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) { | ||||
| 	calculatedSize := f.mi.sizePointer(p.Elem(), opts) | ||||
| 	b = protowire.AppendVarint(b, f.wiretag) | ||||
| 	b = protowire.AppendVarint(b, uint64(f.mi.sizePointer(p.Elem(), opts))) | ||||
| 	return f.mi.marshalAppendPointer(b, p.Elem(), opts) | ||||
| 	b = protowire.AppendVarint(b, uint64(calculatedSize)) | ||||
| 	before := len(b) | ||||
| 	b, err := f.mi.marshalAppendPointer(b, p.Elem(), opts) | ||||
| 	if measuredSize := len(b) - before; calculatedSize != measuredSize && err == nil { | ||||
| 		return nil, errors.MismatchedSizeCalculation(calculatedSize, measuredSize) | ||||
| 	} | ||||
| 	return b, err | ||||
| } | ||||
|  | ||||
| func consumeMessageInfo(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { | ||||
| @@ -262,14 +268,21 @@ func isInitMessageInfo(p pointer, f *coderFieldInfo) error { | ||||
| 	return f.mi.checkInitializedPointer(p.Elem()) | ||||
| } | ||||
|  | ||||
| func sizeMessage(m proto.Message, tagsize int, _ marshalOptions) int { | ||||
| 	return protowire.SizeBytes(proto.Size(m)) + tagsize | ||||
| func sizeMessage(m proto.Message, tagsize int, opts marshalOptions) int { | ||||
| 	return protowire.SizeBytes(opts.Options().Size(m)) + tagsize | ||||
| } | ||||
|  | ||||
| func appendMessage(b []byte, m proto.Message, wiretag uint64, opts marshalOptions) ([]byte, error) { | ||||
| 	mopts := opts.Options() | ||||
| 	calculatedSize := mopts.Size(m) | ||||
| 	b = protowire.AppendVarint(b, wiretag) | ||||
| 	b = protowire.AppendVarint(b, uint64(proto.Size(m))) | ||||
| 	return opts.Options().MarshalAppend(b, m) | ||||
| 	b = protowire.AppendVarint(b, uint64(calculatedSize)) | ||||
| 	before := len(b) | ||||
| 	b, err := mopts.MarshalAppend(b, m) | ||||
| 	if measuredSize := len(b) - before; calculatedSize != measuredSize && err == nil { | ||||
| 		return nil, errors.MismatchedSizeCalculation(calculatedSize, measuredSize) | ||||
| 	} | ||||
| 	return b, err | ||||
| } | ||||
|  | ||||
| func consumeMessage(b []byte, m proto.Message, wtyp protowire.Type, opts unmarshalOptions) (out unmarshalOutput, err error) { | ||||
| @@ -405,8 +418,8 @@ func consumeGroupType(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInf | ||||
| 	return f.mi.unmarshalPointer(b, p.Elem(), f.num, opts) | ||||
| } | ||||
|  | ||||
| func sizeGroup(m proto.Message, tagsize int, _ marshalOptions) int { | ||||
| 	return 2*tagsize + proto.Size(m) | ||||
| func sizeGroup(m proto.Message, tagsize int, opts marshalOptions) int { | ||||
| 	return 2*tagsize + opts.Options().Size(m) | ||||
| } | ||||
|  | ||||
| func appendGroup(b []byte, m proto.Message, wiretag uint64, opts marshalOptions) ([]byte, error) { | ||||
| @@ -482,10 +495,14 @@ func appendMessageSliceInfo(b []byte, p pointer, f *coderFieldInfo, opts marshal | ||||
| 		b = protowire.AppendVarint(b, f.wiretag) | ||||
| 		siz := f.mi.sizePointer(v, opts) | ||||
| 		b = protowire.AppendVarint(b, uint64(siz)) | ||||
| 		before := len(b) | ||||
| 		b, err = f.mi.marshalAppendPointer(b, v, opts) | ||||
| 		if err != nil { | ||||
| 			return b, err | ||||
| 		} | ||||
| 		if measuredSize := len(b) - before; siz != measuredSize { | ||||
| 			return nil, errors.MismatchedSizeCalculation(siz, measuredSize) | ||||
| 		} | ||||
| 	} | ||||
| 	return b, nil | ||||
| } | ||||
| @@ -520,28 +537,34 @@ func isInitMessageSliceInfo(p pointer, f *coderFieldInfo) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func sizeMessageSlice(p pointer, goType reflect.Type, tagsize int, _ marshalOptions) int { | ||||
| func sizeMessageSlice(p pointer, goType reflect.Type, tagsize int, opts marshalOptions) int { | ||||
| 	mopts := opts.Options() | ||||
| 	s := p.PointerSlice() | ||||
| 	n := 0 | ||||
| 	for _, v := range s { | ||||
| 		m := asMessage(v.AsValueOf(goType.Elem())) | ||||
| 		n += protowire.SizeBytes(proto.Size(m)) + tagsize | ||||
| 		n += protowire.SizeBytes(mopts.Size(m)) + tagsize | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
|  | ||||
| func appendMessageSlice(b []byte, p pointer, wiretag uint64, goType reflect.Type, opts marshalOptions) ([]byte, error) { | ||||
| 	mopts := opts.Options() | ||||
| 	s := p.PointerSlice() | ||||
| 	var err error | ||||
| 	for _, v := range s { | ||||
| 		m := asMessage(v.AsValueOf(goType.Elem())) | ||||
| 		b = protowire.AppendVarint(b, wiretag) | ||||
| 		siz := proto.Size(m) | ||||
| 		siz := mopts.Size(m) | ||||
| 		b = protowire.AppendVarint(b, uint64(siz)) | ||||
| 		b, err = opts.Options().MarshalAppend(b, m) | ||||
| 		before := len(b) | ||||
| 		b, err = mopts.MarshalAppend(b, m) | ||||
| 		if err != nil { | ||||
| 			return b, err | ||||
| 		} | ||||
| 		if measuredSize := len(b) - before; siz != measuredSize { | ||||
| 			return nil, errors.MismatchedSizeCalculation(siz, measuredSize) | ||||
| 		} | ||||
| 	} | ||||
| 	return b, nil | ||||
| } | ||||
| @@ -582,11 +605,12 @@ func isInitMessageSlice(p pointer, goType reflect.Type) error { | ||||
| // Slices of messages | ||||
|  | ||||
| func sizeMessageSliceValue(listv protoreflect.Value, tagsize int, opts marshalOptions) int { | ||||
| 	mopts := opts.Options() | ||||
| 	list := listv.List() | ||||
| 	n := 0 | ||||
| 	for i, llen := 0, list.Len(); i < llen; i++ { | ||||
| 		m := list.Get(i).Message().Interface() | ||||
| 		n += protowire.SizeBytes(proto.Size(m)) + tagsize | ||||
| 		n += protowire.SizeBytes(mopts.Size(m)) + tagsize | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| @@ -597,13 +621,17 @@ func appendMessageSliceValue(b []byte, listv protoreflect.Value, wiretag uint64, | ||||
| 	for i, llen := 0, list.Len(); i < llen; i++ { | ||||
| 		m := list.Get(i).Message().Interface() | ||||
| 		b = protowire.AppendVarint(b, wiretag) | ||||
| 		siz := proto.Size(m) | ||||
| 		siz := mopts.Size(m) | ||||
| 		b = protowire.AppendVarint(b, uint64(siz)) | ||||
| 		before := len(b) | ||||
| 		var err error | ||||
| 		b, err = mopts.MarshalAppend(b, m) | ||||
| 		if err != nil { | ||||
| 			return b, err | ||||
| 		} | ||||
| 		if measuredSize := len(b) - before; siz != measuredSize { | ||||
| 			return nil, errors.MismatchedSizeCalculation(siz, measuredSize) | ||||
| 		} | ||||
| 	} | ||||
| 	return b, nil | ||||
| } | ||||
| @@ -651,11 +679,12 @@ var coderMessageSliceValue = valueCoderFuncs{ | ||||
| } | ||||
|  | ||||
| func sizeGroupSliceValue(listv protoreflect.Value, tagsize int, opts marshalOptions) int { | ||||
| 	mopts := opts.Options() | ||||
| 	list := listv.List() | ||||
| 	n := 0 | ||||
| 	for i, llen := 0, list.Len(); i < llen; i++ { | ||||
| 		m := list.Get(i).Message().Interface() | ||||
| 		n += 2*tagsize + proto.Size(m) | ||||
| 		n += 2*tagsize + mopts.Size(m) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| @@ -738,12 +767,13 @@ func makeGroupSliceFieldCoder(fd protoreflect.FieldDescriptor, ft reflect.Type) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func sizeGroupSlice(p pointer, messageType reflect.Type, tagsize int, _ marshalOptions) int { | ||||
| func sizeGroupSlice(p pointer, messageType reflect.Type, tagsize int, opts marshalOptions) int { | ||||
| 	mopts := opts.Options() | ||||
| 	s := p.PointerSlice() | ||||
| 	n := 0 | ||||
| 	for _, v := range s { | ||||
| 		m := asMessage(v.AsValueOf(messageType.Elem())) | ||||
| 		n += 2*tagsize + proto.Size(m) | ||||
| 		n += 2*tagsize + mopts.Size(m) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user