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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ kube-ovn.yaml
!/charts/kube-ovn-v2/crds/kube-ovn-crd.yaml
!/charts/kube-ovn/templates/kube-ovn-crd.yaml
kube-ovn-crd.yaml
yamls/crds
ovn.yaml
ovn-ic-controller.yaml
ovn-ic-server.yaml
Expand Down Expand Up @@ -53,6 +54,7 @@ kube-ovn-cni-sa.yaml
kube-ovn-sa.yaml
ovn-ovs-sa.yaml
ovs-ovn-ds.yaml
kube-ovn-pinger.yaml
cakey.pem
cacert.pem
ovn-req.pem
Expand Down
15 changes: 15 additions & 0 deletions hack/update-codegen-crd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash
# usage: bash -x ./hack/update-codegen-crd.sh
set -eux
cd "$(dirname "$0")/.."

# set GOPROXY you like
export GOPROXY=${GOPROXY:-"https://goproxy.cn"}
# use controller-gen to generate CRDs
# ensure controller-gen is installed
CONTROLLER_TOOLS_VERSION=${CONTROLLER_TOOLS_VERSION:-"v0.19.0"}
go install sigs.k8s.io/controller-tools/cmd/controller-gen@"${CONTROLLER_TOOLS_VERSION}"
go mod tidy

# generate CRDs
controller-gen crd:allowDangerousTypes=true paths=./pkg/apis/kubeovn/v1 output:crd:artifacts:config=./yamls/crds
22 changes: 13 additions & 9 deletions pkg/apis/kubeovn/v1/ippool.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"

"github.com/kubeovn/kube-ovn/pkg/internal"
kotypes "github.com/kubeovn/kube-ovn/pkg/types"
)

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down Expand Up @@ -37,14 +37,18 @@ type IPPoolSpec struct {
}

type IPPoolStatus struct {
V4AvailableIPs internal.BigInt `json:"v4AvailableIPs"`
V4AvailableIPRange string `json:"v4AvailableIPRange"`
V4UsingIPs internal.BigInt `json:"v4UsingIPs"`
V4UsingIPRange string `json:"v4UsingIPRange"`
V6AvailableIPs internal.BigInt `json:"v6AvailableIPs"`
V6AvailableIPRange string `json:"v6AvailableIPRange"`
V6UsingIPs internal.BigInt `json:"v6UsingIPs"`
V6UsingIPRange string `json:"v6UsingIPRange"`
// +kubebuilder:validation:Type=string
V4AvailableIPs kotypes.BigInt `json:"v4AvailableIPs"`
V4AvailableIPRange string `json:"v4AvailableIPRange"`
// +kubebuilder:validation:Type=string
V4UsingIPs kotypes.BigInt `json:"v4UsingIPs"`
V4UsingIPRange string `json:"v4UsingIPRange"`
// +kubebuilder:validation:Type=string
V6AvailableIPs kotypes.BigInt `json:"v6AvailableIPs"`
V6AvailableIPRange string `json:"v6AvailableIPRange"`
// +kubebuilder:validation:Type=string
V6UsingIPs kotypes.BigInt `json:"v6UsingIPs"`
V6UsingIPRange string `json:"v6UsingIPRange"`

// Conditions represents the latest state of the object
// +optional
Expand Down
3 changes: 2 additions & 1 deletion pkg/apis/kubeovn/v1/provider-network.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ type ProviderNetworkSpec struct {
type ProviderNetworkCondition struct {
// Node name
Node string `json:"node"`
Condition

Condition `json:"condition"`
}

type ProviderNetworkStatus struct {
Expand Down
23 changes: 15 additions & 8 deletions pkg/apis/kubeovn/v1/subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"

kotypes "github.com/kubeovn/kube-ovn/pkg/types"
)

const (
Expand Down Expand Up @@ -110,7 +112,8 @@ type NatOutGoingPolicyMatch struct {

type NatOutgoingPolicyRuleStatus struct {
RuleID string `json:"ruleID"`
NatOutgoingPolicyRule

NatOutgoingPolicyRule `json:"natOutgoingPolicyRule"`
}
type SubnetStatus struct {
// Conditions represents the latest state of the object
Expand All @@ -119,13 +122,17 @@ type SubnetStatus struct {
// +patchStrategy=merge
Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`

V4AvailableIPs float64 `json:"v4availableIPs"`
V4AvailableIPRange string `json:"v4availableIPrange"`
V4UsingIPs float64 `json:"v4usingIPs"`
V4UsingIPRange string `json:"v4usingIPrange"`
V6AvailableIPs float64 `json:"v6availableIPs"`
V6AvailableIPRange string `json:"v6availableIPrange"`
V6UsingIPs float64 `json:"v6usingIPs"`
// +kubebuilder:validation:Type=string
V4AvailableIPs kotypes.BigInt `json:"v4availableIPs"`
V4AvailableIPRange string `json:"v4availableIPrange"`
// +kubebuilder:validation:Type=string
V4UsingIPs kotypes.BigInt `json:"v4usingIPs"`
V4UsingIPRange string `json:"v4usingIPrange"`
// +kubebuilder:validation:Type=string
V6AvailableIPs kotypes.BigInt `json:"v6availableIPs"`
V6AvailableIPRange string `json:"v6availableIPrange"`
// +kubebuilder:validation:Type=string
V6UsingIPs kotypes.BigInt `json:"v6usingIPs"`
V6UsingIPRange string `json:"v6usingIPrange"`
ActivateGateway string `json:"activateGateway"`
DHCPv4OptionsUUID string `json:"dhcpV4OptionsUUID"`
Expand Down
132 changes: 132 additions & 0 deletions pkg/apis/kubeovn/v1/subnet_bigint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package v1

import (
"encoding/json"
"testing"

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

kotypes "github.com/kubeovn/kube-ovn/pkg/types"
)

// TestSubnetStatusBigIntJSONSerialization 验证 SubnetStatus 的 BigInt 字段正确序列化为 JSON 字符串
func TestSubnetStatusBigIntJSONSerialization(t *testing.T) {
subnet := &Subnet{
TypeMeta: metav1.TypeMeta{
Kind: "Subnet",
APIVersion: "kubeovn.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-subnet",
},
Spec: SubnetSpec{
CIDRBlock: "10.16.0.0/16",
},
Status: SubnetStatus{
V4AvailableIPs: kotypes.NewBigInt(65533),
V4UsingIPs: kotypes.NewBigInt(3),
V6AvailableIPs: kotypes.NewBigInt(0),
V6UsingIPs: kotypes.NewBigInt(0),
},
}

// Marshal to JSON
data, err := json.Marshal(subnet)
if err != nil {
t.Fatalf("Marshal failed: %v", err)
}

jsonStr := string(data)
t.Logf("Serialized Subnet JSON (truncated): %s", jsonStr[:min(200, len(jsonStr))])

// Verify the status field contains quoted strings
var decoded map[string]any
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("Unmarshal to map failed: %v", err)
}

status, ok := decoded["status"].(map[string]any)
if !ok {
t.Fatal("status field not found or not an object")
}

// Check each BigInt field is a string
checkStringField := func(name, expected string) {
value, ok := status[name]
if !ok {
t.Errorf("Field %s not found in status", name)
return
}
strValue, ok := value.(string)
if !ok {
t.Errorf("Field %s should be string, got %T: %v", name, value, value)
return
}
if strValue != expected {
t.Errorf("Field %s = %q, want %q", name, strValue, expected)
}
}

checkStringField("v4availableIPs", "65533")
checkStringField("v4usingIPs", "3")
checkStringField("v6availableIPs", "0")
checkStringField("v6usingIPs", "0")

// Unmarshal back to Subnet
var decodedSubnet Subnet
if err := json.Unmarshal(data, &decodedSubnet); err != nil {
t.Fatalf("Unmarshal to Subnet failed: %v", err)
}

// Verify values match
if !decodedSubnet.Status.V4AvailableIPs.Equal(subnet.Status.V4AvailableIPs) {
t.Errorf("V4AvailableIPs mismatch after round-trip: got %v, want %v",
decodedSubnet.Status.V4AvailableIPs.String(), subnet.Status.V4AvailableIPs.String())
}
}

// TestSubnetStatusPatchJSON 模拟 Kubernetes patch 操作的 JSON 序列化
func TestSubnetStatusPatchJSON(t *testing.T) {
status := SubnetStatus{
V4AvailableIPs: kotypes.NewBigInt(253),
V4UsingIPs: kotypes.NewBigInt(1),
V6AvailableIPs: kotypes.NewBigInt(0),
V6UsingIPs: kotypes.NewBigInt(0),
}

data, err := json.Marshal(status)
if err != nil {
t.Fatalf("Marshal failed: %v", err)
}

t.Logf("SubnetStatus JSON: %s", string(data))

// Parse as generic map to verify field types
var m map[string]any
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("Unmarshal to map failed: %v", err)
}

// All BigInt fields must be strings, not numbers
for fieldName, expectedValue := range map[string]string{
"v4availableIPs": "253",
"v4usingIPs": "1",
"v6availableIPs": "0",
"v6usingIPs": "0",
} {
value, ok := m[fieldName]
if !ok {
t.Errorf("Field %s not found", fieldName)
continue
}
strValue, ok := value.(string)
if !ok {
t.Errorf("Field %s should be string (for K8s CRD validation), got %T: %v",
fieldName, value, value)
continue
}
if strValue != expectedValue {
t.Errorf("Field %s = %q, want %q", fieldName, strValue, expectedValue)
}
}
}
10 changes: 5 additions & 5 deletions pkg/controller/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,21 @@ func (c *Controller) exportSubnetAvailableIPsGauge(subnet *kubeovnv1.Subnet) {
var availableIPs float64
switch subnet.Spec.Protocol {
case kubeovnv1.ProtocolIPv4:
availableIPs = subnet.Status.V4AvailableIPs
availableIPs = subnet.Status.V4AvailableIPs.Float64()
case kubeovnv1.ProtocolIPv6:
availableIPs = subnet.Status.V6AvailableIPs
availableIPs = subnet.Status.V6AvailableIPs.Float64()
default:
availableIPs = math.Min(subnet.Status.V4AvailableIPs, subnet.Status.V6AvailableIPs)
availableIPs = math.Min(subnet.Status.V4AvailableIPs.Float64(), subnet.Status.V6AvailableIPs.Float64())
}
metricSubnetAvailableIPs.WithLabelValues(subnet.Name, subnet.Spec.Protocol, subnet.Spec.CIDRBlock).Set(availableIPs)
}

func (c *Controller) exportSubnetUsedIPsGauge(subnet *kubeovnv1.Subnet) {
var usingIPs float64
if subnet.Spec.Protocol == kubeovnv1.ProtocolIPv6 {
usingIPs = subnet.Status.V6UsingIPs
usingIPs = subnet.Status.V6UsingIPs.Float64()
} else {
usingIPs = subnet.Status.V4UsingIPs
usingIPs = subnet.Status.V4UsingIPs.Float64()
}
metricSubnetUsedIPs.WithLabelValues(subnet.Name, subnet.Spec.Protocol, subnet.Spec.CIDRBlock).Set(usingIPs)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/controller/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -1681,18 +1681,18 @@ func (c *Controller) getPodDefaultSubnet(pod *v1.Pod) (*kubeovnv1.Subnet, error)

switch subnet.Spec.Protocol {
case kubeovnv1.ProtocolDual:
if subnet.Status.V6AvailableIPs == 0 && !c.podCanUseExcludeIPs(pod, subnet) {
if subnet.Status.V6AvailableIPs.EqualInt64(0) && !c.podCanUseExcludeIPs(pod, subnet) {
klog.Infof("there's no available ipv6 address in subnet %s, try next one", subnet.Name)
continue
}
fallthrough
case kubeovnv1.ProtocolIPv4:
if subnet.Status.V4AvailableIPs == 0 && !c.podCanUseExcludeIPs(pod, subnet) {
if subnet.Status.V4AvailableIPs.EqualInt64(0) && !c.podCanUseExcludeIPs(pod, subnet) {
klog.Infof("there's no available ipv4 address in subnet %s, try next one", subnet.Name)
continue
}
case kubeovnv1.ProtocolIPv6:
if subnet.Status.V6AvailableIPs == 0 && !c.podCanUseExcludeIPs(pod, subnet) {
if subnet.Status.V6AvailableIPs.EqualInt64(0) && !c.podCanUseExcludeIPs(pod, subnet) {
klog.Infof("there's no available ipv6 address in subnet %s, try next one", subnet.Name)
continue
}
Expand Down
Loading
Loading