osbuild: add shell.init stage

Add support for the org.osbuild.shell.init stage and test validator.
This commit is contained in:
Achilleas Koutsou 2023-02-14 17:00:29 +01:00 committed by Tomáš Hozza
parent 636f3ff237
commit 2fcf3582b5
2 changed files with 241 additions and 0 deletions

View file

@ -0,0 +1,57 @@
package osbuild
import (
"fmt"
"regexp"
)
const filenameRegex = "^[a-zA-Z0-9\\.\\-_]{1,250}$"
const envVarRegex = "^[A-Z][A-Z0-9_]*$"
type ShellInitStageOptions struct {
Files map[string]ShellInitFile `json:"files"`
}
func (ShellInitStageOptions) isStageOptions() {}
func (options ShellInitStageOptions) validate() error {
fre := regexp.MustCompile(filenameRegex)
vre := regexp.MustCompile(envVarRegex)
for fname, kvs := range options.Files {
if !fre.MatchString(fname) {
return fmt.Errorf("filename %q doesn't conform to schema (%s)", fname, filenameRegex)
}
if len(kvs.Env) == 0 {
return fmt.Errorf("at least one environment variable must be specified for each file")
}
for _, kv := range kvs.Env {
if !vre.MatchString(kv.Key) {
return fmt.Errorf("variable name %q doesn't conform to schema (%s)", kv.Key, envVarRegex)
}
}
}
return nil
}
type ShellInitFile struct {
Env []EnvironmentVariable `json:"env"`
}
type EnvironmentVariable struct {
Key string `json:"key"`
Value string `json:"value"`
}
func NewShellInitStage(options *ShellInitStageOptions) *Stage {
if err := options.validate(); err != nil {
panic(err)
}
return &Stage{
Type: "org.osbuild.shell.init",
Options: options,
}
}

View file

@ -0,0 +1,184 @@
package osbuild
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestValidShellInitStageOptions(t *testing.T) {
tests := []ShellInitStageOptions{
{
Files: map[string]ShellInitFile{
"filename": {
Env: []EnvironmentVariable{
{
Key: "KEY",
Value: "value",
},
{
Key: "KEY2",
Value: "value2",
},
{
Key: "EMPTY",
Value: "",
},
},
},
"filename2": {
Env: []EnvironmentVariable{
{
Key: "KEY21",
Value: "value21",
},
{
Key: "KEY22",
Value: "value22",
},
{
Key: "EMPTY",
Value: "",
},
},
},
},
},
{
Files: map[string]ShellInitFile{
"gawk.sh": {
Env: []EnvironmentVariable{
{
Key: "AWKPATH",
Value: "$AWKPATH:$*",
},
{
Key: "AWKLIBPATH",
Value: "$AWKLIBPATH:$*",
},
},
},
"flatpak.sh": {
Env: []EnvironmentVariable{
{
Key: "XDG_DATA_DIRS",
Value: "${new_dirs:+${new_dirs}:}${XDG_DATA_DIRS:-/usr/local/share:/usr/share}",
},
},
},
},
},
}
assert := assert.New(t)
for idx := range tests {
tt := tests[idx]
name := fmt.Sprintf("ValidShellInitStage-%d", idx)
t.Run(name, func(t *testing.T) {
assert.NoErrorf(tt.validate(), "%q returned an error [idx: %d]", name, idx)
assert.NotPanics(func() { NewShellInitStage(&tt) })
})
}
}
func TestInvalidShellInitStageOptions(t *testing.T) {
tests := []ShellInitStageOptions{
{
Files: map[string]ShellInitFile{
"path/filename": {
Env: []EnvironmentVariable{
{
Key: "DOESNT",
Value: "matter",
},
},
},
"ok": {
Env: []EnvironmentVariable{
{
Key: "EMPTYOK",
Value: "",
},
},
},
},
},
{
Files: map[string]ShellInitFile{
"gawk.sh": {
Env: []EnvironmentVariable{
{
Key: "",
Value: "badkey",
},
},
},
},
},
{
Files: map[string]ShellInitFile{
"$FILENAME": {
Env: []EnvironmentVariable{
{
Key: "BAD",
Value: "filename",
},
},
},
},
},
{
Files: map[string]ShellInitFile{
"FILENAME": {
Env: []EnvironmentVariable{
{
Key: "bad.var",
Value: "okval",
},
},
},
},
},
{
Files: map[string]ShellInitFile{
"FILENAME": {
Env: []EnvironmentVariable{
{
Key: "BAD.VAR",
Value: "okval",
},
},
},
},
},
{
Files: map[string]ShellInitFile{
"me.sh": {
Env: []EnvironmentVariable{
{
Key: "-SH",
Value: "",
},
},
},
},
},
{
Files: map[string]ShellInitFile{
"empty.sh": {},
},
},
}
assert := assert.New(t)
for idx := range tests {
tt := tests[idx]
name := fmt.Sprintf("InvalidShellInitStage-%d", idx)
t.Run(name, func(t *testing.T) {
assert.Errorf(tt.validate(), "%q didn't return an error [idx: %d]", name, idx)
assert.Panics(func() { NewShellInitStage(&tt) })
})
}
}