Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

manifest,image: add Files,Directories to bootc customizations #1227

Merged
merged 2 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/image/bootc_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/rand"

"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/manifest"
Expand All @@ -31,6 +32,10 @@ type BootcDiskImage struct {
Users []users.User
Groups []users.Group

// Custom directories and files to create in the image
Directories []*fsnode.Directory
Files []*fsnode.File

// SELinux policy, when set it enables the labeling of the tree with the
// selected profile
SELinux string
Expand Down Expand Up @@ -59,6 +64,8 @@ func (img *BootcDiskImage) InstantiateManifestFromContainers(m *manifest.Manifes
rawImage.PartitionTable = img.PartitionTable
rawImage.Users = img.Users
rawImage.Groups = img.Groups
rawImage.Files = img.Files
rawImage.Directories = img.Directories
rawImage.KernelOptionsAppend = img.KernelOptionsAppend
rawImage.SELinux = img.SELinux

Expand Down
45 changes: 45 additions & 0 deletions pkg/image/bootc_disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/osbuild/images/internal/testdisk"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/image"
"github.com/osbuild/images/pkg/manifest"
Expand Down Expand Up @@ -42,6 +43,8 @@ type bootcDiskImageTestOpts struct {
SELinux string
Users []users.User
Groups []users.Group
Directories []*fsnode.Directory
Files []*fsnode.File

KernelOptionsAppend []string
}
Expand Down Expand Up @@ -77,6 +80,8 @@ func makeBootcDiskImageOsbuildManifest(t *testing.T, opts *bootcDiskImageTestOpt
img.Users = opts.Users
img.Groups = opts.Groups
img.SELinux = opts.SELinux
img.Files = opts.Files
img.Directories = opts.Directories

m := &manifest.Manifest{}
runi := &runner.Fedora{}
Expand Down Expand Up @@ -263,3 +268,43 @@ func TestBootcDiskImageInstantiateGroups(t *testing.T) {
}
}
}

func TestBootcDiskImageInstantiateFiles(t *testing.T) {
for _, withFiles := range []bool{true, false} {
opts := &bootcDiskImageTestOpts{}
if withFiles {
file1, err := fsnode.NewFile("/some/file", nil, nil, nil, []byte("data"))
require.NoError(t, err)
opts.Files = []*fsnode.File{file1}
}
osbuildManifest := makeBootcDiskImageOsbuildManifest(t, opts)
imagePipeline := findPipelineFromOsbuildManifest(t, osbuildManifest, "image")
require.NotNil(t, imagePipeline)
copyStage := findStageFromOsbuildPipeline(t, imagePipeline, "org.osbuild.copy")
if withFiles {
require.NotNil(t, copyStage)
} else {
require.Nil(t, copyStage)
}
}
}

func TestBootcDiskImageInstantiateDirs(t *testing.T) {
for _, withDirs := range []bool{true, false} {
opts := &bootcDiskImageTestOpts{}
if withDirs {
dir1, err := fsnode.NewDirectory("/some/dir", nil, nil, nil, true)
require.NoError(t, err)
opts.Directories = []*fsnode.Directory{dir1}
}
osbuildManifest := makeBootcDiskImageOsbuildManifest(t, opts)
imagePipeline := findPipelineFromOsbuildManifest(t, osbuildManifest, "image")
require.NotNil(t, imagePipeline)
mkdirStage := findStageFromOsbuildPipeline(t, imagePipeline, "org.osbuild.mkdir")
if withDirs {
require.NotNil(t, mkdirStage)
} else {
require.Nil(t, mkdirStage)
}
}
}
50 changes: 40 additions & 10 deletions pkg/manifest/raw_bootc.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/artifact"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/osbuild"
Expand Down Expand Up @@ -36,6 +37,10 @@ type RawBootcImage struct {
Users []users.User
Groups []users.Group

// Custom directories and files to create in the image
Directories []*fsnode.Directory
Files []*fsnode.File

// SELinux policy, when set it enables the labeling of the tree with the
// selected profile
SELinux string
Expand Down Expand Up @@ -164,6 +169,8 @@ func (p *RawBootcImage) serialize() osbuild.Pipeline {
mounts = append(mounts, *osbuild.NewBindMount("bind-ostree-deployment-to-tree", "mount://", "tree://"))

// we always include the fstab stage
// XXX: see issue#756 - if we stop doing this, conditionally
// apply selinux again
fstabOpts, err := osbuild.NewFSTabStageOptions(pt)
if err != nil {
panic(err)
Expand Down Expand Up @@ -199,23 +206,46 @@ func (p *RawBootcImage) serialize() osbuild.Pipeline {
usersStage.Mounts = mounts
usersStage.Devices = devices
pipeline.AddStage(usersStage)
}

// First create custom directories, because some of the custom files may depend on them
if len(p.Directories) > 0 {
pipeline.AddStages(osbuild.GenDirectoryNodesStages(p.Directories)...)
}

if len(p.Files) > 0 {
pipeline.AddStages(osbuild.GenFileNodesStages(p.Files)...)
}

// add selinux
if p.SELinux != "" {
opts := &osbuild.SELinuxStageOptions{
FileContexts: fmt.Sprintf("etc/selinux/%s/contexts/files/file_contexts", p.SELinux),
ExcludePaths: []string{"/sysroot"},
}
selinuxStage := osbuild.NewSELinuxStage(opts)
selinuxStage.Mounts = mounts
selinuxStage.Devices = devices
pipeline.AddStage(selinuxStage)
// XXX: maybe go back to adding this conditionally when we stop
// writing an /etc/fstab by default (see issue #756)
// add selinux
if p.SELinux != "" {
opts := &osbuild.SELinuxStageOptions{
FileContexts: fmt.Sprintf("etc/selinux/%s/contexts/files/file_contexts", p.SELinux),
ExcludePaths: []string{"/sysroot"},
}
selinuxStage := osbuild.NewSELinuxStage(opts)
selinuxStage.Mounts = mounts
selinuxStage.Devices = devices
pipeline.AddStage(selinuxStage)
}

return pipeline
}

// XXX: duplicated from os.go
func (p *RawBootcImage) getInline() []string {
inlineData := []string{}

// inline data for custom files
for _, file := range p.Files {
inlineData = append(inlineData, string(file.Data()))
}

return inlineData
}

// XXX: copied from raw.go
func (p *RawBootcImage) Export() *artifact.Artifact {
p.Base.export = true
Expand Down
59 changes: 59 additions & 0 deletions pkg/manifest/raw_bootc_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package manifest_test

import (
"fmt"
"os"
"testing"

Expand All @@ -10,6 +11,7 @@ import (
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/internal/testdisk"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
Expand Down Expand Up @@ -281,3 +283,60 @@ func RawBootcImageSerializeFstabPipelineHasBootcMounts(t *testing.T) {
assert.NotNil(t, stage)
assertBootcDeploymentAndBindMount(t, stage)
}

func TestRawBootcImageSerializeCreateFilesDirs(t *testing.T) {
rawBootcPipeline := makeFakeRawBootcPipeline()

dir1, err := fsnode.NewDirectory("/path/to/dir", nil, nil, nil, false)
require.NoError(t, err)
file1, err := fsnode.NewFile("/path/to/file", nil, nil, nil, []byte("file-content"))
require.NoError(t, err)
for _, tc := range []struct {
dirs []*fsnode.Directory
files []*fsnode.File
}{
{nil, nil},
{[]*fsnode.Directory{dir1}, nil},
{nil, []*fsnode.File{file1}},
{[]*fsnode.Directory{dir1}, []*fsnode.File{file1}},
} {
tcName := fmt.Sprintf("files:%v,dirs:%v", len(tc.files), len(tc.dirs))
t.Run(tcName, func(t *testing.T) {
rawBootcPipeline.SELinux = "/path/to/selinux"
rawBootcPipeline.Directories = tc.dirs
rawBootcPipeline.Files = tc.files

pipeline := rawBootcPipeline.Serialize()

// check dirs
mkdirStage := manifest.FindStage("org.osbuild.mkdir", pipeline.Stages)
if len(tc.dirs) > 0 {
// ensure options got passed
require.NotNil(t, mkdirStage)
mkdirOptions := mkdirStage.Options.(*osbuild.MkdirStageOptions)
assert.Equal(t, "/path/to/dir", mkdirOptions.Paths[0].Path)
} else {
assert.Nil(t, mkdirStage)
}

// check files
copyStage := manifest.FindStage("org.osbuild.copy", pipeline.Stages)
if len(tc.files) > 0 {
// ensure options got passed
require.NotNil(t, copyStage)
copyOptions := copyStage.Options.(*osbuild.CopyStageOptions)
assert.Equal(t, "tree:///path/to/file", copyOptions.Paths[0].To)
} else {
assert.Nil(t, copyStage)
}

selinuxStage := manifest.FindStage("org.osbuild.selinux", pipeline.Stages)

assert.NotNil(t, selinuxStage)

// XXX: we should really check that the inline
// source for files got generated but that is
// currently very hard to test :(
})
}
}
Loading