add support for file resource access/mod times
All checks were successful
Declarative Tests / test (push) Successful in 53s
All checks were successful
Declarative Tests / test (push) Successful in 53s
This commit is contained in:
parent
0f82fde55d
commit
ce95306eb1
@ -125,24 +125,6 @@ func (c *Container) LoadDecl(yamlFileResourceDeclaration string) error {
|
|||||||
return c.loader(yamlFileResourceDeclaration, c)
|
return c.loader(yamlFileResourceDeclaration, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
apiClient, err := client.NewClientWithOpts(client.FromEnv)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer apiClient.Close()
|
|
||||||
|
|
||||||
containers, err := apiClient.ContainerList(context.Background(), container.ListOptions{All: true})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ctr := range containers {
|
|
||||||
fmt.Printf("%s %s (status: %s)\n", ctr.ID, ctr.Image, ctr.Status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
// 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 (
|
||||||
|
@ -6,7 +6,7 @@ package resource
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "log"
|
"log/slog"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,11 +73,12 @@ func (d *Declaration) Resource() Resource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Declaration) SetURI(uri string) error {
|
func (d *Declaration) SetURI(uri string) error {
|
||||||
|
slog.Info("SetURI()", "uri", uri)
|
||||||
d.Implementation = NewResource(uri)
|
d.Implementation = NewResource(uri)
|
||||||
if d.Implementation == nil {
|
if d.Implementation == nil {
|
||||||
panic("unknown resource")
|
panic("unknown resource")
|
||||||
}
|
}
|
||||||
d.Type = d.Implementation.Type()
|
d.Type = d.Implementation.Type()
|
||||||
d.Implementation.Read(context.Background()) // fix
|
d.Implementation.Read(context.Background()) // fix context
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"time"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewDocumentLoader(t *testing.T) {
|
func TestNewDocumentLoader(t *testing.T) {
|
||||||
@ -71,6 +73,15 @@ func TestDocumentGenerator(t *testing.T) {
|
|||||||
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)
|
||||||
|
assert.Nil(t, statErr)
|
||||||
|
mTime := info.ModTime()
|
||||||
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
aTime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec))
|
||||||
|
cTime := time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec))
|
||||||
|
|
||||||
expected := fmt.Sprintf(`
|
expected := fmt.Sprintf(`
|
||||||
resources:
|
resources:
|
||||||
- type: file
|
- type: file
|
||||||
@ -81,9 +92,12 @@ resources:
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
content: |
|
content: |
|
||||||
%s
|
%s
|
||||||
|
atime: %s
|
||||||
|
ctime: %s
|
||||||
|
mtime: %s
|
||||||
filetype: "regular"
|
filetype: "regular"
|
||||||
state: present
|
state: present
|
||||||
`, file, fileContent)
|
`, file, fileContent, aTime.Format(time.RFC3339Nano), cTime.Format(time.RFC3339Nano), mTime.Format(time.RFC3339Nano))
|
||||||
|
|
||||||
var documentYaml strings.Builder
|
var documentYaml strings.Builder
|
||||||
d := NewDocument()
|
d := NewDocument()
|
||||||
@ -101,7 +115,7 @@ resources:
|
|||||||
assert.Equal(t, nil, ey)
|
assert.Equal(t, nil, ey)
|
||||||
|
|
||||||
assert.Greater(t, documentYaml.Len(), 0)
|
assert.Greater(t, documentYaml.Len(), 0)
|
||||||
assert.YAMLEq(t, documentYaml.String(), expected)
|
assert.YAMLEq(t, expected, documentYaml.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDocumentAddResource(t *testing.T) {
|
func TestDocumentAddResource(t *testing.T) {
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileType string
|
type FileType string
|
||||||
@ -46,6 +47,11 @@ type File struct {
|
|||||||
Owner string `yaml:"owner"`
|
Owner string `yaml:"owner"`
|
||||||
Group string `yaml:"group"`
|
Group string `yaml:"group"`
|
||||||
Mode string `yaml:"mode"`
|
Mode string `yaml:"mode"`
|
||||||
|
|
||||||
|
Atime time.Time `yaml:"atime",omitempty`
|
||||||
|
Ctime time.Time `yaml:"ctime",omitempty`
|
||||||
|
Mtime time.Time `yaml:"mtime",omitempty`
|
||||||
|
|
||||||
Content string `yaml:"content",omitempty`
|
Content string `yaml:"content",omitempty`
|
||||||
FileType FileType `yaml:"filetype"`
|
FileType FileType `yaml:"filetype"`
|
||||||
State string `yaml:"state"`
|
State string `yaml:"state"`
|
||||||
@ -114,6 +120,11 @@ func (f *File) Apply() error {
|
|||||||
if writeErr != nil {
|
if writeErr != nil {
|
||||||
return writeErr
|
return writeErr
|
||||||
}
|
}
|
||||||
|
if ! f.Mtime.IsZero() && ! f.Atime.IsZero() {
|
||||||
|
if chtimesErr := os.Chtimes(f.Path, f.Atime, f.Mtime); chtimesErr != nil {
|
||||||
|
return chtimesErr
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if chownErr := os.Chown(f.Path, uid, gid); chownErr != nil {
|
if chownErr := os.Chown(f.Path, uid, gid); chownErr != nil {
|
||||||
@ -152,7 +163,12 @@ func (f *File) Read(ctx context.Context) ([]byte, error) {
|
|||||||
panic(e)
|
panic(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Mtime = info.ModTime()
|
||||||
|
|
||||||
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
||||||
|
f.Atime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec))
|
||||||
|
f.Ctime = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec))
|
||||||
|
|
||||||
userId := strconv.Itoa(int(stat.Uid))
|
userId := strconv.Itoa(int(stat.Uid))
|
||||||
groupId := strconv.Itoa(int(stat.Gid))
|
groupId := strconv.Itoa(int(stat.Gid))
|
||||||
fileUser, userErr := user.LookupId(userId)
|
fileUser, userErr := user.LookupId(userId)
|
||||||
|
@ -16,6 +16,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
_ "strings"
|
_ "strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewFileResource(t *testing.T) {
|
func TestNewFileResource(t *testing.T) {
|
||||||
@ -35,17 +37,22 @@ func TestReadFile(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
file, _ := filepath.Abs(filepath.Join(TempDir, "fooread.txt"))
|
file, _ := filepath.Abs(filepath.Join(TempDir, "fooread.txt"))
|
||||||
|
|
||||||
decl := fmt.Sprintf(`
|
declarationAttributes := `
|
||||||
path: "%s"
|
path: "%s"
|
||||||
owner: "nobody"
|
owner: "nobody"
|
||||||
group: "nobody"
|
group: "nobody"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
|
atime: 2001-12-15T01:01:01.000000001Z
|
||||||
|
ctime: %s
|
||||||
|
mtime: 2001-12-15T01:01:01.000000001Z
|
||||||
content: |-
|
content: |-
|
||||||
test line 1
|
test line 1
|
||||||
test line 2
|
test line 2
|
||||||
filetype: "regular"
|
filetype: "regular"
|
||||||
state: present
|
state: present
|
||||||
`, file)
|
`
|
||||||
|
|
||||||
|
decl := fmt.Sprintf(declarationAttributes, file, "2001-12-15T01:01:01.000000001Z")
|
||||||
|
|
||||||
testFile := NewFile()
|
testFile := NewFile()
|
||||||
e := testFile.LoadDecl(decl)
|
e := testFile.LoadDecl(decl)
|
||||||
@ -59,7 +66,15 @@ func TestReadFile(t *testing.T) {
|
|||||||
r, e := f.Read(ctx)
|
r, e := f.Read(ctx)
|
||||||
assert.Equal(t, nil, e)
|
assert.Equal(t, nil, e)
|
||||||
assert.Equal(t, "nobody", f.Owner)
|
assert.Equal(t, "nobody", f.Owner)
|
||||||
assert.YAMLEq(t, decl, string(r))
|
|
||||||
|
info,statErr := os.Stat(file)
|
||||||
|
assert.Nil(t, statErr)
|
||||||
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||||
|
assert.True(t, ok)
|
||||||
|
cTime := time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec))
|
||||||
|
|
||||||
|
expected := fmt.Sprintf(declarationAttributes, file, cTime.Format(time.RFC3339Nano))
|
||||||
|
assert.YAMLEq(t, expected, string(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateFile(t *testing.T) {
|
func TestCreateFile(t *testing.T) {
|
||||||
@ -130,3 +145,25 @@ func TestFileDirectory(t *testing.T) {
|
|||||||
assert.Nil(t, deleteErr)
|
assert.Nil(t, deleteErr)
|
||||||
assert.NoDirExists(t, file)
|
assert.NoDirExists(t, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFileTimes(t *testing.T) {
|
||||||
|
file, _ := filepath.Abs(filepath.Join(TempDir, "testtimes.txt"))
|
||||||
|
decl := fmt.Sprintf(`
|
||||||
|
path: "%s"
|
||||||
|
owner: "nobody"
|
||||||
|
group: "nobody"
|
||||||
|
mtime: 2001-12-15T01:01:01.1Z
|
||||||
|
mode: "0600"
|
||||||
|
filtetype: "regular"
|
||||||
|
state: "present"
|
||||||
|
`, file)
|
||||||
|
|
||||||
|
expectedTime, timeErr := time.Parse(time.RFC3339, "2001-12-15T01:01:01.1Z")
|
||||||
|
assert.Nil(t, timeErr)
|
||||||
|
|
||||||
|
f := NewFile()
|
||||||
|
e := f.LoadDecl(decl)
|
||||||
|
assert.Nil(t, e)
|
||||||
|
assert.Equal(t, "nobody", f.Owner)
|
||||||
|
assert.True(t, f.Mtime.Equal(expectedTime))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user