Skip to content
This repository was archived by the owner on Sep 9, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions api/v1alpha1/externalip_conditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ExternalIPConditionType is a valid value for .status.conditions.type
type ExternalIPConditionType string

const (
// ExternalIPConditionConfigurationApplied indicates whether the controller
// has processed the current spec version
ExternalIPConditionConfigurationApplied ExternalIPConditionType = "ConfigurationApplied"
)

// GetExternalIPStatusCondition returns the condition with the specified type from the ExternalIP status
func GetExternalIPStatusCondition(ip *ExternalIP, conditionType ExternalIPConditionType) *metav1.Condition {
return meta.FindStatusCondition(ip.Status.Conditions, string(conditionType))
}

// SetExternalIPStatusCondition sets the condition with the specified type in the ExternalIP status
func SetExternalIPStatusCondition(ip *ExternalIP, condition metav1.Condition) {
meta.SetStatusCondition(&ip.Status.Conditions, condition)
}
137 changes: 137 additions & 0 deletions api/v1alpha1/externalip_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ExternalIPSpec defines the desired state of ExternalIP
type ExternalIPSpec struct {
// Pool is the name of the ExternalIPPool this IP is allocated from.
// This field is immutable after creation.
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="pool is immutable"
Pool string `json:"pool"`
}

// ExternalIPPhaseType is a valid value for .status.phase
type ExternalIPPhaseType string

const (
// ExternalIPPhaseProgressing means an update is in progress
ExternalIPPhaseProgressing ExternalIPPhaseType = "Progressing"

// ExternalIPPhaseFailed means the IP provisioning has failed
ExternalIPPhaseFailed ExternalIPPhaseType = "Failed"

// ExternalIPPhaseReady means the IP and all associated resources are ready
ExternalIPPhaseReady ExternalIPPhaseType = "Ready"

// ExternalIPPhaseDeleting means there has been a request to delete the ExternalIP
ExternalIPPhaseDeleting ExternalIPPhaseType = "Deleting"
)

// ExternalIPStateType is a valid value for .status.state
type ExternalIPStateType string

const (
// ExternalIPStatePending means the IP allocation is pending
ExternalIPStatePending ExternalIPStateType = "Pending"

// ExternalIPStateAllocated means the IP has been allocated from the pool
ExternalIPStateAllocated ExternalIPStateType = "Allocated"

// ExternalIPStateFailed means provisioning failed
ExternalIPStateFailed ExternalIPStateType = "Failed"
)

// ExternalIPStatus defines the observed state of ExternalIP
type ExternalIPStatus struct {
// Phase provides a single-value overview of the state of the ExternalIP
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Enum=Progressing;Failed;Ready;Deleting
Phase ExternalIPPhaseType `json:"phase,omitempty"`

// DesiredConfigVersion is a hash of the spec, used to detect spec changes and control retry behavior.
// +kubebuilder:validation:Optional
DesiredConfigVersion string `json:"desiredConfigVersion,omitempty"`

// ProvisioningJobs holds an array of JobStatus tracking provisioning and deprovisioning operations
// +kubebuilder:validation:Optional
ProvisioningJobs []JobStatus `json:"provisioningJobs,omitempty"`

// 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"`
Comment on lines +80 to +82

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


// Address is the allocated external IP address
// +kubebuilder:validation:Optional
Address string `json:"address,omitempty"`

// State tracks the attachment lifecycle of the ExternalIP
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Enum=Pending;Allocated;Failed
State ExternalIPStateType `json:"state,omitempty"`

// Attached indicates whether a ExternalIPAttachment has successfully attached this IP to a target.
// +kubebuilder:validation:Optional
Attached bool `json:"attached,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=externalip
// +kubebuilder:printcolumn:name="Pool",type=string,JSONPath=`.spec.pool`
// +kubebuilder:printcolumn:name="Address",type=string,JSONPath=`.status.address`
// +kubebuilder:printcolumn:name="State",type=string,JSONPath=`.status.state`
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// ExternalIP is the Schema for the externalips API
type ExternalIP struct {
metav1.TypeMeta `json:",inline"`

// metadata is a standard object metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitempty,omitzero"`

// spec defines the desired state of ExternalIP
// +required
Spec ExternalIPSpec `json:"spec"`

// status defines the observed state of ExternalIP
// +optional
Status ExternalIPStatus `json:"status,omitempty,omitzero"`
}

// +kubebuilder:object:root=true

// ExternalIPList contains a list of ExternalIP
type ExternalIPList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ExternalIP `json:"items"`
}

// GetName returns the name of the ExternalIP resource
func (p *ExternalIP) GetName() string {
return p.ObjectMeta.Name
}
105 changes: 105 additions & 0 deletions api/v1alpha1/externalip_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1_test

import (
. "github.com/onsi/ginkgo/v2" //nolint:revive,staticcheck
. "github.com/onsi/gomega" //nolint:revive,staticcheck
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/osac-project/osac-operator/api/v1alpha1"
)

var _ = Describe("ExternalIPSpec", func() {
It("should accept a valid spec with pool", func() {
spec := v1alpha1.ExternalIPSpec{
Pool: "my-pool",
}

Expect(spec.Pool).To(Equal("my-pool"))
})
})

var _ = Describe("ExternalIPPhaseType", func() {
DescribeTable("should have correct string values",
func(phase v1alpha1.ExternalIPPhaseType, expected string) {
Expect(string(phase)).To(Equal(expected))
},
Entry("Progressing phase", v1alpha1.ExternalIPPhaseProgressing, "Progressing"),
Entry("Failed phase", v1alpha1.ExternalIPPhaseFailed, "Failed"),
Entry("Ready phase", v1alpha1.ExternalIPPhaseReady, "Ready"),
Entry("Deleting phase", v1alpha1.ExternalIPPhaseDeleting, "Deleting"),
)
})

var _ = Describe("ExternalIPStateType", func() {
DescribeTable("should have correct string values",
func(state v1alpha1.ExternalIPStateType, expected string) {
Expect(string(state)).To(Equal(expected))
},
Entry("Pending state", v1alpha1.ExternalIPStatePending, "Pending"),
Entry("Allocated state", v1alpha1.ExternalIPStateAllocated, "Allocated"),
Entry("Failed state", v1alpha1.ExternalIPStateFailed, "Failed"),
)
})

var _ = Describe("ExternalIPConditionType", func() {
It("should have ConfigurationApplied condition type", func() {
Expect(string(v1alpha1.ExternalIPConditionConfigurationApplied)).To(Equal("ConfigurationApplied"))
})
})

var _ = Describe("ExternalIP", func() {
Describe("GetName", func() {
It("should return the name", func() {
ip := &v1alpha1.ExternalIP{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ip",
},
}

Expect(ip.GetName()).To(Equal("test-ip"))
})
})
})

var _ = Describe("ExternalIP Condition Helpers", func() {
It("should set and get a condition", func() {
ip := &v1alpha1.ExternalIP{}

condition := metav1.Condition{
Type: string(v1alpha1.ExternalIPConditionConfigurationApplied),
Status: metav1.ConditionTrue,
Reason: "ConfigurationApplied",
Message: "applied",
LastTransitionTime: metav1.Now(),
}

v1alpha1.SetExternalIPStatusCondition(ip, condition)

got := v1alpha1.GetExternalIPStatusCondition(ip, v1alpha1.ExternalIPConditionConfigurationApplied)
Expect(got).ToNot(BeNil())
Expect(got.Status).To(Equal(metav1.ConditionTrue))
})

It("should return nil for missing condition", func() {
ip := &v1alpha1.ExternalIP{}

got := v1alpha1.GetExternalIPStatusCondition(ip, v1alpha1.ExternalIPConditionConfigurationApplied)
Expect(got).To(BeNil())
})
})
41 changes: 41 additions & 0 deletions api/v1alpha1/externalipattachment_conditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ExternalIPAttachmentConditionType is a valid value for .status.conditions.type
type ExternalIPAttachmentConditionType string

const (
// ExternalIPAttachmentConditionConfigurationApplied indicates whether the controller
// has processed the current spec version
ExternalIPAttachmentConditionConfigurationApplied ExternalIPAttachmentConditionType = "ConfigurationApplied"
)

// GetExternalIPAttachmentStatusCondition returns the condition with the specified type from the ExternalIPAttachment status
func GetExternalIPAttachmentStatusCondition(attachment *ExternalIPAttachment, conditionType ExternalIPAttachmentConditionType) *metav1.Condition {
return meta.FindStatusCondition(attachment.Status.Conditions, string(conditionType))
}

// SetExternalIPAttachmentStatusCondition sets the condition with the specified type in the ExternalIPAttachment status
func SetExternalIPAttachmentStatusCondition(attachment *ExternalIPAttachment, condition metav1.Condition) {
meta.SetStatusCondition(&attachment.Status.Conditions, condition)
}
Loading
Loading