187 lines
4.4 KiB
Go
187 lines
4.4 KiB
Go
/*
|
|
© AngelaMos | 2026
|
|
client.go
|
|
|
|
Docker API client wrapper with singleton initialization and per-call
|
|
timeouts
|
|
|
|
Wraps the official Docker SDK client using sync.Once so all analyzers
|
|
share one connection. Every API method creates a child context with
|
|
an operation-specific timeout from config/constants.go. Provides
|
|
ping, info, container list/inspect, and image list/inspect/history.
|
|
|
|
Key exports:
|
|
Client - wraps docker/client.Client with timeout-aware methods
|
|
NewClient - singleton constructor reading DOCKER_HOST from environment
|
|
|
|
Connects to:
|
|
config/constants.go - reads ConnectionTimeout, InspectTimeout,
|
|
DefaultTimeout
|
|
analyzer/container.go - ListContainers, InspectContainer
|
|
analyzer/daemon.go - Info
|
|
analyzer/image.go - ListImages, InspectImage
|
|
scanner.go - constructs Client and passes it to runtime analyzers
|
|
*/
|
|
|
|
package docker
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/CarterPerez-dev/docksec/internal/config"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/image"
|
|
"github.com/docker/docker/api/types/system"
|
|
"github.com/docker/docker/client"
|
|
)
|
|
|
|
type Client struct {
|
|
api *client.Client
|
|
}
|
|
|
|
var (
|
|
instance *Client
|
|
once sync.Once
|
|
initErr error
|
|
)
|
|
|
|
func NewClient() (*Client, error) {
|
|
once.Do(func() {
|
|
cli, err := client.NewClientWithOpts(
|
|
client.FromEnv,
|
|
client.WithAPIVersionNegotiation(),
|
|
)
|
|
if err != nil {
|
|
initErr = fmt.Errorf("creating docker client: %w", err)
|
|
return
|
|
}
|
|
instance = &Client{api: cli}
|
|
})
|
|
|
|
if initErr != nil {
|
|
return nil, initErr
|
|
}
|
|
return instance, nil
|
|
}
|
|
|
|
func (c *Client) Close() error {
|
|
if c.api != nil {
|
|
return c.api.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) Ping(ctx context.Context) error {
|
|
pingCtx, cancel := context.WithTimeout(ctx, config.ConnectionTimeout)
|
|
defer cancel()
|
|
|
|
_, err := c.api.Ping(pingCtx)
|
|
if err != nil {
|
|
return fmt.Errorf("pinging docker daemon: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) Info(ctx context.Context) (system.Info, error) {
|
|
infoCtx, cancel := context.WithTimeout(ctx, config.DefaultTimeout)
|
|
defer cancel()
|
|
|
|
info, err := c.api.Info(infoCtx)
|
|
if err != nil {
|
|
return system.Info{}, fmt.Errorf("getting docker info: %w", err)
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (c *Client) ServerVersion(ctx context.Context) (types.Version, error) {
|
|
versionCtx, cancel := context.WithTimeout(ctx, config.ConnectionTimeout)
|
|
defer cancel()
|
|
|
|
version, err := c.api.ServerVersion(versionCtx)
|
|
if err != nil {
|
|
return types.Version{}, fmt.Errorf("getting docker version: %w", err)
|
|
}
|
|
return version, nil
|
|
}
|
|
|
|
func (c *Client) ListContainers(
|
|
ctx context.Context,
|
|
all bool,
|
|
) ([]container.Summary, error) {
|
|
listCtx, cancel := context.WithTimeout(ctx, config.DefaultTimeout)
|
|
defer cancel()
|
|
|
|
containers, err := c.api.ContainerList(
|
|
listCtx,
|
|
container.ListOptions{All: all},
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("listing containers: %w", err)
|
|
}
|
|
return containers, nil
|
|
}
|
|
|
|
func (c *Client) InspectContainer(
|
|
ctx context.Context,
|
|
containerID string,
|
|
) (container.InspectResponse, error) {
|
|
inspectCtx, cancel := context.WithTimeout(ctx, config.InspectTimeout)
|
|
defer cancel()
|
|
|
|
info, err := c.api.ContainerInspect(inspectCtx, containerID)
|
|
if err != nil {
|
|
return container.InspectResponse{}, fmt.Errorf(
|
|
"inspecting container %s: %w",
|
|
containerID,
|
|
err,
|
|
)
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (c *Client) ListImages(ctx context.Context) ([]image.Summary, error) {
|
|
listCtx, cancel := context.WithTimeout(ctx, config.DefaultTimeout)
|
|
defer cancel()
|
|
|
|
images, err := c.api.ImageList(listCtx, image.ListOptions{All: false})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("listing images: %w", err)
|
|
}
|
|
return images, nil
|
|
}
|
|
|
|
func (c *Client) InspectImage(
|
|
ctx context.Context,
|
|
imageID string,
|
|
) (image.InspectResponse, error) {
|
|
inspectCtx, cancel := context.WithTimeout(ctx, config.InspectTimeout)
|
|
defer cancel()
|
|
|
|
info, err := c.api.ImageInspect(inspectCtx, imageID)
|
|
if err != nil {
|
|
return image.InspectResponse{}, fmt.Errorf(
|
|
"inspecting image %s: %w",
|
|
imageID,
|
|
err,
|
|
)
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (c *Client) ImageHistory(
|
|
ctx context.Context,
|
|
imageID string,
|
|
) ([]image.HistoryResponseItem, error) {
|
|
historyCtx, cancel := context.WithTimeout(ctx, config.InspectTimeout)
|
|
defer cancel()
|
|
|
|
history, err := c.api.ImageHistory(historyCtx, imageID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getting image history %s: %w", imageID, err)
|
|
}
|
|
return history, nil
|
|
}
|