// 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 condition import ( "strconv" "sync" "time" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s" "github.com/go-logr/logr" "github.com/prometheus/client_golang/prometheus" "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/controller-runtime/pkg/metrics" ) type condition struct { hasDataStoreSynced bool log logr.Logger K8sAPI k8s.K8sWrapper lock sync.Mutex windowsPDFeatureFlag bool } const CheckDataStoreSyncedInterval = time.Second * 10 type Conditions interface { // IsWindowsIPAMEnabled to process events only when Windows IPAM is enabled // by the user IsWindowsIPAMEnabled() bool // IsWindowsPrefixDelegationEnabled to process events only when Windows Prefix Delegation is enabled IsWindowsPrefixDelegationEnabled() bool // IsPodSGPEnabled to process events only when Security Group for Pods feature // is enabled by the user // IsPodSGPEnabled() bool We need to check if SGP is enabled via ConfigMap + Environment variables // IsOldVPCControllerDeploymentPresent returns true if the old controller deployment // is still present on the cluster IsOldVPCControllerDeploymentPresent() bool // GetPodDataStoreSyncStatus is used to get Pod Datastore sync status GetPodDataStoreSyncStatus() bool // SetPodDataStoreSyncStatus is used to set Pod Datastore sync status SetPodDataStoreSyncStatus(synced bool) } var ( conditionWindowsIPAMEnabled = prometheus.NewGauge( prometheus.GaugeOpts{ Name: "windows_ipam_enabled", Help: "Binary value to indicate whether user has set enable-windows-ipam to true", }) conditionWindowsPrefixDelegationEnabled = prometheus.NewGauge( prometheus.GaugeOpts{ Name: "windows_prefix_delegation_enabled", Help: "Binary value to indicate whether user has set enable-windows-prefix-delegation to true", }) prometheusRegistered = false ) func prometheusRegister() { if !prometheusRegistered { metrics.Registry.MustRegister( conditionWindowsIPAMEnabled, conditionWindowsPrefixDelegationEnabled, ) } prometheusRegistered = true } func NewControllerConditions(log logr.Logger, k8sApi k8s.K8sWrapper, windowsPDFeatureFlag bool) Conditions { prometheusRegister() conditionWindowsIPAMEnabled.Set(0) conditionWindowsPrefixDelegationEnabled.Set(0) return &condition{ log: log, K8sAPI: k8sApi, windowsPDFeatureFlag: windowsPDFeatureFlag, } } func (c *condition) IsWindowsIPAMEnabled() bool { if c.IsOldVPCControllerDeploymentPresent() { return false } // Return false if configmap not present/any errors vpcCniConfigMap, err := c.K8sAPI.GetConfigMap(config.VpcCniConfigMapName, config.KubeSystemNamespace) if err == nil && vpcCniConfigMap.Data != nil { if val, ok := vpcCniConfigMap.Data[config.EnableWindowsIPAMKey]; ok { enableWinIpamVal, err := strconv.ParseBool(val) if err == nil && enableWinIpamVal { conditionWindowsIPAMEnabled.Set(1) return true } } } conditionWindowsIPAMEnabled.Set(0) return false } func (c *condition) IsWindowsPrefixDelegationEnabled() bool { if c.IsOldVPCControllerDeploymentPresent() { return false } // If the feature flag is OFF, Windows PD cannot be enabled via config map if !c.windowsPDFeatureFlag { return false } // Return false if configmap not present/any errors vpcCniConfigMap, err := c.K8sAPI.GetConfigMap(config.VpcCniConfigMapName, config.KubeSystemNamespace) if err == nil && vpcCniConfigMap.Data != nil { if ipamVal, ok := vpcCniConfigMap.Data[config.EnableWindowsIPAMKey]; ok { // Check if Windows IPAM is enabled enableWinIpamVal, err := strconv.ParseBool(ipamVal) if err == nil && enableWinIpamVal { if pdVal, ok := vpcCniConfigMap.Data[config.EnableWindowsPrefixDelegationKey]; ok { enableWinPDVal, err := strconv.ParseBool(pdVal) // Check if Windows PD is enabled if err == nil && enableWinPDVal { conditionWindowsPrefixDelegationEnabled.Set(1) return true } } } } } conditionWindowsPrefixDelegationEnabled.Set(0) return false } // Watch for deployments of old VPC Resource controller, new controller will block // till the user deletes the old controller deployment. Ideally we should block till // old WebHook is deleted too. But the Field selectors don't support IN operator for // selecting object names and the controller deployment doesn't have labels which removes // possibility of using label selectors to watch for both deployments. However, Old and // new WebHook running together should not have side effects on the existing Pods. func (c *condition) IsOldVPCControllerDeploymentPresent() bool { oldController, err := c.K8sAPI.GetDeployment(config.KubeSystemNamespace, config.OldVPCControllerDeploymentName) if err == nil { c.log.V(1).Info("old controller deployment is present", "deployment", *oldController) return true } else if err != nil && !errors.IsNotFound(err) { c.log.Error(err, "received unexpected error while checking for old controller deployment") return true } return false } // TOOD: Check if SPG is enabled using ConfigMap. func (c *condition) IsPodSGPEnabled() bool { daemonSet, err := c.K8sAPI.GetDaemonSet(config.VpcCNIDaemonSetName, config.KubeSystemNamespace) if err != nil { if errors.IsNotFound(err) { c.log.Info("aws-node ds is not present") return false } c.log.Error(err, "failed to get aws-node") return false } var isSGPEnabled bool for _, container := range daemonSet.Spec.Template.Spec.Containers { if container.Name == "aws-node" { for _, env := range container.Env { if env.Name == "ENABLE_POD_ENI" { isSGPEnabled, err = strconv.ParseBool(env.Value) if err != nil { c.log.Error(err, "failed to parse ENABLE_POD_ENI", "value", env.Value) return false } break } } } } return isSGPEnabled } func (c *condition) GetPodDataStoreSyncStatus() bool { c.lock.Lock() defer c.lock.Unlock() return c.hasDataStoreSynced } func (c *condition) SetPodDataStoreSyncStatus(synced bool) { c.lock.Lock() defer c.lock.Unlock() c.hasDataStoreSynced = synced }