From 73785007cb7ffb66a30da02bb4752e20e59005ba Mon Sep 17 00:00:00 2001 From: Pavel Sviderski Date: Thu, 28 Nov 2024 13:54:10 +1000 Subject: [PATCH] init ucind (Uncloud in Docker) provisioner --- internal/ucind/cluster.go | 82 +++++++++++++++++++++++++++++++++++++ internal/ucind/provision.go | 18 ++++++++ 2 files changed, 100 insertions(+) create mode 100644 internal/ucind/cluster.go create mode 100644 internal/ucind/provision.go diff --git a/internal/ucind/cluster.go b/internal/ucind/cluster.go new file mode 100644 index 00000000..373e60e2 --- /dev/null +++ b/internal/ucind/cluster.go @@ -0,0 +1,82 @@ +package ucind + +import ( + "context" + "errors" + "fmt" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/client" +) + +const ( + ClusterNameLabel = "ucind.cluster.name" + ManagedLabel = "ucind.managed" +) + +type Cluster struct { + Name string +} + +type CreateClusterOptions struct { + Machines int +} + +func (p *Provisioner) CreateCluster(ctx context.Context, name string, opts CreateClusterOptions) error { + _, err := p.InspectCluster(ctx, name) + if err == nil { + return fmt.Errorf("cluster with name '%s' already exists", name) + } + if !errors.Is(err, ErrNotFound) { + return fmt.Errorf("inspect cluster '%s': %w", name, err) + } + + netOpts := network.CreateOptions{ + Labels: map[string]string{ + ClusterNameLabel: name, + ManagedLabel: "", + }, + } + // Docker network name is the same as the cluster name. + if _, err = p.client.NetworkCreate(ctx, name, netOpts); err != nil { + return fmt.Errorf("create Docker network '%s': %w", name, err) + } + + // TODO: create machines (containers) with the cluster name label. + + return nil +} + +func (p *Provisioner) InspectCluster(ctx context.Context, name string) (*Cluster, error) { + // Docker network name is the same as the cluster name. + net, err := p.client.NetworkInspect(ctx, name, network.InspectOptions{}) + if err != nil { + if client.IsErrNotFound(err) { + return nil, ErrNotFound + } + return nil, fmt.Errorf("inspect Docker network '%s': %w", name, err) + } + + if _, ok := net.Labels[ManagedLabel]; !ok { + // The network with the cluster name exists, but it's not managed by ucind. + return nil, ErrNotFound + } + + // TODO: list containers (machines) with the cluster name label and include them in the cluster struct. + + return &Cluster{ + Name: name, + }, nil +} + +func (p *Provisioner) RemoveCluster(ctx context.Context, name string) error { + if _, err := p.InspectCluster(ctx, name); err != nil { + return err + } + + // TODO: remove machines (containers) with the cluster name label. + + if err := p.client.NetworkRemove(ctx, name); err != nil { + return fmt.Errorf("remove Docker network '%s': %w", name, err) + } + return nil +} diff --git a/internal/ucind/provision.go b/internal/ucind/provision.go new file mode 100644 index 00000000..94cea9ef --- /dev/null +++ b/internal/ucind/provision.go @@ -0,0 +1,18 @@ +package ucind + +import ( + "errors" + "github.com/docker/docker/client" +) + +var ErrNotFound = errors.New("not found") + +type Provisioner struct { + client *client.Client +} + +func NewProvisioner(cli *client.Client) *Provisioner { + return &Provisioner{ + client: cli, + } +}