From 061e0eb6acd2ef28938dc5a71bbdd79e816ed753 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 25 Sep 2017 20:58:31 +0300 Subject: [PATCH 1/5] Rework of archiver Common approach for working with archives is introduced. Unpacker and Archiver interfaces are added. Example of file unpacking: archive, _ := debos.NewArchive("file.ext") archive.Unpack("destination") Three archive types are supported: - tar - zip - debian packages Type of archive is guessed by extension or can be set explicitly during archiver creation with option for NewArchive(). Signed-off-by: Denis Pynkin --- archiver.go | 214 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 199 insertions(+), 15 deletions(-) diff --git a/archiver.go b/archiver.go index 357e7f3..e67fdf6 100644 --- a/archiver.go +++ b/archiver.go @@ -1,33 +1,217 @@ package debos import ( - "log" + "fmt" "os" + "path/filepath" + "strings" ) -func TarOptions(compression string) string { +type ArchiveType int + +// Supported types +const ( + _ ArchiveType = iota // Guess archive type from file extension + Tar + Zip + Deb +) + +type ArchiveBase struct { + file string // Path to archive file + atype ArchiveType + options map[interface{}]interface{} // Archiver-depending map with additional hints +} +type ArchiveTar struct { + ArchiveBase +} +type ArchiveZip struct { + ArchiveBase +} +type ArchiveDeb struct { + ArchiveBase +} + +type Unpacker interface { + Unpack(destination string) error + RelaxedUnpack(destination string) error +} + +type Archiver interface { + Type() ArchiveType + AddOption(key, value interface{}) error + Unpacker +} + +type Archive struct { + Archiver +} + +// Unpack archive as is +func (arc *ArchiveBase) Unpack(destination string) error { + return fmt.Errorf("Unpack is not supported for '%s'", arc.file) +} + +/* +RelaxedUnpack unpack archive in relaxed mode allowing to ignore or +avoid minor issues with unpacker tool or framework. +*/ +func (arc *ArchiveBase) RelaxedUnpack(destination string) error { + return arc.Unpack(destination) +} + +func (arc *ArchiveBase) AddOption(key, value interface{}) error { + if arc.options == nil { + arc.options = make(map[interface{}]interface{}) + } + arc.options[key] = value + return nil +} + +func (arc *ArchiveBase) Type() ArchiveType { return arc.atype } + +// Helper function for unpacking with external tool +func unpack(command []string, destination string) error { + if err := os.MkdirAll(destination, 0755); err != nil { + return err + } + return Command{}.Run("unpack", command...) +} + +// Helper function for checking allowed compression types +// Returns empty string for unknown +func tarOptions(compression string) string { unpackTarOpts := map[string]string{ "gz": "-z", "bzip2": "-j", "xz": "-J", - } // Trying to guess all other supported formats + } // Trying to guess all other supported compression types return unpackTarOpts[compression] } -func UnpackTarArchive(infile, destination, compression string, options ...string) error { - if err := os.MkdirAll(destination, 0755); err != nil { - return err - } - log.Printf("Unpacking %s\n", infile) - +func (tar *ArchiveTar) Unpack(destination string) error { command := []string{"tar"} - command = append(command, options...) - command = append(command, "-x") - if unpackTarOpt := TarOptions(compression); len(unpackTarOpt) > 0 { - command = append(command, unpackTarOpt) + if options, ok := tar.options["taroptions"].([]string); ok { + for _, option := range options { + command = append(command, option) + } } - command = append(command, "-f", infile, "-C", destination) + command = append(command, "-C", destination) + command = append(command, "-x") - return Command{}.Run("unpack", command...) + if compression, ok := tar.options["tarcompression"]; ok { + if unpackTarOpt := tarOptions(compression.(string)); len(unpackTarOpt) > 0 { + command = append(command, unpackTarOpt) + } + } + command = append(command, "-f", tar.file) + + return unpack(command, destination) +} + +func (tar *ArchiveTar) RelaxedUnpack(destination string) error { + + taroptions := []string{"--no-same-owner", "--no-same-permissions"} + options, ok := tar.options["taroptions"].([]string) + defer func() { tar.options["taroptions"] = options }() + + if ok { + for _, option := range options { + taroptions = append(taroptions, option) + } + } + tar.options["taroptions"] = taroptions + + return tar.Unpack(destination) +} + +func (tar *ArchiveTar) AddOption(key, value interface{}) error { + + switch key { + case "taroptions": + // expect a slice + options, ok := value.([]string) + if !ok { + return fmt.Errorf("Wrong type for value") + } + tar.options["taroptions"] = options + + case "tarcompression": + compression, ok := value.(string) + if !ok { + return fmt.Errorf("Wrong type for value") + } + option := tarOptions(compression) + if len(option) == 0 { + return fmt.Errorf("Compression '%s' is not supported", compression) + } + tar.options["tarcompression"] = compression + + default: + return fmt.Errorf("Option '%v' is not supported for tar archive type", key) + } + return nil +} + +func (zip *ArchiveZip) Unpack(destination string) error { + command := []string{"unzip", zip.file, "-d", destination} + return unpack(command, destination) +} + +func (zip *ArchiveZip) RelaxedUnpack(destination string) error { + return zip.Unpack(destination) +} + +func (deb *ArchiveDeb) Unpack(destination string) error { + command := []string{"dpkg", "-x", deb.file, destination} + return unpack(command, destination) +} + +func (deb *ArchiveDeb) RelaxedUnpack(destination string) error { + return deb.Unpack(destination) +} + +/* +NewArchive associate correct structure and methods according to +archive type. If ArchiveType is omitted -- trying to guess the type. +Return ArchiveType or nil in case of error. +*/ +func NewArchive(file string, arcType ...ArchiveType) (Archive, error) { + var archive Archive + var atype ArchiveType + + if len(arcType) == 0 { + ext := filepath.Ext(file) + ext = strings.ToLower(ext) + + switch ext { + case ".deb": + atype = Deb + case ".zip": + atype = Zip + default: + //FIXME: guess Tar maybe? + atype = Tar + } + } else { + atype = arcType[0] + } + + common := ArchiveBase{} + common.file = file + common.atype = atype + common.options = make(map[interface{}]interface{}) + + switch atype { + case Tar: + archive = Archive{&ArchiveTar{ArchiveBase: common}} + case Zip: + archive = Archive{&ArchiveZip{ArchiveBase: common}} + case Deb: + archive = Archive{&ArchiveDeb{ArchiveBase: common}} + default: + return archive, fmt.Errorf("Unsupported archive '%s'", file) + } + return archive, nil } From 6cece5a983e8a0f34515a3f7e95e361fa6bf1e78 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 26 Sep 2017 22:10:57 +0300 Subject: [PATCH 2/5] Add tests for archiver module Add tests for Tar, Zip and Deb archive types. go test -coverprofile=cover.out .... PASS coverage: 51.0% of statements Coverage for archiver.go: 100% Signed-off-by: Denis Pynkin tests reworked --- archiver_test.go | 157 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 archiver_test.go diff --git a/archiver_test.go b/archiver_test.go new file mode 100644 index 0000000..3023c12 --- /dev/null +++ b/archiver_test.go @@ -0,0 +1,157 @@ +package debos_test + +import ( + _ "fmt" + "github.com/go-debos/debos" + "github.com/stretchr/testify/assert" + _ "reflect" + _ "strings" + "testing" +) + +func TestBase(t *testing.T) { + + // New archive + // Expect Tar by default + _, err := debos.NewArchive("test.base", 0) + assert.EqualError(t, err, "Unsupported archive 'test.base'") + + // Test base + archive := debos.ArchiveBase{} + arcType := archive.Type() + assert.Equal(t, 0, int(arcType)) + + // Add option + err = archive.AddOption("someoption", "somevalue") + assert.Empty(t, err) + + err = archive.Unpack("/tmp/test") + assert.EqualError(t, err, "Unpack is not supported for ''") + err = archive.RelaxedUnpack("/tmp/test") + assert.EqualError(t, err, "Unpack is not supported for ''") +} + +func TestTar_default(t *testing.T) { + + // New archive + // Expect Tar by default + archive, err := debos.NewArchive("test.tar.gz") + assert.NotEmpty(t, archive) + assert.Empty(t, err) + + // Type must be Tar by default + arcType := archive.Type() + assert.Equal(t, debos.Tar, arcType) + + // Test unpack + err = archive.Unpack("/tmp/test") + // Expect unpack failure + assert.EqualError(t, err, "exit status 2") + + // Expect failure for RelaxedUnpack + err = archive.RelaxedUnpack("/tmp/test") + assert.EqualError(t, err, "exit status 2") + + // Check options + err = archive.AddOption("taroptions", []string{"--option1"}) + assert.Empty(t, err) + err = archive.Unpack("/tmp/test") + assert.EqualError(t, err, "exit status 64") + err = archive.Unpack("/proc/debostest") + assert.EqualError(t, err, "mkdir /proc/debostest: no such file or directory") + err = archive.RelaxedUnpack("/tmp/test") + assert.EqualError(t, err, "exit status 64") + + // Add wrong option + err = archive.AddOption("someoption", "somevalue") + assert.EqualError(t, err, "Option 'someoption' is not supported for tar archive type") +} + +// Check supported compression types +func TestTar_compression(t *testing.T) { + compressions := map[string]string{ + "gz": "tar -C test -x -z -f test.tar.gz", + "bzip2": "tar -C test -x -j -f test.tar.gz", + "xz": "tar -C test -x -J -f test.tar.gz", + } + + // Force type + archive, err := debos.NewArchive("test.tar.gz", debos.Tar) + assert.NotEmpty(t, archive) + assert.Empty(t, err) + // Type must be Tar + arcType := archive.Type() + assert.Equal(t, debos.Tar, arcType) + + for compression, _ := range compressions { + err = archive.AddOption("tarcompression", compression) + assert.Empty(t, err) + err := archive.Unpack("test") + assert.EqualError(t, err, "exit status 2") + } + // Check of unsupported compression type + err = archive.AddOption("tarcompression", "fake") + assert.EqualError(t, err, "Compression 'fake' is not supported") + + // Pass incorrect type + err = archive.AddOption("taroptions", nil) + assert.EqualError(t, err, "Wrong type for value") + err = archive.AddOption("tarcompression", nil) + assert.EqualError(t, err, "Wrong type for value") +} + +func TestDeb(t *testing.T) { + + // Guess Deb + archive, err := debos.NewArchive("test.deb") + assert.NotEmpty(t, archive) + assert.Empty(t, err) + + // Type must be guessed as Deb + arcType := archive.Type() + assert.Equal(t, debos.Deb, arcType) + + // Force Deb type + archive, err = debos.NewArchive("test.deb", debos.Deb) + assert.NotEmpty(t, archive) + assert.Empty(t, err) + + // Type must be Deb + arcType = archive.Type() + assert.Equal(t, debos.Deb, arcType) + + // Expect unpack failure + err = archive.Unpack("/tmp/test") + assert.EqualError(t, err, "exit status 2") + err = archive.Unpack("/proc/debostest") + assert.EqualError(t, err, "mkdir /proc/debostest: no such file or directory") + err = archive.RelaxedUnpack("/tmp/test") + assert.EqualError(t, err, "exit status 2") +} + +func TestZip(t *testing.T) { + // Guess zip + archive, err := debos.NewArchive("test.ZiP") + assert.NotEmpty(t, archive) + assert.Empty(t, err) + // Type must be guessed as Zip + arcType := archive.Type() + assert.Equal(t, debos.Zip, arcType) + + // Force Zip type + archive, err = debos.NewArchive("test.zip", debos.Zip) + assert.NotEmpty(t, archive) + assert.Empty(t, err) + + // Type must be Zip + arcType = archive.Type() + assert.Equal(t, debos.Zip, arcType) + + // Expect unpack failure + err = archive.Unpack("/tmp/test") + assert.EqualError(t, err, "exit status 9") + err = archive.Unpack("/proc/debostest") + assert.EqualError(t, err, "mkdir /proc/debostest: no such file or directory") + err = archive.RelaxedUnpack("/tmp/test") + assert.EqualError(t, err, "exit status 9") +} From 7eff2b1faee18474f898d7bc099bc3f12fde3589 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 25 Sep 2017 20:58:31 +0300 Subject: [PATCH 3/5] Use Archiver interface in actions for files unpack Use Archiver interface for files unpack in 'download' and 'unpack' actions. Now several archive types are supported in actions mentioned above. Improved verification for unpack in actions. Signed-off-by: Denis Pynkin actions reworked --- actions/download_action.go | 68 +++++++++++++++++++++++++++++++------- actions/unpack_action.go | 26 ++++++++++++--- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/actions/download_action.go b/actions/download_action.go index 0de5e48..3a648f0 100644 --- a/actions/download_action.go +++ b/actions/download_action.go @@ -37,13 +37,58 @@ func (d *DownloadAction) validateUrl() (*url.URL, error) { return url, nil } +func (d *DownloadAction) validateFilename(context *debos.DebosContext, url *url.URL) (filename string, err error) { + if len(d.Filename) == 0 { + // Trying to guess the name from URL Path + filename = path.Base(url.Path) + } else { + filename = path.Base(d.Filename) + } + if len(filename) == 0 { + return "", fmt.Errorf("Incorrect filename is provided for '%s'", d.Url) + } + filename = path.Join(context.Scratchdir, filename) + return filename, nil +} + +func (d *DownloadAction) archive(filename string) (debos.Archive, error) { + archive, err := debos.NewArchive(filename) + if err != nil { + return archive, err + } + switch archive.Type() { + case debos.Tar: + if len(d.Compression) > 0 { + if err := archive.AddOption("tarcompression", d.Compression); err != nil { + return archive, err + } + } + default: + } + return archive, nil +} + func (d *DownloadAction) Verify(context *debos.DebosContext) error { + var filename string if len(d.Name) == 0 { return fmt.Errorf("Property 'name' is mandatory for download action\n") } - _, err := d.validateUrl() - return err + + url, err := d.validateUrl() + if err != nil { + return err + } + filename, err = d.validateFilename(context, url) + if err != nil { + return err + } + if d.Unpack == true { + if _, err := d.archive(filename); err != nil { + return err + } + } + return nil } func (d *DownloadAction) Run(context *debos.DebosContext) error { @@ -55,16 +100,10 @@ func (d *DownloadAction) Run(context *debos.DebosContext) error { return err } - if len(d.Filename) == 0 { - // Trying to guess the name from URL Path - filename = path.Base(url.Path) - } else { - filename = path.Base(d.Filename) + filename, err = d.validateFilename(context, url) + if err != nil { + return err } - if len(filename) == 0 { - return fmt.Errorf("Incorrect filename is provided for '%s'", d.Url) - } - filename = path.Join(context.Scratchdir, filename) originPath := filename switch url.Scheme { @@ -78,8 +117,13 @@ func (d *DownloadAction) Run(context *debos.DebosContext) error { } if d.Unpack == true { + archive, err := d.archive(filename) + if err != nil { + return err + } + targetdir := filename + ".d" - err := debos.UnpackTarArchive(filename, targetdir, d.Compression, "--no-same-owner", "--no-same-permissions") + err = archive.RelaxedUnpack(targetdir) if err != nil { return err } diff --git a/actions/unpack_action.go b/actions/unpack_action.go index 1d9fdc6..a302e8b 100644 --- a/actions/unpack_action.go +++ b/actions/unpack_action.go @@ -18,9 +18,17 @@ func (pf *UnpackAction) Verify(context *debos.DebosContext) error { return fmt.Errorf("Filename can't be empty. Please add 'file' and/or 'origin' property.") } - unpackTarOpt := debos.TarOptions(pf.Compression) - if len(pf.Compression) > 0 && len(unpackTarOpt) == 0 { - return fmt.Errorf("Compression '%s' is not supported.\n", pf.Compression) + archive, err := debos.NewArchive(pf.File) + if err != nil { + return err + } + if len(pf.Compression) > 0 { + if archive.Type() != debos.Tar { + return fmt.Errorf("Option 'compression' is supported for Tar archives only.") + } + if err := archive.AddOption("tarcompression", pf.Compression); err != nil { + return fmt.Errorf("'%s': %s", pf.File, err) + } } return nil @@ -46,5 +54,15 @@ func (pf *UnpackAction) Run(context *debos.DebosContext) error { return err } - return debos.UnpackTarArchive(infile, context.Rootdir, pf.Compression) + archive, err := debos.NewArchive(infile) + if err != nil { + return err + } + if len(pf.Compression) > 0 { + if err := archive.AddOption("tarcompression", pf.Compression); err != nil { + return err + } + } + + return archive.Unpack(context.Rootdir) } From 7579ee9d4a6e500f41078205436fbcef873c6529 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 6 Sep 2017 13:43:54 +0300 Subject: [PATCH 4/5] Add .gitignore file Initial version of gitignore file generated by https://www.gitignore.io/api/vim,linux,go Signed-off-by: Denis Pynkin --- .gitignore | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32eae19 --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ + +# Created by https://www.gitignore.io/api/vim,linux,go + +### Go ### +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ + +# Golang project vendor packages which should be ignored +vendor/ + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +# auto-generated tag files +tags + +# End of https://www.gitignore.io/api/vim,linux,go From 4e6e7208051eb052c6894c59b985f11f92ca2447 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 8 Sep 2017 21:47:02 +0300 Subject: [PATCH 5/5] actions: add description of actions Add description of each available action and properties allowed by actions. Used 'godoc' format to keep actions documentation in one place with implementation. Fixes #4 Signed-off-by: Denis Pynkin --- actions/actions_doc.go | 6 ++ actions/apt_action.go | 24 +++++- actions/debootstrap_action.go | 44 +++++++++-- actions/download_action.go | 32 ++++++++ actions/filesystem_deploy_action.go | 21 ++++- actions/image_partition_action.go | 118 ++++++++++++++++++++++++++-- actions/ostree_commit_action.go | 33 +++++++- actions/ostree_deploy_action.go | 47 ++++++++++- actions/overlay_action.go | 24 ++++++ actions/pack_action.go | 23 +++++- actions/raw_action.go | 25 ++++++ actions/run_action.go | 42 ++++++++-- actions/unpack_action.go | 32 ++++++++ 13 files changed, 438 insertions(+), 33 deletions(-) create mode 100644 actions/actions_doc.go diff --git a/actions/actions_doc.go b/actions/actions_doc.go new file mode 100644 index 0000000..7ebd532 --- /dev/null +++ b/actions/actions_doc.go @@ -0,0 +1,6 @@ +// Copyright 2017, Collabora Ltd. + +/* +Package 'actions' implements 'debos' modules used for OS creation. +*/ +package actions diff --git a/actions/apt_action.go b/actions/apt_action.go index 49d3434..2045522 100644 --- a/actions/apt_action.go +++ b/actions/apt_action.go @@ -1,3 +1,23 @@ +/* +Apt Action + +Install packages and their dependencies to the target rootfs with 'apt'. + +Yaml syntax: + - action: apt + recommends: bool + packages: + - package1 + - package2 + +Mandatory properties: + +- packages -- list of packages to install + +Optional properties: + +- recommends -- boolean indicating if suggested packages will be installed +*/ package actions import ( @@ -6,8 +26,8 @@ import ( type AptAction struct { debos.BaseAction `yaml:",inline"` - Recommends bool - Packages []string + Recommends bool + Packages []string } func (apt *AptAction) Run(context *debos.DebosContext) error { diff --git a/actions/debootstrap_action.go b/actions/debootstrap_action.go index 7545204..e257501 100644 --- a/actions/debootstrap_action.go +++ b/actions/debootstrap_action.go @@ -1,3 +1,32 @@ +/* +Debootstrap Action + +Construct the target rootfs with debootstrap tool. + +Yaml syntax: + - action: debootstrap + mirror: URL + suite: "name" + components: + variant: "name" + keyring-package: + +Mandatory properties: + +- suite -- release code name or symbolic name (e.g. "stable") + +Optional properties: + +- mirror -- URL with Debian-compatible repository + +- variant -- name of the bootstrap script variant to use + +- components -- list of components to use for packages selection. +Example: + components: [ main, contrib ] + +- keyring-package -- keyring for packages validation. Currently ignored. +*/ package actions import ( @@ -11,13 +40,13 @@ import ( ) type DebootstrapAction struct { - debos.BaseAction `yaml:",inline"` - Suite string - Mirror string - Variant string - KeyringPackage string `yaml:"keyring-package"` - Components []string - MergedUsr bool `yaml:"merged-usr"` + debos.BaseAction `yaml:",inline"` + Suite string + Mirror string + Variant string + KeyringPackage string `yaml:"keyring-package"` + Components []string + MergedUsr bool `yaml:"merged-usr"` } func NewDebootstrapAction() *DebootstrapAction { @@ -25,6 +54,7 @@ func NewDebootstrapAction() *DebootstrapAction { // Use filesystem with merged '/usr' by default d.MergedUsr = true return &d + } func (d *DebootstrapAction) RunSecondStage(context debos.DebosContext) error { diff --git a/actions/download_action.go b/actions/download_action.go index 3a648f0..a4099e7 100644 --- a/actions/download_action.go +++ b/actions/download_action.go @@ -1,3 +1,35 @@ +/* +Download Action + +Download a single file from Internet and unpack it in place if needed. + +Yaml syntax: + - action: download + url: http://example.domain/path/filename.ext + name: firmware + filename: output_name + unpack: bool + compression: gz + +Mandatory properties: + +- url -- URL to an object for download + +- name -- string which allow to use downloaded object in other actions +via 'origin' property. If 'unpack' property is set to 'true' name will +refer to temporary directory with extracted content. + +Optional properties: + +- filename -- use this property as the name for saved file. Useful if URL does not +contain file name in path, for example it is possible to download files from URLs without path part. + +- unpack -- hint for action to extract all files from downloaded archive. +See the 'Unpack' action for more information. + +- compression -- optional hint for unpack allowing to use proper compression method. +See the 'Unpack' action for more information. +*/ package actions import ( diff --git a/actions/filesystem_deploy_action.go b/actions/filesystem_deploy_action.go index 0b85fd5..5fcab9e 100644 --- a/actions/filesystem_deploy_action.go +++ b/actions/filesystem_deploy_action.go @@ -1,3 +1,22 @@ +/* +FilesystemDeploy Action + +Deploy prepared root filesystem to output image. This action requires +'image-partition' action to be executed before it. + +Yaml syntax: + - action: filesystem-deploy + setup-fstab: bool + setup-kernel-cmdline: bool + +Optional properties: + +- setup-fstab -- generate '/etc/fstab' file according to information provided +by 'image-partition' action. By default is 'true'. + +- setup-kernel-cmdline -- add location of root partition to '/etc/kernel/cmdline' +file on target image. By default is 'true'. +*/ package actions import ( @@ -14,7 +33,7 @@ import ( ) type FilesystemDeployAction struct { - debos.BaseAction `yaml:",inline"` + debos.BaseAction `yaml:",inline"` SetupFSTab bool `yaml:"setup-fstab"` SetupKernelCmdline bool `yaml:"setup-kernel-cmdline"` } diff --git a/actions/image_partition_action.go b/actions/image_partition_action.go index d50bac1..6d8991c 100644 --- a/actions/image_partition_action.go +++ b/actions/image_partition_action.go @@ -1,3 +1,105 @@ +/* +ImagePartition Action + +This action creates an image file, partitions it and formats the filesystems. + +Yaml syntax: + - action: image-partition + imagename: image_name + imagesize: size + partitiontype: gpt + partitions: + + mountpoints: + + +Mandatory properties: + +- imagename -- the name of the image file. + +- imagesize -- generated image size in human-readable form, examples: 100MB, 1GB, etc. + +- partitiontype -- partition table type. Currently only 'gpt' and 'msdos' +partition tables are supported. + +- partitions -- list of partitions, at least one partition is needed. +Partition properties are described below. + +- mountpoints -- list of mount points for partitions. +Properties for mount points are described below. + +Yaml syntax for partitions: + + partitions: + - name: label + name: partition name + fs: filesystem + start: offset + end: offset + flags: list of flags + +Mandatory properties: + +- name -- is used for referencing named partition for mount points +configuration (below) and label the filesystem located on this partition. + +- fs -- filesystem type used for formatting. + +NB: the FAT (vfat) filesystem is named 'fat32' in configuration file. + +- start -- offset from beginning of the disk there the partition starts. + +- end -- offset from beginning of the disk there the partition ends. + +For 'start' and 'end' properties offset can be written in human readable +form -- '32MB', '1GB' or as disk percentage -- '100%'. + +Optional properties: + +- flags -- list of additional flags for partition compatible with parted(8) +'set' command. + +Yaml syntax for mount points: + + mountpoints: + - mountpoint: path + partition: partition label + options: list of options + +Mandatory properties: + +- partition -- partition name for mounting. + +- mountpoint -- path in the target root filesystem where the named partition +should be mounted. + +Optional properties: + +- options -- list of options to be added to appropriate entry in fstab file. + +Layout example for Raspberry PI 3: + + - action: image-partition + imagename: "debian-rpi3.img" + imagesize: 1GB + partitiontype: msdos + mountpoints: + - mountpoint: / + partition: root + - mountpoint: /boot/firmware + partition: firmware + options: [ x-systemd.automount ] + partitions: + - name: firmware + fs: fat32 + start: 0% + end: 64MB + - name: root + fs: ext4 + start: 64MB + end: 100% + flags: [ boot ] +*/ package actions import ( @@ -32,14 +134,14 @@ type Mountpoint struct { } type ImagePartitionAction struct { - debos.BaseAction `yaml:",inline"` - ImageName string - ImageSize string - PartitionType string - Partitions []Partition - Mountpoints []Mountpoint - size int64 - usingLoop bool + debos.BaseAction `yaml:",inline"` + ImageName string + ImageSize string + PartitionType string + Partitions []Partition + Mountpoints []Mountpoint + size int64 + usingLoop bool } func (i *ImagePartitionAction) generateFSTab(context *debos.DebosContext) error { diff --git a/actions/ostree_commit_action.go b/actions/ostree_commit_action.go index dfed866..6d41b89 100644 --- a/actions/ostree_commit_action.go +++ b/actions/ostree_commit_action.go @@ -1,3 +1,28 @@ +/* +OstreeCommit Action + +Create OSTree commit from rootfs. + +Yaml syntax: + - action: ostree-commit + repository: repository name + branch: branch name + subject: commit message + +Mandatory properties: + +- repository -- path to repository with OSTree structure; the same path is +used by 'ostree' tool with '--repo' argument. +This path is relative to 'artifact' directory. +Please keep in mind -- you will need a root privileges for 'bare' repository +type (https://ostree.readthedocs.io/en/latest/manual/repo/#repository-types-and-locations). + +- branch -- OSTree branch name that should be used for the commit. + +Optional properties: + +- subject -- one line message with commit description. +*/ package actions import ( @@ -11,10 +36,10 @@ import ( type OstreeCommitAction struct { debos.BaseAction `yaml:",inline"` - Repository string - Branch string - Subject string - Command string + Repository string + Branch string + Subject string + Command string } func emptyDir(dir string) { diff --git a/actions/ostree_deploy_action.go b/actions/ostree_deploy_action.go index c5a40ed..643e1b4 100644 --- a/actions/ostree_deploy_action.go +++ b/actions/ostree_deploy_action.go @@ -1,3 +1,44 @@ +/* +OstreeDeploy Action + +Deploy the OSTree branch to the image. +If any preparation has been done for rootfs, it can be overwritten +during this step. + +Action 'image-partition' must be called prior to OSTree deploy. + +Yaml syntax: + - action: ostree-deploy + repository: repository name + remote_repository: URL + branch: branch name + os: os name + setup-fstab: bool + setup-kernel-cmdline: bool + appendkernelcmdline: arguments + +Mandatory properties: + +- remote_repository -- URL to remote OSTree repository for pulling stateroot branch. +Currently not implemented, please prepare local repository instead. + +- repository -- path to repository with OSTree structure. +This path is relative to 'artifact' directory. + +- os -- os deployment name, as explained in: +https://ostree.readthedocs.io/en/latest/manual/deployment/ + +- branch -- branch of the repository to use for populating the image. + +Optional properties: + +- setup-fstab -- create '/etc/fstab' file for image + +- setup-kernel-cmdline -- add the information from the 'image-partition' +action to the configured commandline. + +- appendkernelcmdline -- additional kernel command line arguments passed to kernel. +*/ package actions import ( @@ -12,13 +53,13 @@ import ( ) type OstreeDeployAction struct { - debos.BaseAction `yaml:",inline"` + debos.BaseAction `yaml:",inline"` Repository string RemoteRepository string "remote_repository" Branch string Os string - SetupFSTab bool `yaml:"setup-fstab"` - SetupKernelCmdline bool `yaml:"setup-kernel-cmdline"` + SetupFSTab bool `yaml:"setup-fstab"` + SetupKernelCmdline bool `yaml:"setup-kernel-cmdline"` AppendKernelCmdline string } diff --git a/actions/overlay_action.go b/actions/overlay_action.go index ec90312..f17ecb3 100644 --- a/actions/overlay_action.go +++ b/actions/overlay_action.go @@ -1,3 +1,27 @@ +/* +Overlay Action + +Recursive copy of directory or file to target filesystem. + +Yaml syntax: + - action: overlay + origin: name + source: directory + destination: directory + +Mandatory properties: + +- source -- relative path to the directory or file located in path referenced by `origin`. +In case if this property is absent then pure path referenced by 'origin' will be used. + +Optional properties: + +- origin -- reference to named file or directory. + +- destination -- absolute path in the target rootfs where 'source' will be copied. +All existing files will be overwritten. +If destination isn't set '/' of the rootfs will be usedi. +*/ package actions import ( diff --git a/actions/pack_action.go b/actions/pack_action.go index 4e01289..a90cb1d 100644 --- a/actions/pack_action.go +++ b/actions/pack_action.go @@ -1,3 +1,20 @@ +/* +Pack Action + +Create tarball with filesystem. + +Yaml syntax: + - action: pack + file: filename.ext + compression: gz + +Mandatory properties: + +- file -- name of the output tarball. + +- compression -- compression type to use. Only 'gz' is supported at the moment. + +*/ package actions import ( @@ -8,9 +25,9 @@ import ( ) type PackAction struct { - debos.BaseAction `yaml:",inline"` - Compression string - File string + debos.BaseAction `yaml:",inline"` + Compression string + File string } func (pf *PackAction) Run(context *debos.DebosContext) error { diff --git a/actions/raw_action.go b/actions/raw_action.go index d4ad85f..c36f060 100644 --- a/actions/raw_action.go +++ b/actions/raw_action.go @@ -1,3 +1,28 @@ +/* +Raw Action + +Directly write a file to the output image at a given offset. +This is typically useful for bootloaders. + +Yaml syntax: + - action: raw + origin: name + source: filename + offset: bytes + +Mandatory properties: + +- origin -- reference to named file or directory. + +- source -- the name of file located in 'origin' to be written into the output image. + +Optional properties: + +- offset -- offset in bytes for output image file. +It is possible to use internal templating mechanism of debos to calculate offset +with sectors (512 bytes) instead of bytes, for instance: '{{ sector 256 }}'. +The default value is zero. +*/ package actions import ( diff --git a/actions/run_action.go b/actions/run_action.go index 0880238..f37fc57 100644 --- a/actions/run_action.go +++ b/actions/run_action.go @@ -1,3 +1,35 @@ +/* +Run Action + +Allows to run any available command or script in the filesystem or +in host environment. + +Yaml syntax: + - action: run + chroot: bool + postprocess: bool + script: script name + command: command line + +Properties 'command' and 'script' are mutually exclusive. + +- command -- command with arguments; the command expected to be accessible in +host's or chrooted environment -- depending on 'chroot' property. + +- script -- script with arguments; script must be located in recipe directory. + +Optional properties: + +- chroot -- run script or command in target filesystem if set to true. +In other case the command or script is executed within the build process, with +access to the filesystem and the image. In both cases it is run with root privileges. + +- postprocess -- if set script or command is executed after all other commands and +has access to the image file. + + +Properties 'chroot' and 'postprocess' are mutually exclusive. +*/ package actions import ( @@ -10,11 +42,11 @@ import ( ) type RunAction struct { - debos.BaseAction `yaml:",inline"` - Chroot bool - PostProcess bool - Script string - Command string + debos.BaseAction `yaml:",inline"` + Chroot bool + PostProcess bool + Script string + Command string } func (run *RunAction) Verify(context *debos.DebosContext) error { diff --git a/actions/unpack_action.go b/actions/unpack_action.go index a302e8b..d4993b1 100644 --- a/actions/unpack_action.go +++ b/actions/unpack_action.go @@ -1,3 +1,35 @@ +/* +Unpack Action + +Unpack files from archive to the filesystem. +Useful for creating target rootfs from saved tarball with prepared file structure. + +Only (compressed) tar archives are supported currently. + +Yaml syntax: + - action: unpack + origin: name + file: file.ext + compression: gz + +Mandatory properties: + +- file -- archive's file name. It is possible to skip this property if 'origin' +referenced to downloaded file. + +One of the mandatory properties may be omitted with limitations mentioned above. +It is expected to find archive with name pointed in `file` property inside of `origin` in case if both properties are used. + +Optional properties: + +- origin -- reference to a named file or directory. +The default value is 'artifacts' directory in case if this property is omitted. + +- compression -- optional hint for unpack allowing to use proper compression method. + +Currently only 'gz', bzip2' and 'xz' compression types are supported. +If not provided an attempt to autodetect the compression type will be done. +*/ package actions import (