mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-10-30 05:54:16 +00:00 
			
		
		
		
	
							
								
								
									
										43
									
								
								docs/yaml.md
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								docs/yaml.md
									
									
									
									
									
								
							| @@ -123,6 +123,9 @@ file: | ||||
|     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. | ||||
|  | ||||
| ## Image specification | ||||
| @@ -293,3 +296,43 @@ binds: | ||||
|  - /var:/var:rshared,rbind | ||||
| 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" | ||||
|  | ||||
| 	"github.com/linuxkit/linuxkit/src/cmd/linuxkit/moby" | ||||
| 	"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"gopkg.in/yaml.v3" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| @@ -54,6 +56,7 @@ func buildCmd() *cobra.Command { | ||||
| 		noSbom             bool | ||||
| 		sbomOutputFilename string | ||||
| 		sbomCurrentTime    bool | ||||
| 		dryRun             bool | ||||
| 	) | ||||
| 	cmd := &cobra.Command{ | ||||
| 		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) | ||||
| 			} | ||||
|  | ||||
| 			var m moby.Moby | ||||
| 			var ( | ||||
| 				m                  moby.Moby | ||||
| 				templatesSupported bool | ||||
| 			) | ||||
| 			for _, arg := range args { | ||||
| 				var config []byte | ||||
| 				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 { | ||||
| 						return fmt.Errorf("Cannot open config file: %v", err) | ||||
| 					} | ||||
| 					// templates are only supported for local files | ||||
| 					templatesSupported = true | ||||
| 				} | ||||
|  | ||||
| 				c, err := moby.NewConfig(config) | ||||
| 				var pkgFinder spec.PackageResolver | ||||
| 				if templatesSupported { | ||||
| 					pkgFinder = createPackageResolver(filepath.Dir(arg)) | ||||
| 				} | ||||
| 				c, err := moby.NewConfig(config, pkgFinder) | ||||
| 				if err != nil { | ||||
| 					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 w io.Writer | ||||
| 			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(&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().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 | ||||
| } | ||||
|   | ||||
							
								
								
									
										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" | ||||
|  | ||||
| 	"github.com/containerd/containerd/reference" | ||||
| 	"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec" | ||||
| 	"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| 	"github.com/syndtr/gocapability/capability" | ||||
| 	"github.com/xeipuuv/gojsonschema" | ||||
| 	"gopkg.in/yaml.v2" | ||||
| 	"gopkg.in/yaml.v3" | ||||
| ) | ||||
|  | ||||
| // Moby is the type of a Moby config file | ||||
| @@ -239,7 +240,7 @@ func updateImages(m *Moby) { | ||||
| } | ||||
|  | ||||
| // NewConfig parses a config file | ||||
| func NewConfig(config []byte) (Moby, error) { | ||||
| func NewConfig(config []byte, packageFinder spec.PackageResolver) (Moby, error) { | ||||
| 	m := Moby{} | ||||
|  | ||||
| 	// Parse raw yaml | ||||
| @@ -267,6 +268,12 @@ func NewConfig(config []byte) (Moby, error) { | ||||
| 		return m, fmt.Errorf("invalid configuration file") | ||||
| 	} | ||||
|  | ||||
| 	// process the template fields | ||||
| 	config, err = processTemplates(config, packageFinder) | ||||
| 	if err != nil { | ||||
| 		return m, err | ||||
| 	} | ||||
|  | ||||
| 	// Parse yaml | ||||
| 	err = yaml.Unmarshal(config, &m) | ||||
| 	if err != nil { | ||||
| @@ -1071,3 +1078,33 @@ func deviceCgroup(device specs.LinuxDevice) specs.LinuxDeviceCgroup { | ||||
| 		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] | ||||
|  | ||||
| 	m, err := NewConfig([]byte(yaml)) | ||||
| 	m, err := NewConfig([]byte(yaml), nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
| @@ -85,10 +85,10 @@ func pkgCmd() *cobra.Command { | ||||
| 	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(&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(&tag, "tag", "{{.Hash}}", "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(&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", 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().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") | ||||
|   | ||||
							
								
								
									
										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) | ||||
		Reference in New Issue
	
	Block a user