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: parse generate property in sdf #143

Merged
merged 21 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,16 @@ have additional information for identifying the kind of content to expect:
which are only available for certain architectures. Example:
`/usr/bin/hello: {arch: amd64}` will instruct Chisel to extract and install
the "/usr/bin/hello" file only when chiselling an amd64 filesystem.
- **generate**: accepts a `manifest` value to instruct Chisel to generate the
manifest files in the directory. Example: `/var/lib/chisel/**:{generate:
manifest}`. NOTE: the provided path has to be of the form
`/slashed/path/to/dir/**` and no wildcards can appear apart from the trailing
`**`.

## TODO

- [ ] Preserve ownerships when possible
- [ ] GPG signature checking for archives
- [x] GPG signature checking for archives
- [ ] Use a fake server for the archive tests
- [ ] Functional tests

Expand Down
104 changes: 87 additions & 17 deletions internal/setup/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ const (
GlobPath PathKind = "glob"
TextPath PathKind = "text"
SymlinkPath PathKind = "symlink"
// GeneratePath is a special kind of glob path with the following format:
// /slashed/path/to/dir/**
// Wildcard characters can only appear at the end as **, and the path before
// those wildcards must be a directory.
GeneratePath PathKind = "generate"
letFunny marked this conversation as resolved.
Show resolved Hide resolved

// TODO Maybe in the future, for binary support.
//Base64Path PathKind = "base64"
Expand All @@ -77,14 +82,29 @@ const (
UntilMutate PathUntil = "mutate"
)

type GenerateKind string

const (
// There are only two possible values of "generate" as of now:
// "" (empty)
// "manifest"
// Empty value signifies that the path is not a GeneratePath.
letFunny marked this conversation as resolved.
Show resolved Hide resolved
GenerateNone GenerateKind = ""
GenerateManifest GenerateKind = "manifest"
)

type PathInfo struct {
// Disable ==.
_ [0]func()
letFunny marked this conversation as resolved.
Show resolved Hide resolved

Kind PathKind
Info string
Mode uint

Mutable bool
Until PathUntil
Arch []string
Mutable bool
Until PathUntil
Arch []string
Generate GenerateKind
}

// SameContent returns whether the path has the same content properties as some
Expand All @@ -95,7 +115,8 @@ func (pi *PathInfo) SameContent(other *PathInfo) bool {
return (pi.Kind == other.Kind &&
pi.Info == other.Info &&
pi.Mode == other.Mode &&
pi.Mutable == other.Mutable)
pi.Mutable == other.Mutable &&
pi.Generate == other.Generate)
}

type SliceKey struct {
Expand Down Expand Up @@ -142,6 +163,7 @@ func ReadRelease(dir string) (*Release, error) {
func (r *Release) validate() error {
keys := []SliceKey(nil)
paths := make(map[string]*Slice)
// globs contains all glob paths including GeneratePath(s).
letFunny marked this conversation as resolved.
Show resolved Hide resolved
globs := make(map[string]*Slice)

// Check for info conflicts and prepare for following checks.
Expand All @@ -151,14 +173,16 @@ func (r *Release) validate() error {
for newPath, newInfo := range new.Contents {
if old, ok := paths[newPath]; ok {
oldInfo := old.Contents[newPath]
// Note that if extracting content (CopyPath or GlobPath)
// from the same package the content can never be in conflict.
letFunny marked this conversation as resolved.
Show resolved Hide resolved
if !newInfo.SameContent(&oldInfo) || (newInfo.Kind == CopyPath || newInfo.Kind == GlobPath) && new.Package != old.Package {
if old.Package > new.Package || old.Package == new.Package && old.Name > new.Name {
old, new = new, old
}
return fmt.Errorf("slices %s and %s conflict on %s", old, new, newPath)
}
} else {
if newInfo.Kind == GlobPath {
if newInfo.Kind == GlobPath || newInfo.Kind == GeneratePath {
letFunny marked this conversation as resolved.
Show resolved Hide resolved
globs[newPath] = new
}
paths[newPath] = new
Expand All @@ -176,7 +200,13 @@ func (r *Release) validate() error {
// Check for glob conflicts.
for newPath, new := range globs {
for oldPath, old := range paths {
if new.Package == old.Package {
// Same entry, no conflict.
if new == old && newPath == oldPath {
continue
}
// Content extracted (not generated) from the same package can never
// be in conflict.
if new.Package == old.Package && new.Contents[newPath].Kind == GlobPath && old.Contents[oldPath].Kind != GeneratePath {
letFunny marked this conversation as resolved.
Show resolved Hide resolved
continue
}
if strdist.GlobPath(newPath, oldPath) {
Expand Down Expand Up @@ -357,8 +387,9 @@ type yamlPath struct {
Symlink string `yaml:"symlink"`
Mutable bool `yaml:"mutable"`

Until PathUntil `yaml:"until"`
Arch yamlArch `yaml:"arch"`
Until PathUntil `yaml:"until"`
Arch yamlArch `yaml:"arch"`
Generate GenerateKind `yaml:"generate"`
}

// SameContent returns whether the path has the same content properties as some
Expand Down Expand Up @@ -583,7 +614,19 @@ func parsePackage(baseDir, pkgName, pkgPath string, data []byte) (*Package, erro
var mutable bool
var until PathUntil
var arch []string
if strings.ContainsAny(contPath, "*?") {
var generate GenerateKind
if yamlPath != nil && yamlPath.Generate != "" {
zeroPathGenerate := zeroPath
zeroPathGenerate.Generate = yamlPath.Generate
if !yamlPath.SameContent(&zeroPathGenerate) || yamlPath.Until != UntilNone {
return nil, fmt.Errorf("slice %s_%s path %s has invalid generate options",
pkgName, sliceName, contPath)
}
if _, err := GetGeneratePath(contPath); err != nil {
return nil, fmt.Errorf("slice %s_%s has %s", pkgName, sliceName, err)
letFunny marked this conversation as resolved.
Show resolved Hide resolved
}
kinds = append(kinds, GeneratePath)
} else if strings.ContainsAny(contPath, "*?") {
if yamlPath != nil {
if !yamlPath.SameContent(&zeroPath) {
return nil, fmt.Errorf("slice %s_%s path %s has invalid wildcard options",
Expand All @@ -595,6 +638,7 @@ func parsePackage(baseDir, pkgName, pkgPath string, data []byte) (*Package, erro
if yamlPath != nil {
mode = yamlPath.Mode
mutable = yamlPath.Mutable
generate = yamlPath.Generate
if yamlPath.Dir {
if !strings.HasSuffix(contPath, "/") {
return nil, fmt.Errorf("slice %s_%s path %s must end in / for 'make' to be valid",
Expand Down Expand Up @@ -644,12 +688,13 @@ func parsePackage(baseDir, pkgName, pkgPath string, data []byte) (*Package, erro
return nil, fmt.Errorf("slice %s_%s mutable is not a regular file: %s", pkgName, sliceName, contPath)
}
slice.Contents[contPath] = PathInfo{
Kind: kinds[0],
Info: info,
Mode: mode,
Mutable: mutable,
Until: until,
Arch: arch,
Kind: kinds[0],
Info: info,
Mode: mode,
Mutable: mutable,
Until: until,
Arch: arch,
Generate: generate,
}
}

Expand All @@ -659,6 +704,23 @@ func parsePackage(baseDir, pkgName, pkgPath string, data []byte) (*Package, erro
return &pkg, err
}

func GetGeneratePath(path string) (dir string, err error) {
letFunny marked this conversation as resolved.
Show resolved Hide resolved
defer func() {
if err != nil {
err = fmt.Errorf("invalid generate path: %s", err)
}
}()

if !strings.HasSuffix(path, "/**") {
return "", fmt.Errorf("%s does not end with /**", path)
}
dirPath := strings.TrimSuffix(path, "**")
if strings.ContainsAny(dirPath, "*?") {
return "", fmt.Errorf("%s contains wildcard characters in addition to trailing **", path)
}
return dirPath, nil
}

func stripBase(baseDir, path string) string {
// Paths must be clean for this to work correctly.
return strings.TrimPrefix(path, baseDir+string(filepath.Separator))
Expand Down Expand Up @@ -691,9 +753,17 @@ func Select(release *Release, slices []SliceKey) (*Selection, error) {
}
return nil, fmt.Errorf("slices %s and %s conflict on %s", old, new, newPath)
}
continue
} else {
paths[newPath] = new
}
// An invalid "generate" value should only throw an error if that
// particular slice is selected. Hence, the check is here.
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems okay for now, but it's a bit unclear what the final place should be, due to the potential automatic manifest inclusion which could make this be better placed elsewhere.

switch newInfo.Generate {
case GenerateNone, GenerateManifest:
default:
return nil, fmt.Errorf("slice %s has invalid 'generate' for path %s: %q, consider an update if available",
new, newPath, newInfo.Generate)
}
paths[newPath] = new
}
}

Expand Down
Loading
Loading