Skip to content

adjust imagebuild api#40

Merged
bennyz merged 2 commits into
centos-automotive-suite:mainfrom
bennyz:split-image-build
Jan 22, 2026
Merged

adjust imagebuild api#40
bennyz merged 2 commits into
centos-automotive-suite:mainfrom
bennyz:split-image-build

Conversation

@bennyz

@bennyz bennyz commented Jan 22, 2026

Copy link
Copy Markdown
Contributor

handles #35

Summary by CodeRabbit

  • New Features

    • Added nested AIB and Export configuration blocks, new disk export options, push/secret references, and status fields to record builder images used.
  • Chores

    • Migrated spec from flat to structured model and switched internal usage to accessors for consistent behavior and compatibility.
  • Documentation

    • Updated CRDs, samples, defaults (including default distro), enums, and field descriptions to reflect the new spec layout.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 22, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Refactors ImageBuild spec to introduce nested AIB and Export blocks, removes several legacy top-level fields, adds provenance status fields, implements many Get* accessors and DeepCopy support for new types, and updates server, handlers, controllers, samples, tests, and CRD/manifest schemas to the new spec shape.

Changes

Cohort / File(s) Summary
API Types
api/v1alpha1/imagebuild_types.go
Introduces AIBSpec, ExportSpec, DiskExport; moves distro/target/etc. into spec.aib and export options into spec.export; adds PushSecretRef/SecretRef, provenance status fields, and many Get*() accessors (e.g., GetDistro, GetExportOCI, HasDiskExport, GetLegacyExportURL).
DeepCopy Gen
api/v1alpha1/zz_generated.deepcopy.go
Adds DeepCopyInto/DeepCopy for AIBSpec, ExportSpec, DiskExport; updates ImageBuildSpec.DeepCopyInto to deep-copy new nested fields; removes deepcopy helpers for deprecated publisher types.
CRD / Manifests
config/crd/bases/..._imagebuilds.yaml, bundle/manifests/..._imagebuilds.yaml, config/crd/bases/..._catalogimages.yaml, bundle/manifests/..._catalogimages.yaml
Adds spec.aib (with required distro/target) and spec.export schemas; adds spec.pushSecretRef/spec.secretRef; adds status.aibImageUsed/status.builderImageUsed; updates doc strings for distro/architecture.
Build API: server & handlers
internal/buildapi/server.go, internal/buildapi/catalog/handlers.go
Adds buildAIBSpec() and buildExportSpec(); create/get flows now populate/use AIB/Export and call accessors (GetDistro, GetTarget, GetExportOCI, GetContainerPush, etc.); default distro changed to autosd.
Controller: imagebuild
internal/controller/imagebuild/controller.go
Replaces direct spec reads with Get* accessors across pipeline/task wiring; adds extractProvenance() to populate AIBImageUsed/BuilderImageUsed; gates push on HasDiskExport(); switches to SecretRef/PushSecretRef.
Controller: catalog image publisher
internal/controller/catalogimage/publisher.go
Uses accessors for metadata/registry extraction (prefers GetContainerPush() then GetExportOCI()); reads distro/target/mode via getters.
Tasks / Samples / Tests
internal/common/tasks/tasks.go, config/samples/automotive_v1_imagebuild.yaml, test/e2e/e2e_test.go
Default distro updated (cs9autosd); samples/tests updated to new nested aib/export YAML structure and comments; task templates use new param defaults.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant API_Server as BuildAPI
  participant Controller
  participant Tekton as PipelineRun
  participant Registry

  Client->>API_Server: Create BuildRequest
  API_Server->>API_Server: buildAIBSpec(), buildExportSpec()
  API_Server-->>Controller: Create ImageBuild CR (spec.aib/spec.export)
  Controller->>Tekton: Create PipelineRun (build tasks, push task if HasDiskExport)
  Tekton-->>Controller: PipelineRun status/events
  Controller->>Controller: extractProvenance(pipelineRun) -> update status (AIBImageUsed/BuilderImageUsed)
  alt Has disk export
    Controller->>Registry: Push artifact (using PushSecretRef/Export.OCI or container push)
    Registry-->>Controller: Push result
  end
  Controller-->>Client: Update ImageBuild status (phase, provenance, completion)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐇 I hopped through structs and nested nests,
tucked fields in pockets, tidied up the vests,
getters hum softly, status keeps the trace,
builders and AIBs snug in their place,
a tiny hop — a neater spec, a happier space! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'adjust imagebuild api' is vague and generic, using non-descriptive language that doesn't convey the substantial structural changes being made to the ImageBuild API specification. Provide a more specific title that captures the main change, such as 'Refactor ImageBuild API: introduce nested AIB and Export specifications' or 'Restructure ImageBuild to use AIBSpec and ExportSpec blocks'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
@bennyz bennyz force-pushed the split-image-build branch from 7fa3d09 to 020472b Compare January 22, 2026 14:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@api/v1alpha1/imagebuild_types.go`:
- Around line 293-310: The current HasDiskExport() (on ImageBuildSpec) should
not assume a legacy disk export when both Export and AIB are nil; change the
logic to avoid false positives by either (A) adding and checking an explicit
legacy marker field (e.g., a boolean like LegacyDiskExport or
LegacyFlatStructure) on ImageBuildSpec and only returning true when that marker
is set, or (B) tightening legacy detection to look for specific legacy top-level
fields (use concrete legacy field names available in the struct) instead of just
Export==nil && AIB==nil; update the HasDiskExport implementation to reference
the chosen symbol (the new LegacyDiskExport flag or the specific legacy field
names) and update related docs/comments accordingly.

In `@internal/buildapi/server.go`:
- Around line 879-896: In buildExportSpec: do not blindly copy req.ExportFormat
into ExportSpec.Format; validate/normalize it against the allowed set
{"raw","qcow2","simg"} and map or default any legacy/invalid values (e.g. map
"image" → "raw" or use "raw" as a safe default) before assigning to
export.Format, and when req.ExportOCI is non-empty ensure disk export will
actually be produced by enforcing export.BuildDiskImage = true (or set
req.BuildDiskImage to true) so a disk export is not silently skipped; adjust
buildExportSpec to perform these checks and normalization before returning the
ExportSpec.

In `@internal/controller/imagebuild/controller.go`:
- Around line 637-658: The push TaskRun is being created with potentially empty
params: repository-url built from imageBuild.Spec.GetLegacyExportURL() and
artifact-filename built from GetDistro()/GetTarget()/GetExportFormat(), which
can produce malformed values; before constructing the tekton params (or before
creating the push TaskRun), validate that imageBuild.Spec.GetLegacyExportURL()
is non-empty and that GetDistro()/GetTarget()/GetExportFormat() produce
non-empty components (or that imageBuild.Spec.HasDiskExport() implies those
fields are present), and if validation fails either return an error/clear
failure from the controller or skip creating the push TaskRun; update the code
path that builds the ParamValue slice (the repository-url and artifact-filename
entries) to only add those params after validation and include a clear error
return from the function that creates the TaskRun when required fields are
missing.
🧹 Nitpick comments (2)
api/v1alpha1/imagebuild_types.go (1)

312-327: GetLegacyExportURL returns empty string for actual legacy builds.

The comment states this is a "temporary compatibility function," but returning an empty string for legacy builds will cause errors rather than providing a migration path. Consider either:

  1. Removing this function if legacy support isn't needed
  2. Adding clear documentation that users must migrate

The current implementation only delegates to GetExportOCI(), making the "Legacy" naming misleading.

♻️ Suggested clarification
-// GetLegacyExportURL attempts to determine the export URL for legacy ImageBuilds
-// This is a temporary compatibility function
-func (s *ImageBuildSpec) GetLegacyExportURL() string {
-	// For legacy builds, we don't have access to the old Publishers field anymore
-	// since we removed it from the type. The best we can do is provide a reasonable default
-	// or require the user to update their ImageBuild to the new structure.
-
-	// If this is a new structure build, use the proper export URL
-	if url := s.GetExportOCI(); url != "" {
-		return url
-	}
-
-	// For legacy builds, we need the user to migrate to the new structure
-	// Return empty string to force an error that guides them to update
-	return ""
-}
+// GetExportURL returns the OCI export URL for disk images.
+// Returns empty string if no disk export is configured.
+func (s *ImageBuildSpec) GetExportURL() string {
+	return s.GetExportOCI()
+}
internal/controller/imagebuild/controller.go (1)

609-611: Consider improving the error message for missing disk export.

The current error message "no disk export configured" could be more helpful by guiding users to configure spec.export.disk.oci.

💡 Improved error message
 if !imageBuild.Spec.HasDiskExport() {
-	return fmt.Errorf("no disk export configured")
+	return fmt.Errorf("no disk export configured: set spec.export.disk.oci to enable pushing to registry")
 }

Comment on lines +293 to +310
// HasDiskExport returns true if any disk export is configured
// Includes backward compatibility for legacy ImageBuilds
func (s *ImageBuildSpec) HasDiskExport() bool {
// New structure: check export.disk.oci
if s.Export != nil && s.Export.Disk != nil && s.Export.Disk.OCI != "" {
return true
}

// Legacy compatibility: if this appears to be an old ImageBuild structure,
// assume disk export is wanted (old behavior was to always export)
// We detect old structure by checking if Export is nil but other top-level fields exist
if s.Export == nil && s.AIB == nil {
// This appears to be a legacy flat structure ImageBuild
return true
}

return false
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Review backward compatibility logic in HasDiskExport.

The fallback at lines 304-307 returns true when both Export and AIB are nil, assuming this is a legacy flat-structure ImageBuild. This could cause unintended behavior if a user creates a minimal ImageBuild with neither field set (not legacy, just incomplete).

Consider adding a more explicit legacy detection mechanism or documenting this behavior clearly.

💡 Suggestion: Add explicit legacy marker or improve detection
 // HasDiskExport returns true if any disk export is configured
 // Includes backward compatibility for legacy ImageBuilds
 func (s *ImageBuildSpec) HasDiskExport() bool {
 	// New structure: check export.disk.oci
 	if s.Export != nil && s.Export.Disk != nil && s.Export.Disk.OCI != "" {
 		return true
 	}
 
-	// Legacy compatibility: if this appears to be an old ImageBuild structure,
-	// assume disk export is wanted (old behavior was to always export)
-	// We detect old structure by checking if Export is nil but other top-level fields exist
-	if s.Export == nil && s.AIB == nil {
-		// This appears to be a legacy flat structure ImageBuild
-		return true
-	}
+	// For new-style ImageBuilds (with AIB or Export defined), 
+	// only export if explicitly configured
+	// Legacy builds should be migrated to use the new structure
 
 	return false
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// HasDiskExport returns true if any disk export is configured
// Includes backward compatibility for legacy ImageBuilds
func (s *ImageBuildSpec) HasDiskExport() bool {
// New structure: check export.disk.oci
if s.Export != nil && s.Export.Disk != nil && s.Export.Disk.OCI != "" {
return true
}
// Legacy compatibility: if this appears to be an old ImageBuild structure,
// assume disk export is wanted (old behavior was to always export)
// We detect old structure by checking if Export is nil but other top-level fields exist
if s.Export == nil && s.AIB == nil {
// This appears to be a legacy flat structure ImageBuild
return true
}
return false
}
// HasDiskExport returns true if any disk export is configured
// Includes backward compatibility for legacy ImageBuilds
func (s *ImageBuildSpec) HasDiskExport() bool {
// New structure: check export.disk.oci
if s.Export != nil && s.Export.Disk != nil && s.Export.Disk.OCI != "" {
return true
}
// For new-style ImageBuilds (with AIB or Export defined),
// only export if explicitly configured
// Legacy builds should be migrated to use the new structure
return false
}
🤖 Prompt for AI Agents
In `@api/v1alpha1/imagebuild_types.go` around lines 293 - 310, The current
HasDiskExport() (on ImageBuildSpec) should not assume a legacy disk export when
both Export and AIB are nil; change the logic to avoid false positives by either
(A) adding and checking an explicit legacy marker field (e.g., a boolean like
LegacyDiskExport or LegacyFlatStructure) on ImageBuildSpec and only returning
true when that marker is set, or (B) tightening legacy detection to look for
specific legacy top-level fields (use concrete legacy field names available in
the struct) instead of just Export==nil && AIB==nil; update the HasDiskExport
implementation to reference the chosen symbol (the new LegacyDiskExport flag or
the specific legacy field names) and update related docs/comments accordingly.

Comment on lines +879 to +896
// buildExportSpec creates ExportSpec configuration from build request
func buildExportSpec(req *BuildRequest) *automotivev1alpha1.ExportSpec {
export := &automotivev1alpha1.ExportSpec{
Format: string(req.ExportFormat),
Compression: req.Compression,
BuildDiskImage: req.BuildDiskImage,
Container: req.ContainerPush,
}
return &automotivev1alpha1.Publishers{
Registry: &automotivev1alpha1.RegistryPublisher{
RepositoryURL: pushRepository,
Secret: pushSecretName,
},

// Set disk export if OCI URL is specified
if req.ExportOCI != "" {
export.Disk = &automotivev1alpha1.DiskExport{
OCI: req.ExportOCI,
}
}

return export
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid writing invalid export.format values (e.g., "image").

ExportSpec.Format is constrained to raw/qcow2/simg; defaulting req.ExportFormat to "image" will serialize an invalid value and can cause CRD validation failures. Also consider enforcing BuildDiskImage when ExportOCI is set so disk export isn't silently skipped.

🛠️ Suggested fix
 func buildExportSpec(req *BuildRequest) *automotivev1alpha1.ExportSpec {
 	export := &automotivev1alpha1.ExportSpec{
-		Format:         string(req.ExportFormat),
-		Compression:    req.Compression,
-		BuildDiskImage: req.BuildDiskImage,
-		Container:      req.ContainerPush,
+		Compression:    req.Compression,
+		BuildDiskImage: req.BuildDiskImage,
+		Container:      req.ContainerPush,
 	}
 
+	if req.ExportFormat != "" && req.ExportFormat != formatImage {
+		export.Format = string(req.ExportFormat)
+	}
+
 	// Set disk export if OCI URL is specified
 	if req.ExportOCI != "" {
 		export.Disk = &automotivev1alpha1.DiskExport{
 			OCI: req.ExportOCI,
 		}
+		export.BuildDiskImage = true
 	}
 
 	return export
 }
🤖 Prompt for AI Agents
In `@internal/buildapi/server.go` around lines 879 - 896, In buildExportSpec: do
not blindly copy req.ExportFormat into ExportSpec.Format; validate/normalize it
against the allowed set {"raw","qcow2","simg"} and map or default any
legacy/invalid values (e.g. map "image" → "raw" or use "raw" as a safe default)
before assigning to export.Format, and when req.ExportOCI is non-empty ensure
disk export will actually be produced by enforcing export.BuildDiskImage = true
(or set req.BuildDiskImage to true) so a disk export is not silently skipped;
adjust buildExportSpec to perform these checks and normalization before
returning the ExportSpec.

Comment thread internal/controller/imagebuild/controller.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bundle/manifests/automotive.sdv.cloud.redhat.com_imagebuilds.yaml (1)

39-131: Bundle CRD is out of sync with base CRD—regenerate manifests.

The bundle manifest at bundle/manifests/automotive.sdv.cloud.redhat.com_imagebuilds.yaml still contains the old flat structure (e.g., spec.distro, spec.exportFormat, spec.containerPush) while the base CRD at config/crd/bases/automotive.sdv.cloud.redhat.com_imagebuilds.yaml has been refactored to use a nested structure with spec.aib and spec.export. After modifying API types, run make generate manifests to regenerate the bundle manifests and keep them in sync with the base CRD schema.

🤖 Fix all issues with AI agents
In `@internal/buildapi/server.go`:
- Around line 980-987: The code creates pushSecretName via createPushSecret()
but never wires it into automotivev1alpha1.ImageBuildSpec, so the push task only
sees imageBuild.Spec.SecretRef; either (A) add a new field PushSecretRef to
ImageBuildSpec and set it when constructing ImageBuildSpec (assign
PushSecretRef: pushSecretName), then update the push task/controller to consume
imageBuild.Spec.PushSecretRef instead of or alongside SecretRef, or (B) if the
env-based registry secret is sufficient, remove the createPushSecret() call and
any related pushSecretName usage; update buildAIBSpec/build/export callers if
they expect the push secret. Ensure you change all references to push logic to
use the chosen field (ImageBuildSpec.PushSecretRef) and adjust
createPushSecret()/push task accordingly.
♻️ Duplicate comments (2)
api/v1alpha1/imagebuild_types.go (1)

293-310: Backward compatibility logic in HasDiskExport needs attention.

The logic at lines 304-307 returns true when both Export and AIB are nil, assuming legacy flat-structure ImageBuilds. This could incorrectly return true for minimal/incomplete new-style ImageBuilds.

internal/buildapi/server.go (1)

879-896: Invalid Format value still being written to ExportSpec.

The previous review comment about this issue has not been addressed. When req.ExportFormat defaults to "image" (set in applyBuildDefaults at line 783), line 882 writes this invalid value directly to ExportSpec.Format, which is constrained to raw/qcow2/simg per the CRD schema.

Consider normalizing or omitting the format when it's "image":

🛠️ Suggested fix
 func buildExportSpec(req *BuildRequest) *automotivev1alpha1.ExportSpec {
 	export := &automotivev1alpha1.ExportSpec{
-		Format:         string(req.ExportFormat),
 		Compression:    req.Compression,
 		BuildDiskImage: req.BuildDiskImage,
 		Container:      req.ContainerPush,
 	}

+	// Only set Format for valid disk export formats (raw, qcow2, simg)
+	if req.ExportFormat != "" && req.ExportFormat != formatImage {
+		export.Format = string(req.ExportFormat)
+	}
+
 	// Set disk export if OCI URL is specified
 	if req.ExportOCI != "" {
 		export.Disk = &automotivev1alpha1.DiskExport{
 			OCI: req.ExportOCI,
 		}
 	}

 	return export
 }

Comment thread internal/buildapi/server.go
@bennyz bennyz force-pushed the split-image-build branch 2 times, most recently from fe7ac9f to d8f9602 Compare January 22, 2026 15:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@internal/controller/imagebuild/controller.go`:
- Around line 609-687: The artifact filename is being constructed with a
hardcoded ".gz" which breaks when spec.export.compression is "lz4" or "xz";
update the logic around artifactFilename (used for the push Task generated by
GeneratePushArtifactRegistryTask) to call imageBuild.Spec.GetCompression(), map
the compression to the correct extension (e.g., "gz" for gzip, "lz4" for lz4,
"xz" for xz), and use that extension when building artifactFilename (still using
distro, target and exportFormat); ensure the produced filename is passed into
the "artifact-filename" tekton param.
♻️ Duplicate comments (2)
api/v1alpha1/imagebuild_types.go (1)

302-318: Legacy detection still returns true when both AIB/Export are nil.
Same concern as earlier review: this can yield false positives for incomplete specs.

internal/buildapi/server.go (1)

879-896: Avoid writing invalid export.format values (e.g., "image").

The applyBuildDefaults function sets req.ExportFormat to "image" when unspecified (line 783), but ExportSpec.Format is constrained by the CRD enum to raw, qcow2, or simg. Writing "image" will cause CRD validation failure. Since the CRD has a default of qcow2, consider omitting the format field when the value is "image" or not in the allowed set.

🛠️ Suggested fix
 func buildExportSpec(req *BuildRequest) *automotivev1alpha1.ExportSpec {
 	export := &automotivev1alpha1.ExportSpec{
-		Format:         string(req.ExportFormat),
 		Compression:    req.Compression,
 		BuildDiskImage: req.BuildDiskImage,
 		Container:      req.ContainerPush,
 	}

+	// Only set format if it's a valid disk format (not the legacy "image" value)
+	if req.ExportFormat != "" && req.ExportFormat != formatImage {
+		export.Format = string(req.ExportFormat)
+	}
+
 	// Set disk export if OCI URL is specified
 	if req.ExportOCI != "" {
 		export.Disk = &automotivev1alpha1.DiskExport{
 			OCI: req.ExportOCI,
 		}
 	}

 	return export
 }

Comment thread internal/controller/imagebuild/controller.go
Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
@bennyz bennyz force-pushed the split-image-build branch from d8f9602 to faea82d Compare January 22, 2026 15:53
@bennyz bennyz merged commit f774eae into centos-automotive-suite:main Jan 22, 2026
4 checks passed
@bennyz bennyz deleted the split-image-build branch January 22, 2026 16:03
This was referenced Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant