This commit is contained in:
parent
e71d177984
commit
e695278d0c
@ -1,29 +1,27 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Container resource
|
// Container resource
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "os"
|
"github.com/docker/docker/api/types"
|
||||||
_ "gopkg.in/yaml.v3"
|
|
||||||
_ "os/exec"
|
|
||||||
_ "strings"
|
|
||||||
"log/slog"
|
|
||||||
"github.com/docker/docker/api/types/strslice"
|
|
||||||
"github.com/docker/docker/api/types/mount"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
_ "gopkg.in/yaml.v3"
|
||||||
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
_ "os"
|
||||||
|
_ "os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
_ "strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContainerClient interface {
|
type ContainerClient interface {
|
||||||
@ -62,11 +60,11 @@ type Container struct {
|
|||||||
GraphDriver types.GraphDriverData `yaml:"graphdriver"`
|
GraphDriver types.GraphDriverData `yaml:"graphdriver"`
|
||||||
SizeRw *int64 `json:",omitempty"`
|
SizeRw *int64 `json:",omitempty"`
|
||||||
SizeRootFs *int64 `json:",omitempty"`
|
SizeRootFs *int64 `json:",omitempty"`
|
||||||
/*
|
/*
|
||||||
Mounts []MountPoint
|
Mounts []MountPoint
|
||||||
Config *container.Config
|
Config *container.Config
|
||||||
NetworkSettings *NetworkSettings
|
NetworkSettings *NetworkSettings
|
||||||
*/
|
*/
|
||||||
|
|
||||||
State string `yaml:"state"`
|
State string `yaml:"state"`
|
||||||
|
|
||||||
@ -127,7 +125,7 @@ func (c *Container) LoadDecl(yamlFileResourceDeclaration string) error {
|
|||||||
|
|
||||||
func (c *Container) Create(ctx context.Context) error {
|
func (c *Container) Create(ctx context.Context) error {
|
||||||
numberOfEnvironmentVariables := len(c.Environment)
|
numberOfEnvironmentVariables := len(c.Environment)
|
||||||
config := &container.Config {
|
config := &container.Config{
|
||||||
Image: c.Image,
|
Image: c.Image,
|
||||||
Cmd: c.Cmd,
|
Cmd: c.Cmd,
|
||||||
Entrypoint: c.Entrypoint,
|
Entrypoint: c.Entrypoint,
|
||||||
@ -136,13 +134,13 @@ func (c *Container) Create(ctx context.Context) error {
|
|||||||
|
|
||||||
config.Env = make([]string, numberOfEnvironmentVariables)
|
config.Env = make([]string, numberOfEnvironmentVariables)
|
||||||
index := 0
|
index := 0
|
||||||
for k,v := range c.Environment {
|
for k, v := range c.Environment {
|
||||||
config.Env[index] = k + "=" + v
|
config.Env[index] = k + "=" + v
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
for i := range c.HostConfig.Mounts {
|
for i := range c.HostConfig.Mounts {
|
||||||
if c.HostConfig.Mounts[i].Type == mount.TypeBind {
|
if c.HostConfig.Mounts[i].Type == mount.TypeBind {
|
||||||
if mountSourceAbsolutePath,e := filepath.Abs(c.HostConfig.Mounts[i].Source); e == nil {
|
if mountSourceAbsolutePath, e := filepath.Abs(c.HostConfig.Mounts[i].Source); e == nil {
|
||||||
c.HostConfig.Mounts[i].Source = mountSourceAbsolutePath
|
c.HostConfig.Mounts[i].Source = mountSourceAbsolutePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,7 +152,7 @@ func (c *Container) Create(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
c.Id = resp.ID
|
c.Id = resp.ID
|
||||||
|
|
||||||
/*
|
/*
|
||||||
statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
|
statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
|
||||||
select {
|
select {
|
||||||
case err := <-errCh:
|
case err := <-errCh:
|
||||||
@ -163,7 +161,7 @@ func (c *Container) Create(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
case <-statusCh:
|
case <-statusCh:
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if startErr := c.apiClient.ContainerStart(ctx, c.Id, types.ContainerStartOptions{}); startErr != nil {
|
if startErr := c.apiClient.ContainerStart(ctx, c.Id, types.ContainerStartOptions{}); startErr != nil {
|
||||||
return startErr
|
return startErr
|
||||||
@ -176,8 +174,8 @@ func (c *Container) Create(ctx context.Context) error {
|
|||||||
func (c *Container) Read(ctx context.Context) ([]byte, error) {
|
func (c *Container) Read(ctx context.Context) ([]byte, error) {
|
||||||
var containerID string
|
var containerID string
|
||||||
filterArgs := filters.NewArgs()
|
filterArgs := filters.NewArgs()
|
||||||
filterArgs.Add("name", "/" + c.Name)
|
filterArgs.Add("name", "/"+c.Name)
|
||||||
containers,err := c.apiClient.ContainerList(ctx, types.ContainerListOptions{
|
containers, err := c.apiClient.ContainerList(ctx, types.ContainerListOptions{
|
||||||
All: true,
|
All: true,
|
||||||
Filters: filterArgs,
|
Filters: filterArgs,
|
||||||
})
|
})
|
||||||
@ -188,7 +186,7 @@ func (c *Container) Read(ctx context.Context) ([]byte, error) {
|
|||||||
|
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
for _, containerName := range container.Names {
|
for _, containerName := range container.Names {
|
||||||
if containerName == "/" + c.Name {
|
if containerName == "/"+c.Name {
|
||||||
containerID = container.ID
|
containerID = container.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,7 +218,6 @@ func (c *Container) Read(ctx context.Context) ([]byte, error) {
|
|||||||
return yaml.Marshal(c)
|
return yaml.Marshal(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (c *Container) Delete(ctx context.Context) error {
|
func (c *Container) Delete(ctx context.Context) error {
|
||||||
err := c.apiClient.ContainerRemove(ctx, c.Id, types.ContainerRemoveOptions{
|
err := c.apiClient.ContainerRemove(ctx, c.Id, types.ContainerRemoveOptions{
|
||||||
RemoveVolumes: true,
|
RemoveVolumes: true,
|
||||||
@ -237,8 +234,8 @@ func (c *Container) Type() string { return "container" }
|
|||||||
|
|
||||||
func (c *Container) ResolveId(ctx context.Context) string {
|
func (c *Container) ResolveId(ctx context.Context) string {
|
||||||
filterArgs := filters.NewArgs()
|
filterArgs := filters.NewArgs()
|
||||||
filterArgs.Add("name", "/" + c.Name)
|
filterArgs.Add("name", "/"+c.Name)
|
||||||
containers,err := c.apiClient.ContainerList(ctx, types.ContainerListOptions{
|
containers, err := c.apiClient.ContainerList(ctx, types.ContainerListOptions{
|
||||||
All: true,
|
All: true,
|
||||||
Filters: filterArgs,
|
Filters: filterArgs,
|
||||||
})
|
})
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
//
|
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "fmt"
|
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
|
||||||
_ "net/http"
|
|
||||||
_ "net/http/httptest"
|
|
||||||
_ "net/url"
|
|
||||||
_ "io"
|
|
||||||
_ "os"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
_ "encoding/json"
|
|
||||||
_ "strings"
|
|
||||||
"decl/tests/mocks"
|
"decl/tests/mocks"
|
||||||
|
_ "encoding/json"
|
||||||
|
_ "fmt"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
_ "io"
|
||||||
|
_ "net/http"
|
||||||
|
_ "net/http/httptest"
|
||||||
|
_ "net/url"
|
||||||
|
_ "os"
|
||||||
|
_ "strings"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewContainerResource(t *testing.T) {
|
func TestNewContainerResource(t *testing.T) {
|
||||||
@ -34,11 +33,11 @@ func TestReadContainer(t *testing.T) {
|
|||||||
image: "alpine"
|
image: "alpine"
|
||||||
state: present
|
state: present
|
||||||
`
|
`
|
||||||
m := &mocks.MockContainerClient {
|
m := &mocks.MockContainerClient{
|
||||||
InjectContainerList: func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
|
InjectContainerList: func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
|
||||||
return []types.Container{
|
return []types.Container{
|
||||||
{ ID: "123456789abc" },
|
{ID: "123456789abc"},
|
||||||
{ ID: "123456789def" },
|
{ID: "123456789def"},
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
InjectContainerInspect: func(ctx context.Context, containerID string) (types.ContainerJSON, error) {
|
InjectContainerInspect: func(ctx context.Context, containerID string) (types.ContainerJSON, error) {
|
||||||
@ -47,7 +46,7 @@ func TestReadContainer(t *testing.T) {
|
|||||||
ID: "123456789abc",
|
ID: "123456789abc",
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Image: "alpine",
|
Image: "alpine",
|
||||||
} }, nil
|
}}, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,9 +63,9 @@ func TestReadContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateContainer(t *testing.T) {
|
func TestCreateContainer(t *testing.T) {
|
||||||
m := &mocks.MockContainerClient {
|
m := &mocks.MockContainerClient{
|
||||||
InjectContainerCreate: func(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
|
InjectContainerCreate: func(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
|
||||||
return container.CreateResponse{ ID: "abcdef012", Warnings: []string{} }, nil
|
return container.CreateResponse{ID: "abcdef012", Warnings: []string{}}, nil
|
||||||
},
|
},
|
||||||
InjectContainerRemove: func(context.Context, string, container.RemoveOptions) error {
|
InjectContainerRemove: func(context.Context, string, container.RemoveOptions) error {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
//
|
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"log/slog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Declaration struct {
|
type Declaration struct {
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "os"
|
|
||||||
"path/filepath"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "log"
|
|
||||||
"testing"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
_ "log"
|
||||||
|
_ "os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestYamlLoadDecl(t *testing.T) {
|
func TestYamlLoadDecl(t *testing.T) {
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
//
|
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "fmt"
|
_ "fmt"
|
||||||
_ "log"
|
|
||||||
"io"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
_ "net/url"
|
"io"
|
||||||
|
_ "log"
|
||||||
|
_ "net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Document struct {
|
type Document struct {
|
||||||
@ -16,14 +15,14 @@ type Document struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewDocument() *Document {
|
func NewDocument() *Document {
|
||||||
return &Document {}
|
return &Document{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Document) Load(r io.Reader) error {
|
func (d *Document) Load(r io.Reader) error {
|
||||||
yamlDecoder := yaml.NewDecoder(r)
|
yamlDecoder := yaml.NewDecoder(r)
|
||||||
yamlDecoder.Decode(d)
|
yamlDecoder.Decode(d)
|
||||||
for i := range(d.ResourceDecls) {
|
for i := range d.ResourceDecls {
|
||||||
if _,e := d.ResourceDecls[i].LoadResourceFromYaml(); e != nil {
|
if _, e := d.ResourceDecls[i].LoadResourceFromYaml(); e != nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,7 +34,7 @@ func (d *Document) Resources() []Declaration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Document) Apply() error {
|
func (d *Document) Apply() error {
|
||||||
for i := range(d.ResourceDecls) {
|
for i := range d.ResourceDecls {
|
||||||
if e := d.ResourceDecls[i].Resource().Apply(); e != nil {
|
if e := d.ResourceDecls[i].Resource().Apply(); e != nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
@ -43,7 +42,7 @@ func (d *Document) Apply() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Document) Generate(w io.Writer) (error) {
|
func (d *Document) Generate(w io.Writer) error {
|
||||||
yamlEncoder := yaml.NewEncoder(w)
|
yamlEncoder := yaml.NewEncoder(w)
|
||||||
yamlEncoder.Encode(d)
|
yamlEncoder.Encode(d)
|
||||||
return yamlEncoder.Close()
|
return yamlEncoder.Close()
|
||||||
|
@ -3,15 +3,15 @@ package resource
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"time"
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewDocumentLoader(t *testing.T) {
|
func TestNewDocumentLoader(t *testing.T) {
|
||||||
@ -26,7 +26,7 @@ func TestDocumentLoader(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
file,_ := filepath.Abs(filepath.Join(dir, "foo.txt"))
|
file, _ := filepath.Abs(filepath.Join(dir, "foo.txt"))
|
||||||
|
|
||||||
document := fmt.Sprintf(`
|
document := fmt.Sprintf(`
|
||||||
---
|
---
|
||||||
@ -68,12 +68,12 @@ func TestDocumentGenerator(t *testing.T) {
|
|||||||
fileContent := `// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
fileContent := `// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
`
|
`
|
||||||
|
|
||||||
file,_ := filepath.Abs(filepath.Join(TempDir, "foo.txt"))
|
file, _ := filepath.Abs(filepath.Join(TempDir, "foo.txt"))
|
||||||
|
|
||||||
err := os.WriteFile(file, []byte(fileContent), 0644)
|
err := os.WriteFile(file, []byte(fileContent), 0644)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
info,statErr := os.Stat(file)
|
info, statErr := os.Stat(file)
|
||||||
assert.Nil(t, statErr)
|
assert.Nil(t, statErr)
|
||||||
mTime := info.ModTime()
|
mTime := info.ModTime()
|
||||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||||
@ -103,7 +103,7 @@ resources:
|
|||||||
d := NewDocument()
|
d := NewDocument()
|
||||||
assert.NotEqual(t, nil, d)
|
assert.NotEqual(t, nil, d)
|
||||||
|
|
||||||
f,e := ResourceTypes.New("file://")
|
f, e := ResourceTypes.New("file://")
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
assert.NotNil(t, f)
|
assert.NotNil(t, f)
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ resources:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocumentAddResource(t *testing.T) {
|
func TestDocumentAddResource(t *testing.T) {
|
||||||
file,_ := filepath.Abs(filepath.Join(TempDir, "foo.txt"))
|
file, _ := filepath.Abs(filepath.Join(TempDir, "foo.txt"))
|
||||||
err := os.WriteFile(file, []byte(""), 0644)
|
err := os.WriteFile(file, []byte(""), 0644)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
//
|
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "os"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
_ "os/exec"
|
_ "log"
|
||||||
_ "strings"
|
|
||||||
_ "log"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
_ "os"
|
||||||
|
_ "os/exec"
|
||||||
|
_ "strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Exec struct {
|
type Exec struct {
|
||||||
@ -34,7 +33,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewExec() *Exec {
|
func NewExec() *Exec {
|
||||||
return &Exec { loader: YamlLoadDecl }
|
return &Exec{loader: YamlLoadDecl}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Exec) URI() string {
|
func (x *Exec) URI() string {
|
||||||
@ -62,4 +61,3 @@ func (x *Exec) Type() string { return "exec" }
|
|||||||
func (x *Exec) Read(ctx context.Context) ([]byte, error) {
|
func (x *Exec) Read(ctx context.Context) ([]byte, error) {
|
||||||
return yaml.Marshal(x)
|
return yaml.Marshal(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
//
|
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"io"
|
|
||||||
"syscall"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
"strconv"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"net/url"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ type File struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewFile() *File {
|
func NewFile() *File {
|
||||||
return &File{ loader: YamlLoadDecl, FileType: RegularFile }
|
return &File{loader: YamlLoadDecl, FileType: RegularFile}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) URI() string {
|
func (f *File) URI() string {
|
||||||
@ -83,18 +82,19 @@ func (f *File) Apply() error {
|
|||||||
if removeErr != nil {
|
if removeErr != nil {
|
||||||
return removeErr
|
return removeErr
|
||||||
}
|
}
|
||||||
case "present": {
|
case "present":
|
||||||
uid,uidErr := LookupUID(f.Owner)
|
{
|
||||||
|
uid, uidErr := LookupUID(f.Owner)
|
||||||
if uidErr != nil {
|
if uidErr != nil {
|
||||||
return uidErr
|
return uidErr
|
||||||
}
|
}
|
||||||
|
|
||||||
gid,gidErr := LookupGID(f.Group)
|
gid, gidErr := LookupGID(f.Group)
|
||||||
if gidErr != nil {
|
if gidErr != nil {
|
||||||
return gidErr
|
return gidErr
|
||||||
}
|
}
|
||||||
|
|
||||||
mode,modeErr := strconv.ParseInt(f.Mode, 8, 64)
|
mode, modeErr := strconv.ParseInt(f.Mode, 8, 64)
|
||||||
if modeErr != nil {
|
if modeErr != nil {
|
||||||
return modeErr
|
return modeErr
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ func (f *File) Apply() error {
|
|||||||
default:
|
default:
|
||||||
fallthrough
|
fallthrough
|
||||||
case RegularFile:
|
case RegularFile:
|
||||||
createdFile,e := os.Create(f.Path)
|
createdFile, e := os.Create(f.Path)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
@ -116,11 +116,11 @@ func (f *File) Apply() error {
|
|||||||
if chmodErr := createdFile.Chmod(os.FileMode(mode)); chmodErr != nil {
|
if chmodErr := createdFile.Chmod(os.FileMode(mode)); chmodErr != nil {
|
||||||
return chmodErr
|
return chmodErr
|
||||||
}
|
}
|
||||||
_,writeErr := createdFile.Write([]byte(f.Content))
|
_, writeErr := createdFile.Write([]byte(f.Content))
|
||||||
if writeErr != nil {
|
if writeErr != nil {
|
||||||
return writeErr
|
return writeErr
|
||||||
}
|
}
|
||||||
if ! f.Mtime.IsZero() && ! f.Atime.IsZero() {
|
if !f.Mtime.IsZero() && !f.Atime.IsZero() {
|
||||||
if chtimesErr := os.Chtimes(f.Path, f.Atime, f.Mtime); chtimesErr != nil {
|
if chtimesErr := os.Chtimes(f.Path, f.Atime, f.Mtime); chtimesErr != nil {
|
||||||
return chtimesErr
|
return chtimesErr
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
_ "strings"
|
_ "strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewFileResource(t *testing.T) {
|
func TestNewFileResource(t *testing.T) {
|
||||||
@ -67,7 +67,7 @@ func TestReadFile(t *testing.T) {
|
|||||||
assert.Equal(t, nil, e)
|
assert.Equal(t, nil, e)
|
||||||
assert.Equal(t, "nobody", f.Owner)
|
assert.Equal(t, "nobody", f.Owner)
|
||||||
|
|
||||||
info,statErr := os.Stat(file)
|
info, statErr := os.Stat(file)
|
||||||
assert.Nil(t, statErr)
|
assert.Nil(t, statErr)
|
||||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
//
|
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -16,29 +15,29 @@ func LookupUIDString(userName string) string {
|
|||||||
return user.Uid
|
return user.Uid
|
||||||
}
|
}
|
||||||
|
|
||||||
func LookupUID(userName string) (int,error) {
|
func LookupUID(userName string) (int, error) {
|
||||||
user, userLookupErr := user.Lookup(userName)
|
user, userLookupErr := user.Lookup(userName)
|
||||||
if userLookupErr != nil {
|
if userLookupErr != nil {
|
||||||
return -1,userLookupErr
|
return -1, userLookupErr
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, uidErr := strconv.Atoi(user.Uid)
|
uid, uidErr := strconv.Atoi(user.Uid)
|
||||||
if uidErr != nil {
|
if uidErr != nil {
|
||||||
return -1,uidErr
|
return -1, uidErr
|
||||||
}
|
}
|
||||||
|
|
||||||
return uid,nil
|
return uid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LookupGID(groupName string) (int,error) {
|
func LookupGID(groupName string) (int, error) {
|
||||||
group, groupLookupErr := user.LookupGroup(groupName)
|
group, groupLookupErr := user.LookupGroup(groupName)
|
||||||
if groupLookupErr != nil {
|
if groupLookupErr != nil {
|
||||||
return -1,groupLookupErr
|
return -1, groupLookupErr
|
||||||
}
|
}
|
||||||
|
|
||||||
gid, gidErr := strconv.Atoi(group.Gid)
|
gid, gidErr := strconv.Atoi(group.Gid)
|
||||||
if gidErr != nil {
|
if gidErr != nil {
|
||||||
return -1,gidErr
|
return -1, gidErr
|
||||||
}
|
}
|
||||||
|
|
||||||
return gid, nil
|
return gid, nil
|
||||||
|
@ -2,27 +2,27 @@
|
|||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "fmt"
|
_ "context"
|
||||||
_ "context"
|
_ "encoding/json"
|
||||||
"testing"
|
_ "fmt"
|
||||||
_ "net/http"
|
|
||||||
_ "net/http/httptest"
|
|
||||||
_ "net/url"
|
|
||||||
_ "io"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
_ "encoding/json"
|
_ "io"
|
||||||
_ "strings"
|
_ "net/http"
|
||||||
|
_ "net/http/httptest"
|
||||||
|
_ "net/url"
|
||||||
|
_ "strings"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLookupUID(t *testing.T) {
|
func TestLookupUID(t *testing.T) {
|
||||||
uid,e := LookupUID("nobody")
|
uid, e := LookupUID("nobody")
|
||||||
|
|
||||||
assert.Equal(t, nil, e)
|
assert.Equal(t, nil, e)
|
||||||
assert.Equal(t, 65534, uid)
|
assert.Equal(t, 65534, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLookupGID(t *testing.T) {
|
func TestLookupGID(t *testing.T) {
|
||||||
gid,e := LookupGID("nobody")
|
gid, e := LookupGID("nobody")
|
||||||
|
|
||||||
assert.Equal(t, nil, e)
|
assert.Equal(t, nil, e)
|
||||||
assert.Equal(t, 65534, gid)
|
assert.Equal(t, 65534, gid)
|
||||||
|
@ -5,9 +5,9 @@ package resource
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
_ "fmt"
|
_ "fmt"
|
||||||
_ "gopkg.in/yaml.v3"
|
_ "gopkg.in/yaml.v3"
|
||||||
_ "net/url"
|
_ "net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Resource interface {
|
type Resource interface {
|
||||||
@ -42,11 +42,10 @@ type ResourceDeleter interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ResourceDecoder struct {
|
type ResourceDecoder struct {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResource(uri string) Resource {
|
func NewResource(uri string) Resource {
|
||||||
r,e := ResourceTypes.New(uri)
|
r, e := ResourceTypes.New(uri)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@ package resource
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
_ "fmt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
_ "fmt"
|
|
||||||
"log"
|
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var TempDir string
|
var TempDir string
|
||||||
@ -38,7 +38,7 @@ func TestResolveId(t *testing.T) {
|
|||||||
testFile := NewResource("file://../../README.md")
|
testFile := NewResource("file://../../README.md")
|
||||||
assert.NotNil(t, testFile)
|
assert.NotNil(t, testFile)
|
||||||
|
|
||||||
absolutePath,e := filepath.Abs("../../README.md")
|
absolutePath, e := filepath.Abs("../../README.md")
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
|
|
||||||
testFile.ResolveId(context.Background())
|
testFile.ResolveId(context.Background())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
//
|
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -21,7 +20,7 @@ type Types struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewTypes() *Types {
|
func NewTypes() *Types {
|
||||||
return &Types{ registry: make(map[string]TypeFactory) }
|
return &Types{registry: make(map[string]TypeFactory)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Types) Register(name string, factory TypeFactory) {
|
func (t *Types) Register(name string, factory TypeFactory) {
|
||||||
@ -29,19 +28,19 @@ func (t *Types) Register(name string, factory TypeFactory) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Types) New(uri string) (Resource, error) {
|
func (t *Types) New(uri string) (Resource, error) {
|
||||||
u,e := url.Parse(uri)
|
u, e := url.Parse(uri)
|
||||||
if u == nil || e != nil {
|
if u == nil || e != nil {
|
||||||
return nil, fmt.Errorf("%w: %s", ErrUnknownResourceType, e)
|
return nil, fmt.Errorf("%w: %s", ErrUnknownResourceType, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r,ok := t.registry[u.Scheme]; ok {
|
if r, ok := t.registry[u.Scheme]; ok {
|
||||||
return r(u), nil
|
return r(u), nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%w: %s", ErrUnknownResourceType, u.Scheme)
|
return nil, fmt.Errorf("%w: %s", ErrUnknownResourceType, u.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Types) Has(typename string) bool {
|
func (t *Types) Has(typename string) bool {
|
||||||
if _,ok := t.registry[typename]; ok {
|
if _, ok := t.registry[typename]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "context"
|
_ "context"
|
||||||
"testing"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"decl/tests/mocks"
|
"decl/tests/mocks"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewResourceTypes(t *testing.T) {
|
func TestNewResourceTypes(t *testing.T) {
|
||||||
@ -22,7 +22,7 @@ func TestNewResourceTypesRegister(t *testing.T) {
|
|||||||
|
|
||||||
resourceTypes.Register("foo", func(*url.URL) Resource { return m })
|
resourceTypes.Register("foo", func(*url.URL) Resource { return m })
|
||||||
|
|
||||||
r,e := resourceTypes.New("foo://")
|
r, e := resourceTypes.New("foo://")
|
||||||
assert.Equal(t, nil, e)
|
assert.Equal(t, nil, e)
|
||||||
assert.Equal(t, m, r)
|
assert.Equal(t, m, r)
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@ func TestResourceTypesFromURI(t *testing.T) {
|
|||||||
|
|
||||||
resourceTypes.Register("foo", func(*url.URL) Resource { return m })
|
resourceTypes.Register("foo", func(*url.URL) Resource { return m })
|
||||||
|
|
||||||
r,e := resourceTypes.New("foo://bar")
|
r, e := resourceTypes.New("foo://bar")
|
||||||
assert.Equal(t, nil, e)
|
assert.Equal(t, nil, e)
|
||||||
assert.Equal(t, m, r)
|
assert.Equal(t, m, r)
|
||||||
|
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
//
|
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "os"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
_ "os"
|
||||||
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
@ -31,7 +30,7 @@ type User struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewUser() *User {
|
func NewUser() *User {
|
||||||
return &User{ loader: YamlLoadDecl }
|
return &User{loader: YamlLoadDecl}
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -62,8 +61,8 @@ func (u *User) Apply() error {
|
|||||||
args = append(args, "-u", fmt.Sprintf("%d", u.UID))
|
args = append(args, "-u", fmt.Sprintf("%d", u.UID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if _,pathErr := exec.LookPath("useradd"); pathErr != nil {
|
if _, pathErr := exec.LookPath("useradd"); pathErr != nil {
|
||||||
if _,addUserPathErr := exec.LookPath("adduser"); addUserPathErr == nil {
|
if _, addUserPathErr := exec.LookPath("adduser"); addUserPathErr == nil {
|
||||||
userCommandName = "adduser"
|
userCommandName = "adduser"
|
||||||
u.AddUserCommand(&args)
|
u.AddUserCommand(&args)
|
||||||
}
|
}
|
||||||
@ -80,8 +79,8 @@ func (u *User) Apply() error {
|
|||||||
var userDelCommandName string = "userdel"
|
var userDelCommandName string = "userdel"
|
||||||
args := make([]string, 0, 7)
|
args := make([]string, 0, 7)
|
||||||
|
|
||||||
if _,pathErr := exec.LookPath("userdel"); pathErr != nil {
|
if _, pathErr := exec.LookPath("userdel"); pathErr != nil {
|
||||||
if _,delUserPathErr := exec.LookPath("deluser"); delUserPathErr == nil {
|
if _, delUserPathErr := exec.LookPath("deluser"); delUserPathErr == nil {
|
||||||
userDelCommandName = "deluser"
|
userDelCommandName = "deluser"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,10 +130,10 @@ func (u *User) Read(ctx context.Context) ([]byte, error) {
|
|||||||
var readUser *user.User
|
var readUser *user.User
|
||||||
var e error
|
var e error
|
||||||
if u.Name != "" {
|
if u.Name != "" {
|
||||||
readUser,e = user.Lookup(u.Name)
|
readUser, e = user.Lookup(u.Name)
|
||||||
}
|
}
|
||||||
if u.UID >= 0 {
|
if u.UID >= 0 {
|
||||||
readUser,e = user.LookupId(strconv.Itoa(u.UID))
|
readUser, e = user.LookupId(strconv.Itoa(u.UID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if e != nil {
|
if e != nil {
|
||||||
@ -142,7 +141,7 @@ func (u *User) Read(ctx context.Context) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u.Name = readUser.Username
|
u.Name = readUser.Username
|
||||||
u.UID,_ = strconv.Atoi(readUser.Uid)
|
u.UID, _ = strconv.Atoi(readUser.Uid)
|
||||||
if readGroup, groupErr := user.LookupGroupId(readUser.Gid); groupErr == nil {
|
if readGroup, groupErr := user.LookupGroupId(readUser.Gid); groupErr == nil {
|
||||||
u.Group = readGroup.Name
|
u.Group = readGroup.Name
|
||||||
} else {
|
} else {
|
||||||
@ -153,4 +152,3 @@ func (u *User) Read(ctx context.Context) ([]byte, error) {
|
|||||||
|
|
||||||
return yaml.Marshal(u)
|
return yaml.Marshal(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,17 +2,17 @@
|
|||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "fmt"
|
_ "context"
|
||||||
_ "context"
|
_ "encoding/json"
|
||||||
"testing"
|
_ "fmt"
|
||||||
_ "net/http"
|
|
||||||
_ "net/http/httptest"
|
|
||||||
_ "net/url"
|
|
||||||
_ "io"
|
|
||||||
_ "os"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
_ "encoding/json"
|
_ "io"
|
||||||
_ "strings"
|
_ "net/http"
|
||||||
|
_ "net/http/httptest"
|
||||||
|
_ "net/url"
|
||||||
|
_ "os"
|
||||||
|
_ "strings"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewUserResource(t *testing.T) {
|
func TestNewUserResource(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user