Skip to content
This repository was archived by the owner on Sep 9, 2026. It is now read-only.

OSAC-1442: Add ExternalIP CRD types, controllers, and feedback controllers - #318

Merged
openshift-merge-bot[bot] merged 1 commit into
osac-project:mainfrom
ori-amizur:external-ips
Jun 30, 2026
Merged

openshift-merge-bot[bot] merged 1 commit into
osac-project:mainfrom
ori-amizur:external-ips

Conversation

@ori-amizur

@ori-amizur ori-amizur commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Add ExternalIPPool, ExternalIP, and ExternalIPAttachment as new operator resources ( OSAC-1480 , OSAC-1481 , OSAC-1482 ). Each mirrors its PublicIP counterpart with the PublicIP → ExternalIP rename.

Includes CRD type definitions, provisioning controllers, feedback controllers, tests, generated deepcopy methods, CRD manifests, and controller registration in main.go.

Summary by CodeRabbit

  • New Features
    • Added first-class Custom Resources for External IP Pools, External IPs, and External IP Attachments, including lifecycle phases, status fields, and standardized condition handling.
    • Introduced new controllers to reconcile External IP Pool/External IP/Attachment resources, with optional feedback loops when networking is enabled.
  • Bug Fixes
    • Expanded scheme registration and generated deep-copy support for the new resource types.
  • Chores
    • Added CRDs (Helm-templated and base manifests), updated RBAC for the new resources and status/finalizers, and added API type tests.

@openshift-ci-robot

openshift-ci-robot commented Jun 24, 2026

Copy link
Copy Markdown

@ori-amizur: This pull request references OSAC-1442 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the epic to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Add ExternalIPPool, ExternalIP, and ExternalIPAttachment as new operator resources ( OSAC-1480 , OSAC-1481, OSAC-1482 ). Each mirrors its PublicIP counterpart with the PublicIP → ExternalIP rename.

Includes CRD type definitions, provisioning controllers, feedback controllers, tests, generated deepcopy methods, CRD manifests, and controller registration in main.go.

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.

@openshift-ci
openshift-ci Bot requested a review from jhernand June 24, 2026 15:28
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds three new v1alpha1 CRDs with API types, manifests, scheme/deepcopy support, tests, and controller registration. It also updates RBAC and bumps the managed private API dependency used by buf generation.

Changes

ExternalIP CRD API, Conditions, and Controller Wiring

Layer / File(s) Summary
API types and conditions
api/v1alpha1/externalippool_types.go, api/v1alpha1/externalippool_conditions.go, api/v1alpha1/externalip_types.go, api/v1alpha1/externalip_conditions.go, api/v1alpha1/externalipattachment_types.go, api/v1alpha1/externalipattachment_conditions.go
Defines the three CRD spec and status shapes, phase/state enums, condition constants, and typed Get/Set condition helpers.
DeepCopy and scheme registration
api/v1alpha1/zz_generated.deepcopy.go, api/v1alpha1/groupversion_info.go, api/v1alpha1/scheme_test.go
Adds deepcopy methods for the new API types and registers the new kinds in the v1alpha1 scheme and scheme tests.
CRD manifests and RBAC rules
charts/operator-crds/templates/osac.openshift.io_externalippools.yaml, charts/operator-crds/templates/osac.openshift.io_externalips.yaml, charts/operator-crds/templates/osac.openshift.io_externalipattachments.yaml, config/crd/bases/osac.openshift.io_externalippools.yaml, config/crd/bases/osac.openshift.io_externalips.yaml, config/crd/bases/osac.openshift.io_externalipattachments.yaml, config/rbac/role.yaml, charts/operator/templates/clusterrole.yaml
Adds CRD schemas, validation, printer columns, status subresources, and RBAC for primary, finalizer, and status access on all three resources.
Controller reconciler wiring
cmd/main.go
Creates the attach/detach provisioning provider and registers the ExternalIPPool, ExternalIP, and ExternalIPAttachment reconcilers and feedback reconcilers.
API tests
api/v1alpha1/externalippool_types_test.go, api/v1alpha1/externalip_types_test.go, api/v1alpha1/externalipattachment_types_test.go
Covers spec fields, enum string values, condition constants, GetName, and condition helper behavior.
buf dependency bump
buf.gen.yaml
Updates the private API module reference used by buf generation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • akshaynadkarni
  • eranco74
  • SiddarthR56
  • adriengentil
  • danmanor

Poem

Three new kinds arrive with tidy schemes 🌿
Pools, IPs, attachments — all crisp and clean
Conditions settle, deepcopy sings along
Controllers wake and wire their routing song 🎵


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error New ExternalIP controllers log AAP URLs, pool/publicIP UUIDs, compute instance names, and ExternalIP addresses, exposing internal hostnames/customer data. Remove or redact those fields; log only non-sensitive identifiers or hashes, and avoid printing URLs, IPs, UUIDs, or compute-instance names.
Ai-Attribution ⚠️ Warning HEAD commit uses Co-Authored-By: Claude Opus 4.6, and no Assisted-by/Generated-by trailer was found. Replace the AI co-author line with a Red Hat Assisted-by or Generated-by trailer, and remove Co-Authored-By for the AI tool.
✅ Passed checks (9 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the core change and mentions controller work, though it omits ExternalIPPool and ExternalIPAttachment.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No-Hardcoded-Secrets ✅ Passed No hardcoded secrets found in touched files; scans only hit env-var names (e.g. OSAC_AAP_TOKEN) and docs URLs, with no credential literals, base64 blobs, or embedded creds.
No-Weak-Crypto ✅ Passed Touched files contain no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, no custom crypto, and no secret/token comparisons; cmd/main.go only uses standard tls.Config.
No-Injection-Vectors ✅ Passed No listed injection sinks appear in the new ExternalIP/provisioning code; searches found no eval/exec, shell, yaml.load, or SQL concatenation in changed sources.
Container-Privileges ✅ Passed Touched manifests only add CRDs/RBAC; deployment templates keep runAsNonRoot:true and allowPrivilegeEscalation:false, with no privileged or host namespace flags found.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/v1alpha1/externalipattachment_types.go`:
- Around line 28-31: Finish the PublicIP to ExternalIP rename by updating the
ExternalIP field’s JSON serialization tag in the ExternalIPAttachment spec to
use the new serialized name, and update any user-facing path strings that still
reference the old publicIP path. Check the ExternalIPAttachment type definition
and the related formatting/logging code that prints the field path so both the
CRD surface and displayed paths consistently use spec.externalIP instead of
spec.publicIP.
- Around line 33-37: The ExternalIPAttachment target is currently optional even
though the type comment says exactly one target field must be set, so add
admission validation to enforce that invariant. Update the ExternalIPAttachment
spec around ComputeInstance and the other target fields to require exactly one
non-empty target at creation/update, using Kubebuilder validation markers or a
custom validation webhook if needed, and ensure the validation matches the
existing reconcile nil-handling in ExternalIPAttachment.

In `@api/v1alpha1/externalippool_types.go`:
- Around line 38-44: The ImplementationStrategy field in ExternalIPPool is
documented as defaulting to metallb-l2, but the type definition does not declare
a kubebuilder default, so empty values can persist on create. Update the
ExternalIPPool spec field annotation to include the default for
ImplementationStrategy alongside the existing validation and immutability
markers, so generated CRDs and admission set metallb-l2 when the field is
omitted.

In `@config/crd/bases/osac.openshift.io_externalipattachments.yaml`:
- Around line 20-21: The ExternalIPAttachment API contract is still using the
old publicIP field and is missing enforcement for the required target, so update
the source type that defines the spec to rename the field to externalIP and add
validation that exactly one target is set (at minimum require computeInstance,
or add a spec-level CEL rule if multiple targets are planned). Then regenerate
the CRD manifests instead of editing the generated config/crd output directly,
using the ExternalIPAttachment type and its spec/validation definitions as the
source of truth.

In `@config/crd/bases/osac.openshift.io_externalippools.yaml`:
- Around line 70-80: The ExternalIPPool implementationStrategy field is
described as defaulting to metallb-l2 but the API type does not declare that
default, so update the source type for implementationStrategy with the
kubebuilder default marker in the ExternalIPPool/OpenShift API definition and
regenerate the CRD manifests. Make the change in the struct that owns
implementationStrategy, then run the normal generation workflow so the default
appears in the generated CRD instead of hand-editing the YAML.
- Around line 60-69: The ExternalIPPool CRD currently only enforces cidrs
immutability and still allows arbitrary strings, so add source-level validation
in the ExternalIPPool API type (the spec/cidrs field in externalippool_types.go)
to require valid CIDR syntax and ensure each CIDR matches spec.ipFamily using
kubebuilder/CEL or equivalent admission validation. Keep the validation on the
API type, not the generated CRD, then regenerate the manifests so the changes
flow into the config/crd output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 515cc59d-0880-4916-8be7-0667e0676fc0

📥 Commits

Reviewing files that changed from the base of the PR and between 4d6918b and 5ff14c5.

⛔ Files ignored due to path filters (30)
  • internal/api/osac/private/v1/baremetal_instance_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/baremetal_instance_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/compute_instance_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/compute_instance_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachment_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachment_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pool_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pool_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_provider_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_provider_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/ip_family_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/ip_family_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/public_ip_pool_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/public_ip_pool_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/security_group_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/security_group_type_protoopaque.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (34)
  • api/v1alpha1/externalip_conditions.go
  • api/v1alpha1/externalip_types.go
  • api/v1alpha1/externalip_types_test.go
  • api/v1alpha1/externalipattachment_conditions.go
  • api/v1alpha1/externalipattachment_types.go
  • api/v1alpha1/externalipattachment_types_test.go
  • api/v1alpha1/externalippool_conditions.go
  • api/v1alpha1/externalippool_types.go
  • api/v1alpha1/externalippool_types_test.go
  • api/v1alpha1/groupversion_info.go
  • api/v1alpha1/scheme_test.go
  • api/v1alpha1/zz_generated.deepcopy.go
  • buf.gen.yaml
  • cmd/main.go
  • config/crd/bases/osac.openshift.io_externalipattachments.yaml
  • config/crd/bases/osac.openshift.io_externalippools.yaml
  • config/crd/bases/osac.openshift.io_externalips.yaml
  • config/rbac/role.yaml
  • internal/controller/constants_common.go
  • internal/controller/externalip_controller.go
  • internal/controller/externalip_controller_test.go
  • internal/controller/externalip_feedback_controller.go
  • internal/controller/externalip_feedback_controller_test.go
  • internal/controller/externalip_names.go
  • internal/controller/externalipattachment_controller.go
  • internal/controller/externalipattachment_controller_test.go
  • internal/controller/externalipattachment_feedback_controller.go
  • internal/controller/externalipattachment_feedback_controller_test.go
  • internal/controller/externalipattachment_names.go
  • internal/controller/externalippool_controller.go
  • internal/controller/externalippool_controller_test.go
  • internal/controller/externalippool_feedback_controller.go
  • internal/controller/externalippool_feedback_controller_test.go
  • internal/controller/externalippool_names.go

Comment thread api/v1alpha1/externalipattachment_types.go Outdated
Comment thread api/v1alpha1/externalipattachment_types.go
Comment thread api/v1alpha1/externalippool_types.go
Comment thread config/crd/bases/osac.openshift.io_externalipattachments.yaml Outdated
Comment thread config/crd/bases/osac.openshift.io_externalippools.yaml
Comment thread config/crd/bases/osac.openshift.io_externalippools.yaml
@ori-amizur
ori-amizur force-pushed the external-ips branch 2 times, most recently from 87d47af to 036b336 Compare June 24, 2026 16:14
@omer-vishlitzky

Copy link
Copy Markdown
Contributor

🔴 CI Triage: pr_issue | Category: TEST

Root cause: The operator is crash-looping because it lacks RBAC permissions to list the newly added ExternalIP, ExternalIPPool, and ExternalIPAttachment CRDs, causing E2E tests to time out waiting for resources to reconcile.

Explanation: The PR adds new CRDs (ExternalIP, ExternalIPPool, ExternalIPAttachment) and their corresponding controllers. However, the RBAC permissions for the operator's ServiceAccount were not updated in the Helm chart used for E2E deployments. When the operator starts, it attempts to start informers for these new resources but receives a 403 Forbidden error (User "system:serviceaccount:osac-e2e-ci:osac-operator" cannot list resource "externalips"). This causes the manager's cache sync to time out and the operator pod to crash-loop. Because the operator is not running, it cannot reconcile other resources like VirtualNetwork and PublicIPPool, leading to the test timeouts observed in the JUnit results.

Evidence:

fail/osac-logs/pod-osac-operator-8b5796f8b-mzvlt-manager-previous.log:

2026-06-24T17:04:31Z	ERROR	controller-runtime.cache.UnhandledError	Failed to watch	{"reflector": "go/pkg/mod/k8s.io/client-go@v0.36.2/tools/cache/reflector.go:343", "type": "*v1alpha1.ExternalIP", "error": "failed to list *v1alpha1.ExternalIP: externalips.osac.openshift.io is forbidden: User \"system:serviceaccount:osac-e2e-ci:osac-operator\" cannot list resource \"externalips\" in API group \"osac.openshift.io\" at the cluster scope"}

fail/osac-logs/pod-osac-operator-8b5796f8b-mzvlt-manager-previous.log:

2026-06-24T17:06:31Z	ERROR	setup	problem running manager	{"error": "timed out waiting for source &{ %!s(*manager.controllerManager=&{{{} {0 0}} true 0x3efb259018a0 ... to Start. Please ensure that its Start() method is non-blocking"}

Suggestion: Update the operator's ClusterRole in the Helm chart (likely in charts/operator/templates/clusterrole.yaml or the corresponding osac-installer repository) to include get, list, watch, create, update, patch, delete permissions for externalips, externalippools, and externalipattachments in the osac.openshift.io API group.


Prow job | Build 2069816447260954624 | 🤖 triagent

For deeper investigation, use the /osac-debug-e2e skill with this build ID.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/v1alpha1/externalip_types.go`:
- Around line 80-82: The new status.Conditions field is missing kubebuilder
list-map markers, so it will not merge by condition type as intended. Add the
listType=map and listMapKey=type markers to this Conditions definition in
ExternalIPStatus, and apply the same update to every other newly added
[]metav1.Condition field in this change, then regenerate the CRDs so the schema
reflects the merge semantics.

In `@api/v1alpha1/externalippool_types.go`:
- Around line 84-95: The status counters in ExternalIPPoolStatus use int64,
which cannot safely represent common IPv6 pool sizes like a /64. Update the
ExternalIPPoolStatus fields Total, Allocated, and Available to avoid overflow by
either adding validation that restricts supported IPv6 prefix sizes in the
CRD/spec or switching these fields to a wider representation, and make sure any
related status calculations and serializers in ExternalIPPoolStatus are updated
consistently.
- Around line 25-36: The ExternalIPPool schema in ExternalIPPoolSpec currently
enforces only non-empty CIDRs and an IPFamily enum, so invalid CIDR strings or
CIDRs that do not match the declared family can still be admitted. Add
admission-time validation on the CIDRs field, using the ExternalIPPoolSpec/CIDRs
and IPFamily symbols, so each CIDR is parsed and checked against the selected
family and rejected if it does not match.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: cabddead-796c-499c-83de-6829776442b1

📥 Commits

Reviewing files that changed from the base of the PR and between 5ff14c5 and 9a43b8f.

⛔ Files ignored due to path filters (30)
  • internal/api/osac/private/v1/baremetal_instance_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/baremetal_instance_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/compute_instance_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/compute_instance_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachment_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachment_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pool_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pool_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_provider_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_provider_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/ip_family_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/ip_family_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/public_ip_pool_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/public_ip_pool_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/security_group_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/security_group_type_protoopaque.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (38)
  • api/v1alpha1/externalip_conditions.go
  • api/v1alpha1/externalip_types.go
  • api/v1alpha1/externalip_types_test.go
  • api/v1alpha1/externalipattachment_conditions.go
  • api/v1alpha1/externalipattachment_types.go
  • api/v1alpha1/externalipattachment_types_test.go
  • api/v1alpha1/externalippool_conditions.go
  • api/v1alpha1/externalippool_types.go
  • api/v1alpha1/externalippool_types_test.go
  • api/v1alpha1/groupversion_info.go
  • api/v1alpha1/scheme_test.go
  • api/v1alpha1/zz_generated.deepcopy.go
  • buf.gen.yaml
  • charts/operator-crds/templates/osac.openshift.io_externalipattachments.yaml
  • charts/operator-crds/templates/osac.openshift.io_externalippools.yaml
  • charts/operator-crds/templates/osac.openshift.io_externalips.yaml
  • charts/operator/templates/clusterrole.yaml
  • cmd/main.go
  • config/crd/bases/osac.openshift.io_externalipattachments.yaml
  • config/crd/bases/osac.openshift.io_externalippools.yaml
  • config/crd/bases/osac.openshift.io_externalips.yaml
  • config/rbac/role.yaml
  • internal/controller/constants_common.go
  • internal/controller/externalip_controller.go
  • internal/controller/externalip_controller_test.go
  • internal/controller/externalip_feedback_controller.go
  • internal/controller/externalip_feedback_controller_test.go
  • internal/controller/externalip_names.go
  • internal/controller/externalipattachment_controller.go
  • internal/controller/externalipattachment_controller_test.go
  • internal/controller/externalipattachment_feedback_controller.go
  • internal/controller/externalipattachment_feedback_controller_test.go
  • internal/controller/externalipattachment_names.go
  • internal/controller/externalippool_controller.go
  • internal/controller/externalippool_controller_test.go
  • internal/controller/externalippool_feedback_controller.go
  • internal/controller/externalippool_feedback_controller_test.go
  • internal/controller/externalippool_names.go

Comment on lines +80 to +82
// Conditions holds an array of metav1.Condition that describe the state of the ExternalIP
// +kubebuilder:validation:Optional
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that all new condition lists have list-map markers in source
# and generate the expected CRD schema markers downstream.
echo "=== Source condition fields ==="
rg -n -C3 'Conditions \[\]metav1\.Condition|listType=map|listMapKey=type' api/v1alpha1

echo
echo "=== Generated CRD condition schemas ==="
rg -n -C2 'conditions:|x-kubernetes-list-type|x-kubernetes-list-map-keys' config/crd/bases charts/operator-crds/templates

Repository: osac-project/osac-operator

Length of output: 30868


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== condition field annotations in source ==="
sed -n '74,88p' api/v1alpha1/externalip_types.go
echo
sed -n '116,128p' api/v1alpha1/tenant_types.go
echo
sed -n '262,272p' api/v1alpha1/computeinstance_types.go

echo
echo "=== any existing kubebuilder patch markers in comments ==="
rg -n '// \+patchStrategy=merge|// \+patchMergeKey=' api/v1alpha1

echo
echo "=== any listType/listMapKey on metav1.Condition fields ==="
rg -n -C2 'Conditions \[\]metav1\.Condition' api/v1alpha1

Repository: osac-project/osac-operator

Length of output: 2328


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== condition field annotations in source ==="
sed -n '74,88p' api/v1alpha1/externalip_types.go
echo
sed -n '116,128p' api/v1alpha1/tenant_types.go
echo
sed -n '262,272p' api/v1alpha1/computeinstance_types.go

echo
echo "=== any existing kubebuilder patch markers in comments ==="
rg -n '// \+patchStrategy=merge|// \+patchMergeKey=' api/v1alpha1

echo
echo "=== any listType/listMapKey on metav1.Condition fields ==="
rg -n -C2 'Conditions \[\]metav1\.Condition' api/v1alpha1

Repository: osac-project/osac-operator

Length of output: 2328


Add +listType=map / +listMapKey=type to every new status.conditions field.

patchStrategy/patchMergeKey alone don’t give CRD merge-by-type semantics; these arrays are still treated atomically unless the kubebuilder list-map markers are present. Apply the same fix to the other new []metav1.Condition fields in this change, then regenerate the CRDs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v1alpha1/externalip_types.go` around lines 80 - 82, The new
status.Conditions field is missing kubebuilder list-map markers, so it will not
merge by condition type as intended. Add the listType=map and listMapKey=type
markers to this Conditions definition in ExternalIPStatus, and apply the same
update to every other newly added []metav1.Condition field in this change, then
regenerate the CRDs so the schema reflects the merge semantics.

Source: Coding guidelines

Comment on lines +25 to +36
// CIDRs is the list of CIDR blocks for this pool. All CIDRs must match the declared IPFamily.
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="cidrs is immutable"
CIDRs []string `json:"cidrs"`

// IPFamily indicates the IP address family for this pool (IPv4 or IPv6)
// +kubebuilder:validation:Required
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Enum=IPv4;IPv6
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="ipFamily is immutable"
IPFamily string `json:"ipFamily"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Validate cidrs against ipFamily at admission.

CIDRs is documented as “All CIDRs must match the declared IPFamily”, but this schema only enforces a non-empty slice plus the IPFamily enum. Inputs like ["not-a-cidr"] or IPv4 CIDRs with ipFamily: IPv6 will be persisted and fail later in reconciliation/provisioning.

As per path instructions, "Validate at trust boundaries with allow-lists, not deny-lists".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v1alpha1/externalippool_types.go` around lines 25 - 36, The
ExternalIPPool schema in ExternalIPPoolSpec currently enforces only non-empty
CIDRs and an IPFamily enum, so invalid CIDR strings or CIDRs that do not match
the declared family can still be admitted. Add admission-time validation on the
CIDRs field, using the ExternalIPPoolSpec/CIDRs and IPFamily symbols, so each
CIDR is parsed and checked against the selected family and rejected if it does
not match.

Source: Path instructions

Comment on lines +84 to +95
// Total is the total number of usable IP addresses across all CIDRs in this pool.
// Uses int64 to accommodate large IPv6 CIDR ranges.
// +kubebuilder:validation:Optional
Total int64 `json:"total,omitempty"`

// Allocated is the number of IPs currently allocated from the pool.
// +kubebuilder:validation:Optional
Allocated int64 `json:"allocated,omitempty"`

// Available is the number of IPs available for allocation.
// +kubebuilder:validation:Optional
Available int64 `json:"available,omitempty"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

int64 cannot represent common IPv6 pool sizes.

A single IPv6 /64 already has 2^64 addresses, which exceeds math.MaxInt64. Since the spec accepts IPv6 pools, status.total, allocated, and available will overflow or require truncation for valid inputs. Either cap supported IPv6 prefix sizes via validation or switch these counters to a wider representation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v1alpha1/externalippool_types.go` around lines 84 - 95, The status
counters in ExternalIPPoolStatus use int64, which cannot safely represent common
IPv6 pool sizes like a /64. Update the ExternalIPPoolStatus fields Total,
Allocated, and Available to avoid overflow by either adding validation that
restricts supported IPv6 prefix sizes in the CRD/spec or switching these fields
to a wider representation, and make sure any related status calculations and
serializers in ExternalIPPoolStatus are updated consistently.

@omer-vishlitzky

Copy link
Copy Markdown
Contributor

💀 CI Triage: broken_main | Category: BOOT

Root cause: The merge of osac-installer PR 328 broke the main branch by enabling secret-based AAP token configuration, which triggers a Kubernetes strategic merge patch conflict during Helm upgrade on snapshot-booted clusters.

Explanation: The failure is caused by a strategic merge patch conflict when upgrading the 'osac-operator' deployment during the cluster refresh phase. Recently merged PR 316 in 'osac-operator' introduced support for loading the AAP token from a secret via 'valueFrom.secretKeyRef'. Subsequently, PR 328 in 'osac-installer' was merged, which updated the CI Helm values ('values/vmaas-ci/values.yaml') to use this new 'tokenSecret' configuration. However, the pre-built cluster snapshot contains an existing 'osac-operator' deployment where 'OSAC_AAP_TOKEN' is set as a literal 'value'. When 'helm upgrade' runs during the boot step, Kubernetes attempts to merge the new deployment spec into the existing one. Because the strategic merge patch merges list items by name, the resulting environment variable spec contains both 'value' and 'valueFrom', which is invalid in Kubernetes and causes the upgrade to fail with a validation error. This is a broken_main issue affecting all PRs and periodic runs across all repositories.

Evidence:

build-log.txt:

Error: UPGRADE FAILED: cannot patch "osac-operator" with kind Deployment: Deployment.apps "osac-operator" is invalid: spec.template.spec.containers[0].env[1].valueFrom: Invalid value: "": may not be specified when `value` is not empty

Suggestion: To resolve this, apply one of the following fixes:

  1. In 'osac-operator' (charts/operator/templates/deployment.yaml), explicitly set 'value: null' when 'tokenSecret' is used so that the strategic merge patch clears the existing literal value:
           {{- if and .Values.aap.tokenSecret .Values.aap.tokenSecret.name }}
           - name: OSAC_AAP_TOKEN
             value: null
             valueFrom:
               secretKeyRef:
                 name: {{ .Values.aap.tokenSecret.name }}
                 key: {{ .Values.aap.tokenSecret.key | default "token" }}
                 optional: true
  1. Alternatively, in 'osac-installer' (scripts/refresh-after-snapshot.py), delete the existing 'osac-operator' deployment before running 'helm upgrade' to force a clean recreation:
    oc delete deployment osac-operator -n osac-e2e-ci --ignore-not-found

Prow job | Build 2069891699982733312 | 🤖 triagent

For deeper investigation, use the /osac-debug-e2e skill with this build ID.

…llers

Add ExternalIPPool, ExternalIP, and ExternalIPAttachment as new operator
resources (OSAC-1480, OSAC-1481, OSAC-1482). Each mirrors its PublicIP
counterpart with the PublicIP → ExternalIP rename.

Includes CRD type definitions, provisioning controllers, feedback
controllers, tests, generated deepcopy methods, CRD manifests, and
controller registration in main.go.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@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)
api/v1alpha1/scheme_test.go (1)

86-108: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test claims "exactly 24" but never asserts the count. The loop only verifies each expected kind is present; it doesn't check that no other kinds were registered. An accidental extra registration would pass undetected. Assert the actual count to match the description.

Suggested addition
 		for _, kind := range expectedKinds {
 			gvk := v1alpha1.GroupVersion.WithKind(kind)
 			knownTypes := scheme.KnownTypes(v1alpha1.GroupVersion)
 			_, found := knownTypes[gvk.Kind]
 			Expect(found).To(BeTrue(), "expected kind %s to be registered", kind)
 		}
+		Expect(expectedKinds).To(HaveLen(24))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v1alpha1/scheme_test.go` around lines 86 - 108, The test in
scheme_test.go only checks that each expected kind is present in
scheme.KnownTypes(v1alpha1.GroupVersion), but it never verifies the total number
of registered types. Update the It block around expectedKinds and the knownTypes
lookup to also assert the count matches the “exactly 24 registered types plus
internal types” expectation, so any accidental extra registration fails the
test.
♻️ Duplicate comments (1)
config/crd/bases/osac.openshift.io_externalipattachments.yaml (1)

58-74: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Require a target before making the attachment spec immutable.

computeInstance says exactly one target must be set, but only externalIP is required. A user can create spec.externalIP without any target, and Line 72 then makes that invalid attachment unfixable in-place.

Expected generated shape after updating the API type
             required:
             - externalIP
+            - computeInstance
             type: object
             x-kubernetes-validations:
+            - message: exactly one target field must be set
+              rule: has(self.computeInstance)
             - message: spec is immutable after creation
               rule: self == oldSelf

If more target fields are planned, replace the simple required field with an exact-one CEL rule in the source type.

As per coding guidelines, update the API type source and regenerate manifests instead of editing generated config/crd/ files directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config/crd/bases/osac.openshift.io_externalipattachments.yaml` around lines
58 - 74, The ExternalIPAttachment spec currently allows creating an attachment
without any target because only externalIP is required, and the immutability
rule then makes that invalid state impossible to correct. Update the API type
source for the ExternalIPAttachment spec to require a target field as
well—either add the missing required target in the struct/schema or, if multiple
targets are planned, use an exact-one CEL validation on the source type.
Regenerate the CRD manifests from the updated source instead of editing the
generated config/crd artifact directly.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@buf.gen.yaml`:
- Line 27: The Buf module bump to v0.0.67 needs to be paired with regenerated
API artifacts and a checked-in buf.lock for reproducible generation. Update the
Buf configuration via the existing buf.gen.yaml / internal/api generation flow
by running buf generate against the new module version, then commit the
resulting buf.lock alongside any regenerated files so the generated code and
dependency resolution stay in sync.

---

Outside diff comments:
In `@api/v1alpha1/scheme_test.go`:
- Around line 86-108: The test in scheme_test.go only checks that each expected
kind is present in scheme.KnownTypes(v1alpha1.GroupVersion), but it never
verifies the total number of registered types. Update the It block around
expectedKinds and the knownTypes lookup to also assert the count matches the
“exactly 24 registered types plus internal types” expectation, so any accidental
extra registration fails the test.

---

Duplicate comments:
In `@config/crd/bases/osac.openshift.io_externalipattachments.yaml`:
- Around line 58-74: The ExternalIPAttachment spec currently allows creating an
attachment without any target because only externalIP is required, and the
immutability rule then makes that invalid state impossible to correct. Update
the API type source for the ExternalIPAttachment spec to require a target field
as well—either add the missing required target in the struct/schema or, if
multiple targets are planned, use an exact-one CEL validation on the source
type. Regenerate the CRD manifests from the updated source instead of editing
the generated config/crd artifact directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 0eb5ba41-46b4-46f8-936e-55bdc5ee577e

📥 Commits

Reviewing files that changed from the base of the PR and between 9a43b8f and d56c9a3.

⛔ Files ignored due to path filters (30)
  • internal/api/osac/private/v1/baremetal_instance_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/baremetal_instance_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/compute_instance_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/compute_instance_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachment_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachment_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_attachments_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pool_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pool_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_pools_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ip_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/external_ips_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_provider_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_provider_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service_grpc.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/identity_providers_service_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/ip_family_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/ip_family_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/public_ip_pool_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/public_ip_pool_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/security_group_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/security_group_type_protoopaque.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (38)
  • api/v1alpha1/externalip_conditions.go
  • api/v1alpha1/externalip_types.go
  • api/v1alpha1/externalip_types_test.go
  • api/v1alpha1/externalipattachment_conditions.go
  • api/v1alpha1/externalipattachment_types.go
  • api/v1alpha1/externalipattachment_types_test.go
  • api/v1alpha1/externalippool_conditions.go
  • api/v1alpha1/externalippool_types.go
  • api/v1alpha1/externalippool_types_test.go
  • api/v1alpha1/groupversion_info.go
  • api/v1alpha1/scheme_test.go
  • api/v1alpha1/zz_generated.deepcopy.go
  • buf.gen.yaml
  • charts/operator-crds/templates/osac.openshift.io_externalipattachments.yaml
  • charts/operator-crds/templates/osac.openshift.io_externalippools.yaml
  • charts/operator-crds/templates/osac.openshift.io_externalips.yaml
  • charts/operator/templates/clusterrole.yaml
  • cmd/main.go
  • config/crd/bases/osac.openshift.io_externalipattachments.yaml
  • config/crd/bases/osac.openshift.io_externalippools.yaml
  • config/crd/bases/osac.openshift.io_externalips.yaml
  • config/rbac/role.yaml
  • internal/controller/constants_common.go
  • internal/controller/externalip_controller.go
  • internal/controller/externalip_controller_test.go
  • internal/controller/externalip_feedback_controller.go
  • internal/controller/externalip_feedback_controller_test.go
  • internal/controller/externalip_names.go
  • internal/controller/externalipattachment_controller.go
  • internal/controller/externalipattachment_controller_test.go
  • internal/controller/externalipattachment_feedback_controller.go
  • internal/controller/externalipattachment_feedback_controller_test.go
  • internal/controller/externalipattachment_names.go
  • internal/controller/externalippool_controller.go
  • internal/controller/externalippool_controller_test.go
  • internal/controller/externalippool_feedback_controller.go
  • internal/controller/externalippool_feedback_controller_test.go
  • internal/controller/externalippool_names.go

Comment thread buf.gen.yaml
inputs:

- module: buf.build/osac-project/private-api:v0.0.65
- module: buf.build/osac-project/private-api:v0.0.67

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the pinned version and that generated client compiles/matches expectations.
rg -n 'private-api:v0\.0\.6[0-9]' buf.gen.yaml buf.lock 2>/dev/null
fd -t f . internal/api | head -50

Repository: osac-project/osac-operator

Length of output: 3660


🏁 Script executed:

#!/bin/bash
# Check for buf.lock to verify the exact commit hash pinned for v0.0.67
if [ -f "buf.lock" ]; then
    echo "=== buf.lock contents ==="
    rg -A 2 "private-api" buf.lock
else
    echo "buf.lock not found in repository root."
    fd -f "buf.lock" . 2>/dev/null
fi
# Also check the very first line of one generated file for version comments if available
head -n 5 internal/api/osac/private/v1/access_key_type.pb.go

Repository: osac-project/osac-operator

Length of output: 420


🌐 Web query:

buf build version pinning buf.lock usage

💡 Result:

In the Buf ecosystem, the buf.lock file is a machine-generated file that records the exact state of external dependencies for your workspace, ensuring that builds are reproducible, protected against tampering, and consistent across environments [1][2][3]. It should never be edited by hand and should always be checked into version control [2][3]. Core Functions of buf.lock: 1. Dependency Version Pinning: While your buf.yaml file declares the dependencies you want to use (often using flexible references), the buf.lock file records the specific commit and cryptographic manifest digest for every direct and transitive dependency [1][2]. This locks your project to a known, stable version [2]. 2. Tamper-Proofing: Every BSR commit is content-addressed by a cryptographic manifest digest [3]. The Buf CLI records this digest in buf.lock [1][3]. During every buf build, the CLI verifies the downloaded dependency's content against this digest; if they do not match, the build fails, protecting you from man-in-the-middle attacks or unauthorized upstream changes [1][3]. 3. Reproducibility: By checking buf.lock into source control, you ensure that every developer and build system uses the exact same version of dependencies, preventing "it works on my machine" issues [2][3]. Workflow for Managing buf.lock: - Initial Setup: When you add dependencies to the deps field in buf.yaml, you must generate or update the buf.lock file [1][2]. - Update Dependencies: Run the command buf dep update at the root of your workspace [2][4]. This fetches the latest commits for your dependencies, updates the commit and digest information in buf.lock, and includes any new transitive dependencies [1][4]. - Clean Up: If you remove dependencies from buf.yaml, you can run buf dep prune to remove unused entries from buf.lock [1]. - Code Review: Treat changes to buf.lock as security-relevant during code reviews, as they represent shifts in the underlying code your project depends on [3].

Citations:


🏁 Script executed:

#!/bin/bash
# Attempt to resolve the exact commit for v0.0.67 from BSR to verify if local files match the latest hash
# Since buf.lock is missing, this is a high risk. 
# Let's check the git history of the generated files to see when they were updated.
git log --oneline -n 10 internal/api/ | head -10
# Check if the comment in generated files contains a version tag or timestamp
grep -r "v0.0.67" internal/api/ || echo "No visible v0.0.67 tag in file headers"
# Check the git log of buf.gen.yaml to see when the version was changed
git log --oneline -n 10 buf.gen.yaml

Repository: osac-project/osac-operator

Length of output: 369


Ensure generated code matches the v0.0.67 bump and add buf.lock.

The buf.gen.yaml and internal/api/ were updated in the same commit (d56c9a3), indicating generation likely ran correctly. However, buf.lock is missing.

Per AGENTS.md and BSR best practices, run buf generate (which updates dependencies) and commit the resulting buf.lock to guarantee reproducible builds and prevent silent drift between the module version and the actual generated code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@buf.gen.yaml` at line 27, The Buf module bump to v0.0.67 needs to be paired
with regenerated API artifacts and a checked-in buf.lock for reproducible
generation. Update the Buf configuration via the existing buf.gen.yaml /
internal/api generation flow by running buf generate against the new module
version, then commit the resulting buf.lock alongside any regenerated files so
the generated code and dependency resolution stay in sync.

@openshift-ci

openshift-ci Bot commented Jun 30, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: danmanor, ori-amizur

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-merge-bot
openshift-merge-bot Bot merged commit 75f48c7 into osac-project:main Jun 30, 2026
11 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants