osbuild2: new schema types: stages, inputs, sources
Adding new types and adapting copies of all the old types to match the
new Manifest schema:
New types:
- Stages
- org.osbuild.ostree.init
- org.osbuild.ostree.pull
- org.osbuild.ostree.preptree (replaces org.osbuild.rpm-ostree)
- org.osbuild.curl
- Converted from assemblers
The concept of a Build and Assembler stage in gone now. Instead they
are regular Stages like any other.
- org.osbuild.oci-archive
- org.osbuild.ostree.commit
- Sources
- org.osbuild.curl
- org.osbuild.ostree
- Inputs
- org.osbuild.files
- org.osbuild.ostree
Types with changes:
- Stages
- org.osbuild.rpm:
- New input structure for defining packages
- New options
Basically copies:
- The rest simply rename the `Name` field to `Type`
Decoding types with interface fields:
Types that contain interfaces with multiple implementations implement
their own UnmarshalJSON method. In these cases, we use a JSON decoder
with the `DisallowUnknownFields` option to catch errors during the
deserialization while trying to determine which implementation matches
the data.
Copied tests for copied types are adapted accordingly.
This commit is contained in:
parent
8090621300
commit
756d5b063f
58 changed files with 1926 additions and 0 deletions
14
internal/osbuild2/chrony_stage.go
Normal file
14
internal/osbuild2/chrony_stage.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package osbuild2
|
||||
|
||||
type ChronyStageOptions struct {
|
||||
Timeservers []string `json:"timeservers"`
|
||||
}
|
||||
|
||||
func (ChronyStageOptions) isStageOptions() {}
|
||||
|
||||
func NewChronyStage(options *ChronyStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.chrony",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/chrony_stage_test.go
Normal file
16
internal/osbuild2/chrony_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewChronyStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.chrony",
|
||||
Options: &ChronyStageOptions{},
|
||||
}
|
||||
actualStage := NewChronyStage(&ChronyStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
66
internal/osbuild2/curl_source.go
Normal file
66
internal/osbuild2/curl_source.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type CurlSource struct {
|
||||
Items map[string]CurlSourceItem `json:"items"`
|
||||
}
|
||||
|
||||
func (CurlSource) isSource() {}
|
||||
|
||||
// CurlSourceItem can be either a URL string or a URL paired with a secrets
|
||||
// provider
|
||||
type CurlSourceItem interface {
|
||||
isCurlSourceItem()
|
||||
}
|
||||
|
||||
type URL string
|
||||
|
||||
func (URL) isCurlSourceItem() {}
|
||||
|
||||
type URLWithSecrets struct {
|
||||
URL string `json:"url"`
|
||||
Secrets *URLSecrets `json:"secrets,omitempty"`
|
||||
}
|
||||
|
||||
func (URLWithSecrets) isCurlSourceItem() {}
|
||||
|
||||
type URLSecrets struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Unmarshal method for CurlSource for handling the CurlSourceItem interface:
|
||||
// Tries each of the implementations until it finds the one that works.
|
||||
func (cs *CurlSource) UnmarshalJSON(data []byte) (err error) {
|
||||
cs.Items = make(map[string]CurlSourceItem)
|
||||
type csSimple struct {
|
||||
Items map[string]URL `json:"items"`
|
||||
}
|
||||
simple := new(csSimple)
|
||||
b := bytes.NewReader(data)
|
||||
dec := json.NewDecoder(b)
|
||||
dec.DisallowUnknownFields()
|
||||
if err = dec.Decode(simple); err == nil {
|
||||
for k, v := range simple.Items {
|
||||
cs.Items[k] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type csWithSecrets struct {
|
||||
Items map[string]URLWithSecrets `json:"items"`
|
||||
}
|
||||
withSecrets := new(csWithSecrets)
|
||||
b.Reset(data)
|
||||
if err = dec.Decode(withSecrets); err == nil {
|
||||
for k, v := range withSecrets.Items {
|
||||
cs.Items[k] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
18
internal/osbuild2/files_input.go
Normal file
18
internal/osbuild2/files_input.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package osbuild2
|
||||
|
||||
// Inputs for individual files
|
||||
|
||||
// Provides all the files, named via their content hash, specified
|
||||
// via `references` in a new directory.
|
||||
type FilesInput struct {
|
||||
inputCommon
|
||||
}
|
||||
|
||||
func (FilesInput) isInput() {}
|
||||
|
||||
func NewFilesInput() *FilesInput {
|
||||
input := new(FilesInput)
|
||||
input.Type = "org.osbuild.files"
|
||||
input.Origin = "org.osbuild.source"
|
||||
return input
|
||||
}
|
||||
16
internal/osbuild2/firewall_stage.go
Normal file
16
internal/osbuild2/firewall_stage.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
type FirewallStageOptions struct {
|
||||
Ports []string `json:"ports,omitempty"`
|
||||
EnabledServices []string `json:"enabled_services,omitempty"`
|
||||
DisabledServices []string `json:"disabled_services,omitempty"`
|
||||
}
|
||||
|
||||
func (FirewallStageOptions) isStageOptions() {}
|
||||
|
||||
func NewFirewallStage(options *FirewallStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.firewall",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/firewall_stage_test.go
Normal file
16
internal/osbuild2/firewall_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewFirewallStage(t *testing.T) {
|
||||
expectedFirewall := &Stage{
|
||||
Type: "org.osbuild.firewall",
|
||||
Options: &FirewallStageOptions{},
|
||||
}
|
||||
actualFirewall := NewFirewallStage(&FirewallStageOptions{})
|
||||
assert.Equal(t, expectedFirewall, actualFirewall)
|
||||
}
|
||||
15
internal/osbuild2/first_boot_stage.go
Normal file
15
internal/osbuild2/first_boot_stage.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package osbuild2
|
||||
|
||||
type FirstBootStageOptions struct {
|
||||
Commands []string `json:"commands"`
|
||||
WaitForNetwork bool `json:"wait_for_network,omitempty"`
|
||||
}
|
||||
|
||||
func (FirstBootStageOptions) isStageOptions() {}
|
||||
|
||||
func NewFirstBootStage(options *FirstBootStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.first-boot",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/first_boot_stage_test.go
Normal file
16
internal/osbuild2/first_boot_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewFirstBootStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.first-boot",
|
||||
Options: &FirstBootStageOptions{},
|
||||
}
|
||||
actualStage := NewFirstBootStage(&FirstBootStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
21
internal/osbuild2/fix_bls_stage.go
Normal file
21
internal/osbuild2/fix_bls_stage.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package osbuild2
|
||||
|
||||
// A FixBLSStageOptions struct is empty, as the stage takes no options.
|
||||
//
|
||||
// The FixBLSStage fixes the paths in the Boot Loader Specification
|
||||
// snippets installed into /boot. grub2's kernel install script will
|
||||
// try to guess the correct path to the kernel and bootloader, and adjust
|
||||
// the boot loader scripts accordingly. When run under OSBuild this does
|
||||
// not work correctly, so this stage essentially reverts the "fixup".
|
||||
type FixBLSStageOptions struct {
|
||||
}
|
||||
|
||||
func (FixBLSStageOptions) isStageOptions() {}
|
||||
|
||||
// NewFixBLSStage creates a new FixBLSStage.
|
||||
func NewFixBLSStage() *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.fix-bls",
|
||||
Options: &FixBLSStageOptions{},
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/fix_bls_stage_test.go
Normal file
16
internal/osbuild2/fix_bls_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewFixBLSStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.fix-bls",
|
||||
Options: &FixBLSStageOptions{},
|
||||
}
|
||||
actualStage := NewFixBLSStage()
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
44
internal/osbuild2/fstab_stage.go
Normal file
44
internal/osbuild2/fstab_stage.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package osbuild2
|
||||
|
||||
// The FSTabStageOptions describe the content of the /etc/fstab file.
|
||||
//
|
||||
// The structure of the options follows the format of /etc/fstab, except
|
||||
// that filesystem must be identified by their UUID and ommitted fields
|
||||
// are set to their defaults (if possible).
|
||||
type FSTabStageOptions struct {
|
||||
FileSystems []*FSTabEntry `json:"filesystems"`
|
||||
}
|
||||
|
||||
func (FSTabStageOptions) isStageOptions() {}
|
||||
|
||||
// NewFSTabStage creates a now FSTabStage object
|
||||
func NewFSTabStage(options *FSTabStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.fstab",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// An FSTabEntry represents one line in /etc/fstab. With the one exception
|
||||
// that the the spec field must be represented as an UUID.
|
||||
type FSTabEntry struct {
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
VFSType string `json:"vfs_type"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Options string `json:"options,omitempty"`
|
||||
Freq uint64 `json:"freq,omitempty"`
|
||||
PassNo uint64 `json:"passno,omitempty"`
|
||||
}
|
||||
|
||||
// AddFilesystem adds one entry to and FSTabStageOptions object.
|
||||
func (options *FSTabStageOptions) AddFilesystem(id string, vfsType string, path string, opts string, freq uint64, passNo uint64) {
|
||||
options.FileSystems = append(options.FileSystems, &FSTabEntry{
|
||||
UUID: id,
|
||||
VFSType: vfsType,
|
||||
Path: path,
|
||||
Options: opts,
|
||||
Freq: freq,
|
||||
PassNo: passNo,
|
||||
})
|
||||
}
|
||||
52
internal/osbuild2/fstab_stage_test.go
Normal file
52
internal/osbuild2/fstab_stage_test.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewFSTabStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.fstab",
|
||||
Options: &FSTabStageOptions{},
|
||||
}
|
||||
actualStage := NewFSTabStage(&FSTabStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
|
||||
func TestAddFilesystem(t *testing.T) {
|
||||
options := &FSTabStageOptions{}
|
||||
filesystems := []*FSTabEntry{
|
||||
{
|
||||
UUID: "76a22bf4-f153-4541-b6c7-0332c0dfaeac",
|
||||
VFSType: "ext4",
|
||||
Path: "/",
|
||||
Options: "defaults",
|
||||
Freq: 1,
|
||||
PassNo: 1,
|
||||
},
|
||||
{
|
||||
UUID: "bba22bf4-f153-4541-b6c7-0332c0dfaeac",
|
||||
VFSType: "xfs",
|
||||
Path: "/home",
|
||||
Options: "defaults",
|
||||
Freq: 1,
|
||||
PassNo: 2,
|
||||
},
|
||||
{
|
||||
UUID: "cca22bf4-f153-4541-b6c7-0332c0dfaeac",
|
||||
VFSType: "xfs",
|
||||
Path: "/var",
|
||||
Options: "defaults",
|
||||
Freq: 1,
|
||||
PassNo: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for i, fs := range filesystems {
|
||||
options.AddFilesystem(fs.UUID, fs.VFSType, fs.Path, fs.Options, fs.Freq, fs.PassNo)
|
||||
assert.Equal(t, options.FileSystems[i], fs)
|
||||
}
|
||||
assert.Equal(t, len(filesystems), len(options.FileSystems))
|
||||
}
|
||||
19
internal/osbuild2/groups_stage.go
Normal file
19
internal/osbuild2/groups_stage.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package osbuild2
|
||||
|
||||
type GroupsStageOptions struct {
|
||||
Groups map[string]GroupsStageOptionsGroup `json:"groups"`
|
||||
}
|
||||
|
||||
func (GroupsStageOptions) isStageOptions() {}
|
||||
|
||||
type GroupsStageOptionsGroup struct {
|
||||
Name string `json:"name"`
|
||||
GID *int `json:"gid,omitempty"`
|
||||
}
|
||||
|
||||
func NewGroupsStage(options *GroupsStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.groups",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/groups_stage_test.go
Normal file
16
internal/osbuild2/groups_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewGroupsStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.groups",
|
||||
Options: &GroupsStageOptions{},
|
||||
}
|
||||
actualStage := NewGroupsStage(&GroupsStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
33
internal/osbuild2/grub2_stage.go
Normal file
33
internal/osbuild2/grub2_stage.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package osbuild2
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
// The GRUB2StageOptions describes the bootloader configuration.
|
||||
//
|
||||
// The stage is responsible for installing all bootloader files in
|
||||
// /boot as well as config files in /etc necessary for regenerating
|
||||
// the configuration in /boot.
|
||||
//
|
||||
// Note that it is the role of an assembler to install any necessary
|
||||
// bootloaders that are stored in the image outside of any filesystem.
|
||||
type GRUB2StageOptions struct {
|
||||
RootFilesystemUUID uuid.UUID `json:"root_fs_uuid"`
|
||||
BootFilesystemUUID *uuid.UUID `json:"boot_fs_uuid,omitempty"`
|
||||
KernelOptions string `json:"kernel_opts,omitempty"`
|
||||
Legacy string `json:"legacy,omitempty"`
|
||||
UEFI *GRUB2UEFI `json:"uefi,omitempty"`
|
||||
}
|
||||
|
||||
type GRUB2UEFI struct {
|
||||
Vendor string `json:"vendor"`
|
||||
}
|
||||
|
||||
func (GRUB2StageOptions) isStageOptions() {}
|
||||
|
||||
// NewGRUB2Stage creates a new GRUB2 stage object.
|
||||
func NewGRUB2Stage(options *GRUB2StageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.grub2",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/grub2_stage_test.go
Normal file
16
internal/osbuild2/grub2_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewGRUB2Stage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.grub2",
|
||||
Options: &GRUB2StageOptions{},
|
||||
}
|
||||
actualStage := NewGRUB2Stage(&GRUB2StageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
14
internal/osbuild2/hostname_stage.go
Normal file
14
internal/osbuild2/hostname_stage.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package osbuild2
|
||||
|
||||
type HostnameStageOptions struct {
|
||||
Hostname string `json:"hostname"`
|
||||
}
|
||||
|
||||
func (HostnameStageOptions) isStageOptions() {}
|
||||
|
||||
func NewHostnameStage(options *HostnameStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.hostname",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/hostname_stage_test.go
Normal file
16
internal/osbuild2/hostname_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewHostnameStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.hostname",
|
||||
Options: &HostnameStageOptions{},
|
||||
}
|
||||
actualStage := NewHostnameStage(&HostnameStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
19
internal/osbuild2/kernel_cmdline_stage.go
Normal file
19
internal/osbuild2/kernel_cmdline_stage.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package osbuild2
|
||||
|
||||
// KernelCmdlineStageOptions describe how to create kernel-cmdline stage
|
||||
//
|
||||
// Configures the kernel boot parameters, also known as the kernel command line.
|
||||
type KernelCmdlineStageOptions struct {
|
||||
RootFsUUID string `json:"root_fs_uuid,omitempty"`
|
||||
KernelOpts string `json:"kernel_opts,omitempty"`
|
||||
}
|
||||
|
||||
func (KernelCmdlineStageOptions) isStageOptions() {}
|
||||
|
||||
// NewKernelCmdlineStage creates a new kernel-cmdline Stage object.
|
||||
func NewKernelCmdlineStage(options *KernelCmdlineStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.kernel-cmdline",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/kernel_cmdline_stage_test.go
Normal file
16
internal/osbuild2/kernel_cmdline_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewKernelCmdlineStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.kernel-cmdline",
|
||||
Options: &KernelCmdlineStageOptions{},
|
||||
}
|
||||
actualStage := NewKernelCmdlineStage(&KernelCmdlineStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
14
internal/osbuild2/keymap_stage.go
Normal file
14
internal/osbuild2/keymap_stage.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package osbuild2
|
||||
|
||||
type KeymapStageOptions struct {
|
||||
Keymap string `json:"keymap"`
|
||||
}
|
||||
|
||||
func (KeymapStageOptions) isStageOptions() {}
|
||||
|
||||
func NewKeymapStage(options *KeymapStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.keymap",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/keymap_stage_test.go
Normal file
16
internal/osbuild2/keymap_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewKeymapStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.keymap",
|
||||
Options: &KeymapStageOptions{},
|
||||
}
|
||||
actualStage := NewKeymapStage(&KeymapStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
19
internal/osbuild2/locale_stage.go
Normal file
19
internal/osbuild2/locale_stage.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package osbuild2
|
||||
|
||||
// The LocaleStageOptions describes the image's locale.
|
||||
//
|
||||
// A locale is typically specified as language_[territory], where language
|
||||
// is specified in ISO 639 and territory in ISO 3166.
|
||||
type LocaleStageOptions struct {
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
func (LocaleStageOptions) isStageOptions() {}
|
||||
|
||||
// NewLocaleStage creates a new Locale Stage object.
|
||||
func NewLocaleStage(options *LocaleStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.locale",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/locale_stage_test.go
Normal file
16
internal/osbuild2/locale_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewLocaleStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.locale",
|
||||
Options: &LocaleStageOptions{},
|
||||
}
|
||||
actualStage := NewLocaleStage(&LocaleStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
51
internal/osbuild2/oci_archive_stage.go
Normal file
51
internal/osbuild2/oci_archive_stage.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package osbuild2
|
||||
|
||||
type OCIArchiveStageOptions struct {
|
||||
// The CPU architecture of the image
|
||||
Architecture string `json:"architecture"`
|
||||
|
||||
// Resulting image filename
|
||||
Filename string `json:"filename"`
|
||||
|
||||
// The execution parameters
|
||||
Config *OCIArchiveConfig `json:"config,omitempty"`
|
||||
}
|
||||
|
||||
type OCIArchiveConfig struct {
|
||||
Cmd []string `json:"Cmd,omitempty"`
|
||||
Env []string `json:"Env,omitempty"`
|
||||
ExposedPorts []string `json:"ExposedPorts,omitempty"`
|
||||
User string `json:"User,omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty"`
|
||||
StopSignal string `json:"StopSignal,omitempty"`
|
||||
Volumes []string `json:"Volumes,omitempty"`
|
||||
WorkingDir string `json:"WorkingDir,omitempty"`
|
||||
}
|
||||
|
||||
func (OCIArchiveStageOptions) isStageOptions() {}
|
||||
|
||||
type OCIArchiveStageInputs struct {
|
||||
Base *OCIArchiveStageInput `json:"base"`
|
||||
}
|
||||
|
||||
func (OCIArchiveStageInputs) isStageInputs() {}
|
||||
|
||||
type OCIArchiveStageInput struct {
|
||||
inputCommon
|
||||
References OCIArchiveStageReferences `json:"references"`
|
||||
}
|
||||
|
||||
func (OCIArchiveStageInput) isStageInput() {}
|
||||
|
||||
type OCIArchiveStageReferences []string
|
||||
|
||||
func (OCIArchiveStageReferences) isReferences() {}
|
||||
|
||||
// A new OCIArchiveStage to to assemble an OCI image archive
|
||||
func NewOCIArchiveStage(options *OCIArchiveStageOptions, inputs *OCIArchiveStageInputs) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.oci-archive",
|
||||
Options: options,
|
||||
Inputs: inputs,
|
||||
}
|
||||
}
|
||||
35
internal/osbuild2/osbuild.go
Normal file
35
internal/osbuild2/osbuild.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Package osbuild provides primitives for representing and (un)marshalling
|
||||
// OSBuild (schema v1) types.
|
||||
package osbuild2
|
||||
|
||||
// A Manifest represents an OSBuild source and pipeline manifest
|
||||
type Manifest struct {
|
||||
Version string `json:"version"`
|
||||
Pipelines []Pipeline `json:"pipelines"`
|
||||
Sources Sources `json:"sources"`
|
||||
}
|
||||
|
||||
// A Pipeline represents an OSBuild pipeline
|
||||
type Pipeline struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
// The build environment which can run this pipeline
|
||||
Build string `json:"build,omitempty"`
|
||||
|
||||
Runner string `json:"runner,omitempty"`
|
||||
|
||||
// Sequence of stages that produce the filesystem tree, which is the
|
||||
// payload of the produced image.
|
||||
Stages []*Stage `json:"stages,omitempty"`
|
||||
}
|
||||
|
||||
// SetBuild sets the pipeline and runner for generating the build environment
|
||||
// for a pipeline.
|
||||
func (p *Pipeline) SetBuild(build string) {
|
||||
p.Build = build
|
||||
}
|
||||
|
||||
// AddStage appends a stage to the list of stages of a pipeline. The stages
|
||||
// will be executed in the order they are appended.
|
||||
func (p *Pipeline) AddStage(stage *Stage) {
|
||||
p.Stages = append(p.Stages, stage)
|
||||
}
|
||||
28
internal/osbuild2/osbuild_test.go
Normal file
28
internal/osbuild2/osbuild_test.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Package osbuild provides primitives for representing and (un)marshalling
|
||||
// OSBuild types.
|
||||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPipeline_AddStage(t *testing.T) {
|
||||
expectedPipeline := &Pipeline{
|
||||
Build: "name:build",
|
||||
Stages: []*Stage{
|
||||
{
|
||||
Type: "org.osbuild.rpm",
|
||||
},
|
||||
},
|
||||
}
|
||||
actualPipeline := &Pipeline{
|
||||
Build: "name:build",
|
||||
}
|
||||
actualPipeline.AddStage(&Stage{
|
||||
Type: "org.osbuild.rpm",
|
||||
})
|
||||
assert.Equal(t, expectedPipeline, actualPipeline)
|
||||
assert.Equal(t, 1, len(actualPipeline.Stages))
|
||||
}
|
||||
41
internal/osbuild2/ostree_commit_stage.go
Normal file
41
internal/osbuild2/ostree_commit_stage.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package osbuild2
|
||||
|
||||
type OSTreeCommitStageOptions struct {
|
||||
// OStree ref to create for the commit
|
||||
Ref string `json:"ref"`
|
||||
|
||||
// Set the version of the OS as commit metadata
|
||||
OSVersion string `json:"os_version,omitempty"`
|
||||
|
||||
// Commit ID of the parent commit
|
||||
Parent string `json:"parent,omitempty"`
|
||||
}
|
||||
|
||||
func (OSTreeCommitStageOptions) isStageOptions() {}
|
||||
|
||||
type OSTreeCommitStageInput struct {
|
||||
inputCommon
|
||||
References OSTreeCommitStageReferences `json:"references"`
|
||||
}
|
||||
|
||||
func (OSTreeCommitStageInput) isStageInput() {}
|
||||
|
||||
type OSTreeCommitStageInputs struct {
|
||||
Tree *OSTreeCommitStageInput `json:"tree"`
|
||||
}
|
||||
|
||||
func (OSTreeCommitStageInputs) isStageInputs() {}
|
||||
|
||||
type OSTreeCommitStageReferences []string
|
||||
|
||||
func (OSTreeCommitStageReferences) isReferences() {}
|
||||
|
||||
// The OSTreeCommitStage (org.osbuild.ostree.commit) describes how to assemble
|
||||
// a tree into an OSTree commit.
|
||||
func NewOSTreeCommitStage(options *OSTreeCommitStageOptions, inputs *OSTreeCommitStageInputs) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.ostree.commit",
|
||||
Options: options,
|
||||
Inputs: inputs,
|
||||
}
|
||||
}
|
||||
29
internal/osbuild2/ostree_init_stage.go
Normal file
29
internal/osbuild2/ostree_init_stage.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package osbuild2
|
||||
|
||||
type InitMode string
|
||||
|
||||
const (
|
||||
ModeBare InitMode = "bare"
|
||||
ModeBareUser InitMode = "bare-user"
|
||||
ModeBareUserOnly InitMode = "bare-user-only"
|
||||
ModeArchvie InitMode = "archive"
|
||||
)
|
||||
|
||||
// Options for the org.osbuild.ostree.init stage.
|
||||
type OSTreeInitStageOptions struct {
|
||||
// The Mode in which to initialise the repo
|
||||
Mode InitMode `json:"mode,omitempty"`
|
||||
|
||||
// Location in which to create the repo
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func (OSTreeInitStageOptions) isStageOptions() {}
|
||||
|
||||
// A new org.osbuild.ostree.init stage to create an OSTree repository
|
||||
func NewOSTreeInitStage(options *OSTreeInitStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.ostree.init",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
15
internal/osbuild2/ostree_input.go
Normal file
15
internal/osbuild2/ostree_input.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package osbuild2
|
||||
|
||||
// Inputs for ostree commits
|
||||
type OSTreeInput struct {
|
||||
inputCommon
|
||||
}
|
||||
|
||||
func (OSTreeInput) isInput() {}
|
||||
|
||||
func NewOSTreeInput() *OSTreeInput {
|
||||
input := new(OSTreeInput)
|
||||
input.Type = "org.osbuild.ostree"
|
||||
input.Origin = "org.osbuild.source"
|
||||
return input
|
||||
}
|
||||
16
internal/osbuild2/ostree_preptree_stage.go
Normal file
16
internal/osbuild2/ostree_preptree_stage.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
type OSTreePrepTreeStageOptions struct {
|
||||
EtcGroupMembers []string `json:"etc_group_members,omitempty"`
|
||||
}
|
||||
|
||||
func (OSTreePrepTreeStageOptions) isStageOptions() {}
|
||||
|
||||
// The OSTree PrepTree (org.osbuild.ostree.preptree) stage transforms the
|
||||
// tree to an ostree layout.
|
||||
func NewOSTreePrepTreeStage(options *OSTreePrepTreeStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.ostree.preptree",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
39
internal/osbuild2/ostree_pull_stage.go
Normal file
39
internal/osbuild2/ostree_pull_stage.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package osbuild2
|
||||
|
||||
// Options for the org.osbuild.ostree.pull stage.
|
||||
type OSTreePullStageOptions struct {
|
||||
// Location of the ostree repo
|
||||
Repo string `json:"repo"`
|
||||
}
|
||||
|
||||
func (OSTreePullStageOptions) isStageOptions() {}
|
||||
|
||||
// A new org.osbuild.ostree.pull stage to pull OSTree commits into an existing repo
|
||||
func NewOSTreePullStage(options *OSTreePullStageOptions, inputs Inputs) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.ostree.pull",
|
||||
Inputs: inputs,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
type OSTreePullStageInput struct {
|
||||
inputCommon
|
||||
References OSTreePullStageReferences `json:"references"`
|
||||
}
|
||||
|
||||
func (OSTreePullStageInput) isStageInput() {}
|
||||
|
||||
type OSTreePullStageInputs struct {
|
||||
Commits *OSTreePullStageInput `json:"commits"`
|
||||
}
|
||||
|
||||
func (OSTreePullStageInputs) isStageInputs() {}
|
||||
|
||||
type OSTreePullStageReferences map[string]OSTreePullStageReference
|
||||
|
||||
func (OSTreePullStageReferences) isReferences() {}
|
||||
|
||||
type OSTreePullStageReference struct {
|
||||
Ref string `json:"ref"`
|
||||
}
|
||||
19
internal/osbuild2/ostree_source.go
Normal file
19
internal/osbuild2/ostree_source.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package osbuild2
|
||||
|
||||
// The commits to fetch indexed their checksum
|
||||
type OSTreeSource struct {
|
||||
Items map[string]OSTreeSourceItem `json:"items"`
|
||||
}
|
||||
|
||||
func (OSTreeSource) isSource() {}
|
||||
|
||||
type OSTreeSourceItem struct {
|
||||
Remote OSTreeRemote `json:"remote"`
|
||||
}
|
||||
|
||||
type OSTreeRemote struct {
|
||||
// URL of the repository.
|
||||
URL string `json:"url"`
|
||||
// GPG keys to verify the commits
|
||||
GPGKeys []string `json:"secrets,omitempty"`
|
||||
}
|
||||
22
internal/osbuild2/result.go
Normal file
22
internal/osbuild2/result.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type PipelineResult []StageResult
|
||||
|
||||
type StageResult struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Output string `json:"output"`
|
||||
Success bool `json:"success,omitempty"`
|
||||
Error string `json:"string,omitempty"`
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Type string `json:"type"`
|
||||
Success bool `json:"success"`
|
||||
Error json.RawMessage `json:"error"`
|
||||
Log map[string]PipelineResult `json:"log"`
|
||||
}
|
||||
1
internal/osbuild2/result_test.go
Normal file
1
internal/osbuild2/result_test.go
Normal file
|
|
@ -0,0 +1 @@
|
|||
package osbuild2
|
||||
34
internal/osbuild2/rhsm_stage.go
Normal file
34
internal/osbuild2/rhsm_stage.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package osbuild2
|
||||
|
||||
// RHSMStageOptions describes configuration of the RHSM stage.
|
||||
//
|
||||
// The RHSM stage allows configuration of Red Hat Subscription Manager (RHSM)
|
||||
// related components. Currently it allows only configuration of the enablement
|
||||
// state of DNF plugins used by the Subscription Manager.
|
||||
type RHSMStageOptions struct {
|
||||
DnfPlugins *RHSMStageOptionsDnfPlugins `json:"dnf-plugins,omitempty"`
|
||||
}
|
||||
|
||||
func (RHSMStageOptions) isStageOptions() {}
|
||||
|
||||
// RHSMStageOptionsDnfPlugins describes configuration of all RHSM DNF plugins
|
||||
type RHSMStageOptionsDnfPlugins struct {
|
||||
ProductID *RHSMStageOptionsDnfPlugin `json:"product-id,omitempty"`
|
||||
SubscriptionManager *RHSMStageOptionsDnfPlugin `json:"subscription-manager,omitempty"`
|
||||
}
|
||||
|
||||
// RHSMStageOptionsDnfPlugin describes configuration of a specific RHSM DNF
|
||||
// plugin
|
||||
//
|
||||
// Only the enablement state of a DNF plugin can be currenlty set.
|
||||
type RHSMStageOptionsDnfPlugin struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// NewRHSMStage creates a new RHSM stage
|
||||
func NewRHSMStage(options *RHSMStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.rhsm",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/rhsm_stage_test.go
Normal file
16
internal/osbuild2/rhsm_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewRhsmStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.rhsm",
|
||||
Options: &RHSMStageOptions{},
|
||||
}
|
||||
actualStage := NewRHSMStage(&RHSMStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
53
internal/osbuild2/rpm_stage.go
Normal file
53
internal/osbuild2/rpm_stage.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package osbuild2
|
||||
|
||||
type RPMStageOptions struct {
|
||||
// Array of GPG key contents to import
|
||||
GPGKeys []string `json:"gpgkeys,omitempty"`
|
||||
|
||||
// Prevent dracut from running
|
||||
DisableDracut bool `json:"disable_dracut,omitempty"`
|
||||
|
||||
Exclude *Exclude `json:"exclude,omitempty"`
|
||||
}
|
||||
|
||||
type Exclude struct {
|
||||
// Do not install documentation.
|
||||
Docs bool `json:"docs,omitempty"`
|
||||
}
|
||||
|
||||
// RPMPackage represents one RPM, as referenced by its content hash
|
||||
// (checksum). The files source must indicate where to fetch the given
|
||||
// RPM. If CheckGPG is `true` the RPM must be signed with one of the
|
||||
// GPGKeys given in the RPMStageOptions.
|
||||
type RPMPackage struct {
|
||||
Checksum string `json:"checksum"`
|
||||
CheckGPG bool `json:"check_gpg,omitempty"`
|
||||
}
|
||||
|
||||
func (RPMStageOptions) isStageOptions() {}
|
||||
|
||||
type RPMStageInputs struct {
|
||||
Packages *RPMStageInput `json:"packages"`
|
||||
}
|
||||
|
||||
func (RPMStageInputs) isStageInputs() {}
|
||||
|
||||
type RPMStageInput struct {
|
||||
inputCommon
|
||||
References RPMStageReferences `json:"references"`
|
||||
}
|
||||
|
||||
func (RPMStageInput) isStageInput() {}
|
||||
|
||||
type RPMStageReferences []string
|
||||
|
||||
func (RPMStageReferences) isReferences() {}
|
||||
|
||||
// NewRPMStage creates a new RPM stage.
|
||||
func NewRPMStage(options *RPMStageOptions, inputs *RPMStageInputs) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.rpm",
|
||||
Inputs: inputs,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
17
internal/osbuild2/rpm_stage_test.go
Normal file
17
internal/osbuild2/rpm_stage_test.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewRPMStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.rpm",
|
||||
Options: &RPMStageOptions{},
|
||||
Inputs: &RPMStageInputs{},
|
||||
}
|
||||
actualStage := NewRPMStage(&RPMStageOptions{}, &RPMStageInputs{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
24
internal/osbuild2/script_stage.go
Normal file
24
internal/osbuild2/script_stage.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package osbuild2
|
||||
|
||||
// The ScriptStageOptions specifies a custom script to run in the image
|
||||
type ScriptStageOptions struct {
|
||||
Script string `json:"script"`
|
||||
}
|
||||
|
||||
func (ScriptStageOptions) isStageOptions() {}
|
||||
|
||||
// NewScriptStageOptions creates a new script stage options object, with
|
||||
// the mandatory fields set.
|
||||
func NewScriptStageOptions(script string) *ScriptStageOptions {
|
||||
return &ScriptStageOptions{
|
||||
Script: script,
|
||||
}
|
||||
}
|
||||
|
||||
// NewScriptStage creates a new Script Stage object.
|
||||
func NewScriptStage(options *ScriptStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.script",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
24
internal/osbuild2/script_stage_test.go
Normal file
24
internal/osbuild2/script_stage_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewScriptStageOptions(t *testing.T) {
|
||||
expectedOptions := &ScriptStageOptions{
|
||||
Script: "/root/test.sh",
|
||||
}
|
||||
actualOptions := NewScriptStageOptions("/root/test.sh")
|
||||
assert.Equal(t, expectedOptions, actualOptions)
|
||||
}
|
||||
|
||||
func TestNewScriptStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.script",
|
||||
Options: &ScriptStageOptions{},
|
||||
}
|
||||
actualStage := NewScriptStage(&ScriptStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
28
internal/osbuild2/selinux_stage.go
Normal file
28
internal/osbuild2/selinux_stage.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package osbuild2
|
||||
|
||||
// The SELinuxStageOptions describe how to apply selinux labels.
|
||||
//
|
||||
// A file contexts configuration file is sepcified that describes
|
||||
// the filesystem labels to apply to the image.
|
||||
type SELinuxStageOptions struct {
|
||||
FileContexts string `json:"file_contexts"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
func (SELinuxStageOptions) isStageOptions() {}
|
||||
|
||||
// NewSELinuxStageOptions creates a new SELinuxStaeOptions object, with
|
||||
// the mandatory fields set.
|
||||
func NewSELinuxStageOptions(fileContexts string) *SELinuxStageOptions {
|
||||
return &SELinuxStageOptions{
|
||||
FileContexts: fileContexts,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSELinuxStage creates a new SELinux Stage object.
|
||||
func NewSELinuxStage(options *SELinuxStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.selinux",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
24
internal/osbuild2/selinux_stage_test.go
Normal file
24
internal/osbuild2/selinux_stage_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewSELinuxStageOptions(t *testing.T) {
|
||||
expectedOptions := &SELinuxStageOptions{
|
||||
FileContexts: "etc/selinux/targeted/contexts/files/file_contexts",
|
||||
}
|
||||
actualOptions := NewSELinuxStageOptions("etc/selinux/targeted/contexts/files/file_contexts")
|
||||
assert.Equal(t, expectedOptions, actualOptions)
|
||||
}
|
||||
|
||||
func TestNewSELinuxStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.selinux",
|
||||
Options: &SELinuxStageOptions{},
|
||||
}
|
||||
actualStage := NewSELinuxStage(&SELinuxStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
49
internal/osbuild2/source.go
Normal file
49
internal/osbuild2/source.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// A Sources map contains all the sources made available to an osbuild run
|
||||
type Sources map[string]Source
|
||||
|
||||
// Source specifies the operations of a given source-type.
|
||||
type Source interface {
|
||||
isSource()
|
||||
}
|
||||
|
||||
type SourceOptions interface {
|
||||
isSourceOptions()
|
||||
}
|
||||
|
||||
type rawSources map[string]json.RawMessage
|
||||
|
||||
// UnmarshalJSON unmarshals JSON into a Source object. Each type of source has
|
||||
// a custom unmarshaller for its options, selected based on the source name.
|
||||
func (sources *Sources) UnmarshalJSON(data []byte) error {
|
||||
var rawSources rawSources
|
||||
err := json.Unmarshal(data, &rawSources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*sources = make(map[string]Source)
|
||||
for name, rawSource := range rawSources {
|
||||
var source Source
|
||||
switch name {
|
||||
case "org.osbuild.curl":
|
||||
source = new(CurlSource)
|
||||
case "org.osbuild.ostree":
|
||||
source = new(OSTreeSource)
|
||||
default:
|
||||
return errors.New("unexpected source name: " + name)
|
||||
}
|
||||
err = json.Unmarshal(rawSource, source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
(*sources)[name] = source
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
115
internal/osbuild2/source_test.go
Normal file
115
internal/osbuild2/source_test.go
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSource_UnmarshalJSON(t *testing.T) {
|
||||
type fields struct {
|
||||
Type string
|
||||
Source Source
|
||||
}
|
||||
type args struct {
|
||||
data []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid json",
|
||||
args: args{
|
||||
data: []byte(`{"name":"org.osbuild.foo","options":{"bar":null}`),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unknown source",
|
||||
args: args{
|
||||
data: []byte(`{"name":"org.osbuild.foo","options":{"bar":null}}`),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing options",
|
||||
args: args{
|
||||
data: []byte(`{"name":"org.osbuild.curl"}`),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing name",
|
||||
args: args{
|
||||
data: []byte(`{"foo":null,"options":{"bar":null}}`),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "curl-empty",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.curl",
|
||||
Source: &CurlSource{Items: map[string]CurlSourceItem{}},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"org.osbuild.curl":{"items":{}}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "curl-with-secrets",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.curl",
|
||||
Source: &CurlSource{
|
||||
Items: map[string]CurlSourceItem{
|
||||
"checksum1": URLWithSecrets{URL: "url1", Secrets: &URLSecrets{Name: "org.osbuild.rhsm"}},
|
||||
"checksum2": URLWithSecrets{URL: "url2", Secrets: &URLSecrets{Name: "whatever"}},
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"org.osbuild.curl":{"items":{"checksum1":{"url":"url1","secrets":{"name":"org.osbuild.rhsm"}},"checksum2":{"url":"url2","secrets":{"name":"whatever"}}}}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "curl-url-only",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.curl",
|
||||
Source: &CurlSource{
|
||||
Items: map[string]CurlSourceItem{
|
||||
"checksum1": URL("url1"),
|
||||
"checksum2": URL("url2"),
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"org.osbuild.curl":{"items":{"checksum1":"url1","checksum2":"url2"}}}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
for idx, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sources := &Sources{
|
||||
tt.fields.Type: tt.fields.Source,
|
||||
}
|
||||
var gotSources Sources
|
||||
if err := gotSources.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Sources.UnmarshalJSON() error = %v, wantErr %v [idx: %d]", err, tt.wantErr, idx)
|
||||
}
|
||||
if tt.wantErr {
|
||||
return
|
||||
}
|
||||
gotBytes, err := json.Marshal(sources)
|
||||
if err != nil {
|
||||
t.Errorf("Could not marshal source: %v [idx: %d]", err, idx)
|
||||
}
|
||||
if !bytes.Equal(gotBytes, tt.args.data) {
|
||||
t.Errorf("Expected '%v', got '%v' [idx: %d]", string(tt.args.data), string(gotBytes), idx)
|
||||
}
|
||||
if !reflect.DeepEqual(&gotSources, sources) {
|
||||
t.Errorf("got '%v', expected '%v' [idx:%d]", &gotSources, sources, idx)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
131
internal/osbuild2/stage.go
Normal file
131
internal/osbuild2/stage.go
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Single stage of a pipeline executing one step
|
||||
type Stage struct {
|
||||
// Well-known name in reverse domain-name notation, uniquely identifying
|
||||
// the stage type.
|
||||
Type string `json:"type"`
|
||||
// Stage-type specific options fully determining the operations of the
|
||||
|
||||
Inputs Inputs `json:"inputs,omitempty"`
|
||||
Options StageOptions `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
// Collection of Inputs for a Stage
|
||||
type Inputs interface {
|
||||
isStageInputs()
|
||||
}
|
||||
|
||||
// Single Input for a Stage
|
||||
type Input interface {
|
||||
isInput()
|
||||
}
|
||||
|
||||
// Fields shared between all Input types (should be embedded in each instance)
|
||||
type inputCommon struct {
|
||||
Type string `json:"type"`
|
||||
// Origin should be either 'org.osbuild.source' or 'org.osbuild.pipeline'
|
||||
Origin string `json:"origin"`
|
||||
|
||||
// References References `json:"references"`
|
||||
}
|
||||
|
||||
type StageInput interface {
|
||||
isStageInput()
|
||||
}
|
||||
|
||||
type References interface {
|
||||
isReferences()
|
||||
}
|
||||
|
||||
// StageOptions specify the operations of a given stage-type.
|
||||
type StageOptions interface {
|
||||
isStageOptions()
|
||||
}
|
||||
|
||||
type InputOptions interface {
|
||||
}
|
||||
|
||||
type rawStage struct {
|
||||
Type string `json:"type"`
|
||||
Options json.RawMessage `json:"options"`
|
||||
Inputs json.RawMessage `json:"inputs"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals JSON into a Stage object. Each type of stage has
|
||||
// a custom unmarshaller for its options, selected based on the stage name.
|
||||
func (stage *Stage) UnmarshalJSON(data []byte) error {
|
||||
var rawStage rawStage
|
||||
if err := json.Unmarshal(data, &rawStage); err != nil {
|
||||
return err
|
||||
}
|
||||
var options StageOptions
|
||||
var inputs Inputs
|
||||
switch rawStage.Type {
|
||||
case "org.osbuild.fix-bls":
|
||||
options = new(FixBLSStageOptions)
|
||||
case "org.osbuild.fstab":
|
||||
options = new(FSTabStageOptions)
|
||||
case "org.osbuild.grub2":
|
||||
options = new(GRUB2StageOptions)
|
||||
case "org.osbuild.locale":
|
||||
options = new(LocaleStageOptions)
|
||||
case "org.osbuild.selinux":
|
||||
options = new(SELinuxStageOptions)
|
||||
case "org.osbuild.hostname":
|
||||
options = new(HostnameStageOptions)
|
||||
case "org.osbuild.users":
|
||||
options = new(UsersStageOptions)
|
||||
case "org.osbuild.groups":
|
||||
options = new(GroupsStageOptions)
|
||||
case "org.osbuild.timezone":
|
||||
options = new(TimezoneStageOptions)
|
||||
case "org.osbuild.chrony":
|
||||
options = new(ChronyStageOptions)
|
||||
case "org.osbuild.keymap":
|
||||
options = new(KeymapStageOptions)
|
||||
case "org.osbuild.firewall":
|
||||
options = new(FirewallStageOptions)
|
||||
case "org.osbuild.rhsm":
|
||||
options = new(RHSMStageOptions)
|
||||
case "org.osbuild.systemd":
|
||||
options = new(SystemdStageOptions)
|
||||
case "org.osbuild.script":
|
||||
options = new(ScriptStageOptions)
|
||||
case "org.osbuild.rpm":
|
||||
options = new(RPMStageOptions)
|
||||
inputs = new(RPMStageInputs)
|
||||
case "org.osbuild.oci-archive":
|
||||
options = new(OCIArchiveStageOptions)
|
||||
inputs = new(OCIArchiveStageInputs)
|
||||
case "org.osbuild.ostree.commit":
|
||||
options = new(OSTreeCommitStageOptions)
|
||||
inputs = new(OSTreeCommitStageInputs)
|
||||
case "org.osbuild.ostree.pull":
|
||||
options = new(OSTreePullStageOptions)
|
||||
inputs = new(OSTreePullStageInputs)
|
||||
case "org.osbuild.ostree.preptree":
|
||||
options = new(RPMOSTreePrepTreeStageOptions)
|
||||
default:
|
||||
return fmt.Errorf("unexpected stage type: %s", rawStage.Type)
|
||||
}
|
||||
if err := json.Unmarshal(rawStage.Options, options); err != nil {
|
||||
return err
|
||||
}
|
||||
if inputs != nil && rawStage.Inputs != nil {
|
||||
if err := json.Unmarshal(rawStage.Inputs, inputs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
stage.Type = rawStage.Type
|
||||
stage.Options = options
|
||||
stage.Inputs = inputs
|
||||
|
||||
return nil
|
||||
}
|
||||
383
internal/osbuild2/stage_test.go
Normal file
383
internal/osbuild2/stage_test.go
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestStage_UnmarshalJSON(t *testing.T) {
|
||||
nullUUID := uuid.MustParse("00000000-0000-0000-0000-000000000000")
|
||||
type fields struct {
|
||||
Type string
|
||||
Options StageOptions
|
||||
}
|
||||
type args struct {
|
||||
data []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid json",
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.foo","options":{"bar":null}}`),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unknown stage",
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.foo","options":{"bar":null}}`),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing options",
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.locale"}`),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing name",
|
||||
args: args{
|
||||
data: []byte(`{"foo":null,"options":{"bar":null}}`),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "chrony",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.chrony",
|
||||
Options: &ChronyStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.chrony","options":{"timeservers":null}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "firewall",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.firewall",
|
||||
Options: &FirewallStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.firewall","options":{}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fix-bls",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.fix-bls",
|
||||
Options: &FixBLSStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.fix-bls","options":{}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fstab",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.fstab",
|
||||
Options: &FSTabStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.fstab","options":{"filesystems":null}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "groups",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.groups",
|
||||
Options: &GroupsStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.groups","options":{"groups":null}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "grub2",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.grub2",
|
||||
Options: &GRUB2StageOptions{
|
||||
RootFilesystemUUID: nullUUID,
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000"}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "grub2-uefi",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.grub2",
|
||||
Options: &GRUB2StageOptions{
|
||||
RootFilesystemUUID: nullUUID,
|
||||
UEFI: &GRUB2UEFI{
|
||||
Vendor: "vendor",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000","uefi":{"vendor":"vendor"}}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "grub2-separate-boot",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.grub2",
|
||||
Options: &GRUB2StageOptions{
|
||||
RootFilesystemUUID: nullUUID,
|
||||
BootFilesystemUUID: &nullUUID,
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000","boot_fs_uuid":"00000000-0000-0000-0000-000000000000"}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hostname",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.hostname",
|
||||
Options: &HostnameStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.hostname","options":{"hostname":""}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keymap",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.keymap",
|
||||
Options: &KeymapStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.keymap","options":{"keymap":""}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "locale",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.locale",
|
||||
Options: &LocaleStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.locale","options":{"language":""}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rhsm-empty",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.rhsm",
|
||||
Options: &RHSMStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.rhsm","options":{}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rhsm",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.rhsm",
|
||||
Options: &RHSMStageOptions{
|
||||
DnfPlugins: &RHSMStageOptionsDnfPlugins{
|
||||
ProductID: &RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
SubscriptionManager: &RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.rhsm","options":{"dnf-plugins":{"product-id":{"enabled":false},"subscription-manager":{"enabled":false}}}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "script",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.script",
|
||||
Options: &ScriptStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.script","options":{"script":""}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "selinux",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.selinux",
|
||||
Options: &SELinuxStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.selinux","options":{"file_contexts":""}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "systemd",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.systemd",
|
||||
Options: &SystemdStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.systemd","options":{}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "systemd-enabled",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.systemd",
|
||||
Options: &SystemdStageOptions{
|
||||
EnabledServices: []string{"foo.service"},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.systemd","options":{"enabled_services":["foo.service"]}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "timezone",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.timezone",
|
||||
Options: &TimezoneStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.timezone","options":{"zone":""}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "users",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.users",
|
||||
Options: &UsersStageOptions{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.users","options":{"users":null}}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
for idx, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stage := &Stage{
|
||||
Type: tt.fields.Type,
|
||||
Options: tt.fields.Options,
|
||||
}
|
||||
var gotStage Stage
|
||||
if err := gotStage.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Stage.UnmarshalJSON() error = %v, wantErr %v [idx: %d]", err, tt.wantErr, idx)
|
||||
}
|
||||
if tt.wantErr {
|
||||
return
|
||||
}
|
||||
gotBytes, err := json.Marshal(stage)
|
||||
if err != nil {
|
||||
t.Errorf("Could not marshal stage: %v", err)
|
||||
}
|
||||
if !bytes.Equal(gotBytes, tt.args.data) {
|
||||
t.Errorf("Expected `%v`, got `%v` [idx: %d]", string(tt.args.data), string(gotBytes), idx)
|
||||
}
|
||||
if !reflect.DeepEqual(&gotStage, stage) {
|
||||
t.Errorf("got {%v, %v}, expected {%v, %v} [%d]", gotStage.Type, gotStage.Options, stage.Type, stage.Options, idx)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test new stages that have Inputs (osbuild v2 schema)
|
||||
func TestStageV2_UnmarshalJSON(t *testing.T) {
|
||||
type fields struct {
|
||||
Type string
|
||||
Options StageOptions
|
||||
Inputs Inputs
|
||||
}
|
||||
type args struct {
|
||||
data []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "rpm-empty",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.rpm",
|
||||
Options: &RPMStageOptions{},
|
||||
Inputs: &RPMStageInputs{},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.rpm","inputs":{"packages":null},"options":{}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rpm",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.rpm",
|
||||
Inputs: &RPMStageInputs{
|
||||
Packages: &RPMStageInput{
|
||||
References: RPMStageReferences{
|
||||
"checksum1",
|
||||
"checksum2",
|
||||
},
|
||||
},
|
||||
},
|
||||
Options: &RPMStageOptions{
|
||||
GPGKeys: []string{"key1", "key2"},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.rpm","inputs":{"packages":{"type":"","origin":"","references":["checksum1","checksum2"]}},"options":{"gpgkeys":["key1","key2"]}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ostree-preptree",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.ostree.preptree",
|
||||
Options: &OSTreePrepTreeStageOptions{
|
||||
EtcGroupMembers: []string{
|
||||
"wheel",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.ostree.preptree","options":{"etc_group_members":["wheel"]}}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
for idx, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stage := &Stage{
|
||||
Type: tt.fields.Type,
|
||||
Options: tt.fields.Options,
|
||||
Inputs: tt.fields.Inputs,
|
||||
}
|
||||
var gotStage Stage
|
||||
if err := gotStage.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
|
||||
println("data: ", string(tt.args.data))
|
||||
t.Errorf("Stage.UnmarshalJSON() error = %v, wantErr %v [idx: %d]", err, tt.wantErr, idx)
|
||||
}
|
||||
if tt.wantErr {
|
||||
return
|
||||
}
|
||||
gotBytes, err := json.Marshal(stage)
|
||||
if err != nil {
|
||||
t.Errorf("Could not marshal stage: %v", err)
|
||||
}
|
||||
if !bytes.Equal(gotBytes, tt.args.data) {
|
||||
t.Errorf("Expected `%v`, got `%v` [idx: %d]", string(tt.args.data), string(gotBytes), idx)
|
||||
}
|
||||
if !reflect.DeepEqual(&gotStage, stage) {
|
||||
t.Errorf("got {%v, %v, %v}, expected {%v, %v, %v} [%d]", gotStage.Type, gotStage.Options, gotStage.Inputs, stage.Type, stage.Options, stage.Inputs, idx)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
25
internal/osbuild2/sysconfig_stage.go
Normal file
25
internal/osbuild2/sysconfig_stage.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package osbuild2
|
||||
|
||||
type SysconfigStageOptions struct {
|
||||
Kernel SysconfigKernelOptions `json:"kernel,omitempty"`
|
||||
Network SysconfigNetworkOptions `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
type SysconfigNetworkOptions struct {
|
||||
Networking bool `json:"networking,omitempty"`
|
||||
NoZeroConf bool `json:"no_zero_conf,omitempty"`
|
||||
}
|
||||
|
||||
type SysconfigKernelOptions struct {
|
||||
UpdateDefault bool `json:"update_default,omitempty"`
|
||||
DefaultKernel string `json:"default_kernel,omitempty"`
|
||||
}
|
||||
|
||||
func (SysconfigStageOptions) isStageOptions() {}
|
||||
|
||||
func NewSysconfigStage(options *SysconfigStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.sysconfig",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/sysconfig_stage_test.go
Normal file
16
internal/osbuild2/sysconfig_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewSysconfigStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.sysconfig",
|
||||
Options: &SysconfigStageOptions{},
|
||||
}
|
||||
actualStage := NewSysconfigStage(&SysconfigStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
16
internal/osbuild2/systemd_stage.go
Normal file
16
internal/osbuild2/systemd_stage.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
type SystemdStageOptions struct {
|
||||
EnabledServices []string `json:"enabled_services,omitempty"`
|
||||
DisabledServices []string `json:"disabled_services,omitempty"`
|
||||
DefaultTarget string `json:"default_target,omitempty"`
|
||||
}
|
||||
|
||||
func (SystemdStageOptions) isStageOptions() {}
|
||||
|
||||
func NewSystemdStage(options *SystemdStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.systemd",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/systemd_stage_test.go
Normal file
16
internal/osbuild2/systemd_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewSystemdStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.systemd",
|
||||
Options: &SystemdStageOptions{},
|
||||
}
|
||||
actualStage := NewSystemdStage(&SystemdStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
14
internal/osbuild2/timezone_stage.go
Normal file
14
internal/osbuild2/timezone_stage.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package osbuild2
|
||||
|
||||
type TimezoneStageOptions struct {
|
||||
Zone string `json:"zone"`
|
||||
}
|
||||
|
||||
func (TimezoneStageOptions) isStageOptions() {}
|
||||
|
||||
func NewTimezoneStage(options *TimezoneStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.timezone",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/timezone_stage_test.go
Normal file
16
internal/osbuild2/timezone_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewTimezoneStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.timezone",
|
||||
Options: &TimezoneStageOptions{},
|
||||
}
|
||||
actualStage := NewTimezoneStage(&TimezoneStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
15
internal/osbuild2/tree_input.go
Normal file
15
internal/osbuild2/tree_input.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package osbuild2
|
||||
|
||||
// Tree inputs
|
||||
type TreeInput struct {
|
||||
inputCommon
|
||||
}
|
||||
|
||||
func (TreeInput) isInput() {}
|
||||
|
||||
func NewTreeInput() *TreeInput {
|
||||
input := new(TreeInput)
|
||||
input.Type = "org.osbuild.tree"
|
||||
input.Origin = "org.osbuild.pipeline"
|
||||
return input
|
||||
}
|
||||
25
internal/osbuild2/users_stage.go
Normal file
25
internal/osbuild2/users_stage.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package osbuild2
|
||||
|
||||
type UsersStageOptions struct {
|
||||
Users map[string]UsersStageOptionsUser `json:"users"`
|
||||
}
|
||||
|
||||
func (UsersStageOptions) isStageOptions() {}
|
||||
|
||||
type UsersStageOptionsUser struct {
|
||||
UID *int `json:"uid,omitempty"`
|
||||
GID *int `json:"gid,omitempty"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Home *string `json:"home,omitempty"`
|
||||
Shell *string `json:"shell,omitempty"`
|
||||
Password *string `json:"password,omitempty"`
|
||||
Key *string `json:"key,omitempty"`
|
||||
}
|
||||
|
||||
func NewUsersStage(options *UsersStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.users",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/users_stage_test.go
Normal file
16
internal/osbuild2/users_stage_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewUsersStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.users",
|
||||
Options: &UsersStageOptions{},
|
||||
}
|
||||
actualStage := NewUsersStage(&UsersStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
25
internal/osbuild2/zipl_stage.go
Normal file
25
internal/osbuild2/zipl_stage.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package osbuild2
|
||||
|
||||
// The ZiplStageOptions describe how to create zipl stage
|
||||
//
|
||||
// The only configuration option available is a boot timeout and it is optional
|
||||
type ZiplStageOptions struct {
|
||||
Timeout int `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
func (ZiplStageOptions) isStageOptions() {}
|
||||
|
||||
// NewZiplStageOptions creates a new ZiplStageOptions object with no timeout
|
||||
func NewZiplStageOptions() *ZiplStageOptions {
|
||||
return &ZiplStageOptions{
|
||||
Timeout: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// NewZiplStage creates a new zipl Stage object.
|
||||
func NewZiplStage(options *ZiplStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.zipl",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
24
internal/osbuild2/zipl_stage_test.go
Normal file
24
internal/osbuild2/zipl_stage_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewZiplStageOptions(t *testing.T) {
|
||||
expectedOptions := &ZiplStageOptions{
|
||||
Timeout: 0,
|
||||
}
|
||||
actualOptions := NewZiplStageOptions()
|
||||
assert.Equal(t, expectedOptions, actualOptions)
|
||||
}
|
||||
|
||||
func TestNewZiplStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.zipl",
|
||||
Options: &ZiplStageOptions{},
|
||||
}
|
||||
actualStage := NewZiplStage(&ZiplStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue