Skip to content
Open
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
143 changes: 143 additions & 0 deletions api/v1/conditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
Copyright 2021.

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 v1

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

// Condition types used across SR-IOV Network Operator CRDs
const (
// ConditionProgressing indicates that the resource is being actively reconciled
ConditionProgressing = "Progressing"

// ConditionDegraded indicates that the resource is not functioning as expected
ConditionDegraded = "Degraded"

// ConditionReady indicates that the resource has reached its desired state and is fully functional
ConditionReady = "Ready"

// ConditionDrainProgressing indicates that the node is being actively drained
ConditionDrainProgressing = "DrainProgressing"

// ConditionDrainDegraded indicates that the drain process is not functioning as expected
ConditionDrainDegraded = "DrainDegraded"

// ConditionDrainComplete indicates that the drain operation completed successfully
ConditionDrainComplete = "DrainComplete"
)

// Common condition reasons used across SR-IOV Network Operator CRDs
const (
// Reasons for Ready condition
ReasonNetworkReady = "NetworkReady"
ReasonNodeReady = "NodeConfigurationReady"
ReasonNodeDrainReady = "NodeDrainReady"
ReasonOperatorReady = "OperatorReady"
ReasonNotReady = "NotReady"

// Reasons for Degraded condition
ReasonProvisioningFailed = "ProvisioningFailed"
ReasonConfigurationFailed = "ConfigurationFailed"
ReasonNetworkAttachmentDefNotFound = "NetworkAttachmentDefinitionNotFound"
ReasonNetworkAttachmentDefInvalid = "NetworkAttachmentDefinitionInvalid"
ReasonNamespaceNotFound = "NamespaceNotFound"
ReasonHardwareError = "HardwareError"
ReasonDriverError = "DriverError"
ReasonOperatorComponentsNotHealthy = "OperatorComponentsNotHealthy"
ReasonNotDegraded = "NotDegraded"

// Reasons for Progressing condition
ReasonConfiguringNode = "ConfiguringNode"
ReasonApplyingConfiguration = "ApplyingConfiguration"
ReasonCreatingVFs = "CreatingVFs"
ReasonLoadingDriver = "LoadingDriver"
ReasonDrainingNode = "DrainingNode"
ReasonNotProgressing = "NotProgressing"

// Reasons for DrainComplete condition
ReasonDrainCompleted = "DrainCompleted"
ReasonDrainNotNeeded = "DrainNotNeeded"
ReasonDrainPending = "DrainPending"

// Reasons for DrainDegraded condition
ReasonDrainFailed = "DrainFailed"

// Reasons for Policy conditions
ReasonPolicyReady = "PolicyReady"
ReasonPolicyNotReady = "PolicyNotReady"
ReasonNoMatchingNodes = "NoMatchingNodes"
ReasonPartiallyApplied = "PartiallyApplied"
ReasonAllNodesConfigured = "AllNodesConfigured"
ReasonSomeNodesFailed = "SomeNodesFailed"
ReasonSomeNodesProgressing = "SomeNodesProgressing"
ReasonAllNodesProgressingOrReady = "AllNodesProgressingOrReady"
)

// DrainState represents the current state of a drain operation
type DrainState string

const (
// DrainStateIdle indicates no drain is in progress
DrainStateIdle DrainState = "Idle"
// DrainStateDraining indicates drain is in progress with no errors
DrainStateDraining DrainState = "Draining"
// DrainStateDrainingWithErrors indicates drain is in progress but encountering errors
DrainStateDrainingWithErrors DrainState = "DrainingWithErrors"
// DrainStateComplete indicates drain completed successfully
DrainStateComplete DrainState = "Complete"
)

// NetworkStatus defines the common observed state for network-type CRDs
type NetworkStatus struct {
// Conditions represent the latest available observations of the network's state
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}

// ConditionsEqual compares two condition slices ignoring LastTransitionTime.
// This is useful to avoid unnecessary API updates when conditions haven't actually changed.
func ConditionsEqual(a, b []metav1.Condition) bool {
if len(a) != len(b) {
return false
}

// Create maps for easier comparison
aMap := make(map[string]metav1.Condition)
for _, c := range a {
aMap[c.Type] = c
}

for _, bc := range b {
ac, exists := aMap[bc.Type]
if !exists {
return false
}
// Compare all fields except LastTransitionTime
if ac.Status != bc.Status ||
ac.Reason != bc.Reason ||
ac.Message != bc.Message ||
ac.ObservedGeneration != bc.ObservedGeneration {
return false
}
}
return true
}
29 changes: 29 additions & 0 deletions api/v1/conditions_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright 2021.

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 v1_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestConditions(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Conditions Suite")
}
121 changes: 121 additions & 0 deletions api/v1/conditions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
Copyright 2021.

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 v1_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
)

var _ = Describe("ConditionsEqual", func() {
DescribeTable("comparing conditions",
func(a, b []metav1.Condition, expected bool) {
result := v1.ConditionsEqual(a, b)
Expect(result).To(Equal(expected))
},
Entry("both empty",
[]metav1.Condition{},
[]metav1.Condition{},
true,
),
Entry("different length",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue},
},
[]metav1.Condition{},
false,
),
Entry("same conditions",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
},
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
},
true,
),
Entry("different status",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
},
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionFalse, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
},
false,
),
Entry("different reason",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
},
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNotReady, Message: "ready", ObservedGeneration: 1},
},
false,
),
Entry("different message",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
},
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "not ready", ObservedGeneration: 1},
},
false,
),
Entry("different observedGeneration",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
},
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 2},
},
false,
),
Entry("different LastTransitionTime should still be equal",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1, LastTransitionTime: metav1.Now()},
},
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1, LastTransitionTime: metav1.Now()},
},
true,
),
Entry("multiple conditions same",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
{Type: v1.ConditionProgressing, Status: metav1.ConditionFalse, Reason: v1.ReasonNotProgressing, Message: "not progressing", ObservedGeneration: 1},
},
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
{Type: v1.ConditionProgressing, Status: metav1.ConditionFalse, Reason: v1.ReasonNotProgressing, Message: "not progressing", ObservedGeneration: 1},
},
true,
),
Entry("condition type not found in second slice",
[]metav1.Condition{
{Type: v1.ConditionReady, Status: metav1.ConditionTrue, Reason: v1.ReasonNodeReady, Message: "ready", ObservedGeneration: 1},
},
[]metav1.Condition{
{Type: v1.ConditionProgressing, Status: metav1.ConditionFalse, Reason: v1.ReasonNotProgressing, Message: "not progressing", ObservedGeneration: 1},
},
false,
),
)
})
30 changes: 30 additions & 0 deletions api/v1/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,16 @@ func (cr *SriovIBNetwork) NetworkNamespace() string {
return cr.Spec.NetworkNamespace
}

// GetConditions returns the conditions from the status
func (cr *SriovIBNetwork) GetConditions() []metav1.Condition {
return cr.Status.Conditions
}

// SetConditions sets the conditions in the status
func (cr *SriovIBNetwork) SetConditions(conditions []metav1.Condition) {
cr.Status.Conditions = conditions
}

// RenderNetAttDef renders a net-att-def for sriov CNI
func (cr *SriovNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
logger := log.WithName("RenderNetAttDef")
Expand Down Expand Up @@ -811,6 +821,16 @@ func (cr *SriovNetwork) NetworkNamespace() string {
return cr.Spec.NetworkNamespace
}

// GetConditions returns the conditions from the status
func (cr *SriovNetwork) GetConditions() []metav1.Condition {
return cr.Status.Conditions
}

// SetConditions sets the conditions in the status
func (cr *SriovNetwork) SetConditions(conditions []metav1.Condition) {
cr.Status.Conditions = conditions
}

// RenderNetAttDef renders a net-att-def for sriov CNI
func (cr *OVSNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
logger := log.WithName("RenderNetAttDef")
Expand Down Expand Up @@ -874,6 +894,16 @@ func (cr *OVSNetwork) NetworkNamespace() string {
return cr.Spec.NetworkNamespace
}

// GetConditions returns the conditions from the status
func (cr *OVSNetwork) GetConditions() []metav1.Condition {
return cr.Status.Conditions
}

// SetConditions sets the conditions in the status
func (cr *OVSNetwork) SetConditions(conditions []metav1.Condition) {
cr.Status.Conditions = conditions
}

// NetFilterMatch -- parse netFilter and check for a match
func NetFilterMatch(netFilter string, netValue string) (isMatch bool) {
logger := log.WithName("NetFilterMatch")
Expand Down
4 changes: 4 additions & 0 deletions api/v1/ovsnetwork_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,14 @@ type TrunkConfig struct {

// OVSNetworkStatus defines the observed state of OVSNetwork
type OVSNetworkStatus struct {
NetworkStatus `json:",inline"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
//+kubebuilder:printcolumn:name="Degraded",type=string,JSONPath=`.status.conditions[?(@.type=="Degraded")].status`
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// OVSNetwork is the Schema for the ovsnetworks API
type OVSNetwork struct {
Expand Down
6 changes: 4 additions & 2 deletions api/v1/sriovibnetwork_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ type SriovIBNetworkSpec struct {

// SriovIBNetworkStatus defines the observed state of SriovIBNetwork
type SriovIBNetworkStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
NetworkStatus `json:",inline"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
//+kubebuilder:printcolumn:name="Degraded",type=string,JSONPath=`.status.conditions[?(@.type=="Degraded")].status`
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// SriovIBNetwork is the Schema for the sriovibnetworks API
type SriovIBNetwork struct {
Expand Down
6 changes: 4 additions & 2 deletions api/v1/sriovnetwork_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,14 @@ type SriovNetworkSpec struct {

// SriovNetworkStatus defines the observed state of SriovNetwork
type SriovNetworkStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
NetworkStatus `json:",inline"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
//+kubebuilder:printcolumn:name="Degraded",type=string,JSONPath=`.status.conditions[?(@.type=="Degraded")].status`
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// SriovNetwork is the Schema for the sriovnetworks API
type SriovNetwork struct {
Expand Down
Loading
Loading