controllers/core/configmap_controller.go (143 lines of code) (raw):
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 controllers
import (
"context"
"fmt"
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/condition"
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config"
rcHealthz "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/healthz"
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s"
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/node/manager"
cooldown "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/provider/branch/cooldown"
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/healthz"
)
// ConfigMapReconciler reconciles a ConfigMap object
type ConfigMapReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
NodeManager manager.Manager
K8sAPI k8s.K8sWrapper
Condition condition.Conditions
curWinIPAMEnabledCond bool
curWinPrefixDelegationEnabledCond bool
curWinWarmIPTarget int
curWinMinIPTarget int
curWinPDWarmPrefixTarget int
Context context.Context
}
//+kubebuilder:rbac:groups=core,resources=configmaps,namespace=kube-system,resourceNames=amazon-vpc-cni,verbs=get;list;watch
// Reconcile handles configmap create/update/delete events by invoking NodeManager
// to update the status of the nodes as per the enable-windows-ipam flag value.
func (r *ConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := r.Log.WithValues("configmap", req.NamespacedName)
// only update nodes on amazon-vpc-cni updates, return here for other updates
if req.Name != config.VpcCniConfigMapName || req.Namespace != config.KubeSystemNamespace {
return ctrl.Result{}, nil
}
configmap := &corev1.ConfigMap{}
if err := r.Client.Get(ctx, req.NamespacedName, configmap); err != nil {
if errors.IsNotFound(err) {
// If the configMap is deleted, de-register all the nodes
logger.Info("amazon-vpc-cni configMap is deleted")
} else {
// Error reading the object
logger.Error(err, "Failed to get configMap")
return ctrl.Result{}, err
}
}
// Check if branch ENI cooldown period is updated
curCoolDownPeriod := cooldown.GetCoolDown().GetCoolDownPeriod()
if newCoolDownPeriod, err := cooldown.GetVpcCniConfigMapCoolDownPeriodOrDefault(r.K8sAPI, r.Log); err == nil {
if curCoolDownPeriod != newCoolDownPeriod {
r.Log.Info("Branch ENI cool down period has been updated", "newCoolDownPeriod", newCoolDownPeriod, "OldCoolDownPeriod", curCoolDownPeriod)
cooldown.GetCoolDown().SetCoolDownPeriod(newCoolDownPeriod)
utils.SendBroadcastNodeEvent(
r.K8sAPI,
utils.BranchENICoolDownUpdateReason,
fmt.Sprintf("Branch ENI cool down period has been updated to %s", cooldown.GetCoolDown().GetCoolDownPeriod()),
corev1.EventTypeNormal,
r.Log,
)
}
} else {
r.Log.Info("branch ENI cool down period not configured in amazon-vpc-cni configmap, will retain the current cooldown period", "cool down period", curCoolDownPeriod)
}
// Check if the Windows IPAM flag has changed
newWinIPAMEnabledCond := r.Condition.IsWindowsIPAMEnabled()
var isIPAMFlagUpdated bool
if r.curWinIPAMEnabledCond != newWinIPAMEnabledCond {
r.curWinIPAMEnabledCond = newWinIPAMEnabledCond
logger.Info("updated configmap", config.EnableWindowsIPAMKey, r.curWinIPAMEnabledCond)
isIPAMFlagUpdated = true
}
// Check if the prefix delegation flag has changed
newWinPrefixDelegationEnabledCond := r.Condition.IsWindowsPrefixDelegationEnabled()
var isPrefixFlagUpdated bool
if r.curWinPrefixDelegationEnabledCond != newWinPrefixDelegationEnabledCond {
r.curWinPrefixDelegationEnabledCond = newWinPrefixDelegationEnabledCond
logger.Info("updated configmap", config.EnableWindowsPrefixDelegationKey, r.curWinPrefixDelegationEnabledCond)
isPrefixFlagUpdated = true
}
// Check if Windows IP target configurations in ConfigMap have changed
var isWinIPConfigsUpdated bool
warmIPTarget, minIPTarget, warmPrefixTarget, isPDEnabled := config.ParseWinIPTargetConfigs(r.Log, configmap)
var winMinIPTargetUpdated = r.curWinMinIPTarget != minIPTarget
var winWarmIPTargetUpdated = r.curWinWarmIPTarget != warmIPTarget
var winPDWarmPrefixTargetUpdated = r.curWinPDWarmPrefixTarget != warmPrefixTarget
if winWarmIPTargetUpdated || winMinIPTargetUpdated {
r.curWinWarmIPTarget = warmIPTarget
r.curWinMinIPTarget = minIPTarget
isWinIPConfigsUpdated = true
}
if isPDEnabled && winPDWarmPrefixTargetUpdated {
r.curWinPDWarmPrefixTarget = warmPrefixTarget
isWinIPConfigsUpdated = true
}
if isWinIPConfigsUpdated {
logger.Info(
"Detected update in Windows IP configuration parameter values in ConfigMap",
config.WinWarmIPTarget, r.curWinWarmIPTarget,
config.WinMinimumIPTarget, r.curWinMinIPTarget,
config.WinWarmPrefixTarget, r.curWinPDWarmPrefixTarget,
config.EnableWindowsPrefixDelegationKey, isPDEnabled,
)
}
if isIPAMFlagUpdated || isPrefixFlagUpdated || isWinIPConfigsUpdated {
err := UpdateNodesOnConfigMapChanges(r.K8sAPI, r.NodeManager)
if err != nil {
// Error in updating nodes
logger.Error(err, "Failed to update nodes on configmap changes")
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *ConfigMapReconciler) SetupWithManager(mgr ctrl.Manager, healthzHandler *rcHealthz.HealthzHandler) error {
// add health check on subpath for CM controller
healthzHandler.AddControllersHealthCheckers(
map[string]healthz.Checker{"health-cm-controller": r.check()},
)
// Explicitly set MaxConcurrentReconciles to 1 to ensure concurrent reconciliation NOT supported for config map controller.
// Don't change to more than 1 unless the struct is guarded against concurrency issues.
return ctrl.NewControllerManagedBy(mgr).
For(&corev1.ConfigMap{}).
WithOptions(controller.Options{MaxConcurrentReconciles: 1}).
Complete(r)
}
func UpdateNodesOnConfigMapChanges(k8sAPI k8s.K8sWrapper, nodeManager manager.Manager) error {
nodeList, err := k8sAPI.ListNodes()
if err != nil {
return err
}
var errList []error
for _, node := range nodeList.Items {
_, found := nodeManager.GetNode(node.Name)
if found {
err = nodeManager.UpdateNode(node.Name)
if err != nil {
errList = append(errList, err)
}
}
}
if len(errList) > 0 {
return fmt.Errorf("failed to update one or more nodes %v", errList)
}
return nil
}
func (r *ConfigMapReconciler) check() healthz.Checker {
r.Log.Info("ConfigMap controller's healthz subpath was added")
// We can revisit this to use PingWithTimeout() instead if we have concerns on this controller.
return rcHealthz.SimplePing("configmap controller", r.Log)
}