add file size and checksum
This commit is contained in:
parent
2ce6c44407
commit
f6f4258609
10
Makefile
10
Makefile
@ -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 ./...
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
`
|
`
|
||||||
|
Loading…
Reference in New Issue
Block a user