chore(images): initialise Docker service with containerd client

This commit is contained in:
Pasha Sviderski
2025-10-02 15:51:54 +10:00
parent 92975eec51
commit 59074a275f
5 changed files with 78 additions and 32 deletions
+4 -4
View File
@@ -14,6 +14,9 @@ require (
github.com/charmbracelet/huh v0.6.0 github.com/charmbracelet/huh v0.6.0
github.com/charmbracelet/lipgloss v1.1.0 github.com/charmbracelet/lipgloss v1.1.0
github.com/compose-spec/compose-go/v2 v2.4.5 github.com/compose-spec/compose-go/v2 v2.4.5
github.com/containerd/containerd/v2 v2.1.1
github.com/containerd/errdefs v1.0.0
github.com/containerd/log v0.1.0
github.com/containerd/platforms v1.0.0-rc.1 github.com/containerd/platforms v1.0.0-rc.1
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/deckarep/golang-set/v2 v2.8.0 github.com/deckarep/golang-set/v2 v2.8.0
@@ -24,6 +27,7 @@ require (
github.com/docker/docker v27.4.0-rc.2+incompatible github.com/docker/docker v27.4.0-rc.2+incompatible
github.com/docker/go-connections v0.5.0 github.com/docker/go-connections v0.5.0
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
github.com/dustin/go-humanize v1.0.1
github.com/goccy/go-yaml v1.17.1 github.com/goccy/go-yaml v1.17.1
github.com/google/go-cmp v0.7.0 github.com/google/go-cmp v0.7.0
github.com/google/go-containerregistry v0.20.2 github.com/google/go-containerregistry v0.20.2
@@ -101,12 +105,9 @@ require (
github.com/containerd/console v1.0.4 // indirect github.com/containerd/console v1.0.4 // indirect
github.com/containerd/containerd v1.7.24 // indirect github.com/containerd/containerd v1.7.24 // indirect
github.com/containerd/containerd/api v1.9.0 // indirect github.com/containerd/containerd/api v1.9.0 // indirect
github.com/containerd/containerd/v2 v2.1.1 // indirect
github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/plugin v1.0.0 // indirect github.com/containerd/plugin v1.0.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect github.com/containerd/ttrpc v1.2.7 // indirect
@@ -127,7 +128,6 @@ require (
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-metrics v0.0.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fatih/color v1.17.0 // indirect github.com/fatih/color v1.17.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
+39
View File
@@ -0,0 +1,39 @@
package containerd
import (
"fmt"
"time"
"github.com/containerd/containerd/v2/client"
)
// Client is a containerd client for image operations.
type Client struct {
Client *client.Client
}
// NewClient creates a new containerd client connected to the given socket path.
func NewClient(sockPath string) (*Client, error) {
if sockPath == "" {
return nil, fmt.Errorf("containerd socket path is required")
}
c, err := client.New(
sockPath,
client.WithDefaultNamespace("moby"),
client.WithTimeout(60*time.Second),
)
if err != nil {
return nil, err
}
return &Client{Client: c}, nil
}
// Close closes the containerd client connection.
func (c *Client) Close() error {
if c.Client != nil {
return c.Client.Close()
}
return nil
}
+12 -20
View File
@@ -63,25 +63,16 @@ type Server struct {
waitForNetworkReady func(ctx context.Context) error waitForNetworkReady func(ctx context.Context) error
} }
// ServerOption configures the Docker server. type ServerOptions struct {
type ServerOption func(*Server) // TODO: verify if we still need the network readiness checks as the cluster controller ensures the network
// is ready before starting the network API server. It may still be needed when communicating with the local
// WithNetworkReady sets the network readiness check function. // API server but in this case we should probably fail until the cluster is initialised.
func WithNetworkReady(networkReady func() bool) ServerOption { NetworkReady func() bool
return func(s *Server) { WaitForNetworkReady func(ctx context.Context) error
s.networkReady = networkReady
}
}
// WithWaitForNetworkReady sets the network readiness wait function.
func WithWaitForNetworkReady(waitForNetworkReady func(ctx context.Context) error) ServerOption {
return func(s *Server) {
s.waitForNetworkReady = waitForNetworkReady
}
} }
// NewServer creates a new Docker gRPC server with the provided Docker service. // NewServer creates a new Docker gRPC server with the provided Docker service.
func NewServer(service *Service, db *sqlx.DB, internalDNSIP func() netip.Addr, opts ...ServerOption) *Server { func NewServer(service *Service, db *sqlx.DB, internalDNSIP func() netip.Addr, opts ServerOptions) *Server {
s := &Server{ s := &Server{
client: service.Client, client: service.Client,
service: service, service: service,
@@ -89,9 +80,8 @@ func NewServer(service *Service, db *sqlx.DB, internalDNSIP func() netip.Addr, o
internalDNSIP: internalDNSIP, internalDNSIP: internalDNSIP,
} }
for _, opt := range opts { s.networkReady = opts.NetworkReady
opt(s) s.waitForNetworkReady = opts.WaitForNetworkReady
}
return s return s
} }
@@ -731,7 +721,9 @@ func (s *Server) injectConfigs(ctx context.Context, containerID string, configs
} }
// Copy the config content directly into the container // Copy the config content directly into the container
if err := s.copyContentToContainer(ctx, containerID, config.Content, targetPath, uid, gid, fileMode); err != nil { if err := s.copyContentToContainer(
ctx, containerID, config.Content, targetPath, uid, gid, fileMode,
); err != nil {
return fmt.Errorf("copy config file '%s' to container: %w", config.Name, err) return fmt.Errorf("copy config file '%s' to container: %w", config.Name, err)
} }
+10 -4
View File
@@ -12,21 +12,27 @@ import (
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/psviderski/uncloud/internal/containerd"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
) )
// Service provides higher-level Docker operations that extends Docker API with Uncloud-specific data // Service provides higher-level Docker operations that extends Docker API with Uncloud-specific data
// from the machine database. // from the machine database.
type Service struct { type Service struct {
// Client is a Docker client for managing Docker resources.
Client *client.Client Client *client.Client
db *sqlx.DB // containerd is a containerd client for accessing containerd images.
containerd *containerd.Client
// db is a connection to the machine database.
db *sqlx.DB
} }
// NewService creates a new Docker service instance. // NewService creates a new Docker service instance.
func NewService(client *client.Client, db *sqlx.DB) *Service { func NewService(client *client.Client, containerdClient *containerd.Client, db *sqlx.DB) *Service {
return &Service{ return &Service{
Client: client, Client: client,
db: db, containerd: containerdClient,
db: db,
} }
} }
+13 -4
View File
@@ -17,6 +17,7 @@ import (
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/go-connections/sockets" "github.com/docker/go-connections/sockets"
"github.com/psviderski/uncloud/internal/containerd"
"github.com/psviderski/uncloud/internal/corrosion" "github.com/psviderski/uncloud/internal/corrosion"
"github.com/psviderski/uncloud/internal/docker" "github.com/psviderski/uncloud/internal/docker"
"github.com/psviderski/uncloud/internal/fs" "github.com/psviderski/uncloud/internal/fs"
@@ -255,7 +256,14 @@ func NewMachine(config *Config) (*Machine, error) {
return nil, fmt.Errorf("init machine database: %w", err) return nil, fmt.Errorf("init machine database: %w", err)
} }
dockerService := machinedocker.NewService(config.DockerClient, db) if config.ContainerdSockPath == "" {
return nil, errors.New("containerd socket path must be configured")
}
containerdClient, err := containerd.NewClient(config.ContainerdSockPath)
if err != nil {
return nil, fmt.Errorf("create containerd client: %w", err)
}
dockerService := machinedocker.NewService(config.DockerClient, containerdClient, db)
// Init a local gRPC proxy server that proxies requests to the local or remote machine API servers. // Init a local gRPC proxy server that proxies requests to the local or remote machine API servers.
proxyDirector := apiproxy.NewDirector(config.MachineSockPath, constants.MachineAPIPort) proxyDirector := apiproxy.NewDirector(config.MachineSockPath, constants.MachineAPIPort)
@@ -283,9 +291,10 @@ func NewMachine(config *Config) (*Machine, error) {
internalDNSIP := func() netip.Addr { internalDNSIP := func() netip.Addr {
return m.IP() return m.IP()
} }
m.dockerServer = machinedocker.NewServer(dockerService, db, internalDNSIP, m.dockerServer = machinedocker.NewServer(dockerService, db, internalDNSIP, machinedocker.ServerOptions{
machinedocker.WithNetworkReady(m.IsNetworkReady), NetworkReady: m.IsNetworkReady,
machinedocker.WithWaitForNetworkReady(m.WaitForNetworkReady)) WaitForNetworkReady: m.WaitForNetworkReady,
})
caddyServer := caddyconfig.NewServer(caddyconfig.NewService(config.CaddyConfigDir)) caddyServer := caddyconfig.NewServer(caddyconfig.NewService(config.CaddyConfigDir))
m.localMachineServer = newGRPCServer(m, c, m.dockerServer, caddyServer) m.localMachineServer = newGRPCServer(m, c, m.dockerServer, caddyServer)