add file size and checksum
Some checks failed
Lint / golangci-lint (push) Failing after 9m57s
Declarative Tests / test (push) Failing after 12s

This commit is contained in:
Matthew Rich 2024-04-23 15:35:08 -07:00
parent 2ce6c44407
commit f6f4258609
8 changed files with 177 additions and 6 deletions

View File

@ -1,10 +1,12 @@
LDFLAGS?=--ldflags '-extldflags "-static"' LDFLAGS?=--ldflags '-extldflags "-static"' --ldflags="-X 'main.commit=$(shell git rev-parse HEAD)' -X 'main.version=$(shell git describe --tags)' -X 'main.date=$(shell date '+%Y-%m-%d %T.%s%z')'"
export CGO_ENABLED=0 export CGO_ENABLED=0
build: jx .PHONY=jx-cli
jx: build: jx-cli
jx-cli:
go build -o jx $(LDFLAGS) ./cmd/cli/main.go go build -o jx $(LDFLAGS) ./cmd/cli/main.go
test: test: jx-cli
go test ./... go test ./...

View File

@ -14,6 +14,7 @@ _ "errors"
_ "gopkg.in/yaml.v3" _ "gopkg.in/yaml.v3"
"decl/internal/resource" "decl/internal/resource"
"decl/internal/source" "decl/internal/source"
"decl/internal/target"
) )
const ( const (
@ -21,7 +22,14 @@ const (
FormatJson = "json" FormatJson = "json"
) )
var (
version string
commit string
date string
)
var GlobalOformat *string var GlobalOformat *string
var GlobalOutput *string
var GlobalQuiet *bool var GlobalQuiet *bool
var ImportMerge *bool var ImportMerge *bool
@ -52,6 +60,13 @@ var jxSubCommands = []SubCommand {
}, },
} }
func VersionUsage() {
fmt.Println("jx")
fmt.Printf("version: %s\n", version)
fmt.Printf("commit: %s\n", commit)
fmt.Printf("date: %s\n", date)
}
func LoggerConfig() { func LoggerConfig() {
var programLevel = new(slog.LevelVar) var programLevel = new(slog.LevelVar)
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})) logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel}))
@ -97,7 +112,7 @@ func ImportSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
documents = append(documents, loaded...) documents = append(documents, loaded...)
} }
} }
switch *GlobalOformat { switch *GlobalOformat {
case FormatYaml: case FormatYaml:
encoder = resource.NewYAMLEncoder(output) encoder = resource.NewYAMLEncoder(output)
@ -105,6 +120,14 @@ func ImportSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
encoder = resource.NewJSONEncoder(output) encoder = resource.NewJSONEncoder(output)
} }
if *GlobalOutput != "" {
_, err := target.TargetTypes.New(*GlobalOutput)
if err != nil {
log.Fatal(err)
}
//outputTarget.EmitResources(
}
if len(documents) == 0 { if len(documents) == 0 {
documents = append(documents, resource.NewDocument()) documents = append(documents, resource.NewDocument())
} }
@ -253,6 +276,8 @@ func main() {
for _,subCmd := range jxSubCommands { for _,subCmd := range jxSubCommands {
cmdFlagSet := flag.NewFlagSet(subCmd.Name, flag.ExitOnError) cmdFlagSet := flag.NewFlagSet(subCmd.Name, flag.ExitOnError)
GlobalOformat = cmdFlagSet.String("oformat", "yaml", "Output serialization format") GlobalOformat = cmdFlagSet.String("oformat", "yaml", "Output serialization format")
GlobalOutput = cmdFlagSet.String("output", "-", "Output target (default stdout)")
GlobalOutput = cmdFlagSet.String("o", "-", "Output target (default stdout)")
GlobalQuiet = cmdFlagSet.Bool("quiet", false, "Generate terse output.") GlobalQuiet = cmdFlagSet.Bool("quiet", false, "Generate terse output.")
switch subCmd.Name { switch subCmd.Name {
@ -260,16 +285,19 @@ func main() {
cmdFlagSet.Usage = func() { cmdFlagSet.Usage = func() {
fmt.Println("jx diff source [source2]") fmt.Println("jx diff source [source2]")
cmdFlagSet.PrintDefaults() cmdFlagSet.PrintDefaults()
VersionUsage()
} }
case "apply": case "apply":
cmdFlagSet.Usage = func() { cmdFlagSet.Usage = func() {
fmt.Println("jx diff source [source2]") fmt.Println("jx diff source [source2]")
cmdFlagSet.PrintDefaults() cmdFlagSet.PrintDefaults()
VersionUsage()
} }
case "import": case "import":
cmdFlagSet.Usage = func() { cmdFlagSet.Usage = func() {
fmt.Println("jx import source [source2]") fmt.Println("jx import source [source2]")
cmdFlagSet.PrintDefaults() cmdFlagSet.PrintDefaults()
VersionUsage()
} }
} }
slog.Info("command", "command", subCmd) slog.Info("command", "command", subCmd)
@ -278,6 +306,10 @@ func main() {
log.Fatal(e) log.Fatal(e)
} }
return return
} else {
flag.PrintDefaults()
VersionUsage()
os.Exit(1)
} }
} }

View File

@ -21,6 +21,17 @@ func NewDocument() *Document {
return &Document{} return &Document{}
} }
func (d *Document) Filter(filter ResourceSelector) []*Declaration {
resources := make([]*Declaration, 0, len(d.ResourceDecls))
for i := range d.ResourceDecls {
filterResource := &d.ResourceDecls[i]
if filter(filterResource) {
resources = append(resources, &d.ResourceDecls[i])
}
}
return resources
}
func (d *Document) Clone() *Document { func (d *Document) Clone() *Document {
clone := NewDocument() clone := NewDocument()
clone.ResourceDecls = make([]Declaration, len(d.ResourceDecls)) clone.ResourceDecls = make([]Declaration, len(d.ResourceDecls))

View File

@ -96,6 +96,7 @@ resources:
ctime: %s ctime: %s
mtime: %s mtime: %s
sha256: ea33e2082ca777f82dc9571b08df95d81925eed04e1bdbac7cdc6dc52d330eca sha256: ea33e2082ca777f82dc9571b08df95d81925eed04e1bdbac7cdc6dc52d330eca
size: 82
filetype: "regular" filetype: "regular"
state: present state: present
`, file, fileContent, aTime.Format(time.RFC3339Nano), cTime.Format(time.RFC3339Nano), mTime.Format(time.RFC3339Nano)) `, file, fileContent, aTime.Format(time.RFC3339Nano), cTime.Format(time.RFC3339Nano), mTime.Format(time.RFC3339Nano))
@ -185,3 +186,39 @@ resources:
assert.YAMLEq(t, string(document), string(marshalledYAML)) assert.YAMLEq(t, string(document), string(marshalledYAML))
} }
func TestDocumentResourceFilter(t *testing.T) {
document := `
---
resources:
- type: user
attributes:
name: "testuser"
uid: 10022
home: "/home/testuser"
state: present
- type: file
attributes:
path: "foo.txt"
state: present
- type: file
attributes:
path: "bar.txt"
state: present
`
d := NewDocument()
assert.NotNil(t, d)
docReader := strings.NewReader(document)
e := d.Load(docReader)
assert.Nil(t, e)
resources := d.Filter(func(d *Declaration) bool {
if d.Type == "file" {
return true
}
return false
})
assert.Equal(t, 2, len(resources))
}

View File

@ -9,6 +9,7 @@ import (
"log/slog" "log/slog"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"io" "io"
"io/fs"
"net/url" "net/url"
"os" "os"
"os/user" "os/user"
@ -55,15 +56,20 @@ type File struct {
Content string `json:"content,omitempty" yaml:"content,omitempty"` Content string `json:"content,omitempty" yaml:"content,omitempty"`
Sha256 string `json:"sha256,omitempty" yaml:"sha256,omitempty"` Sha256 string `json:"sha256,omitempty" yaml:"sha256,omitempty"`
Size int64 `json:"size,omitempty" yaml:"size,omitempty"`
Target string `json:"target,omitempty" yaml:"target,omitempty"` Target string `json:"target,omitempty" yaml:"target,omitempty"`
FileType FileType `json:"filetype" yaml:"filetype"` FileType FileType `json:"filetype" yaml:"filetype"`
State string `json:"state" yaml:"state"` State string `json:"state" yaml:"state"`
} }
type ResourceFileInfo struct {
resource *File
}
func NewFile() *File { func NewFile() *File {
currentUser, _ := user.Current() currentUser, _ := user.Current()
group, _ := user.LookupGroupId(currentUser.Gid) group, _ := user.LookupGroupId(currentUser.Gid)
f := &File{Owner: currentUser.Username, Group: group.Name, Mode: "0666", FileType: RegularFile} f := &File{Owner: currentUser.Username, Group: group.Name, Mode: "0644", FileType: RegularFile}
slog.Info("NewFile()", "file", f) slog.Info("NewFile()", "file", f)
return f return f
} }
@ -78,6 +84,8 @@ func (f *File) Clone() Resource {
Ctime: f.Ctime, Ctime: f.Ctime,
Mtime: f.Mtime, Mtime: f.Mtime,
Content: f.Content, Content: f.Content,
Sha256: f.Sha256,
Size: f.Size,
Target: f.Target, Target: f.Target,
FileType: f.FileType, FileType: f.FileType,
State: f.State, State: f.State,
@ -127,10 +135,12 @@ func (f *File) Apply() error {
return gidErr return gidErr
} }
slog.Info("File.Mode", "mode", f.Mode)
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
} }
slog.Info("File.Mode Parse", "mode", mode, "err", modeErr)
//e := os.Stat(f.path) //e := os.Stat(f.path)
//if os.IsNotExist(e) { //if os.IsNotExist(e) {
@ -199,6 +209,43 @@ func (f *File) NormalizePath() error {
return fileAbsErr return fileAbsErr
} }
func (f *File) FileInfo() fs.FileInfo {
return &ResourceFileInfo{ resource: f }
}
func (f *ResourceFileInfo) Name() string {
return filepath.Base(f.resource.Path)
}
func (f *ResourceFileInfo) Size() int64 {
return f.resource.Size
}
func (f *ResourceFileInfo) Mode() (mode os.FileMode) {
if fileMode, fileModeErr := strconv.ParseInt(f.resource.Mode, 8, 64); fileModeErr == nil {
mode |= os.FileMode(fileMode)
} else {
panic(fileModeErr)
}
mode |= f.resource.FileType.GetMode()
return
}
func (f *ResourceFileInfo) ModTime() time.Time {
return f.resource.Mtime
}
func (f *ResourceFileInfo) IsDir() bool {
if f.resource.FileType == DirectoryFile {
return true
}
return false
}
func (f *ResourceFileInfo) Sys() any {
return nil
}
func (f *File) UpdateAttributesFromFileInfo(info os.FileInfo) error { func (f *File) UpdateAttributesFromFileInfo(info os.FileInfo) error {
if info != nil { if info != nil {
f.Mtime = info.ModTime() f.Mtime = info.ModTime()
@ -223,6 +270,7 @@ func (f *File) UpdateAttributesFromFileInfo(info os.FileInfo) error {
f.Group = fileGroup.Name f.Group = fileGroup.Name
} }
} }
f.Size = info.Size()
f.Mode = fmt.Sprintf("%04o", info.Mode().Perm()) f.Mode = fmt.Sprintf("%04o", info.Mode().Perm())
f.FileType.SetMode(info.Mode()) f.FileType.SetMode(info.Mode())
return nil return nil
@ -309,3 +357,22 @@ func (f *FileType) SetMode(mode os.FileMode) {
*f = BlockDeviceFile *f = BlockDeviceFile
} }
} }
func (f *FileType) GetMode() (mode os.FileMode) {
switch *f {
case RegularFile:
case DirectoryFile:
mode |= os.ModeDir
case SymbolicLinkFile:
mode |= os.ModeSymlink
case NamedPipeFile:
mode |= os.ModeNamedPipe
case SocketFile:
mode |= os.ModeSocket
case CharacterDeviceFile:
mode |= os.ModeCharDevice
case BlockDeviceFile:
mode |= os.ModeDevice
}
return
}

View File

@ -1,4 +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 (
@ -49,6 +50,7 @@ func TestReadFile(t *testing.T) {
test line 1 test line 1
test line 2 test line 2
sha256: f2082f984f1bf1a7886e2af32ccc9ca474fbff3553d131204b070c438114dd51 sha256: f2082f984f1bf1a7886e2af32ccc9ca474fbff3553d131204b070c438114dd51
size: 23
filetype: "regular" filetype: "regular"
state: present state: present
` `
@ -261,3 +263,20 @@ func TestFileReadStat(t *testing.T) {
assert.Nil(t, testReadErr) assert.Nil(t, testReadErr)
assert.Equal(t, linkTargetFile, testRead.Target) assert.Equal(t, linkTargetFile, testRead.Target)
} }
func TestFileResourceFileInfo(t *testing.T) {
testFile := filepath.Join(TempDir, "testuri.txt")
f := NewFile()
assert.NotNil(t, f)
f.Path = testFile
f.Mode = "0600"
f.State = "present"
assert.Nil(t, f.Apply())
f.Read(context.Background())
fi := f.FileInfo()
assert.Equal(t, os.FileMode(0600), fi.Mode().Perm())
}

View File

@ -11,6 +11,8 @@ import (
_ "net/url" _ "net/url"
) )
type ResourceSelector func(r *Declaration) bool
type Resource interface { type Resource interface {
Type() string Type() string
URI() string URI() string

View File

@ -45,6 +45,7 @@ func TestSchemaValidateJSON(t *testing.T) {
test line 1 test line 1
test line 2 test line 2
sha256: f2082f984f1bf1a7886e2af32ccc9ca474fbff3553d131204b070c438114dd51 sha256: f2082f984f1bf1a7886e2af32ccc9ca474fbff3553d131204b070c438114dd51
size: 23
filetype: "regular" filetype: "regular"
state: present state: present
` `