pkg/cni/overlay.go (152 lines of code) (raw):
package cni
import (
"context"
"fmt"
"time"
nodenetworkconfig_v1alpha "github.com/Azure/azure-container-networking/crd/nodenetworkconfig/api/v1alpha"
overlayextensionconfig_v1alpha1 "github.com/Azure/azure-container-networking/crd/overlayextensionconfig/api/v1alpha1"
"github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
)
const (
ResourceManagedByLabel = "app.kubernetes.io/managed-by"
ResourceManagedByAddonValue = "ingress-appgw-addon"
ResourceManagedByHelmValue = "ingress-appgw-helm"
)
const (
// PodNetworkTypeLabel is the name of the label on NNCs to tell what mode the network is in.
PodNetworkTypeLabel = "kubernetes.azure.com/podnetwork-type"
// OverlayExtensionConfigName is the name of the overlay extension config resource
OverlayExtensionConfigName = "agic-overlay-extension-config"
// OverlayConfigReconcileTimeout for checking overlay extension config status
OverlayConfigReconcileTimeout = 30 * time.Second
// OverlayConfigReconcilePollInterval for checking overlay extension config status
OverlayConfigReconcilePollInterval = 2 * time.Second
)
func (r *Reconciler) reconcileOverlayCniIfNeeded(ctx context.Context, subnetID string) error {
if r.reconciledOverlayCNI {
return nil
}
isOverlay, err := r.isClusterOverlayCNI(ctx)
if err != nil {
return errors.New("failed to check if cluster is using overlay CNI")
}
if !isOverlay {
return nil
}
klog.Infof("Cluster is using overlay CNI, using subnetID %q for application gateway", subnetID)
subnet, err := r.armClient.GetSubnet(subnetID)
if err != nil {
return errors.Wrap(err, "failed to get subnet")
}
var subnetCIDR string
if subnet.AddressPrefix != nil {
subnetCIDR = *subnet.AddressPrefix
} else if subnet.AddressPrefixes != nil && len(*subnet.AddressPrefixes) > 0 {
subnetCIDR = (*subnet.AddressPrefixes)[0]
} else {
return errors.New("subnet does not have an address prefix(es)")
}
klog.Infof("Using subnet prefix %q", subnetCIDR)
err = r.reconcileOverlayExtensionConfig(ctx, subnetCIDR)
if err != nil {
return errors.Wrap(err, "failed to reconcile overlay resources")
}
r.reconciledOverlayCNI = true
return nil
}
func (r *Reconciler) isClusterOverlayCNI(ctx context.Context) (bool, error) {
var nodeNetworkConfigs nodenetworkconfig_v1alpha.NodeNetworkConfigList
if err := r.client.List(ctx, &nodeNetworkConfigs); err != nil {
if meta.IsNoMatchError(err) {
return false, nil
}
return false, errors.Wrap(err, "failed to list node network configs")
}
// if any NNCs are overlay then this cluster is using CNI Overlay
for _, nnc := range nodeNetworkConfigs.Items {
if val, ok := nnc.Labels[PodNetworkTypeLabel]; ok && val == "overlay" {
return true, nil
}
}
return false, nil
}
func (r *Reconciler) reconcileOverlayExtensionConfig(ctx context.Context, subnetCIDR string) error {
var config overlayextensionconfig_v1alpha1.OverlayExtensionConfig
if err := r.client.Get(ctx, client.ObjectKey{
Name: OverlayExtensionConfigName,
Namespace: r.namespace,
}, &config); err != nil {
if !apierrors.IsNotFound(err) {
return errors.Wrap(err, "failed to get overlay extension config")
}
return r.createOverlayExtensionConfig(ctx, subnetCIDR)
}
if config.Spec.ExtensionIPRange == subnetCIDR {
return r.checkOverlayExtensionConfigStatus(ctx)
}
klog.Infof("Updating overlay extension config with subnet CIDR %s", subnetCIDR)
// Delete the existing config and create a new one
if err := r.client.Delete(ctx, &config); err != nil {
return errors.Wrap(err, "failed to delete overlay extension config")
}
return r.createOverlayExtensionConfig(ctx, subnetCIDR)
}
func (r *Reconciler) createOverlayExtensionConfig(ctx context.Context, subnetCIDR string) error {
managedByValue := ResourceManagedByHelmValue
if r.addonMode {
managedByValue = ResourceManagedByAddonValue
}
config := overlayextensionconfig_v1alpha1.OverlayExtensionConfig{
ObjectMeta: meta_v1.ObjectMeta{
Name: OverlayExtensionConfigName,
Namespace: r.namespace,
Labels: map[string]string{
ResourceManagedByLabel: managedByValue,
},
},
Spec: overlayextensionconfig_v1alpha1.OverlayExtensionConfigSpec{
ExtensionIPRange: subnetCIDR,
},
}
klog.Infof("Creating overlay extension config with subnet CIDR %s", subnetCIDR)
if err := r.client.Create(ctx, &config); err != nil {
return errors.Wrap(err, "failed to create overlay extension config")
}
return r.checkOverlayExtensionConfigStatus(ctx)
}
func (r *Reconciler) checkOverlayExtensionConfigStatus(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, OverlayConfigReconcileTimeout)
defer cancel()
checkStatus := func() (bool, error) {
var config overlayextensionconfig_v1alpha1.OverlayExtensionConfig
if err := r.client.Get(ctx, client.ObjectKey{
Name: OverlayExtensionConfigName,
Namespace: r.namespace,
}, &config); err != nil {
return false, errors.Wrap(err, "failed to get overlay extension config")
}
switch config.Status.State {
case overlayextensionconfig_v1alpha1.Succeeded:
klog.Infof("Overlay extension config is ready")
return true, nil
case overlayextensionconfig_v1alpha1.Failed:
return true, fmt.Errorf("overlay extension config failed with error: %s", config.Status.Message)
}
klog.Infof("Waiting for overlay extension config to be ready")
return false, nil
}
// Initial check
done, err := checkStatus()
if done {
return err
}
for {
select {
case <-ctx.Done():
return errors.New("timed out waiting for overlay extension config to be ready")
case <-time.After(OverlayConfigReconcilePollInterval):
done, err := checkStatus()
if done {
return err
}
}
}
}