Skip to content

CORS-4005: refactor worker manifest serialization#10407

Open
patrickdillon wants to merge 1 commit intoopenshift:mainfrom
patrickdillon:refactor-worker-serialize
Open

CORS-4005: refactor worker manifest serialization#10407
patrickdillon wants to merge 1 commit intoopenshift:mainfrom
patrickdillon:refactor-worker-serialize

Conversation

@patrickdillon
Copy link
Contributor

Make manifest serialization a common, shared function.

This is in preparation for adding CAPI compute node manifests, where we will be expanding this boiler plate code even more.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Mar 18, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 18, 2026

@patrickdillon: This pull request references CORS-4005 which is a valid jira issue.

Details

In response to this:

Make manifest serialization a common, shared function.

This is in preparation for adding CAPI compute node manifests, where we will be expanding this boiler plate code even more.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

Walkthrough

Added an internal serialize helper in worker.go to marshal and write multiple runtime.Object manifests, replacing repeated per-item YAML marshaling for MachineSets, IPClaims, IPAddresses, and Machines; supports index-based or object-name-based filenames via a useObjectName flag.

Changes

Cohort / File(s) Summary
Serialization refactor
pkg/asset/machines/worker.go
Added serialize helper to emit multiple runtime.Object manifests; replaced per-item YAML marshaling for MachineSets, IPClaims, IPAddresses, and Machines with calls to serialize; introduced useObjectName filename mode using meta.Accessor; adjusted imports and error handling; updated filename patterns and added comments.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.11.3)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


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

@openshift-ci openshift-ci bot requested review from bfournie and tthvo March 18, 2026 18:47
@patrickdillon
Copy link
Contributor Author

/test e2e-azure-ovn e2e-vsphere-ovn e2e-gcp-ovn e2e-openstack-ovn e2e-nutanix-ovn e2e-ibmcloud-ovn

@patrickdillon
Copy link
Contributor Author

@jcpowermac @vr4manta vsphere ip address claims are affected here. which tests would exercise those so we can see the address claim manifests?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/asset/machines/worker.go`:
- Around line 730-738: The loops that append pointers currently capture the
reused range variables (for _, m := range data.Machines, for _, c := range
data.IPClaims, for _, a := range data.IPAddresses) causing pointer aliasing; fix
by creating a new local copy inside each loop (e.g., mCopy := m / cCopy := c /
aCopy := a) and append the address of that copy to machines, ipClaims, and
ipAddrs respectively so each slice element points to a distinct struct instance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 284beed1-37a1-40ca-aaa8-bed89d5f703a

📥 Commits

Reviewing files that changed from the base of the PR and between b4f5094 and 9771fbb.

📒 Files selected for processing (1)
  • pkg/asset/machines/worker.go

Comment on lines +730 to +738
for _, m := range data.Machines {
machines = append(machines, &m)
}
for _, c := range data.IPClaims {
ipClaims = append(ipClaims, &c)
}
for _, a := range data.IPAddresses {
ipAddrs = append(ipAddrs, &a)
}
Copy link

@coderabbitai coderabbitai bot Mar 18, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant loop + append sites.
rg -n -C2 'for _, m := range data\.Machines|machines = append\(machines, &m\)|for _, c := range data\.IPClaims|ipClaims = append\(ipClaims, &c\)|for _, a := range data\.IPAddresses|ipAddrs = append\(ipAddrs, &a\)' pkg/asset/machines/worker.go

# After the fix, this should produce no matches.
rg -n 'append\((machines|ipClaims|ipAddrs), &[mca]\)' pkg/asset/machines/worker.go || true

Repository: openshift/installer

Length of output: 483


Fix pointer aliasing in range loops before serialization

At Lines 731, 734, and 737, taking &m, &c, and &a captures reused range variables. All appended pointers will reference the same memory location, ending up with slices containing repeated pointers to the last iterated object instead of distinct elements.

Proposed fix
-				for _, m := range data.Machines {
-					machines = append(machines, &m)
-				}
-				for _, c := range data.IPClaims {
-					ipClaims = append(ipClaims, &c)
-				}
-				for _, a := range data.IPAddresses {
-					ipAddrs = append(ipAddrs, &a)
-				}
+				for i := range data.Machines {
+					machines = append(machines, &data.Machines[i])
+				}
+				for i := range data.IPClaims {
+					ipClaims = append(ipClaims, &data.IPClaims[i])
+				}
+				for i := range data.IPAddresses {
+					ipAddrs = append(ipAddrs, &data.IPAddresses[i])
+				}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/asset/machines/worker.go` around lines 730 - 738, The loops that append
pointers currently capture the reused range variables (for _, m := range
data.Machines, for _, c := range data.IPClaims, for _, a := range
data.IPAddresses) causing pointer aliasing; fix by creating a new local copy
inside each loop (e.g., mCopy := m / cCopy := c / aCopy := a) and append the
address of that copy to machines, ipClaims, and ipAddrs respectively so each
slice element points to a distinct struct instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No fix needed. The module uses Go 1.25, and since Go 1.22, range variables are scoped per iteration — each m, c, and a is a fresh variable, so &m, &c, and &a point to distinct instances. There's no aliasing bug here.

Copy link
Member

Choose a reason for hiding this comment

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

See https://go.dev/doc/go1.22#language for release note that contains the fix for pointer aliasing.

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Make manifest serialization a common, shared function.
@patrickdillon patrickdillon force-pushed the refactor-worker-serialize branch from 9771fbb to d2e1be6 Compare March 18, 2026 20:15
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/asset/machines/worker.go`:
- Around line 937-955: In serialize, compute the key and final filename before
calling yaml.Marshal so error messages use the actual filename: generate key
(using padFormat or meta.Accessor when useObjectName is true) and build filename
:= filepath.Join(directory, fmt.Sprintf(fileName, key)) prior to marshaling,
then call yaml.Marshal(m) and on marshal error return fmt.Errorf("marshaling %s:
%w", filename, err); finally use that same filename when creating the asset.File
(asset.File{Filename: filename, ...}) to ensure consistent, correct error output
and file assignment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2fd89d48-82c8-42ad-8c9c-ed3c61013847

📥 Commits

Reviewing files that changed from the base of the PR and between 9771fbb and d2e1be6.

📒 Files selected for processing (1)
  • pkg/asset/machines/worker.go

Comment on lines +937 to +955
func serialize(manifests []runtime.Object, fileName string, useObjectName bool) ([]*asset.File, error) {
files := make([]*asset.File, len(manifests))
padFormat := fmt.Sprintf("%%0%dd", len(fmt.Sprintf("%d", len(manifests))))
for i, m := range manifests {
data, err := yaml.Marshal(m)
if err != nil {
return nil, fmt.Errorf("marshaling %s: %w", fmt.Sprintf(fileName, i), err)
}

key := fmt.Sprintf(padFormat, i)
if useObjectName {
accessor, err := meta.Accessor(m)
if err != nil {
return nil, fmt.Errorf("accessing object metadata: %w", err)
}
key = accessor.GetName()
}
files[i] = &asset.File{
Filename: filepath.Join(directory, fmt.Sprintf(fileName, key)),
Copy link

@coderabbitai coderabbitai bot Mar 18, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "serialize() error path:"
sed -n '937,945p' pkg/asset/machines/worker.go

echo
echo "Filename format definitions used by the changed call sites:"
rg -n -C1 'workerMachineSetFileName =|workerMachineFileName =|ipClaimFileName\s*=|ipAddressFileName\s*=' pkg/asset/machines

Repository: openshift/installer

Length of output: 1544


🏁 Script executed:

cat -n pkg/asset/machines/worker.go | sed -n '937,960p'

Repository: openshift/installer

Length of output: 974


Move key and filename computation before the marshal call to fix the error message.

Line 943 uses fmt.Sprintf(fileName, i) where fileName is a format string with %s placeholder (e.g., "99_openshift-cluster-api_worker-machines-%s.yaml"), but i is an int. This produces malformed error output like marshaling 99_openshift-cluster-api_worker-machines-%!s(int=0).yaml: ... instead of the actual filename. Build key and filename first, then reuse filename in both the error message and the File assignment.

Suggested fix
 func serialize(manifests []runtime.Object, fileName string, useObjectName bool) ([]*asset.File, error) {
 	files := make([]*asset.File, len(manifests))
 	padFormat := fmt.Sprintf("%%0%dd", len(fmt.Sprintf("%d", len(manifests))))
 	for i, m := range manifests {
-		data, err := yaml.Marshal(m)
-		if err != nil {
-			return nil, fmt.Errorf("marshaling %s: %w", fmt.Sprintf(fileName, i), err)
-		}
-
 		key := fmt.Sprintf(padFormat, i)
 		if useObjectName {
 			accessor, err := meta.Accessor(m)
 			if err != nil {
 				return nil, fmt.Errorf("accessing object metadata: %w", err)
 			}
 			key = accessor.GetName()
 		}
+		filename := filepath.Join(directory, fmt.Sprintf(fileName, key))
+
+		data, err := yaml.Marshal(m)
+		if err != nil {
+			return nil, fmt.Errorf("marshaling %s: %w", filename, err)
+		}
 		files[i] = &asset.File{
-			Filename: filepath.Join(directory, fmt.Sprintf(fileName, key)),
+			Filename: filename,
 			Data:     data,
 		}
 	}
 	return files, nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/asset/machines/worker.go` around lines 937 - 955, In serialize, compute
the key and final filename before calling yaml.Marshal so error messages use the
actual filename: generate key (using padFormat or meta.Accessor when
useObjectName is true) and build filename := filepath.Join(directory,
fmt.Sprintf(fileName, key)) prior to marshaling, then call yaml.Marshal(m) and
on marshal error return fmt.Errorf("marshaling %s: %w", filename, err); finally
use that same filename when creating the asset.File (asset.File{Filename:
filename, ...}) to ensure consistent, correct error output and file assignment.

Copy link
Member

Choose a reason for hiding this comment

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

+1 here. It should only affect error message, but makes sense to fix 👍

Copy link

Choose a reason for hiding this comment

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

@tthvo, thanks for the confirmation! Here's the planned fix:

  • Move key computation before yaml.Marshal in serialize()
  • Build filename from key before marshaling
  • Use filename in both the error message and the asset.File assignment

Failed to handle agent chat message. Please try again.

Copy link
Member

@tthvo tthvo left a comment

Choose a reason for hiding this comment

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

/approve

I just have a nit and coderabbit also had a good suggestion above :D

// e.g. "99_openshift-machine-api_claim-cluster-worker-0-claim-0-0.yaml".
// When false, a zero-padded index is used instead,
// e.g. "99_openshift-cluster-api_worker-machineset-0.yaml".
func serialize(manifests []runtime.Object, fileName string, useObjectName bool) ([]*asset.File, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
func serialize(manifests []runtime.Object, fileName string, useObjectName bool) ([]*asset.File, error) {
func serialize(manifests []runtime.Object, fileNameTemplate string, useObjectName bool) ([]*asset.File, error) {

nit: It's actually a name template, right? Probably clearer to use it this way.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 18, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: tthvo

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 18, 2026
@tthvo
Copy link
Member

tthvo commented Mar 18, 2026

@jcpowermac @vr4manta vsphere ip address claims are affected here. which tests would exercise those so we can see the address claim manifests?

I am guessing these jobs (I noticed last time we fixed the IPAM version in #10169) 👀

/test e2e-vsphere-static-ovn
/payload-job-with-prs periodic-ci-openshift-release-master-nightly-4.22-e2e-vsphere-static-ovn-techpreview

@tthvo
Copy link
Member

tthvo commented Mar 18, 2026

/payload-job periodic-ci-openshift-release-master-nightly-4.22-e2e-vsphere-static-ovn-techpreview

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 18, 2026

@tthvo: trigger 0 job(s) for the /payload-(with-prs|job|aggregate|job-with-prs|aggregate-with-prs) command

@tthvo
Copy link
Member

tthvo commented Mar 18, 2026

/payload-job periodic-ci-openshift-release-main-nightly-4.22-e2e-vsphere-static-ovn-techpreview

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 18, 2026

@tthvo: trigger 1 job(s) for the /payload-(with-prs|job|aggregate|job-with-prs|aggregate-with-prs) command

  • periodic-ci-openshift-release-main-nightly-4.22-e2e-vsphere-static-ovn-techpreview

See details on https://pr-payload-tests.ci.openshift.org/runs/ci/877a1590-2310-11f1-98cf-849bf6d4e350-0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants