Add content keyword to bundle definition file

The content keyword in the bundle definition file specifies the path to
a chroot whose file contents will be added to the bundle's manifest.

Ex: content(/path)

Signed-off-by: John Akre <john.w.akre@intel.com>
This commit is contained in:
John Akre
2020-01-31 13:54:12 -08:00
committed by Reagan Lopez
parent 0fdf014167
commit fb47eee2fb
3 changed files with 132 additions and 13 deletions
+4 -2
View File
@@ -748,8 +748,10 @@ const bundleTemplateFormat = `# [TITLE]: %s
# [CAPABILITIES]:
# [MAINTAINER]:
#
# List bundles one per line. Includes have format: include(bundle)
# also-adds have format: also-add(bundle)
# List packages one per line.
# includes have format: include(bundle)
# also-adds have format: also-add(bundle)
# content chroots have format: content(path)
`
func createBundleFile(bundle string, path string) error {
+20 -1
View File
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
@@ -31,7 +32,8 @@ type bundle struct {
Files map[string]bool
/* hidden property, not to be included in file usr/share/clear/allbundles */
AllRpms map[string]packageMetadata `json:"-"`
AllRpms map[string]packageMetadata `json:"-"`
ContentChroots map[string]bool `json:"-"`
}
type bundleSet map[string]*bundle
@@ -308,6 +310,8 @@ func parseBundle(contents []byte) (*bundle, error) {
var duplicate bool
var includes, packages, optional []string
b.ContentChroots = make(map[string]bool)
line := 0
for scanner.Scan() {
line++
@@ -355,6 +359,21 @@ func parseBundle(contents []byte) (*bundle, error) {
return nil, fmt.Errorf("Invalid bundle name %q in line %d", text, line)
}
optional = append(optional, text)
} else if strings.HasPrefix(text, "content(") {
if !strings.HasSuffix(text, ")") {
return nil, fmt.Errorf("Missing end parenthesis in line %d: %q", line, text)
}
text = text[8 : len(text)-1]
// Remove unnecessary trailing slashes
for len(text) > 1 && strings.HasSuffix(text, "/") {
text = strings.TrimSuffix(text, "/")
}
if _, err := os.Stat(text); err != nil {
return nil, fmt.Errorf("Invalid content path %q in line %d", text, line)
}
b.ContentChroots[text] = true
} else {
if !validPackageNameRegex.MatchString(text) {
return nil, fmt.Errorf("Invalid package name %q in line %d", text, line)
+108 -10
View File
@@ -13,16 +13,29 @@ import (
)
func TestParseBundle(t *testing.T) {
dir1, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create valid testdir")
}
defer func() { _ = os.RemoveAll(dir1) }()
dir2, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create valid testdir")
}
defer func() { _ = os.RemoveAll(dir2) }()
tests := []struct {
Contents []byte
ExpectedHeader swupd.BundleHeader
ExpectedIncludes []string
ExpectedOptional []string
ExpectedPackages map[string]bool
ExpectedChroots map[string]bool
ShouldFail bool
}{
{
Contents: []byte(`# Simple fake bundle
Contents: []byte(fmt.Sprintf(`# Simple fake bundle
# [TITLE]: fake
# [DESCRIPTION]: a description
# [STATUS]: a status
@@ -32,9 +45,11 @@ include(a)
include(b)
also-add(c)
also-add(d)
content(%s)
content(%s)
pkg1 # Comment
pkg2
`),
`, dir1, dir2)),
ExpectedHeader: swupd.BundleHeader{
Title: "fake",
Description: "a description",
@@ -45,9 +60,10 @@ pkg2
ExpectedIncludes: []string{"a", "b"},
ExpectedOptional: []string{"c", "d"},
ExpectedPackages: map[string]bool{"pkg1": true, "pkg2": true},
ExpectedChroots: map[string]bool{dir1: true, dir2: true},
},
{
Contents: []byte(`# Bundle with empty header values
Contents: []byte(fmt.Sprintf(`# Bundle with empty header values
# [TITLE]: fake
# [DESCRIPTION]: a description
# [STATUS]:
@@ -55,8 +71,9 @@ pkg2
# [MAINTAINER]:
include(a)
also-add(b)
content(%s)
pkg1
`),
`, dir1)),
ExpectedHeader: swupd.BundleHeader{
Title: "fake",
Description: "a description",
@@ -64,6 +81,7 @@ pkg1
ExpectedIncludes: []string{"a"},
ExpectedOptional: []string{"b"},
ExpectedPackages: map[string]bool{"pkg1": true},
ExpectedChroots: map[string]bool{dir1: true},
},
{
Contents: []byte(`# Bundle with tricky comments
@@ -81,6 +99,28 @@ pkg1 # [TITLE]: wrongtitle
},
ExpectedIncludes: []string{"a"},
ExpectedPackages: map[string]bool{"pkg1": true},
ExpectedChroots: map[string]bool{},
},
{
Contents: []byte(fmt.Sprintf(`# Duplicate content chroots and trailing /s
# [TITLE]: fake
# [DESCRIPTION]: a description
# [STATUS]: a status
# [CAPABILITIES]: the capabilities
# [MAINTAINER]: the maintainer
content(%s/)
content(%s)
content(%s///)
`, dir1, dir1, dir2)),
ExpectedHeader: swupd.BundleHeader{
Title: "fake",
Description: "a description",
Status: "a status",
Capabilities: "the capabilities",
Maintainer: "the maintainer",
},
ExpectedPackages: map[string]bool{},
ExpectedChroots: map[string]bool{dir1: true, dir2: true},
},
// Error cases.
@@ -93,6 +133,11 @@ pkg1 # [TITLE]: wrongtitle
{Contents: []byte(`Also-add(`), ShouldFail: true},
{Contents: []byte(`also-add())`), ShouldFail: true},
{Contents: []byte(`also-add(abc))`), ShouldFail: true},
{Contents: []byte(`content(`), ShouldFail: true},
{Contents: []byte(`Content(`), ShouldFail: true},
{Contents: []byte(`content())`), ShouldFail: true},
{Contents: []byte(fmt.Sprintf(`content(%s))`, dir1)), ShouldFail: true},
{Contents: []byte(fmt.Sprintf(`content(%s/invalidPath)`, dir1)), ShouldFail: true},
}
for _, tt := range tests {
@@ -125,31 +170,51 @@ pkg1 # [TITLE]: wrongtitle
if !reflect.DeepEqual(b.DirectPackages, tt.ExpectedPackages) {
t.Errorf("got wrong packages when parsing bundle\nCONTENTS:\n%s\nPARSED PACKAGES (%d):\n%v\nEXPECTED PACKAGES (%d):\n%v", tt.Contents, len(b.DirectPackages), b.DirectPackages, len(tt.ExpectedPackages), tt.ExpectedPackages)
}
if !reflect.DeepEqual(b.ContentChroots, tt.ExpectedChroots) {
t.Errorf("got wrong content chroot when parsing bundle\nCONTENTS:\n%s\nPARSED CHROOTS (%d):\n%v\nEXPECTED CHROOTS (%d):\n%v", tt.Contents, len(b.ContentChroots), b.ContentChroots, len(tt.ExpectedChroots), tt.ExpectedChroots)
}
}
}
func TestParseBundleFile(t *testing.T) {
dir1, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create valid testdir")
}
defer func() { _ = os.RemoveAll(dir1) }()
dir2, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create valid testdir")
}
defer func() { _ = os.RemoveAll(dir2) }()
tests := []struct {
Filename string
Contents []byte
ExpectedIncludes []string
ExpectedOptional []string
ExpectedPackages map[string]bool
ExpectedChroots map[string]bool
ShouldFail bool
}{
{
Filename: "simple-bundle",
Contents: []byte(`# Simple fake bundle
Contents: []byte(fmt.Sprintf(`# Simple fake bundle
include(a)
include(b)
also-add(c)
also-add(d)
content(%s)
content(%s)
pkg1 # Comment
pkg2
`),
`, dir1, dir2)),
ExpectedIncludes: []string{"a", "b"},
ExpectedOptional: []string{"c", "d"},
ExpectedPackages: map[string]bool{"pkg1": true, "pkg2": true},
ExpectedChroots: map[string]bool{dir1: true, dir2: true},
},
// Bundle contents error (catching parseBundle's error)
@@ -196,17 +261,33 @@ pkg2
if !reflect.DeepEqual(bundle.DirectPackages, tt.ExpectedPackages) {
t.Errorf("got wrong packages when parsing bundle\nCONTENTS:\n%s\nPARSED PACKAGES (%d):\n%v\nEXPECTED PACKAGES (%d):\n%v", tt.Contents, len(bundle.DirectPackages), bundle.DirectPackages, len(tt.ExpectedPackages), tt.ExpectedPackages)
}
if !reflect.DeepEqual(bundle.ContentChroots, tt.ExpectedChroots) {
t.Errorf("got wrong content chroot when parsing bundle\nCONTENTS:\n%s\nPARSED CHROOTS (%d):\n%v\nEXPECTED CHROOTS (%d):\n%v", tt.Contents, len(bundle.ContentChroots), bundle.ContentChroots, len(tt.ExpectedChroots), tt.ExpectedChroots)
}
}
}
func TestValidateBundle(t *testing.T) {
dir1, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create valid testdir")
}
defer func() { _ = os.RemoveAll(dir1) }()
dir2, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create valid testdir")
}
defer func() { _ = os.RemoveAll(dir2) }()
tests := []struct {
Contents []byte
ExpectedErrors []string
ShouldFail bool
}{
{
Contents: []byte(`# Simple fake bundle
Contents: []byte(fmt.Sprintf(`# Simple fake bundle
# [TITLE]: fake
# [DESCRIPTION]: a description
# [STATUS]: a status
@@ -216,9 +297,11 @@ include(a)
include(b)
also-add(c)
also-add(d)
content(%s)
content(%s)
pkg1 # Comment
pkg2
`),
`, dir1, dir2)),
},
// Bundle header errors
@@ -265,6 +348,18 @@ pkg2
}
func TestValidateBundleFile(t *testing.T) {
dir1, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create valid testdir")
}
defer func() { _ = os.RemoveAll(dir1) }()
dir2, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create valid testdir")
}
defer func() { _ = os.RemoveAll(dir2) }()
tests := []struct {
Filename string
Contents []byte
@@ -274,7 +369,7 @@ func TestValidateBundleFile(t *testing.T) {
}{
{
Filename: "simple-bundle",
Contents: []byte(`# Simple fake bundle
Contents: []byte(fmt.Sprintf(`# Simple fake bundle
# [TITLE]: simple-bundle
# [DESCRIPTION]: a description
# [STATUS]: a status
@@ -284,9 +379,11 @@ include(a)
include(b)
also-add(c)
also-add(d)
content(%s)
content(%s)
pkg1 # Comment
pkg2
`),
`, dir1, dir2)),
Level: StrictValidation,
},
// Bundle filename header Title missmatch with basic validatoin
@@ -303,6 +400,7 @@ pkg2
// Bundle contents error (catching errors passed up from parseBundle)
{Filename: "b", Contents: []byte(`include(`), Level: BasicValidation, ExpectedErrors: []string{"Missing end parenthesis in line"}, ShouldFail: true},
{Filename: "c", Contents: []byte(`also-add(a`), Level: BasicValidation, ExpectedErrors: []string{"Missing end parenthesis in line"}, ShouldFail: true},
{Filename: "d", Contents: []byte(`content(a`), Level: BasicValidation, ExpectedErrors: []string{"Missing end parenthesis in line"}, ShouldFail: true},
}
testDir, err := ioutil.TempDir("", "bundleset-test-")