1- // Package container provides utilities for managing containers ,
1+ // Package docker provides Docker-specific implementation of container runtime ,
22// including creating, starting, stopping, and monitoring containers.
3- package container
3+ package docker
44
55import (
66 "context"
@@ -21,6 +21,7 @@ import (
2121 "github.com/docker/docker/client"
2222 "github.com/docker/go-connections/nat"
2323
24+ "github.com/stacklok/vibetool/pkg/container/runtime"
2425 "github.com/stacklok/vibetool/pkg/permissions"
2526)
2627
@@ -36,7 +37,7 @@ const (
3637
3738// Client implements the Runtime interface for container operations
3839type Client struct {
39- runtimeType RuntimeType
40+ runtimeType runtime. Type
4041 socketPath string
4142 client * client.Client
4243}
@@ -53,7 +54,7 @@ func NewClient(ctx context.Context) (*Client, error) {
5354}
5455
5556// NewClientWithSocketPath creates a new container client with a specific socket path
56- func NewClientWithSocketPath (ctx context.Context , socketPath string , runtimeType RuntimeType ) (* Client , error ) {
57+ func NewClientWithSocketPath (ctx context.Context , socketPath string , runtimeType runtime. Type ) (* Client , error ) {
5758 // Create a custom HTTP client that uses the Unix socket
5859 httpClient := & http.Client {
5960 Transport : & http.Transport {
@@ -99,32 +100,32 @@ func (c *Client) ping(ctx context.Context) error {
99100}
100101
101102// findContainerSocket finds a container socket path, preferring Podman over Docker
102- func findContainerSocket () (string , RuntimeType , error ) {
103+ func findContainerSocket () (string , runtime. Type , error ) {
103104 // Try Podman sockets first
104105 // Check standard Podman location
105106 if _ , err := os .Stat (PodmanSocketPath ); err == nil {
106- return PodmanSocketPath , RuntimeTypePodman , nil
107+ return PodmanSocketPath , runtime . TypePodman , nil
107108 }
108109
109110 // Check XDG_RUNTIME_DIR location for Podman
110111 if xdgRuntimeDir := os .Getenv ("XDG_RUNTIME_DIR" ); xdgRuntimeDir != "" {
111112 xdgSocketPath := filepath .Join (xdgRuntimeDir , PodmanXDGRuntimeSocketPath )
112113 if _ , err := os .Stat (xdgSocketPath ); err == nil {
113- return xdgSocketPath , RuntimeTypePodman , nil
114+ return xdgSocketPath , runtime . TypePodman , nil
114115 }
115116 }
116117
117118 // Check user-specific location for Podman
118119 if home := os .Getenv ("HOME" ); home != "" {
119120 userSocketPath := filepath .Join (home , ".local/share/containers/podman/machine/podman.sock" )
120121 if _ , err := os .Stat (userSocketPath ); err == nil {
121- return userSocketPath , RuntimeTypePodman , nil
122+ return userSocketPath , runtime . TypePodman , nil
122123 }
123124 }
124125
125126 // Try Docker socket as fallback
126127 if _ , err := os .Stat (DockerSocketPath ); err == nil {
127- return DockerSocketPath , RuntimeTypeDocker , nil
128+ return DockerSocketPath , runtime . TypeDocker , nil
128129 }
129130
130131 return "" , "" , ErrRuntimeNotFound
@@ -142,7 +143,7 @@ func convertEnvVars(envVars map[string]string) []string {
142143}
143144
144145// convertMounts converts internal mount format to Docker mount format
145- func convertMounts (mounts []Mount ) []mount.Mount {
146+ func convertMounts (mounts []runtime. Mount ) []mount.Mount {
146147 result := make ([]mount.Mount , 0 , len (mounts ))
147148 for _ , m := range mounts {
148149 result = append (result , mount.Mount {
@@ -174,7 +175,7 @@ func setupExposedPorts(config *container.Config, exposedPorts map[string]struct{
174175}
175176
176177// setupPortBindings configures port bindings for a container
177- func setupPortBindings (hostConfig * container.HostConfig , portBindings map [string ][]PortBinding ) error {
178+ func setupPortBindings (hostConfig * container.HostConfig , portBindings map [string ][]runtime. PortBinding ) error {
178179 if len (portBindings ) == 0 {
179180 return nil
180181 }
@@ -209,7 +210,7 @@ func (c *Client) CreateContainer(
209210 envVars , labels map [string ]string ,
210211 permissionProfile * permissions.Profile ,
211212 transportType string ,
212- options * CreateContainerOptions ,
213+ options * runtime. CreateContainerOptions ,
213214) (string , error ) {
214215 // Get permission config from profile
215216 permissionConfig , err := c .getPermissionConfigFromProfile (permissionProfile , transportType )
@@ -281,7 +282,7 @@ func (c *Client) StartContainer(ctx context.Context, containerID string) error {
281282}
282283
283284// ListContainers lists containers
284- func (c * Client ) ListContainers (ctx context.Context ) ([]ContainerInfo , error ) {
285+ func (c * Client ) ListContainers (ctx context.Context ) ([]runtime. ContainerInfo , error ) {
285286 // Create filter for vibetool containers
286287 filterArgs := filters .NewArgs ()
287288 filterArgs .Add ("label" , "vibetool=true" )
@@ -296,7 +297,7 @@ func (c *Client) ListContainers(ctx context.Context) ([]ContainerInfo, error) {
296297 }
297298
298299 // Convert to our ContainerInfo format
299- result := make ([]ContainerInfo , 0 , len (containers ))
300+ result := make ([]runtime. ContainerInfo , 0 , len (containers ))
300301 for _ , c := range containers {
301302 // Extract container name (remove leading slash)
302303 name := ""
@@ -306,9 +307,9 @@ func (c *Client) ListContainers(ctx context.Context) ([]ContainerInfo, error) {
306307 }
307308
308309 // Extract port mappings
309- ports := make ([]PortMapping , 0 , len (c .Ports ))
310+ ports := make ([]runtime. PortMapping , 0 , len (c .Ports ))
310311 for _ , p := range c .Ports {
311- ports = append (ports , PortMapping {
312+ ports = append (ports , runtime. PortMapping {
312313 ContainerPort : int (p .PrivatePort ),
313314 HostPort : int (p .PublicPort ),
314315 Protocol : p .Type ,
@@ -318,7 +319,7 @@ func (c *Client) ListContainers(ctx context.Context) ([]ContainerInfo, error) {
318319 // Convert creation time
319320 created := time .Unix (c .Created , 0 )
320321
321- result = append (result , ContainerInfo {
322+ result = append (result , runtime. ContainerInfo {
322323 ID : c .ID ,
323324 Name : name ,
324325 Image : c .Image ,
@@ -394,19 +395,19 @@ func (c *Client) IsContainerRunning(ctx context.Context, containerID string) (bo
394395}
395396
396397// GetContainerInfo gets container information
397- func (c * Client ) GetContainerInfo (ctx context.Context , containerID string ) (ContainerInfo , error ) {
398+ func (c * Client ) GetContainerInfo (ctx context.Context , containerID string ) (runtime. ContainerInfo , error ) {
398399 // Inspect container
399400 info , err := c .client .ContainerInspect (ctx , containerID )
400401 if err != nil {
401402 // Check if the error is because the container doesn't exist
402403 if client .IsErrNotFound (err ) {
403- return ContainerInfo {}, NewContainerError (ErrContainerNotFound , containerID , "container not found" )
404+ return runtime. ContainerInfo {}, NewContainerError (ErrContainerNotFound , containerID , "container not found" )
404405 }
405- return ContainerInfo {}, NewContainerError (err , containerID , fmt .Sprintf ("failed to inspect container: %v" , err ))
406+ return runtime. ContainerInfo {}, NewContainerError (err , containerID , fmt .Sprintf ("failed to inspect container: %v" , err ))
406407 }
407408
408409 // Extract port mappings
409- ports := make ([]PortMapping , 0 )
410+ ports := make ([]runtime. PortMapping , 0 )
410411 for containerPort , bindings := range info .NetworkSettings .Ports {
411412 for _ , binding := range bindings {
412413 hostPort := 0
@@ -415,7 +416,7 @@ func (c *Client) GetContainerInfo(ctx context.Context, containerID string) (Cont
415416 fmt .Printf ("Warning: Failed to parse host port %s: %v\n " , binding .HostPort , err )
416417 }
417418
418- ports = append (ports , PortMapping {
419+ ports = append (ports , runtime. PortMapping {
419420 ContainerPort : containerPort .Int (),
420421 HostPort : hostPort ,
421422 Protocol : containerPort .Proto (),
@@ -429,7 +430,7 @@ func (c *Client) GetContainerInfo(ctx context.Context, containerID string) (Cont
429430 created = time.Time {} // Use zero time if parsing fails
430431 }
431432
432- return ContainerInfo {
433+ return runtime. ContainerInfo {
433434 ID : info .ID ,
434435 Name : strings .TrimPrefix (info .Name , "/" ),
435436 Image : info .Config .Image ,
@@ -544,7 +545,7 @@ func (c *Client) PullImage(ctx context.Context, imageName string) error {
544545// getPermissionConfigFromProfile converts a permission profile to a container permission config
545546// with transport-specific settings (internal function)
546547// addReadOnlyMounts adds read-only mounts to the permission config
547- func (* Client ) addReadOnlyMounts (config * PermissionConfig , mounts []permissions.MountDeclaration ) {
548+ func (* Client ) addReadOnlyMounts (config * runtime. PermissionConfig , mounts []permissions.MountDeclaration ) {
548549 for _ , mountDecl := range mounts {
549550 source , target , err := mountDecl .Parse ()
550551 if err != nil {
@@ -565,7 +566,7 @@ func (*Client) addReadOnlyMounts(config *PermissionConfig, mounts []permissions.
565566 continue
566567 }
567568
568- config .Mounts = append (config .Mounts , Mount {
569+ config .Mounts = append (config .Mounts , runtime. Mount {
569570 Source : source ,
570571 Target : target ,
571572 ReadOnly : true ,
@@ -574,7 +575,7 @@ func (*Client) addReadOnlyMounts(config *PermissionConfig, mounts []permissions.
574575}
575576
576577// addReadWriteMounts adds read-write mounts to the permission config
577- func (* Client ) addReadWriteMounts (config * PermissionConfig , mounts []permissions.MountDeclaration ) {
578+ func (* Client ) addReadWriteMounts (config * runtime. PermissionConfig , mounts []permissions.MountDeclaration ) {
578579 for _ , mountDecl := range mounts {
579580 source , target , err := mountDecl .Parse ()
580581 if err != nil {
@@ -608,7 +609,7 @@ func (*Client) addReadWriteMounts(config *PermissionConfig, mounts []permissions
608609
609610 // If not already mounted, add a new mount
610611 if ! alreadyMounted {
611- config .Mounts = append (config .Mounts , Mount {
612+ config .Mounts = append (config .Mounts , runtime. Mount {
612613 Source : source ,
613614 Target : target ,
614615 ReadOnly : false ,
@@ -640,10 +641,14 @@ func (*Client) needsNetworkAccess(profile *permissions.Profile, transportType st
640641 return false
641642}
642643
643- func (c * Client ) getPermissionConfigFromProfile (profile * permissions.Profile , transportType string ) (* PermissionConfig , error ) {
644+ // getPermissionConfigFromProfile converts a permission profile to a container permission config
645+ func (c * Client ) getPermissionConfigFromProfile (
646+ profile * permissions.Profile ,
647+ transportType string ,
648+ ) (* runtime.PermissionConfig , error ) {
644649 // Start with a default permission config
645- config := & PermissionConfig {
646- Mounts : []Mount {},
650+ config := & runtime. PermissionConfig {
651+ Mounts : []runtime. Mount {},
647652 NetworkMode : "none" ,
648653 CapDrop : []string {"ALL" },
649654 CapAdd : []string {},
@@ -666,3 +671,70 @@ func (c *Client) getPermissionConfigFromProfile(profile *permissions.Profile, tr
666671
667672 return config , nil
668673}
674+
675+ // Error types for container operations
676+ var (
677+ // ErrContainerNotFound is returned when a container is not found
678+ ErrContainerNotFound = fmt .Errorf ("container not found" )
679+
680+ // ErrContainerAlreadyExists is returned when a container already exists
681+ ErrContainerAlreadyExists = fmt .Errorf ("container already exists" )
682+
683+ // ErrContainerNotRunning is returned when a container is not running
684+ ErrContainerNotRunning = fmt .Errorf ("container not running" )
685+
686+ // ErrContainerAlreadyRunning is returned when a container is already running
687+ ErrContainerAlreadyRunning = fmt .Errorf ("container already running" )
688+
689+ // ErrRuntimeNotFound is returned when a container runtime is not found
690+ ErrRuntimeNotFound = fmt .Errorf ("container runtime not found" )
691+
692+ // ErrInvalidRuntimeType is returned when an invalid runtime type is specified
693+ ErrInvalidRuntimeType = fmt .Errorf ("invalid runtime type" )
694+
695+ // ErrAttachFailed is returned when attaching to a container fails
696+ ErrAttachFailed = fmt .Errorf ("failed to attach to container" )
697+
698+ // ErrContainerExited is returned when a container has exited unexpectedly
699+ ErrContainerExited = fmt .Errorf ("container exited unexpectedly" )
700+ )
701+
702+ // ContainerError represents an error related to container operations
703+ type ContainerError struct {
704+ // Err is the underlying error
705+ Err error
706+ // ContainerID is the ID of the container
707+ ContainerID string
708+ // Message is an optional error message
709+ Message string
710+ }
711+
712+ // Error returns the error message
713+ func (e * ContainerError ) Error () string {
714+ if e .Message != "" {
715+ if e .ContainerID != "" {
716+ return fmt .Sprintf ("%s: %s (container: %s)" , e .Err , e .Message , e .ContainerID )
717+ }
718+ return fmt .Sprintf ("%s: %s" , e .Err , e .Message )
719+ }
720+
721+ if e .ContainerID != "" {
722+ return fmt .Sprintf ("%s (container: %s)" , e .Err , e .ContainerID )
723+ }
724+
725+ return e .Err .Error ()
726+ }
727+
728+ // Unwrap returns the underlying error
729+ func (e * ContainerError ) Unwrap () error {
730+ return e .Err
731+ }
732+
733+ // NewContainerError creates a new container error
734+ func NewContainerError (err error , containerID , message string ) * ContainerError {
735+ return & ContainerError {
736+ Err : err ,
737+ ContainerID : containerID ,
738+ Message : message ,
739+ }
740+ }
0 commit comments