-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Chris Koch <[email protected]>
- Loading branch information
Showing
4 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// Copyright 2024 the u-root Authors. All rights reserved | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Package templates defines a uimage template configuration file parser. | ||
package templates | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/u-root/gobusybox/src/pkg/golang" | ||
"github.com/u-root/mkuimage/uimage" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
// ErrTemplateNotExist is returned when the given config name did not exist. | ||
var ErrTemplateNotExist = errors.New("config template does not exist") | ||
|
||
func findConfigFile(name string) (string, error) { | ||
dir, err := os.Getwd() | ||
if err != nil { | ||
return "", err | ||
} | ||
for dir != "/" { | ||
p := filepath.Join(dir, name) | ||
if _, err := os.Stat(p); err == nil { | ||
return p, nil | ||
} | ||
dir = filepath.Dir(dir) | ||
} | ||
return "", fmt.Errorf("%w: could not find %s in current directory or any parent", os.ErrNotExist, name) | ||
} | ||
|
||
// Command represents commands to build. | ||
type Command struct { | ||
// Builder is bb, gbb, or binary. | ||
// | ||
// Defaults to bb if not given. | ||
Builder string | ||
|
||
// Commands are commands or template names. | ||
Commands []string | ||
} | ||
|
||
// Config is a mkuimage build configuration. | ||
type Config struct { | ||
GOOS string | ||
GOARCH string | ||
BuildTags []string `yaml:"build_tags"` | ||
Commands []Command | ||
Files []string | ||
Init string | ||
Uinit string | ||
Shell string | ||
} | ||
|
||
// Templates are a set of mkuimage build configs and command templates. | ||
type Templates struct { | ||
Configs map[string]Config | ||
|
||
// Commands defines a set of command template name -> commands to expand. | ||
Commands map[string][]string | ||
} | ||
|
||
// Uimage returns the uimage modifiers for the given templated config name. | ||
func (t *Templates) Uimage(config string) ([]uimage.Modifier, error) { | ||
c, ok := t.Configs[config] | ||
if !ok { | ||
return nil, fmt.Errorf("%w: %q", ErrTemplateNotExist, config) | ||
} | ||
m := []uimage.Modifier{ | ||
uimage.WithFiles(c.Files...), | ||
uimage.WithInit(c.Init), | ||
uimage.WithUinitCommand(c.Uinit), | ||
uimage.WithShell(c.Shell), | ||
uimage.WithEnv( | ||
golang.WithGOOS(c.GOOS), | ||
golang.WithGOARCH(c.GOARCH), | ||
golang.WithBuildTag(c.BuildTags...), | ||
), | ||
} | ||
for _, cmds := range c.Commands { | ||
switch cmds.Builder { | ||
case "binary": | ||
m = append(m, uimage.WithBinaryCommands(t.CommandsFor(cmds.Commands...)...)) | ||
case "bb", "gbb": | ||
fallthrough | ||
default: | ||
m = append(m, uimage.WithBusyboxCommands(t.CommandsFor(cmds.Commands...)...)) | ||
} | ||
} | ||
return m, nil | ||
} | ||
|
||
// CommandsFor expands commands according to command templates. | ||
func (t *Templates) CommandsFor(names ...string) []string { | ||
var c []string | ||
for _, name := range names { | ||
cmds, ok := t.Commands[name] | ||
if ok { | ||
c = append(c, cmds...) | ||
} else { | ||
c = append(c, name) | ||
} | ||
} | ||
return c | ||
} | ||
|
||
// TemplateFrom parses a template from bytes. | ||
func TemplateFrom(b []byte) (*Templates, error) { | ||
var tpl Templates | ||
if err := yaml.Unmarshal(b, &tpl); err != nil { | ||
return nil, err | ||
} | ||
return &tpl, nil | ||
} | ||
|
||
// Template parses the first file named .mkuimage.yaml in the current directory or any of its parents. | ||
func Template() (*Templates, error) { | ||
p, err := findConfigFile(".mkuimage.yaml") | ||
if err != nil { | ||
return nil, fmt.Errorf("%w: no templates found", os.ErrNotExist) | ||
} | ||
return TemplateFromFile(p) | ||
} | ||
|
||
// TemplateFromFile parses a template from the given file. | ||
func TemplateFromFile(p string) (*Templates, error) { | ||
b, err := os.ReadFile(p) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return TemplateFrom(b) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// Copyright 2024 the u-root Authors. All rights reserved | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package templates | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
"path/filepath" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/u-root/gobusybox/src/pkg/golang" | ||
"github.com/u-root/mkuimage/uimage" | ||
"github.com/u-root/mkuimage/uimage/builder" | ||
) | ||
|
||
func TestMods(t *testing.T) { | ||
for _, tt := range []struct { | ||
name string | ||
tpl string | ||
config string | ||
want *uimage.Opts | ||
err error | ||
}{ | ||
{ | ||
name: "ok", | ||
tpl: ` | ||
commands: | ||
core: | ||
- github.com/u-root/u-root/cmds/core/ip | ||
- github.com/u-root/u-root/cmds/core/init | ||
- github.com/u-root/u-root/cmds/core/gosh | ||
minimal: | ||
- github.com/u-root/u-root/cmds/core/ls | ||
- github.com/u-root/u-root/cmds/core/init | ||
configs: | ||
plan9: | ||
goarch: amd64 | ||
goos: plan9 | ||
build_tags: [grpcnotrace] | ||
files: | ||
- /bin/bash | ||
init: init | ||
uinit: gosh script.sh | ||
shell: gosh | ||
commands: | ||
- builder: bb | ||
commands: [core, minimal] | ||
- builder: bb | ||
commands: [./u-bmc/cmd/foo] | ||
- builder: binary | ||
commands: [./u-bmc/cmd/bar] | ||
- builder: binary | ||
commands: [cmd/test2json] | ||
`, | ||
config: "plan9", | ||
want: &uimage.Opts{ | ||
Env: golang.Default(golang.WithGOARCH("amd64"), golang.WithGOOS("plan9"), golang.WithBuildTag("grpcnotrace")), | ||
ExtraFiles: []string{"/bin/bash"}, | ||
InitCmd: "init", | ||
UinitCmd: "gosh", | ||
UinitArgs: []string{"script.sh"}, | ||
DefaultShell: "gosh", | ||
Commands: []uimage.Commands{ | ||
{ | ||
Builder: builder.Busybox, | ||
Packages: []string{ | ||
"github.com/u-root/u-root/cmds/core/ip", | ||
"github.com/u-root/u-root/cmds/core/init", | ||
"github.com/u-root/u-root/cmds/core/gosh", | ||
"github.com/u-root/u-root/cmds/core/ls", | ||
"github.com/u-root/u-root/cmds/core/init", | ||
"./u-bmc/cmd/foo", | ||
}, | ||
}, | ||
{ | ||
Builder: builder.Binary, | ||
Packages: []string{"./u-bmc/cmd/bar"}, | ||
}, | ||
{ | ||
Builder: builder.Binary, | ||
Packages: []string{"cmd/test2json"}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "missing_config", | ||
tpl: ` | ||
configs: | ||
plan9: | ||
goarch: amd64 | ||
goos: plan9 | ||
`, | ||
config: "plan10", | ||
err: ErrTemplateNotExist, | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
tpl, err := TemplateFrom([]byte(tt.tpl)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
mods, err := tpl.Uimage(tt.config) | ||
if !errors.Is(err, tt.err) { | ||
t.Fatalf("UimageMods = %v, want %v", err, tt.err) | ||
} | ||
if len(mods) == 0 { | ||
return | ||
} | ||
got, err := uimage.OptionsFor(mods...) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Logf("got: %#v", got) | ||
t.Logf("want: %#v", tt.want) | ||
t.Errorf("not equal") | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestTemplateErr(t *testing.T) { | ||
if _, err := TemplateFrom([]byte("\t")); err == nil { | ||
t.Fatal("Expected error") | ||
} | ||
|
||
d := t.TempDir() | ||
wd, _ := os.Getwd() | ||
_ = os.Chdir(d) | ||
defer func() { _ = os.Chdir(wd) }() | ||
|
||
if _, err := Template(); !errors.Is(err, os.ErrNotExist) { | ||
t.Fatalf("Template = %v, want ErrNotExist", err) | ||
} | ||
if _, err := TemplateFromFile(filepath.Join(d, "foobar")); !os.IsNotExist(err) { | ||
t.Fatalf("Template = %v, want not exist", err) | ||
} | ||
} |