Skip to content
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ endif

# Set the Operator SDK version to use. By default, what is installed on the system is used.
# This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit.
OPERATOR_SDK_VERSION ?= v1.39.1
OPERATOR_SDK_VERSION ?= v1.42.0
# Image URL to use all building/pushing image targets
IMG ?= $(IMAGE_TAG_BASE):v$(VERSION)
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
Expand Down
74 changes: 33 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,62 +81,54 @@ oc apply -f config/samples/automotive_v1_operatorconfig.yaml

### Creating Your First Build

1. **Create a ConfigMap with your AIB manifest:**

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-build-manifest
data:
simple.aib.yml: |
name: container

content:
rpms:
- openssh-server
systemd:
enabled_services:
- sshd.service
add_files:
- path: /usr/share/hello.txt
text: |
hello!
image:
image_size: 8 GiB

auth:
# "password"
root_password: $6$xoLqEUz0cGGJRx01$H3H/bFm0myJPULNMtbSsOFd/2BnHqHkMD92Sfxd.EKM9hXTWSmELG8cf205l6dktomuTcgKGGtGDgtvHVXSWU.
sshd_config:
PermitRootLogin: true
PasswordAuthentication: true
```

2. **Create an ImageBuild resource:**
1. **Create an ImageBuild resource with an inline AIB manifest:**

```yaml
apiVersion: automotive.sdv.cloud.redhat.com/v1alpha1
kind: ImageBuild
metadata:
name: my-automotive-image
spec:
distro: autosd
architecture: amd64
mode: image
target: qemu
exportFormat: qcow2
manifestConfigMap: my-build-manifest
aib:
distro: autosd
target: qemu
mode: image
manifest: |
name: container

content:
rpms:
- openssh-server
systemd:
enabled_services:
- sshd.service
add_files:
- path: /usr/share/hello.txt
text: |
hello!
image:
image_size: 8 GiB

auth:
# "password"
root_password: $6$xoLqEUz0cGGJRx01$H3H/bFm0myJPULNMtbSsOFd/2BnHqHkMD92Sfxd.EKM9hXTWSmELG8cf205l6dktomuTcgKGGtGDgtvHVXSWU.
sshd_config:
PermitRootLogin: true
PasswordAuthentication: true
manifestFileName: "simple.aib.yml"
export:
format: qcow2
compression: gzip
```

3. **Apply the resources:**
2. **Apply the resource:**

```sh
oc apply -f manifest-configmap.yaml
oc apply -f imagebuild.yaml
```

4. **Monitor the build:**
3. **Monitor the build:**

```sh
oc get imagebuild my-automotive-image -w
Expand Down
44 changes: 39 additions & 5 deletions api/v1alpha1/imagebuild_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,12 @@ type AIBSpec struct {
// +kubebuilder:default=image
Mode string `json:"mode,omitempty"`

// ManifestConfigMap specifies the name of the ConfigMap containing the AIB manifest
ManifestConfigMap string `json:"manifestConfigMap,omitempty"`
// Manifest holds the inline AIB manifest YAML content
Manifest string `json:"manifest,omitempty" yaml:"manifest,omitempty"`

// ManifestFileName is the original filename of the manifest, used for naming the file
// when writing it to disk before invoking automotive-image-builder
ManifestFileName string `json:"manifestFileName,omitempty" yaml:"manifestFileName,omitempty"`

// Image specifies the automotive-image-builder container image to use
// If not specified, the default from OperatorConfig is used
Expand All @@ -101,6 +105,12 @@ type AIBSpec struct {
// ContainerRef is the reference to an existing bootc container image
// Required when mode=disk to create a disk image from an existing container
ContainerRef string `json:"containerRef,omitempty"`

// CustomDefs are custom environment variable definitions for the build
CustomDefs []string `json:"customDefs,omitempty"`

// AIBExtraArgs are extra arguments to pass to automotive-image-builder
AIBExtraArgs []string `json:"aibExtraArgs,omitempty"`
}

// ExportSpec defines the configuration for exporting build artifacts
Expand Down Expand Up @@ -241,10 +251,18 @@ func (s *ImageBuildSpec) GetMode() string {
return "image"
}

// GetManifestConfigMap returns the manifest ConfigMap name from AIB spec
func (s *ImageBuildSpec) GetManifestConfigMap() string {
// GetManifest returns the inline manifest YAML content from AIB spec
func (s *ImageBuildSpec) GetManifest() string {
if s.AIB != nil {
return s.AIB.Manifest
}
return ""
}

// GetManifestFileName returns the manifest filename from AIB spec
func (s *ImageBuildSpec) GetManifestFileName() string {
if s.AIB != nil {
return s.AIB.ManifestConfigMap
return s.AIB.ManifestFileName
}
return ""
}
Expand Down Expand Up @@ -281,6 +299,22 @@ func (s *ImageBuildSpec) GetContainerRef() string {
return ""
}

// GetCustomDefs returns the custom environment variable definitions from AIB spec
func (s *ImageBuildSpec) GetCustomDefs() []string {
if s.AIB != nil {
return s.AIB.CustomDefs
}
return nil
}

// GetAIBExtraArgs returns extra arguments to pass to automotive-image-builder
func (s *ImageBuildSpec) GetAIBExtraArgs() []string {
if s.AIB != nil {
return s.AIB.AIBExtraArgs
}
return nil
}

// GetExportFormat returns the export format, or "qcow2" as default
func (s *ImageBuildSpec) GetExportFormat() string {
if s.Export != nil && s.Export.Format != "" {
Expand Down
203 changes: 203 additions & 0 deletions api/v1alpha1/imagebuild_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package v1alpha1

import (
"testing"
)

func TestGetManifest(t *testing.T) {
tests := []struct {
name string
spec ImageBuildSpec
want string
}{
{
name: "returns manifest when AIB is set",
spec: ImageBuildSpec{
AIB: &AIBSpec{
Manifest: "name: my-build\npackages:\n - vim\n",
},
},
want: "name: my-build\npackages:\n - vim\n",
},
{
name: "returns empty string when AIB is nil",
spec: ImageBuildSpec{},
want: "",
},
{
name: "returns empty string when manifest is empty",
spec: ImageBuildSpec{
AIB: &AIBSpec{},
},
want: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.spec.GetManifest()
if got != tt.want {
t.Errorf("GetManifest() = %q, want %q", got, tt.want)
}
})
}
}

func TestGetManifestFileName(t *testing.T) {
tests := []struct {
name string
spec ImageBuildSpec
want string
}{
{
name: "returns filename when set",
spec: ImageBuildSpec{
AIB: &AIBSpec{
ManifestFileName: "my-build.aib.yml",
},
},
want: "my-build.aib.yml",
},
{
name: "returns empty string when AIB is nil",
spec: ImageBuildSpec{},
want: "",
},
{
name: "returns empty string when filename is empty",
spec: ImageBuildSpec{
AIB: &AIBSpec{},
},
want: "",
},
{
name: "handles mpp.yml extension",
spec: ImageBuildSpec{
AIB: &AIBSpec{
ManifestFileName: "build.mpp.yml",
},
},
want: "build.mpp.yml",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.spec.GetManifestFileName()
if got != tt.want {
t.Errorf("GetManifestFileName() = %q, want %q", got, tt.want)
}
})
}
}

func TestGetCustomDefs(t *testing.T) {
tests := []struct {
name string
spec ImageBuildSpec
want []string
}{
{
name: "returns custom defs when set",
spec: ImageBuildSpec{
AIB: &AIBSpec{
CustomDefs: []string{"FOO=bar", "BAZ=qux"},
},
},
want: []string{"FOO=bar", "BAZ=qux"},
},
{
name: "returns nil when AIB is nil",
spec: ImageBuildSpec{},
want: nil,
},
{
name: "returns nil when custom defs is nil",
spec: ImageBuildSpec{
AIB: &AIBSpec{},
},
want: nil,
},
{
name: "returns empty slice when custom defs is empty",
spec: ImageBuildSpec{
AIB: &AIBSpec{
CustomDefs: []string{},
},
},
want: []string{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.spec.GetCustomDefs()
if tt.want == nil {
if got != nil {
t.Errorf("GetCustomDefs() = %v, want nil", got)
}
return
}
if len(got) != len(tt.want) {
t.Errorf("GetCustomDefs() length = %d, want %d", len(got), len(tt.want))
return
}
for i := range got {
if got[i] != tt.want[i] {
t.Errorf("GetCustomDefs()[%d] = %q, want %q", i, got[i], tt.want[i])
}
}
})
}
}

func TestGetAIBExtraArgs(t *testing.T) {
tests := []struct {
name string
spec ImageBuildSpec
want []string
}{
{
name: "returns extra args when set",
spec: ImageBuildSpec{
AIB: &AIBSpec{
AIBExtraArgs: []string{"--verbose", "--no-cache"},
},
},
want: []string{"--verbose", "--no-cache"},
},
{
name: "returns nil when AIB is nil",
spec: ImageBuildSpec{},
want: nil,
},
{
name: "returns nil when extra args is nil",
spec: ImageBuildSpec{
AIB: &AIBSpec{},
},
want: nil,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.spec.GetAIBExtraArgs()
if tt.want == nil {
if got != nil {
t.Errorf("GetAIBExtraArgs() = %v, want nil", got)
}
return
}
if len(got) != len(tt.want) {
t.Errorf("GetAIBExtraArgs() length = %d, want %d", len(got), len(tt.want))
return
}
for i := range got {
if got[i] != tt.want[i] {
t.Errorf("GetAIBExtraArgs()[%d] = %q, want %q", i, got[i], tt.want[i])
}
}
})
}
}
5 changes: 0 additions & 5 deletions api/v1alpha1/operatorconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,6 @@ func (c *JumpstarterConfig) GetJumpstarterImage() string {

// BuildAPIConfig defines configuration for the Build API server
type BuildAPIConfig struct {
// MaxManifestSize is the maximum allowed manifest size in bytes
// Default: 10485760 (10MB)
// +optional
MaxManifestSize int64 `json:"maxManifestSize,omitempty"`

// MaxUploadFileSize is the maximum size for individual uploaded files in bytes
// Default: 1073741824 (1GB)
// +optional
Expand Down
Loading