jx/internal/resource/container_network.go
Matthew Rich 1460d2285b
Some checks failed
Declarative Tests / build-ubuntu-focal (push) Waiting to run
Lint / golangci-lint (push) Failing after 15s
Declarative Tests / test (push) Failing after 5s
Declarative Tests / build-fedora (push) Has been cancelled
add support for configuration documents
2024-07-01 14:54:18 -07:00

197 lines
4.5 KiB
Go

// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
// Container resource
package resource
import (
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
_ "github.com/docker/docker/api/types/mount"
_ "github.com/docker/docker/api/types/network"
_ "github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/client"
"gopkg.in/yaml.v3"
_ "log/slog"
"net/url"
_ "os"
_ "os/exec"
"path/filepath"
_ "strings"
"encoding/json"
"io"
"gitea.rosskeen.house/rosskeen.house/machine"
"decl/internal/codec"
)
type ContainerNetworkClient interface {
ContainerClient
NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
}
type ContainerNetwork struct {
stater machine.Stater `json:"-" yaml:"-"`
Id string `json:"ID,omitempty" yaml:"ID,omitempty"`
Name string `json:"name" yaml:"name"`
State string `yaml:"state"`
config ConfigurationValueGetter
apiClient ContainerNetworkClient
}
func init() {
ResourceTypes.Register([]string{"container-network"}, func(u *url.URL) Resource {
n := NewContainerNetwork(nil)
n.Name = filepath.Join(u.Hostname(), u.Path)
return n
})
}
func NewContainerNetwork(containerClientApi ContainerNetworkClient) *ContainerNetwork {
var apiClient ContainerNetworkClient = containerClientApi
if apiClient == nil {
var err error
apiClient, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
panic(err)
}
}
return &ContainerNetwork{
apiClient: apiClient,
}
}
func (n *ContainerNetwork) Clone() Resource {
return &ContainerNetwork {
Id: n.Id,
Name: n.Name,
State: n.State,
apiClient: n.apiClient,
}
}
func (n *ContainerNetwork) StateMachine() machine.Stater {
if n.stater == nil {
n.stater = StorageMachine(n)
}
return n.stater
}
func (n *ContainerNetwork) Notify(m *machine.EventMessage) {
ctx := context.Background()
switch m.On {
case machine.ENTERSTATEEVENT:
switch m.Dest {
case "start_create":
if e := n.Create(ctx); e == nil {
if triggerErr := n.stater.Trigger("created"); triggerErr == nil {
return
}
}
n.State = "absent"
case "present":
n.State = "present"
}
case machine.EXITSTATEEVENT:
}
}
func (n *ContainerNetwork) URI() string {
return fmt.Sprintf("container-network://%s", n.Name)
}
func (n *ContainerNetwork) SetURI(uri string) error {
resourceUri, e := url.Parse(uri)
if e == nil {
if resourceUri.Scheme == n.Type() {
n.Name, e = filepath.Abs(filepath.Join(resourceUri.Hostname(), resourceUri.RequestURI()))
} else {
e = fmt.Errorf("%w: %s is not a %s", ErrInvalidResourceURI, uri, n.Type())
}
}
return e
}
func (n *ContainerNetwork) UseConfig(config ConfigurationValueGetter) {
n.config = config
}
func (n *ContainerNetwork) JSON() ([]byte, error) {
return json.Marshal(n)
}
func (n *ContainerNetwork) Validate() error {
return fmt.Errorf("failed")
}
func (n *ContainerNetwork) Apply() error {
ctx := context.Background()
switch n.State {
case "absent":
return n.Delete(ctx)
case "present":
return n.Create(ctx)
}
return nil
}
func (n *ContainerNetwork) Load(r io.Reader) error {
return codec.NewYAMLDecoder(r).Decode(n)
}
func (n *ContainerNetwork) LoadDecl(yamlResourceDeclaration string) error {
return codec.NewYAMLStringDecoder(yamlResourceDeclaration).Decode(n)
}
func (n *ContainerNetwork) Create(ctx context.Context) error {
networkResp, err := n.apiClient.NetworkCreate(ctx, n.Name, types.NetworkCreate{
Driver: "bridge",
})
if err != nil {
panic(err)
}
n.Id = networkResp.ID
return nil
}
// produce yaml representation of any resource
func (n *ContainerNetwork) Read(ctx context.Context) ([]byte, error) {
return yaml.Marshal(n)
}
func (n *ContainerNetwork) Delete(ctx context.Context) error {
return nil
}
func (n *ContainerNetwork) Type() string { return "container-network" }
func (n *ContainerNetwork) ResolveId(ctx context.Context) string {
filterArgs := filters.NewArgs()
filterArgs.Add("name", "/"+n.Name)
containers, err := n.apiClient.ContainerList(ctx, container.ListOptions{
All: true,
Filters: filterArgs,
})
if err != nil {
panic(fmt.Errorf("%w: %s %s", err, n.Type(), n.Name))
}
for _, container := range containers {
for _, containerName := range container.Names {
if containerName == n.Name {
if n.Id == "" {
n.Id = container.ID
}
return container.ID
}
}
}
return ""
}