mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 17:26:28 +00:00
Merge pull request #3988 from deitch/yaml-template
template in yaml file
This commit is contained in:
commit
86d11e2879
43
docs/yaml.md
43
docs/yaml.md
@ -123,6 +123,9 @@ file:
|
|||||||
metadata: yaml
|
metadata: yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that if you use templates in the yaml, the final resolved version will be included in the image,
|
||||||
|
and not the original input template.
|
||||||
|
|
||||||
Because a `tmpfs` is mounted onto `/var`, `/run`, and `/tmp` by default, the `tmpfs` mounts will shadow anything specified in `files` section for those directories.
|
Because a `tmpfs` is mounted onto `/var`, `/run`, and `/tmp` by default, the `tmpfs` mounts will shadow anything specified in `files` section for those directories.
|
||||||
|
|
||||||
## Image specification
|
## Image specification
|
||||||
@ -293,3 +296,43 @@ binds:
|
|||||||
- /var:/var:rshared,rbind
|
- /var:/var:rshared,rbind
|
||||||
rootfsPropagation: shared
|
rootfsPropagation: shared
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Templates
|
||||||
|
|
||||||
|
The `yaml` file supports templates for the names of images. Anyplace an image is used in a file and begins
|
||||||
|
with the character `@`, it indicates that it is not an actual name, but a template. The first word after
|
||||||
|
the `@` indicates the type of template, and the rest of the line is the argument to the template. The
|
||||||
|
templates currently supported are:
|
||||||
|
|
||||||
|
* `@pkg:` - the argument is the path to a linuxkit package. For example, `@pkg:./pkg/init`.
|
||||||
|
|
||||||
|
For `pkg`, linuxkit will resolve the path to the package, and then run the equivalent of `linuxkit pkg show-tag <dir>`.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
init:
|
||||||
|
- "@pkg:../pkg/init"
|
||||||
|
```
|
||||||
|
|
||||||
|
Will cause linuxkit to resolve `../pkg/init` to a package, and then run `linuxkit pkg show-tag ../pkg/init`.
|
||||||
|
|
||||||
|
The paths are relative to the directory of the yaml file.
|
||||||
|
You can specify absolute paths, although it is not recommended, as that can make the yaml file less portable.
|
||||||
|
|
||||||
|
The `@pkg:` templating is supported **only** when the yaml file is being read from a local filesystem. It does not
|
||||||
|
support when using via stdin, e.g. `cat linuxkit.yml | linuxkit build -`, or URLs, e.g. `linuxkit build https://example.com/foo.yml`.
|
||||||
|
|
||||||
|
The `@pkg:` template currently supports only default `linuxkit pkg` options, i.e. `build.yml` and `tag` options. There
|
||||||
|
are no command-line options to override them.
|
||||||
|
|
||||||
|
**Note:** The character `@` is reserved in yaml. To use it in the beginning of a string, you must put the entire string in
|
||||||
|
quotes.
|
||||||
|
|
||||||
|
If you use the template, the actual derived value, and not the initial template, is what will be stored in the final
|
||||||
|
image when adding it via:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
files:
|
||||||
|
- path: etc/linuxkit.yml
|
||||||
|
metadata: yaml
|
||||||
|
```
|
||||||
|
38
linuxkit-template.yml
Normal file
38
linuxkit-template.yml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
kernel:
|
||||||
|
image: linuxkit/kernel:6.6.13
|
||||||
|
cmdline: "console=tty0 console=ttyS0 console=ttyAMA0"
|
||||||
|
init:
|
||||||
|
- "@pkg:./pkg/init"
|
||||||
|
- "@pkg:./pkg/runc"
|
||||||
|
- "@pkg:./pkg/containerd"
|
||||||
|
- "@pkg:./pkg/ca-certificates"
|
||||||
|
onboot:
|
||||||
|
- name: sysctl
|
||||||
|
image: "@pkg:./pkg/sysctl"
|
||||||
|
- name: dhcpcd
|
||||||
|
image: "@pkg:./pkg/dhcpcd"
|
||||||
|
command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
|
||||||
|
onshutdown:
|
||||||
|
- name: shutdown
|
||||||
|
image: busybox:latest
|
||||||
|
command: ["/bin/echo", "so long and thanks for all the fish"]
|
||||||
|
services:
|
||||||
|
- name: getty
|
||||||
|
image: "@pkg:./pkg/getty"
|
||||||
|
env:
|
||||||
|
- INSECURE=true
|
||||||
|
- name: rngd
|
||||||
|
image: "@pkg:./pkg/rngd"
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.19.5-alpine
|
||||||
|
capabilities:
|
||||||
|
- CAP_NET_BIND_SERVICE
|
||||||
|
- CAP_CHOWN
|
||||||
|
- CAP_SETUID
|
||||||
|
- CAP_SETGID
|
||||||
|
- CAP_DAC_OVERRIDE
|
||||||
|
binds:
|
||||||
|
- /etc/resolv.conf:/etc/resolv.conf
|
||||||
|
files:
|
||||||
|
- path: etc/linuxkit-config
|
||||||
|
metadata: yaml
|
@ -11,8 +11,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/moby"
|
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/moby"
|
||||||
|
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -54,6 +56,7 @@ func buildCmd() *cobra.Command {
|
|||||||
noSbom bool
|
noSbom bool
|
||||||
sbomOutputFilename string
|
sbomOutputFilename string
|
||||||
sbomCurrentTime bool
|
sbomCurrentTime bool
|
||||||
|
dryRun bool
|
||||||
)
|
)
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "build",
|
Use: "build",
|
||||||
@ -141,7 +144,10 @@ The generated image can be in one of multiple formats which can be run on variou
|
|||||||
log.Fatalf("Unable to parse disk size: %v", err)
|
log.Fatalf("Unable to parse disk size: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var m moby.Moby
|
var (
|
||||||
|
m moby.Moby
|
||||||
|
templatesSupported bool
|
||||||
|
)
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
var config []byte
|
var config []byte
|
||||||
if conf := arg; conf == "-" {
|
if conf := arg; conf == "-" {
|
||||||
@ -168,9 +174,14 @@ The generated image can be in one of multiple formats which can be run on variou
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot open config file: %v", err)
|
return fmt.Errorf("Cannot open config file: %v", err)
|
||||||
}
|
}
|
||||||
|
// templates are only supported for local files
|
||||||
|
templatesSupported = true
|
||||||
}
|
}
|
||||||
|
var pkgFinder spec.PackageResolver
|
||||||
c, err := moby.NewConfig(config)
|
if templatesSupported {
|
||||||
|
pkgFinder = createPackageResolver(filepath.Dir(arg))
|
||||||
|
}
|
||||||
|
c, err := moby.NewConfig(config, pkgFinder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Invalid config: %v", err)
|
return fmt.Errorf("Invalid config: %v", err)
|
||||||
}
|
}
|
||||||
@ -180,6 +191,15 @@ The generated image can be in one of multiple formats which can be run on variou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
yml, err := yaml.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error generating YAML: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(yml))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var tf *os.File
|
var tf *os.File
|
||||||
var w io.Writer
|
var w io.Writer
|
||||||
if outfile != nil {
|
if outfile != nil {
|
||||||
@ -240,6 +260,7 @@ The generated image can be in one of multiple formats which can be run on variou
|
|||||||
cmd.Flags().BoolVar(&noSbom, "no-sbom", false, "suppress consolidation of sboms on input container images to a single sbom and saving in the output filesystem")
|
cmd.Flags().BoolVar(&noSbom, "no-sbom", false, "suppress consolidation of sboms on input container images to a single sbom and saving in the output filesystem")
|
||||||
cmd.Flags().BoolVar(&sbomCurrentTime, "sbom-current-time", false, "whether to use the current time as the build time in the sbom; this will make the build non-reproducible (default false)")
|
cmd.Flags().BoolVar(&sbomCurrentTime, "sbom-current-time", false, "whether to use the current time as the build time in the sbom; this will make the build non-reproducible (default false)")
|
||||||
cmd.Flags().StringVar(&sbomOutputFilename, "sbom-output", defaultSbomFilename, "filename to save the output to in the root filesystem")
|
cmd.Flags().StringVar(&sbomOutputFilename, "sbom-output", defaultSbomFilename, "filename to save the output to in the root filesystem")
|
||||||
|
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Do not actually build, just print the final yml file that would be used, including all merges and templates")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
46
src/cmd/linuxkit/buildtemplate.go
Normal file
46
src/cmd/linuxkit/buildtemplate.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/pkglib"
|
||||||
|
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
templateFlag = "@"
|
||||||
|
templatePkg = "pkg:"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createPackageResolver(baseDir string) spec.PackageResolver {
|
||||||
|
return func(pkgTmpl string) (tag string, err error) {
|
||||||
|
var pkgValue string
|
||||||
|
switch {
|
||||||
|
case len(pkgTmpl) == 0, pkgTmpl[0:1] != templateFlag:
|
||||||
|
pkgValue = pkgTmpl
|
||||||
|
case strings.HasPrefix(pkgTmpl, templateFlag+templatePkg):
|
||||||
|
pkgPath := strings.TrimPrefix(pkgTmpl, templateFlag+templatePkg)
|
||||||
|
|
||||||
|
var pkgs []pkglib.Pkg
|
||||||
|
pkgConfig := pkglib.PkglibConfig{
|
||||||
|
BuildYML: defaultPkgBuildYML,
|
||||||
|
HashCommit: defaultPkgCommit,
|
||||||
|
Tag: defaultPkgTag,
|
||||||
|
}
|
||||||
|
pkgs, err = pkglib.NewFromConfig(pkgConfig, path.Join(baseDir, pkgPath))
|
||||||
|
if err != nil {
|
||||||
|
return tag, err
|
||||||
|
}
|
||||||
|
if len(pkgs) == 0 {
|
||||||
|
return tag, fmt.Errorf("no packages found")
|
||||||
|
}
|
||||||
|
if len(pkgs) > 1 {
|
||||||
|
return tag, fmt.Errorf("multiple packages found")
|
||||||
|
}
|
||||||
|
pkgValue = pkgs[0].FullTag()
|
||||||
|
}
|
||||||
|
return pkgValue, nil
|
||||||
|
}
|
||||||
|
}
|
7
src/cmd/linuxkit/const.go
Normal file
7
src/cmd/linuxkit/const.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultPkgBuildYML = "build.yml"
|
||||||
|
defaultPkgCommit = "HEAD"
|
||||||
|
defaultPkgTag = "{{.Hash}}"
|
||||||
|
)
|
@ -8,13 +8,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/containerd/reference"
|
"github.com/containerd/containerd/reference"
|
||||||
|
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
||||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/syndtr/gocapability/capability"
|
"github.com/syndtr/gocapability/capability"
|
||||||
"github.com/xeipuuv/gojsonschema"
|
"github.com/xeipuuv/gojsonschema"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Moby is the type of a Moby config file
|
// Moby is the type of a Moby config file
|
||||||
@ -239,7 +240,7 @@ func updateImages(m *Moby) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig parses a config file
|
// NewConfig parses a config file
|
||||||
func NewConfig(config []byte) (Moby, error) {
|
func NewConfig(config []byte, packageFinder spec.PackageResolver) (Moby, error) {
|
||||||
m := Moby{}
|
m := Moby{}
|
||||||
|
|
||||||
// Parse raw yaml
|
// Parse raw yaml
|
||||||
@ -267,6 +268,12 @@ func NewConfig(config []byte) (Moby, error) {
|
|||||||
return m, fmt.Errorf("invalid configuration file")
|
return m, fmt.Errorf("invalid configuration file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process the template fields
|
||||||
|
config, err = processTemplates(config, packageFinder)
|
||||||
|
if err != nil {
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
|
||||||
// Parse yaml
|
// Parse yaml
|
||||||
err = yaml.Unmarshal(config, &m)
|
err = yaml.Unmarshal(config, &m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1071,3 +1078,33 @@ func deviceCgroup(device specs.LinuxDevice) specs.LinuxDeviceCgroup {
|
|||||||
Access: "rwm", // read, write, mknod
|
Access: "rwm", // read, write, mknod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processTemplates given a raw config []byte and a package finder, process the templates to find the packages.
|
||||||
|
// This eventually should expand to other types of templates. Since we only have @pkg: for now,
|
||||||
|
// this will do to start.
|
||||||
|
func processTemplates(b []byte, packageFinder spec.PackageResolver) ([]byte, error) {
|
||||||
|
if packageFinder == nil {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
var node yaml.Node
|
||||||
|
if err := yaml.Unmarshal(b, &node); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
handleTemplate(&node, packageFinder)
|
||||||
|
return yaml.Marshal(&node)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleTemplate(node *yaml.Node, packageFinder spec.PackageResolver) {
|
||||||
|
switch node.Kind {
|
||||||
|
case yaml.SequenceNode, yaml.MappingNode, yaml.DocumentNode:
|
||||||
|
for i := 0; i < len(node.Content); i++ {
|
||||||
|
handleTemplate(node.Content[i], packageFinder)
|
||||||
|
}
|
||||||
|
case yaml.ScalarNode:
|
||||||
|
val := node.Value
|
||||||
|
if resolved, err := packageFinder(node.Value); err == nil {
|
||||||
|
val = resolved
|
||||||
|
}
|
||||||
|
node.Value = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -43,7 +43,7 @@ func ensureLinuxkitImage(name, cache string) error {
|
|||||||
|
|
||||||
yaml := linuxkitYaml[name]
|
yaml := linuxkitYaml[name]
|
||||||
|
|
||||||
m, err := NewConfig([]byte(yaml))
|
m, err := NewConfig([]byte(yaml), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -85,10 +85,10 @@ func pkgCmd() *cobra.Command {
|
|||||||
cmd.PersistentFlags().BoolVar(&argNetwork, "network", piBase.Network, "Allow network use during build")
|
cmd.PersistentFlags().BoolVar(&argNetwork, "network", piBase.Network, "Allow network use during build")
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVar(&argOrg, "org", piBase.Org, "Override the hub org")
|
cmd.PersistentFlags().StringVar(&argOrg, "org", piBase.Org, "Override the hub org")
|
||||||
cmd.PersistentFlags().StringVar(&buildYML, "build-yml", "build.yml", "Override the name of the yml file")
|
cmd.PersistentFlags().StringVar(&buildYML, "build-yml", defaultPkgBuildYML, "Override the name of the yml file")
|
||||||
cmd.PersistentFlags().StringVar(&hash, "hash", "", "Override the image hash (default is to query git for the package's tree-sh)")
|
cmd.PersistentFlags().StringVar(&hash, "hash", "", "Override the image hash (default is to query git for the package's tree-sh)")
|
||||||
cmd.PersistentFlags().StringVar(&tag, "tag", "{{.Hash}}", "Override the tag using fixed strings and/or text templates. Acceptable are .Hash for the hash")
|
cmd.PersistentFlags().StringVar(&tag, "tag", defaultPkgTag, "Override the tag using fixed strings and/or text templates. Acceptable are .Hash for the hash")
|
||||||
cmd.PersistentFlags().StringVar(&hashCommit, "hash-commit", "HEAD", "Override the git commit to use for the hash")
|
cmd.PersistentFlags().StringVar(&hashCommit, "hash-commit", defaultPkgCommit, "Override the git commit to use for the hash")
|
||||||
cmd.PersistentFlags().StringVar(&hashPath, "hash-path", "", "Override the directory to use for the image hash, must be a parent of the package dir (default is to use the package dir)")
|
cmd.PersistentFlags().StringVar(&hashPath, "hash-path", "", "Override the directory to use for the image hash, must be a parent of the package dir (default is to use the package dir)")
|
||||||
cmd.PersistentFlags().BoolVar(&dirty, "force-dirty", false, "Force the pkg(s) to be considered dirty")
|
cmd.PersistentFlags().BoolVar(&dirty, "force-dirty", false, "Force the pkg(s) to be considered dirty")
|
||||||
cmd.PersistentFlags().BoolVar(&devMode, "dev", false, "Force org and hash to $USER and \"dev\" respectively")
|
cmd.PersistentFlags().BoolVar(&devMode, "dev", false, "Force org and hash to $USER and \"dev\" respectively")
|
||||||
|
4
src/cmd/linuxkit/spec/packagefinder.go
Normal file
4
src/cmd/linuxkit/spec/packagefinder.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package spec
|
||||||
|
|
||||||
|
// PackageResolver is an interface for resolving a template into a proper tagged package name
|
||||||
|
type PackageResolver func(path string) (tag string, err error)
|
@ -39,8 +39,7 @@ services:
|
|||||||
- /etc/aaa:/etc/aaa
|
- /etc/aaa:/etc/aaa
|
||||||
# And some runtime settings
|
# And some runtime settings
|
||||||
runtime:
|
runtime:
|
||||||
mkdir: ["/var/lib/docker"]
|
mkdir: ["/var/lib/docker","/var/lib/aaa"]
|
||||||
mkdir: ["/var/lib/aaa"]
|
|
||||||
|
|
||||||
files:
|
files:
|
||||||
- path: etc/linuxkit-config
|
- path: etc/linuxkit-config
|
||||||
|
Loading…
Reference in New Issue
Block a user