Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add file and directory report skeleton #116

Merged
merged 70 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
a09f06f
Add fileCreator proxy for use in chisel.db
letFunny Dec 1, 2023
ec3690f
Tests
letFunny Dec 1, 2023
a171780
Fix tests when running whole suite
letFunny Dec 1, 2023
2a1731b
Link
letFunny Dec 1, 2023
f6346be
Tests for report
letFunny Dec 4, 2023
0295aed
Final touches
letFunny Dec 4, 2023
c371d1a
Better test coverage
letFunny Dec 7, 2023
dfc7647
Address review comments
letFunny Dec 12, 2023
d39f337
sort imports
letFunny Jan 11, 2024
dc19319
remove proxy
letFunny Jan 15, 2024
bdf1f70
Merge branch 'main' into chisel-db-proxy
letFunny Jan 15, 2024
1fdad8a
fix tests
letFunny Jan 15, 2024
5a06152
increase test coverage
letFunny Jan 15, 2024
b0e0712
remove unnecessary tests
letFunny Jan 16, 2024
d47ec8d
sort imports
letFunny Jan 16, 2024
c1eb773
remove report to split PRs
letFunny Jan 16, 2024
1441994
simplify naming terminology
letFunny Jan 16, 2024
c8c2ba7
more naming simplification
letFunny Jan 16, 2024
3b5bb4a
more naming simplification
letFunny Jan 16, 2024
13d4b06
more naming simplification
letFunny Jan 16, 2024
631b2b5
Merge branch 'chisel-db-proxy' into chisel-db-report
letFunny Jan 16, 2024
3dd73c3
feat: add file and directory report skeleton
letFunny Jan 16, 2024
0449354
remove unnecessary mutex
letFunny Jan 17, 2024
0fccd1f
address review comments
letFunny Jan 29, 2024
6e62d64
rename fsCreator
letFunny Jan 29, 2024
0737e43
default for missing Creator in options
letFunny Jan 29, 2024
2aa844d
comments
letFunny Jan 29, 2024
5eb8f7e
conform to godoc format
letFunny Jan 29, 2024
fad7257
Merge branch 'chisel-db-proxy' into chisel-db-report
letFunny Jan 29, 2024
325531b
remove unused attribute for now
letFunny Jan 29, 2024
aa09fd0
simplify redundant checks
letFunny Jan 30, 2024
fd87b6d
change comment
letFunny Jan 30, 2024
8d53d66
typo
letFunny Jan 30, 2024
a21f777
clarified tests with comment
letFunny Feb 2, 2024
07091ac
remove stateful tests
letFunny Feb 2, 2024
c06869c
nitpick naming
letFunny Feb 2, 2024
ac5decc
remove redundant check
letFunny Feb 2, 2024
1314466
typo
letFunny Feb 2, 2024
ae7dac3
typo in comment
letFunny Feb 2, 2024
abdc587
typo in comment
letFunny Feb 2, 2024
9897388
sort slice for stable tests
letFunny Feb 2, 2024
803e9c5
naming and nitpicks
letFunny Feb 5, 2024
6d111ac
change test expected from slice to map
letFunny Feb 5, 2024
7660433
reword comment
letFunny Feb 5, 2024
9ee449f
remove shallow copy of Options
letFunny Feb 5, 2024
d2227b0
Merge branch 'chisel-db-proxy' into chisel-db-report
letFunny Feb 5, 2024
8fdbcd9
comments and naming changes
letFunny Feb 5, 2024
42d6c6e
move fsutil.Creator creation to the outer scope
letFunny Feb 5, 2024
38b899a
Merge branch 'main' into chisel-db-report
letFunny Feb 5, 2024
41c1c89
improve performance
letFunny Feb 5, 2024
30bb29e
remove Creator and use a closure
letFunny Feb 6, 2024
7d8fa69
add error check to report.Add
letFunny Feb 6, 2024
04c7117
final touches
letFunny Feb 7, 2024
22933d4
slicer returns Report to allow future testing
letFunny Feb 7, 2024
74feda8
remove experimental slices package
letFunny Feb 7, 2024
3aa311c
fix tests
letFunny Feb 7, 2024
55dd7c4
move comment
letFunny Feb 7, 2024
9f51f80
return ptr instead of value
letFunny Feb 8, 2024
002204c
add slicer tests and make path in report relative
letFunny Feb 9, 2024
1745ba9
renaming
letFunny Feb 12, 2024
1554233
better relative path management
letFunny Feb 19, 2024
b5bf664
better error reporting for report
letFunny Feb 26, 2024
a6179ee
naming and comments
letFunny Feb 26, 2024
7c013a3
fsutil.Info -> fsutil.Entry
letFunny Feb 27, 2024
cdb50f0
remove internal error prefix
letFunny Feb 27, 2024
4c2d67f
/root/ -> /base/
letFunny Feb 27, 2024
b2b7231
tests for Options.Create
letFunny Feb 27, 2024
dbe9e2e
info -> entry, variables and comments
letFunny Feb 27, 2024
f4557ec
explicit test for nil Create closure
letFunny Feb 28, 2024
5f16450
change created to notCreated for better test clarity
letFunny Mar 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/chisel/cmd_cut.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,12 @@ func (cmd *cmdCut) Execute(args []string) error {
archives[archiveName] = openArchive
}

return slicer.Run(&slicer.RunOptions{
_, err = slicer.Run(&slicer.RunOptions{
Selection: selection,
Archives: archives,
TargetDir: cmd.RootDir,
})
return err
}

// TODO These need testing, and maybe moving into a common file.
Expand Down
2 changes: 0 additions & 2 deletions internal/archive/archive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"github.com/canonical/chisel/internal/archive"
"github.com/canonical/chisel/internal/archive/testarchive"
"github.com/canonical/chisel/internal/deb"
"github.com/canonical/chisel/internal/fsutil"
"github.com/canonical/chisel/internal/testutil"
)

Expand Down Expand Up @@ -501,7 +500,6 @@ func (s *S) testOpenArchiveArch(c *C, release ubuntuRelease, arch string) {
{Path: "/hostname"},
},
},
Creator: fsutil.NewCreator(),
})
c.Assert(err, IsNil)

Expand Down
28 changes: 19 additions & 9 deletions internal/deb/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type ExtractOptions struct {
TargetDir string
Extract map[string][]ExtractInfo
Globbed map[string][]string
Creator *fsutil.Creator
Create func(options *fsutil.CreateOptions) error
}

type ExtractInfo struct {
Expand All @@ -34,16 +34,26 @@ type ExtractInfo struct {
Optional bool
}

func checkExtractOptions(options *ExtractOptions) error {
func validateExtractOptions(options *ExtractOptions) (*ExtractOptions, error) {
letFunny marked this conversation as resolved.
Show resolved Hide resolved
for extractPath, extractInfos := range options.Extract {
isGlob := strings.ContainsAny(extractPath, "*?")
if isGlob {
if len(extractInfos) != 1 || extractInfos[0].Path != extractPath || extractInfos[0].Mode != 0 {
return fmt.Errorf("when using wildcards source and target paths must match: %s", extractPath)
return nil, fmt.Errorf("when using wildcards source and target paths must match: %s", extractPath)
}
}
}
return nil

if options.Create == nil {
validOpts := *options
validOpts.Create = func(o *fsutil.CreateOptions) error {
_, err := fsutil.Create(o)
return err
}
return &validOpts, nil
}

return options, nil
}

func Extract(pkgReader io.Reader, options *ExtractOptions) (err error) {
Expand All @@ -55,12 +65,12 @@ func Extract(pkgReader io.Reader, options *ExtractOptions) (err error) {

logf("Extracting files from package %q...", options.Package)

err = checkExtractOptions(options)
validOpts, err := validateExtractOptions(options)
if err != nil {
return err
}

_, err = os.Stat(options.TargetDir)
_, err = os.Stat(validOpts.TargetDir)
if os.IsNotExist(err) {
return fmt.Errorf("target directory does not exist")
} else if err != nil {
Expand Down Expand Up @@ -100,7 +110,7 @@ func Extract(pkgReader io.Reader, options *ExtractOptions) (err error) {
dataReader = zstdReader
}
}
return extractData(dataReader, options)
return extractData(dataReader, validOpts)
}

func extractData(dataReader io.Reader, options *ExtractOptions) error {
Expand Down Expand Up @@ -186,7 +196,7 @@ func extractData(dataReader io.Reader, options *ExtractOptions) error {
// Base directory for extracted content. Relevant mainly to preserve
// the metadata, since the extracted content itself will also create
// any missing directories unaccounted for in the options.
err := options.Creator.Create(&fsutil.CreateOptions{
err := options.Create(&fsutil.CreateOptions{
Path: filepath.Join(options.TargetDir, sourcePath),
Mode: tarHeader.FileInfo().Mode(),
MakeParents: true,
Expand Down Expand Up @@ -227,7 +237,7 @@ func extractData(dataReader io.Reader, options *ExtractOptions) error {
if extractInfo.Mode != 0 {
tarHeader.Mode = int64(extractInfo.Mode)
}
err := options.Creator.Create(&fsutil.CreateOptions{
err := options.Create(&fsutil.CreateOptions{
Path: targetPath,
Mode: tarHeader.FileInfo().Mode(),
Data: pathReader,
Expand Down
2 changes: 0 additions & 2 deletions internal/deb/extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
. "gopkg.in/check.v1"

"github.com/canonical/chisel/internal/deb"
"github.com/canonical/chisel/internal/fsutil"
"github.com/canonical/chisel/internal/testutil"
)

Expand Down Expand Up @@ -291,7 +290,6 @@ func (s *S) TestExtract(c *C) {
options := test.options
options.Package = "base-files"
options.TargetDir = dir
options.Creator = fsutil.NewCreator()
letFunny marked this conversation as resolved.
Show resolved Hide resolved

if test.globbed != nil {
options.Globbed = make(map[string][]string)
Expand Down
24 changes: 7 additions & 17 deletions internal/fsutil/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,9 @@ type Info struct {
Link string
}

type Creator struct {
// Created keeps track of information about the filesystem entries created.
// If an entry is created several times it only tracks the latest one.
Created map[string]Info
}

func NewCreator() *Creator {
return &Creator{Created: make(map[string]Info)}
}

// Create creates a filesystem entry according to the provided options.
func (c *Creator) Create(options *CreateOptions) error {
// Create creates a filesystem entry according to the provided options and returns
// the information about the created entry.
func Create(options *CreateOptions) (*Info, error) {
rp := &readerProxy{inner: options.Data, h: sha256.New()}
// Use the proxy instead of the raw Reader.
optsCopy := *options
Expand All @@ -50,7 +41,7 @@ func (c *Creator) Create(options *CreateOptions) error {
var err error
if o.MakeParents {
if err := os.MkdirAll(filepath.Dir(o.Path), 0755); err != nil {
return err
return nil, err
}
}
switch o.Mode & fs.ModeType {
Expand All @@ -64,18 +55,17 @@ func (c *Creator) Create(options *CreateOptions) error {
err = fmt.Errorf("unsupported file type: %s", o.Path)
}
if err != nil {
return err
return nil, err
}

info := Info{
info := &Info{
Path: o.Path,
Mode: o.Mode,
Hash: hex.EncodeToString(rp.h.Sum(nil)),
Size: rp.size,
Link: o.Link,
}
c.Created[o.Path] = info
return nil
return info, nil
}

func createDir(o *CreateOptions) error {
Expand Down
67 changes: 30 additions & 37 deletions internal/fsutil/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,52 +83,45 @@ func (s *S) TestCreate(c *C) {
dir := c.MkDir()
options := test.options
options.Path = filepath.Join(dir, options.Path)
creator := fsutil.NewCreator()
err := creator.Create(&options)
info, err := fsutil.Create(&options)

if test.error != "" {
c.Assert(err, ErrorMatches, test.error)
} else {
c.Assert(err, IsNil)
continue
}

c.Assert(err, IsNil)
c.Assert(testutil.TreeDump(dir), DeepEquals, test.result)
if test.options.MakeParents {
// The creator does not record the parent directories created
// implicitly.
for path, info := range treeDumpFSCreator(creator, dir) {
c.Assert(info, Equals, test.result[path])
}
} else {
c.Assert(treeDumpFSCreator(creator, dir), DeepEquals, test.result)
}
// [fsutil.Create] does not return information about parent directories
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bracketed naming doesn't seem typical. I believe the typical convention in Go is to document types cleanly (foo.Bar), or has something changed in this regard recently?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that you can use brackets for documentation links here: https://tip.golang.org/doc/comment#doclinks

// created implicitly. We only check for the requested path.
c.Assert(dumpFSInfo(info, dir)[test.options.Path], DeepEquals, test.result[test.options.Path])
}
}

// treeDumpFSCreator dumps the contents stored in Creator about the filesystem
// entries created using the same format as [testutil.TreeDump].
func treeDumpFSCreator(cr *fsutil.Creator, root string) map[string]string {
// dumpFSInfo returns the file information in the same format as
// [testutil.TreeDump].
func dumpFSInfo(info *fsutil.Info, root string) map[string]string {
result := make(map[string]string)
for _, file := range cr.Created {
path := strings.TrimPrefix(file.Path, root)
fperm := file.Mode.Perm()
if file.Mode&fs.ModeSticky != 0 {
fperm |= 01000
}
switch file.Mode.Type() {
case fs.ModeDir:
result[path+"/"] = fmt.Sprintf("dir %#o", fperm)
case fs.ModeSymlink:
result[path] = fmt.Sprintf("symlink %s", file.Link)
case 0: // Regular
var entry string
if file.Size == 0 {
entry = fmt.Sprintf("file %#o empty", file.Mode.Perm())
} else {
entry = fmt.Sprintf("file %#o %s", fperm, file.Hash[:8])
}
result[path] = entry
default:
panic(fmt.Errorf("unknown file type %d: %s", file.Mode.Type(), path))
path := strings.TrimPrefix(info.Path, root)
fperm := info.Mode.Perm()
if info.Mode&fs.ModeSticky != 0 {
fperm |= 01000
}
switch info.Mode.Type() {
case fs.ModeDir:
result[path+"/"] = fmt.Sprintf("dir %#o", fperm)
case fs.ModeSymlink:
result[path] = fmt.Sprintf("symlink %s", info.Link)
case 0: // Regular
var entry string
if info.Size == 0 {
entry = fmt.Sprintf("file %#o empty", info.Mode.Perm())
} else {
entry = fmt.Sprintf("file %#o %s", fperm, info.Hash[:8])
}
result[path] = entry
default:
panic(fmt.Errorf("unknown file type %d: %s", info.Mode.Type(), path))
}
return result
}
2 changes: 1 addition & 1 deletion internal/setup/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func extractTar(dataReader io.Reader, targetDir string) error {

//debugf("Extracting header: %#v", tarHeader)

err = fsutil.NewCreator().Create(&fsutil.CreateOptions{
_, err = fsutil.Create(&fsutil.CreateOptions{
Path: filepath.Join(targetDir, sourcePath),
Mode: tarHeader.FileInfo().Mode(),
Data: tarReader,
Expand Down
64 changes: 64 additions & 0 deletions internal/slicer/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package slicer

import (
"fmt"
"io/fs"
"path/filepath"

"github.com/canonical/chisel/internal/fsutil"
"github.com/canonical/chisel/internal/setup"
)

type ReportEntry struct {
letFunny marked this conversation as resolved.
Show resolved Hide resolved
Path string
Mode fs.FileMode
Hash string
Size int
Slices map[*setup.Slice]bool
Link string
}

// Report holds the information about files and directories created when slicing
// packages.
type Report struct {
// Root is the filesystem path where the all reported content is based.
Root string
letFunny marked this conversation as resolved.
Show resolved Hide resolved
// Entries holds all reported content, indexed by their path.
Entries map[string]ReportEntry
letFunny marked this conversation as resolved.
Show resolved Hide resolved
}

// NewReport returns an empty report for content that will be based at the
// provided root path.
func NewReport(root string) *Report {
letFunny marked this conversation as resolved.
Show resolved Hide resolved
return &Report{
Root: root,
Entries: make(map[string]ReportEntry),
}
}

func (r *Report) Add(slice *setup.Slice, info *fsutil.Info) error {
relPath, err := filepath.Rel(r.Root, info.Path)
if err != nil {
return fmt.Errorf("internal error: cannot add path %q outside of root %q", info.Path, r.Root)
}
relPath = "/" + relPath

if entry, ok := r.Entries[relPath]; ok {
if info.Mode != entry.Mode || info.Link != entry.Link ||
info.Size != entry.Size || info.Hash != entry.Hash {
return fmt.Errorf("internal error: cannot add conflicting data for path %q", relPath)
letFunny marked this conversation as resolved.
Show resolved Hide resolved
}
entry.Slices[slice] = true
r.Entries[relPath] = entry
} else {
r.Entries[relPath] = ReportEntry{
Path: relPath,
Mode: info.Mode,
Hash: info.Hash,
Size: info.Size,
Slices: map[*setup.Slice]bool{slice: true},
Link: info.Link,
}
}
return nil
}
Loading
Loading