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
+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
}
// ServerOption configures the Docker server.
type ServerOption func(*Server)
// WithNetworkReady sets the network readiness check function.
func WithNetworkReady(networkReady func() bool) ServerOption {
return func(s *Server) {
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
}
type ServerOptions struct {
// 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
// API server but in this case we should probably fail until the cluster is initialised.
NetworkReady func() bool
WaitForNetworkReady func(ctx context.Context) error
}
// 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{
client: service.Client,
service: service,
@@ -89,9 +80,8 @@ func NewServer(service *Service, db *sqlx.DB, internalDNSIP func() netip.Addr, o
internalDNSIP: internalDNSIP,
}
for _, opt := range opts {
opt(s)
}
s.networkReady = opts.NetworkReady
s.waitForNetworkReady = opts.WaitForNetworkReady
return s
}
@@ -731,7 +721,9 @@ func (s *Server) injectConfigs(ctx context.Context, containerID string, configs
}
// 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)
}
+10 -4
View File
@@ -12,21 +12,27 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/jmoiron/sqlx"
"github.com/psviderski/uncloud/internal/containerd"
"github.com/psviderski/uncloud/pkg/api"
)
// Service provides higher-level Docker operations that extends Docker API with Uncloud-specific data
// from the machine database.
type Service struct {
// Client is a Docker client for managing Docker resources.
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.
func NewService(client *client.Client, db *sqlx.DB) *Service {
func NewService(client *client.Client, containerdClient *containerd.Client, db *sqlx.DB) *Service {
return &Service{
Client: client,
db: db,
Client: client,
containerd: containerdClient,
db: db,
}
}
+13 -4
View File
@@ -17,6 +17,7 @@ import (
"github.com/docker/docker/client"
"github.com/docker/go-connections/sockets"
"github.com/psviderski/uncloud/internal/containerd"
"github.com/psviderski/uncloud/internal/corrosion"
"github.com/psviderski/uncloud/internal/docker"
"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)
}
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.
proxyDirector := apiproxy.NewDirector(config.MachineSockPath, constants.MachineAPIPort)
@@ -283,9 +291,10 @@ func NewMachine(config *Config) (*Machine, error) {
internalDNSIP := func() netip.Addr {
return m.IP()
}
m.dockerServer = machinedocker.NewServer(dockerService, db, internalDNSIP,
machinedocker.WithNetworkReady(m.IsNetworkReady),
machinedocker.WithWaitForNetworkReady(m.WaitForNetworkReady))
m.dockerServer = machinedocker.NewServer(dockerService, db, internalDNSIP, machinedocker.ServerOptions{
NetworkReady: m.IsNetworkReady,
WaitForNetworkReady: m.WaitForNetworkReady,
})
caddyServer := caddyconfig.NewServer(caddyconfig.NewService(config.CaddyConfigDir))
m.localMachineServer = newGRPCServer(m, c, m.dockerServer, caddyServer)