pkg/condition/conditions.go (152 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 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
}