From ce95306eb139c239067c5a300db675ceceb8d238 Mon Sep 17 00:00:00 2001 From: Matthew Rich Date: Sun, 24 Mar 2024 18:36:21 -0700 Subject: [PATCH] add support for file resource access/mod times --- internal/resource/container.go | 18 ------------ internal/resource/container_test.go | 2 ++ internal/resource/declaration.go | 5 ++-- internal/resource/document_test.go | 18 ++++++++++-- internal/resource/file.go | 16 +++++++++++ internal/resource/file_test.go | 43 +++++++++++++++++++++++++++-- 6 files changed, 77 insertions(+), 25 deletions(-) diff --git a/internal/resource/container.go b/internal/resource/container.go index c57355e..cb92585 100644 --- a/internal/resource/container.go +++ b/internal/resource/container.go @@ -125,24 +125,6 @@ func (c *Container) LoadDecl(yamlFileResourceDeclaration string) error { 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 { numberOfEnvironmentVariables := len(c.Environment) config := &container.Config { diff --git a/internal/resource/container_test.go b/internal/resource/container_test.go index e46db82..93504f9 100644 --- a/internal/resource/container_test.go +++ b/internal/resource/container_test.go @@ -1,4 +1,6 @@ // Copyright 2024 Matthew Rich . All rights reserved. + +// package resource import ( diff --git a/internal/resource/declaration.go b/internal/resource/declaration.go index cf3bd42..e7e3da9 100644 --- a/internal/resource/declaration.go +++ b/internal/resource/declaration.go @@ -6,7 +6,7 @@ package resource import ( "context" "fmt" -_ "log" + "log/slog" "gopkg.in/yaml.v3" ) @@ -73,11 +73,12 @@ func (d *Declaration) Resource() Resource { } func (d *Declaration) SetURI(uri string) error { + slog.Info("SetURI()", "uri", uri) d.Implementation = NewResource(uri) if d.Implementation == nil { panic("unknown resource") } d.Type = d.Implementation.Type() - d.Implementation.Read(context.Background()) // fix + d.Implementation.Read(context.Background()) // fix context return nil } diff --git a/internal/resource/document_test.go b/internal/resource/document_test.go index 52e2f9e..7ce256b 100644 --- a/internal/resource/document_test.go +++ b/internal/resource/document_test.go @@ -10,6 +10,8 @@ import ( "path/filepath" "testing" "github.com/stretchr/testify/assert" + "time" + "syscall" ) func TestNewDocumentLoader(t *testing.T) { @@ -71,6 +73,15 @@ func TestDocumentGenerator(t *testing.T) { err := os.WriteFile(file, []byte(fileContent), 0644) 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(` resources: - type: file @@ -81,9 +92,12 @@ resources: mode: "0644" content: | %s + atime: %s + ctime: %s + mtime: %s filetype: "regular" state: present -`, file, fileContent) +`, file, fileContent, aTime.Format(time.RFC3339Nano), cTime.Format(time.RFC3339Nano), mTime.Format(time.RFC3339Nano)) var documentYaml strings.Builder d := NewDocument() @@ -101,7 +115,7 @@ resources: assert.Equal(t, nil, ey) assert.Greater(t, documentYaml.Len(), 0) - assert.YAMLEq(t, documentYaml.String(), expected) + assert.YAMLEq(t, expected, documentYaml.String()) } func TestDocumentAddResource(t *testing.T) { diff --git a/internal/resource/file.go b/internal/resource/file.go index 66e0ea8..690538e 100644 --- a/internal/resource/file.go +++ b/internal/resource/file.go @@ -15,6 +15,7 @@ import ( "strconv" "path/filepath" "net/url" + "time" ) type FileType string @@ -46,6 +47,11 @@ type File struct { Owner string `yaml:"owner"` Group string `yaml:"group"` 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` FileType FileType `yaml:"filetype"` State string `yaml:"state"` @@ -114,6 +120,11 @@ func (f *File) Apply() error { if writeErr != nil { 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 { @@ -152,7 +163,12 @@ func (f *File) Read(ctx context.Context) ([]byte, error) { panic(e) } + f.Mtime = info.ModTime() + 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)) groupId := strconv.Itoa(int(stat.Gid)) fileUser, userErr := user.LookupId(userId) diff --git a/internal/resource/file_test.go b/internal/resource/file_test.go index 63ba8b0..1a73ddd 100644 --- a/internal/resource/file_test.go +++ b/internal/resource/file_test.go @@ -16,6 +16,8 @@ import ( "path/filepath" _ "strings" "testing" + "time" + "syscall" ) func TestNewFileResource(t *testing.T) { @@ -35,17 +37,22 @@ func TestReadFile(t *testing.T) { ctx := context.Background() file, _ := filepath.Abs(filepath.Join(TempDir, "fooread.txt")) - decl := fmt.Sprintf(` + declarationAttributes := ` path: "%s" owner: "nobody" group: "nobody" mode: "0600" + atime: 2001-12-15T01:01:01.000000001Z + ctime: %s + mtime: 2001-12-15T01:01:01.000000001Z content: |- test line 1 test line 2 filetype: "regular" state: present -`, file) +` + + decl := fmt.Sprintf(declarationAttributes, file, "2001-12-15T01:01:01.000000001Z") testFile := NewFile() e := testFile.LoadDecl(decl) @@ -59,7 +66,15 @@ func TestReadFile(t *testing.T) { r, e := f.Read(ctx) assert.Equal(t, nil, e) 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) { @@ -130,3 +145,25 @@ func TestFileDirectory(t *testing.T) { assert.Nil(t, deleteErr) 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)) +}