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: 1 addition & 1 deletion cmd/antrea-agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ func run(o *Options) error {

if o.enableEgress || features.DefaultFeatureGate.Enabled(features.ServiceExternalIP) {
externalIPPoolController = externalippool.NewExternalIPPoolController(
crdClient, externalIPPoolInformer,
crdClient, externalIPPoolInformer, v4Enabled, v6Enabled,
)
var nodeTransportIP net.IP
if nodeConfig.NodeTransportIPv4Addr != nil {
Expand Down
26 changes: 24 additions & 2 deletions cmd/antrea-controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,30 @@ func run(o *Options) error {
var egressController *egress.EgressController
var externalIPPoolController *externalippool.ExternalIPPoolController
var externalIPController *serviceexternalip.ServiceExternalIPController

// Determine the cluster's IP family configuration from NodeIPAM ClusterCIDRs.
// If NodeIPAM is not configured, both IPv4 and IPv6 are considered enabled.
ipv4Enabled, ipv6Enabled := true, true
if features.DefaultFeatureGate.Enabled(features.NodeIPAM) && o.config.NodeIPAM.EnableNodeIPAM {
cidrs, err := netutils.ParseCIDRs(o.config.NodeIPAM.ClusterCIDRs)
if err != nil {
// This should not happen because NodeIPAM options validation enforces valid, non-empty ClusterCIDRs.
klog.Errorf("Failed to parse NodeIPAM ClusterCIDRs %v; assuming both IPv4 and IPv6 are enabled: %v", o.config.NodeIPAM.ClusterCIDRs, err)
} else {
ipv4Enabled, ipv6Enabled = false, false
for _, cidr := range cidrs {
if cidr.IP.To4() != nil {
ipv4Enabled = true
} else {
ipv6Enabled = true
}
}
}
}

if features.DefaultFeatureGate.Enabled(features.Egress) || features.DefaultFeatureGate.Enabled(features.ServiceExternalIP) {
externalIPPoolController = externalippool.NewExternalIPPoolController(
crdClient, externalIPPoolInformer,
crdClient, externalIPPoolInformer, ipv4Enabled, ipv6Enabled,
)
}

Expand Down Expand Up @@ -282,7 +303,8 @@ func run(o *Options) error {
ipPoolInformer,
namespaceInformer,
podInformer,
statefulSetInformer)
statefulSetInformer,
ipv4Enabled, ipv6Enabled)
}

apiServerConfig, err := createAPIServerConfig(o.config.ClientConnection.Kubeconfig,
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/egress/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func newController(objects, crdObjects []runtime.Object) *egressController {
crdClient.PrependReactor("patch", "egresses", egressPatchReactor)
informerFactory := informers.NewSharedInformerFactory(client, resyncPeriod)
crdInformerFactory := crdinformers.NewSharedInformerFactory(crdClient, resyncPeriod)
externalIPAllocator := externalippool.NewExternalIPPoolController(crdClient, crdInformerFactory.Crd().V1beta1().ExternalIPPools())
externalIPAllocator := externalippool.NewExternalIPPoolController(crdClient, crdInformerFactory.Crd().V1beta1().ExternalIPPools(), true, true)
egressGroupStore := store.NewEgressGroupStore()
egressInformer := crdInformerFactory.Crd().V1beta1().Egresses()
groupEntityIndex := grouping.NewGroupEntityIndex()
Expand Down
9 changes: 8 additions & 1 deletion pkg/controller/externalippool/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,15 @@ type ExternalIPPoolController struct {

// queue maintains the ExternalIPPool objects that need to be synced.
queue workqueue.TypedRateLimitingInterface[string]

// ipv4Enabled indicates whether IPv4 is enabled in the cluster.
ipv4Enabled bool
// ipv6Enabled indicates whether IPv6 is enabled in the cluster.
ipv6Enabled bool
}

// NewExternalIPPoolController returns a new *ExternalIPPoolController.
func NewExternalIPPoolController(crdClient clientset.Interface, externalIPPoolInformer antreainformers.ExternalIPPoolInformer) *ExternalIPPoolController {
func NewExternalIPPoolController(crdClient clientset.Interface, externalIPPoolInformer antreainformers.ExternalIPPoolInformer, ipv4Enabled, ipv6Enabled bool) *ExternalIPPoolController {
c := &ExternalIPPoolController{
crdClient: crdClient,
externalIPPoolLister: externalIPPoolInformer.Lister(),
Expand All @@ -135,6 +140,8 @@ func NewExternalIPPoolController(crdClient clientset.Interface, externalIPPoolIn
),
ipAllocatorInitialized: &atomic.Value{},
ipAllocatorMap: make(map[string]ipallocator.MultiIPAllocator),
ipv4Enabled: ipv4Enabled,
ipv6Enabled: ipv6Enabled,
}
externalIPPoolInformer.Informer().AddEventHandlerWithResyncPeriod(
cache.ResourceEventHandlerFuncs{
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/externalippool/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ type controller struct {
func newController(crdObjects []runtime.Object) *controller {
crdClient := fakeversioned.NewSimpleClientset(crdObjects...)
crdInformerFactory := crdinformers.NewSharedInformerFactory(crdClient, resyncPeriod)
externalIPPoolController := NewExternalIPPoolController(crdClient, crdInformerFactory.Crd().V1beta1().ExternalIPPools())
externalIPPoolController := NewExternalIPPoolController(crdClient, crdInformerFactory.Crd().V1beta1().ExternalIPPools(), true, true)
return &controller{
externalIPPoolController,
crdClient,
Expand Down
10 changes: 10 additions & 0 deletions pkg/controller/externalippool/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,22 @@ func (c *ExternalIPPoolController) ValidateExternalIPPool(review *admv1.Admissio
switch review.Request.Operation {
case admv1.Create:
klog.V(2).Info("Validating CREATE request for ExternalIPPool")
if err := validation.ValidateIPRangeIPFamily(newObj.Spec.IPRanges, c.ipv4Enabled, c.ipv6Enabled); err != nil {
msg = err.Error()
allowed = false
break
}
if err := validateIPRangesAndSubnetInfoForExternalIPPool(&newObj, externalIPPools); err != nil {
msg = err.Error()
allowed = false
}
case admv1.Update:
klog.V(2).Info("Validating UPDATE request for ExternalIPPool")
if err := validation.ValidateIPRangeIPFamily(newObj.Spec.IPRanges, c.ipv4Enabled, c.ipv6Enabled); err != nil {
msg = err.Error()
allowed = false
break
}
if err := validateIPRangesAndSubnetInfoForExternalIPPool(&newObj, externalIPPools); err != nil {
msg = err.Error()
allowed = false
Expand Down
102 changes: 102 additions & 0 deletions pkg/controller/externalippool/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"k8s.io/client-go/tools/cache"

crdv1b1 "antrea.io/antrea/pkg/apis/crd/v1beta1"
fakeversioned "antrea.io/antrea/pkg/client/clientset/versioned/fake"
crdinformers "antrea.io/antrea/pkg/client/informers/externalversions"
)

func marshal(object runtime.Object) []byte {
Expand Down Expand Up @@ -367,3 +369,103 @@ func TestValidateIPRangesAndSubnetInfo(t *testing.T) {
})
}
}

func TestValidateExternalIPPoolIPFamily(t *testing.T) {
tests := []struct {
name string
ipv4Enabled bool
ipv6Enabled bool
pool *crdv1b1.ExternalIPPool
operation admv1.Operation
expectedResponse *admv1.AdmissionResponse
}{
{
name: "CREATE IPv4 pool in IPv4-only cluster should be allowed",
ipv4Enabled: true,
ipv6Enabled: false,
pool: newExternalIPPool("foo", "10.10.10.0/24", "", ""),
operation: admv1.Create,
expectedResponse: &admv1.AdmissionResponse{Allowed: true},
},
{
name: "CREATE IPv4 pool in IPv6-only cluster should not be allowed",
ipv4Enabled: false,
ipv6Enabled: true,
pool: newExternalIPPool("foo", "10.10.10.0/24", "", ""),
operation: admv1.Create,
expectedResponse: &admv1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Message: "IPv4 range 10.10.10.0/24 is not allowed in an IPv6-only cluster",
},
},
},
{
name: "CREATE IPv6 pool in IPv6-only cluster should be allowed",
ipv4Enabled: false,
ipv6Enabled: true,
pool: newExternalIPPool("foo", "fd00:10:96::/112", "", ""),
operation: admv1.Create,
expectedResponse: &admv1.AdmissionResponse{Allowed: true},
},
{
name: "CREATE IPv6 pool in IPv4-only cluster should not be allowed",
ipv4Enabled: true,
ipv6Enabled: false,
pool: newExternalIPPool("foo", "fd00:10:96::/112", "", ""),
operation: admv1.Create,
expectedResponse: &admv1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Message: "IPv6 range fd00:10:96::/112 is not allowed in an IPv4-only cluster",
},
},
},
{
name: "CREATE IPv4 pool in dual-stack cluster should be allowed",
ipv4Enabled: true,
ipv6Enabled: true,
pool: newExternalIPPool("foo", "10.10.10.0/24", "", ""),
operation: admv1.Create,
expectedResponse: &admv1.AdmissionResponse{Allowed: true},
},
{
name: "UPDATE IPv4 pool in IPv6-only cluster should not be allowed",
ipv4Enabled: false,
ipv6Enabled: true,
pool: newExternalIPPool("foo", "10.10.10.0/24", "", ""),
operation: admv1.Update,
expectedResponse: &admv1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Message: "IPv4 range 10.10.10.0/24 is not allowed in an IPv6-only cluster",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
crdClient := fakeversioned.NewSimpleClientset()
crdInformerFactory := crdinformers.NewSharedInformerFactory(crdClient, resyncPeriod)
c := NewExternalIPPoolController(crdClient, crdInformerFactory.Crd().V1beta1().ExternalIPPools(), tt.ipv4Enabled, tt.ipv6Enabled)
stopCh := make(chan struct{})
defer close(stopCh)
crdInformerFactory.Start(stopCh)
crdInformerFactory.WaitForCacheSync(stopCh)
go c.Run(stopCh)
require.True(t, cache.WaitForCacheSync(stopCh, c.HasSynced))

request := &admv1.AdmissionRequest{
Name: tt.pool.Name,
Operation: tt.operation,
Object: runtime.RawExtension{Raw: marshal(tt.pool)},
}
if tt.operation == admv1.Update {
request.OldObject = runtime.RawExtension{Raw: marshal(tt.pool)}
}
review := &admv1.AdmissionReview{Request: request}
gotResponse := c.ValidateExternalIPPool(review)
assert.Equal(t, tt.expectedResponse, gotResponse)
})
}
}
10 changes: 9 additions & 1 deletion pkg/controller/ipam/antrea_ipam_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ type AntreaIPAMController struct {

// statusQueue maintains the IPPool objects that need to be synced.
statusQueue workqueue.TypedRateLimitingInterface[string]

// ipv4Enabled indicates whether IPv4 is enabled in the cluster.
ipv4Enabled bool
// ipv6Enabled indicates whether IPv6 is enabled in the cluster.
ipv6Enabled bool
}

func statefulSetIndexFunc(obj interface{}) ([]string, error) {
Expand All @@ -109,7 +114,8 @@ func NewAntreaIPAMController(crdClient versioned.Interface,
ipPoolInformer crdinformers.IPPoolInformer,
namespaceInformer coreinformers.NamespaceInformer,
podInformer coreinformers.PodInformer,
statefulSetInformer appsinformers.StatefulSetInformer) *AntreaIPAMController {
statefulSetInformer appsinformers.StatefulSetInformer,
ipv4Enabled, ipv6Enabled bool) *AntreaIPAMController {

ipPoolInformer.Informer().AddIndexers(cache.Indexers{statefulSetIndex: statefulSetIndexFunc})

Expand All @@ -136,6 +142,8 @@ func NewAntreaIPAMController(crdClient versioned.Interface,
Name: "IPPoolStatus",
},
),
ipv4Enabled: ipv4Enabled,
ipv6Enabled: ipv6Enabled,
}

// Add handlers for Stateful Set events.
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/ipam/antrea_ipam_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func newFakeAntreaIPAMController(pool *crdv1b1.IPPool, namespace *corev1.Namespa
poolInformer := crdInformerFactory.Crd().V1beta1().IPPools()
poolLister := poolInformer.Lister()

controller := NewAntreaIPAMController(crdClient, poolInformer, namespaceInformer, podInformer, statefulSetInformer)
controller := NewAntreaIPAMController(crdClient, poolInformer, namespaceInformer, podInformer, statefulSetInformer, true, true)
return &fakeAntreaIPAMController{
AntreaIPAMController: controller,
fakeK8sClient: k8sClient,
Expand Down
10 changes: 10 additions & 0 deletions pkg/controller/ipam/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,22 @@ func (c *AntreaIPAMController) ValidateIPPool(review *admv1.AdmissionReview) *ad
switch review.Request.Operation {
case admv1.Create:
klog.V(2).Info("Validating CREATE request for IPPool")
if err := validation.ValidateIPRangeIPFamily(newObj.Spec.IPRanges, c.ipv4Enabled, c.ipv6Enabled); err != nil {
msg = err.Error()
allowed = false
break
}
if _, err := validation.ValidateIPRangesAndSubnetInfo(&newObj.Spec.SubnetInfo, newObj.Spec.IPRanges); err != nil {
msg = err.Error()
allowed = false
}
case admv1.Update:
klog.V(2).Info("Validating UPDATE request for IPPool")
if err := validation.ValidateIPRangeIPFamily(newObj.Spec.IPRanges, c.ipv4Enabled, c.ipv6Enabled); err != nil {
msg = err.Error()
allowed = false
break
}
if _, err := validation.ValidateIPRangesAndSubnetInfo(&newObj.Spec.SubnetInfo, newObj.Spec.IPRanges); err != nil {
msg = err.Error()
allowed = false
Expand Down
Loading
Loading