Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 3 additions & 0 deletions cmd/chisel/cmd_cut.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ func (cmd *cmdCut) Execute(args []string) error {
if err != nil {
return err
}
if sliceKey.IsPrivate() {
return fmt.Errorf("cannot cut private slice %s", sliceRef)
}
sliceKeys[i] = sliceKey
}

Expand Down
13 changes: 13 additions & 0 deletions cmd/chisel/cmd_cut_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main_test

import (
. "gopkg.in/check.v1"

chisel "github.com/canonical/chisel/cmd/chisel"
)

func (s *ChiselSuite) TestCutRejectsPrivateSlice(c *C) {
dir := c.MkDir()
_, err := chisel.Parser().ParseArgs([]string{"cut", "--root", dir, "mypkg__priv"})
c.Assert(err, ErrorMatches, `cannot cut private slice mypkg__priv`)
}
5 changes: 4 additions & 1 deletion cmd/chisel/cmd_find.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,17 @@ func match(slice *setup.Slice, query string) bool {
}

// findSlices returns slices from the provided release that match all of the
// query strings (AND).
// query strings (AND). Private slices are never returned.
func findSlices(release *setup.Release, query []string) (slices []*setup.Slice, err error) {
slices = []*setup.Slice{}
for _, pkg := range release.Packages {
for _, slice := range pkg.Slices {
if slice == nil {
continue
}
if (setup.SliceKey{Package: slice.Package, Slice: slice.Name}).IsPrivate() {
continue
}
allMatch := true
for _, term := range query {
if !match(slice, term) {
Expand Down
23 changes: 22 additions & 1 deletion cmd/chisel/cmd_find_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ var sampleRelease = &setup.Release{
},
Packages: map[string]*setup.Package{
"openjdk-8-jdk": makeSamplePackage("openjdk-8-jdk", []string{"bins", "config", "core", "libs", "utils"}),
"python3.10": makeSamplePackage("python3.10", []string{"bins", "config", "core", "libs", "utils"}),
"python3.10": makeSamplePackage("python3.10", []string{"bins", "config", "core", "libs", "utils", "_priv"}),
},
}

Expand Down Expand Up @@ -123,6 +123,27 @@ var findTests = []findTest{{
release: sampleRelease,
query: []string{"python", "slice"},
result: []*setup.Slice{},
}, {
summary: "Private slices are hidden when matching by package",
release: sampleRelease,
query: []string{"python3.10"},
result: []*setup.Slice{
sampleRelease.Packages["python3.10"].Slices["bins"],
sampleRelease.Packages["python3.10"].Slices["config"],
sampleRelease.Packages["python3.10"].Slices["core"],
sampleRelease.Packages["python3.10"].Slices["libs"],
sampleRelease.Packages["python3.10"].Slices["utils"],
},
}, {
summary: "Private slices are hidden when matching by full slice name",
release: sampleRelease,
query: []string{"python3.10__priv"},
result: []*setup.Slice{},
}, {
summary: "Private slices are hidden when matching by slice-only query",
release: sampleRelease,
query: []string{"__priv"},
result: []*setup.Slice{},
}}

func (s *ChiselSuite) TestFindSlices(c *C) {
Expand Down
40 changes: 40 additions & 0 deletions cmd/chisel/cmd_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,43 @@ func (s *ChiselSuite) TestInfoCommand(c *C) {
c.Assert(s.Stdout(), Equals, strings.TrimSpace(test.stdout)+"\n")
}
}

func (s *ChiselSuite) TestInfoPrivateSlice(c *C) {
s.ResetStdStreams()

input := map[string]string{
"chisel.yaml": strings.ReplaceAll(testutil.DefaultChiselYaml, "format: v1", "format: v3"),
"slices/mypkg.yaml": `
package: mypkg
slices:
myslice:
essential:
mypkg__priv: {}
_priv:
contents:
/usr/bin/cc:
`,
}

dir := c.MkDir()
for path, data := range input {
fpath := filepath.Join(dir, path)
err := os.MkdirAll(filepath.Dir(fpath), 0755)
c.Assert(err, IsNil)
err = os.WriteFile(fpath, testutil.Reindent(data), 0644)
c.Assert(err, IsNil)
}

_, err := chisel.Parser().ParseArgs([]string{"info", "--release", dir, "mypkg__priv"})
c.Assert(err, IsNil)

expected := `
package: mypkg
slices:
_priv:
contents:
/usr/bin/cc: {}
`
expected = string(testutil.Reindent(expected))
c.Assert(s.Stdout(), Equals, strings.TrimSpace(expected)+"\n")
}
11 changes: 9 additions & 2 deletions internal/apacheutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package apacheutil
import (
"fmt"
"regexp"
"strings"
)

type SliceKey struct {
Expand All @@ -14,14 +15,20 @@ type SliceKey struct {

func (s SliceKey) String() string { return s.Package + "_" + s.Slice }

// IsPrivate reports whether the slice name marks the slice as private.
// Private slices begin with a single underscore and may only be used as
// essentials of other slices; they cannot be selected directly.
func (s SliceKey) IsPrivate() bool { return strings.HasPrefix(s.Slice, "_") }

// FnameExp matches the slice definition file basename.
var FnameExp = regexp.MustCompile(`^([a-z0-9](?:-?[.a-z0-9+]){1,})\.yaml$`)

// SnameExp matches only the slice name, without the leading package name.
var SnameExp = regexp.MustCompile(`^([a-z](?:-?[a-z0-9]){2,})$`)
// An optional single leading underscore marks the slice as private.
var SnameExp = regexp.MustCompile(`^(_?[a-z](?:-?[a-z0-9]){2,})$`)

// knameExp matches the slice full name in pkg_slice format.
var knameExp = regexp.MustCompile(`^([a-z0-9](?:-?[.a-z0-9+]){1,})_([a-z](?:-?[a-z0-9]){2,})$`)
var knameExp = regexp.MustCompile(`^([a-z0-9](?:-?[.a-z0-9+]){1,})_(_?[a-z](?:-?[a-z0-9]){2,})$`)

func ParseSliceKey(sliceKey string) (SliceKey, error) {
match := knameExp.FindStringSubmatch(sliceKey)
Expand Down
18 changes: 18 additions & 0 deletions internal/apacheutil/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ var sliceKeyTests = []struct {
}, {
input: "a._bar",
expected: apacheutil.SliceKey{Package: "a.", Slice: "bar"},
}, {
input: "foo__bar",
expected: apacheutil.SliceKey{Package: "foo", Slice: "_bar"},
}, {
input: "foo__abc",
expected: apacheutil.SliceKey{Package: "foo", Slice: "_abc"},
}, {
input: "foo___bar",
err: `invalid slice reference: "foo___bar"`,
}, {
input: "foo__ab",
err: `invalid slice reference: "foo__ab"`,
}, {
input: "foo_ba",
err: `invalid slice reference: "foo_ba"`,
Expand Down Expand Up @@ -94,3 +106,9 @@ func (s *S) TestParseSliceKey(c *C) {
c.Assert(key, DeepEquals, test.expected)
}
}

func (s *S) TestIsPrivate(c *C) {
c.Assert(apacheutil.SliceKey{Package: "foo", Slice: "_bar"}.IsPrivate(), Equals, true)
c.Assert(apacheutil.SliceKey{Package: "foo", Slice: "bar"}.IsPrivate(), Equals, false)
c.Assert(apacheutil.SliceKey{Package: "foo", Slice: ""}.IsPrivate(), Equals, false)
}
26 changes: 26 additions & 0 deletions internal/setup/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,26 @@ func (r *Release) validate() error {
}
}

// Check that every private slice is referenced as an essential by some
// other slice. Private slices cannot be selected directly, so an
// unreferenced one is dead code.
referenced := make(map[SliceKey]bool)
for _, pkg := range r.Packages {
for _, slice := range pkg.Slices {
for ref := range slice.Essential {
referenced[ref] = true
}
}
}
for _, pkg := range r.Packages {
for _, slice := range pkg.Slices {
key := SliceKey{Package: pkg.Name, Slice: slice.Name}
if key.IsPrivate() && !referenced[key] {
return fmt.Errorf("private slice %s is not referenced by any other slice", key)
}
}
}

// Check for cycles.
// Note: For release validation an essential with a specific arch is the
// same as an essential with all archs, i.e. Chisel does not use arch to
Expand Down Expand Up @@ -468,6 +488,12 @@ func stripBase(baseDir, path string) string {
func Select(release *Release, slices []SliceKey, arch string) (*Selection, error) {
logf("Selecting slices...")

for _, key := range slices {
if key.IsPrivate() {
return nil, fmt.Errorf("cannot select private slice %s", key)
}
}

var err error
if arch == "" {
arch, err = deb.InferArch()
Expand Down
Loading
Loading