mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 05:14:13 +01:00
add EvictionProtection filed for DefaultEvictorArgs
Signed-off-by: googs1025 <googs1025@gmail.com>
This commit is contained in:
51
README.md
51
README.md
@@ -148,21 +148,44 @@ In general, each plugin can consume metrics from a different provider so multipl
|
||||
|
||||
The Default Evictor Plugin is used by default for filtering pods before processing them in an strategy plugin, or for applying a PreEvictionFilter of pods before eviction. You can also create your own Evictor Plugin or use the Default one provided by Descheduler. Other uses for the Evictor plugin can be to sort, filter, validate or group pods by different criteria, and that's why this is handled by a plugin and not configured in the top level config.
|
||||
|
||||
| Name |type| Default Value | Description |
|
||||
|---------------------------|----|---------------|-----------------------------------------------------------------------------------------------------------------------------|
|
||||
| `nodeSelector` |`string`| `nil` | limiting the nodes which are processed |
|
||||
| `evictLocalStoragePods` |`bool`| `false` | allows eviction of pods with local storage |
|
||||
| `evictDaemonSetPods` | bool | false | allows eviction of DaemonSet managed Pods. |
|
||||
| `evictSystemCriticalPods` |`bool`| `false` | [Warning: Will evict Kubernetes system pods] allows eviction of pods with any priority, including system pods like kube-dns |
|
||||
| `ignorePvcPods` |`bool`| `false` | set whether PVC pods should be evicted or ignored |
|
||||
| `evictFailedBarePods` |`bool`| `false` | allow eviction of pods without owner references and in failed phase |
|
||||
| `labelSelector` |`metav1.LabelSelector`|| (see [label filtering](#label-filtering)) |
|
||||
| `priorityThreshold` |`priorityThreshold`|| (see [priority filtering](#priority-filtering)) |
|
||||
| `nodeFit` |`bool`|`false`| (see [node fit filtering](#node-fit-filtering)) |
|
||||
| `minReplicas` |`uint`|`0`| ignore eviction of pods where owner (e.g. `ReplicaSet`) replicas is below this threshold |
|
||||
| `minPodAge` |`metav1.Duration`|`0`| ignore eviction of pods with a creation time within this threshold |
|
||||
| `ignorePodsWithoutPDB` |`bool`|`false`| set whether pods without PodDisruptionBudget should be evicted or ignored |
|
||||
| Name | Type | Default Value | Description |
|
||||
|---------------------------|-------------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `nodeSelector` | `string` | `nil` | Limits the nodes that are processed. |
|
||||
| `evictLocalStoragePods` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"PodsWithLocalStorage"` instead]**<br>Allows eviction of pods using local storage. |
|
||||
| `evictDaemonSetPods` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"DaemonSetPods"` instead]**<br>Allows eviction of DaemonSet managed Pods. |
|
||||
| `evictSystemCriticalPods` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"SystemCriticalPods"` instead]**<br>[Warning: Will evict Kubernetes system pods] Allows eviction of pods with any priority, including system-critical pods like kube-dns. |
|
||||
| `ignorePvcPods` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"PodsWithPVC"` instead]**<br>Sets whether PVC pods should be evicted or ignored. |
|
||||
| `evictFailedBarePods` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"FailedBarePods"` instead]**<br>Allows eviction of pods without owner references and in a failed phase. |
|
||||
| `ignorePodsWithoutPDB` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"PodsWithoutPDB"` instead]**<br>Sets whether pods without PodDisruptionBudget should be evicted or ignored. |
|
||||
| `labelSelector` | `metav1.LabelSelector` | | (See [label filtering](#label-filtering)) |
|
||||
| `priorityThreshold` | `priorityThreshold` | | (See [priority filtering](#priority-filtering)) |
|
||||
| `nodeFit` | `bool` | `false` | (See [node fit filtering](#node-fit-filtering)) |
|
||||
| `minReplicas` | `uint` | `0` | Ignores eviction of pods where the owner (e.g., `ReplicaSet`) replicas are below this threshold. |
|
||||
| `minPodAge` | `metav1.Duration` | `0` | Ignores eviction of pods with a creation time within this threshold. |
|
||||
| `noEvictionPolicy` |`enum`|``| sets whether a `descheduler.alpha.kubernetes.io/prefer-no-eviction` pod annotation is considered preferred or mandatory. Accepted values: "", "Preferred", "Mandatory". Defaults to "Preferred". |
|
||||
| `podProtections` | `PodProtections` | `{}` | Holds the list of enabled and disabled protection pod policies.<br>Users can selectively disable certain default protection rules or enable extra ones. See below for supported values. |
|
||||
|
||||
#### Supported Values for `podProtections.DefaultDisabled`
|
||||
|
||||
> Setting a value in `defaultDisabled` **disables the corresponding default protection rule**. This means the specified type of Pods will **no longer be protected** from eviction and may be evicted if they meet other criteria.
|
||||
|
||||
| Value | Meaning |
|
||||
|--------------------------|-------------------------------------------------------------------------|
|
||||
| `"PodsWithLocalStorage"` | Allow eviction of Pods using local storage. |
|
||||
| `"DaemonSetPods"` | Allow eviction of DaemonSet-managed Pods. |
|
||||
| `"SystemCriticalPods"` | Allow eviction of system-critical Pods. |
|
||||
| `"FailedBarePods"` | Allow eviction of failed bare Pods (without controllers). |
|
||||
|
||||
---
|
||||
|
||||
#### Supported Values for `podProtections.ExtraEnabled`
|
||||
|
||||
> Setting a value in `extraEnabled` **enables an additional protection rule**. This means the specified type of Pods will be **protected** from eviction.
|
||||
|
||||
| Value | Meaning |
|
||||
|--------------------|------------------------------------------------------------------------|
|
||||
| `"PodsWithPVC"` | Prevents eviction of Pods using Persistent Volume Claims (PVCs). |
|
||||
| `"PodsWithoutPDB"` | Prevents eviction of Pods without a PodDisruptionBudget (PDB). |
|
||||
|
||||
### Example policy
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@ func setDefaultEvictor(profile api.DeschedulerProfile, client clientset.Interfac
|
||||
IgnorePvcPods: false,
|
||||
EvictFailedBarePods: false,
|
||||
IgnorePodsWithoutPDB: false,
|
||||
PodProtections: defaultevictor.PodProtections{
|
||||
DefaultDisabled: []defaultevictor.PodProtection{},
|
||||
ExtraEnabled: []defaultevictor.PodProtection{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
fakeclientset "k8s.io/client-go/kubernetes/fake"
|
||||
utilptr "k8s.io/utils/ptr"
|
||||
@@ -496,6 +497,313 @@ profiles:
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "test DisabledDefaultPodProtections configuration",
|
||||
policy: []byte(`apiVersion: "descheduler/v1alpha2"
|
||||
kind: "DeschedulerPolicy"
|
||||
profiles:
|
||||
- name: ProfileName
|
||||
pluginConfig:
|
||||
- name: "DefaultEvictor"
|
||||
args:
|
||||
podProtections:
|
||||
defaultDisabled:
|
||||
- "PodsWithLocalStorage"
|
||||
- "DaemonSetPods"
|
||||
priorityThreshold:
|
||||
value: 2000000000
|
||||
nodeFit: true
|
||||
plugins:
|
||||
filter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
preEvictionFilter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
`),
|
||||
result: &api.DeschedulerPolicy{
|
||||
Profiles: []api.DeschedulerProfile{
|
||||
{
|
||||
Name: "ProfileName",
|
||||
PluginConfigs: []api.PluginConfig{
|
||||
{
|
||||
Name: defaultevictor.PluginName,
|
||||
Args: &defaultevictor.DefaultEvictorArgs{
|
||||
PodProtections: defaultevictor.PodProtections{
|
||||
DefaultDisabled: []defaultevictor.PodProtection{
|
||||
defaultevictor.PodsWithLocalStorage,
|
||||
defaultevictor.DaemonSetPods,
|
||||
},
|
||||
},
|
||||
PriorityThreshold: &api.PriorityThreshold{Value: utilptr.To[int32](2000000000)},
|
||||
NodeFit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Plugins: api.Plugins{
|
||||
Filter: api.PluginSet{
|
||||
Enabled: []string{defaultevictor.PluginName},
|
||||
},
|
||||
PreEvictionFilter: api.PluginSet{
|
||||
Enabled: []string{defaultevictor.PluginName},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "test podProtections extraEnabled configuration",
|
||||
policy: []byte(`apiVersion: "descheduler/v1alpha2"
|
||||
kind: "DeschedulerPolicy"
|
||||
profiles:
|
||||
- name: ProfileName
|
||||
pluginConfig:
|
||||
- name: "DefaultEvictor"
|
||||
args:
|
||||
podProtections:
|
||||
extraEnabled:
|
||||
- "PodsWithPVC"
|
||||
- "PodsWithoutPDB"
|
||||
priorityThreshold:
|
||||
value: 2000000000
|
||||
nodeFit: true
|
||||
plugins:
|
||||
filter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
preEvictionFilter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
`),
|
||||
result: &api.DeschedulerPolicy{
|
||||
Profiles: []api.DeschedulerProfile{
|
||||
{
|
||||
Name: "ProfileName",
|
||||
PluginConfigs: []api.PluginConfig{
|
||||
{
|
||||
Name: defaultevictor.PluginName,
|
||||
Args: &defaultevictor.DefaultEvictorArgs{
|
||||
PodProtections: defaultevictor.PodProtections{
|
||||
ExtraEnabled: []defaultevictor.PodProtection{
|
||||
defaultevictor.PodsWithPVC,
|
||||
defaultevictor.PodsWithoutPDB,
|
||||
},
|
||||
},
|
||||
PriorityThreshold: &api.PriorityThreshold{Value: utilptr.To[int32](2000000000)},
|
||||
NodeFit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Plugins: api.Plugins{
|
||||
Filter: api.PluginSet{
|
||||
Enabled: []string{defaultevictor.PluginName},
|
||||
},
|
||||
PreEvictionFilter: api.PluginSet{
|
||||
Enabled: []string{defaultevictor.PluginName},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "test both ExtraPodProtections and DisabledDefaultPodProtections configuration",
|
||||
policy: []byte(`apiVersion: "descheduler/v1alpha2"
|
||||
kind: "DeschedulerPolicy"
|
||||
profiles:
|
||||
- name: ProfileName
|
||||
pluginConfig:
|
||||
- name: "DefaultEvictor"
|
||||
args:
|
||||
podProtections:
|
||||
extraEnabled:
|
||||
- "PodsWithPVC"
|
||||
- "PodsWithoutPDB"
|
||||
defaultDisabled:
|
||||
- "PodsWithLocalStorage"
|
||||
- "DaemonSetPods"
|
||||
priorityThreshold:
|
||||
value: 2000000000
|
||||
nodeFit: true
|
||||
plugins:
|
||||
filter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
preEvictionFilter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
`),
|
||||
result: &api.DeschedulerPolicy{
|
||||
Profiles: []api.DeschedulerProfile{
|
||||
{
|
||||
Name: "ProfileName",
|
||||
PluginConfigs: []api.PluginConfig{
|
||||
{
|
||||
Name: defaultevictor.PluginName,
|
||||
Args: &defaultevictor.DefaultEvictorArgs{
|
||||
PodProtections: defaultevictor.PodProtections{
|
||||
ExtraEnabled: []defaultevictor.PodProtection{
|
||||
defaultevictor.PodsWithPVC,
|
||||
defaultevictor.PodsWithoutPDB,
|
||||
},
|
||||
DefaultDisabled: []defaultevictor.PodProtection{
|
||||
defaultevictor.PodsWithLocalStorage,
|
||||
defaultevictor.DaemonSetPods,
|
||||
},
|
||||
},
|
||||
PriorityThreshold: &api.PriorityThreshold{Value: utilptr.To[int32](2000000000)},
|
||||
NodeFit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Plugins: api.Plugins{
|
||||
Filter: api.PluginSet{
|
||||
Enabled: []string{defaultevictor.PluginName},
|
||||
},
|
||||
PreEvictionFilter: api.PluginSet{
|
||||
Enabled: []string{defaultevictor.PluginName},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "test error when using both Deprecated fields and DisabledDefaultPodProtections/ExtraPodProtections",
|
||||
policy: []byte(`apiVersion: "descheduler/v1alpha2"
|
||||
kind: "DeschedulerPolicy"
|
||||
profiles:
|
||||
- name: ProfileName
|
||||
pluginConfig:
|
||||
- name: "DefaultEvictor"
|
||||
args:
|
||||
evictSystemCriticalPods: true
|
||||
podProtections:
|
||||
extraEnabled:
|
||||
- "PodsWithPVC"
|
||||
- "PodsWithoutPDB"
|
||||
defaultDisabled:
|
||||
- "PodsWithLocalStorage"
|
||||
- "DaemonSetPods"
|
||||
priorityThreshold:
|
||||
value: 2000000000
|
||||
nodeFit: true
|
||||
plugins:
|
||||
filter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
preEvictionFilter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
`),
|
||||
result: nil,
|
||||
err: fmt.Errorf("in profile ProfileName: cannot use Deprecated fields alongside PodProtections.ExtraEnabled or PodProtections.DefaultDisabled"),
|
||||
},
|
||||
{
|
||||
description: "test error when Disables a default protection that does not exist",
|
||||
policy: []byte(`apiVersion: "descheduler/v1alpha2"
|
||||
kind: "DeschedulerPolicy"
|
||||
profiles:
|
||||
- name: ProfileName
|
||||
pluginConfig:
|
||||
- name: "DefaultEvictor"
|
||||
args:
|
||||
podProtections:
|
||||
defaultDisabled:
|
||||
- "InvalidProtection"
|
||||
priorityThreshold:
|
||||
value: 2000000000
|
||||
nodeFit: true
|
||||
plugins:
|
||||
filter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
preEvictionFilter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
`),
|
||||
result: nil,
|
||||
err: fmt.Errorf("in profile ProfileName: invalid pod protection policy in DefaultDisabled: \"InvalidProtection\". Valid options are: [PodsWithLocalStorage SystemCriticalPods FailedBarePods DaemonSetPods]"),
|
||||
},
|
||||
{
|
||||
description: "test error when Enables an extra protection that does not exist",
|
||||
policy: []byte(`apiVersion: "descheduler/v1alpha2"
|
||||
kind: "DeschedulerPolicy"
|
||||
profiles:
|
||||
- name: ProfileName
|
||||
pluginConfig:
|
||||
- name: "DefaultEvictor"
|
||||
args:
|
||||
podProtections:
|
||||
extraEnabled:
|
||||
- "InvalidProtection"
|
||||
priorityThreshold:
|
||||
value: 2000000000
|
||||
nodeFit: true
|
||||
plugins:
|
||||
filter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
preEvictionFilter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
`),
|
||||
result: nil,
|
||||
err: fmt.Errorf("in profile ProfileName: invalid pod protection policy in ExtraEnabled: \"InvalidProtection\". Valid options are: [PodsWithPVC PodsWithoutPDB]"),
|
||||
},
|
||||
{
|
||||
description: "test error when Disables an extra protection",
|
||||
policy: []byte(`apiVersion: "descheduler/v1alpha2"
|
||||
kind: "DeschedulerPolicy"
|
||||
profiles:
|
||||
- name: ProfileName
|
||||
pluginConfig:
|
||||
- name: "DefaultEvictor"
|
||||
args:
|
||||
podProtections:
|
||||
defaultDisabled:
|
||||
- "PodsWithPVC"
|
||||
priorityThreshold:
|
||||
value: 2000000000
|
||||
nodeFit: true
|
||||
plugins:
|
||||
filter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
preEvictionFilter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
`),
|
||||
result: nil,
|
||||
err: fmt.Errorf("in profile ProfileName: invalid pod protection policy in DefaultDisabled: \"PodsWithPVC\". Valid options are: [PodsWithLocalStorage SystemCriticalPods FailedBarePods DaemonSetPods]"),
|
||||
},
|
||||
{
|
||||
description: "test error when Enables a default protection",
|
||||
policy: []byte(`apiVersion: "descheduler/v1alpha2"
|
||||
kind: "DeschedulerPolicy"
|
||||
profiles:
|
||||
- name: ProfileName
|
||||
pluginConfig:
|
||||
- name: "DefaultEvictor"
|
||||
args:
|
||||
podProtections:
|
||||
extraEnabled:
|
||||
- "DaemonSetPods"
|
||||
priorityThreshold:
|
||||
value: 2000000000
|
||||
nodeFit: true
|
||||
plugins:
|
||||
filter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
preEvictionFilter:
|
||||
enabled:
|
||||
- "DefaultEvictor"
|
||||
`),
|
||||
result: nil,
|
||||
err: fmt.Errorf("in profile ProfileName: invalid pod protection policy in ExtraEnabled: \"DaemonSetPods\". Valid options are: [PodsWithPVC PodsWithoutPDB]"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -503,14 +811,14 @@ profiles:
|
||||
result, err := decode("filename", tc.policy, client, pluginregistry.PluginRegistry)
|
||||
if err != nil {
|
||||
if tc.err == nil {
|
||||
t.Errorf("unexpected error: %s.", err.Error())
|
||||
} else {
|
||||
t.Errorf("unexpected error: %s. Was expecting %s", err.Error(), tc.err.Error())
|
||||
t.Fatalf("unexpected error: %s.", err.Error())
|
||||
} else if err.Error() != tc.err.Error() {
|
||||
t.Fatalf("unexpected error: %s. Was expecting %s", err.Error(), tc.err.Error())
|
||||
}
|
||||
}
|
||||
diff := cmp.Diff(tc.result, result)
|
||||
if diff != "" && err == nil {
|
||||
t.Errorf("test '%s' failed. Results are not deep equal. mismatch (-want +got):\n%s", tc.description, diff)
|
||||
if diff != "" {
|
||||
t.Fatalf("test '%s' failed. Results are not deep equal. mismatch (-want +got):\n%s", tc.description, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -87,15 +88,12 @@ func New(ctx context.Context, args runtime.Object, handle frameworktypes.Handle)
|
||||
|
||||
func (d *DefaultEvictor) addAllConstraints(logger klog.Logger, handle frameworktypes.Handle) error {
|
||||
args := d.args
|
||||
d.constraints = append(d.constraints, evictionConstraintsForFailedBarePods(logger, args.EvictFailedBarePods)...)
|
||||
if constraints, err := evictionConstraintsForSystemCriticalPods(logger, args.EvictSystemCriticalPods, args.PriorityThreshold, handle); err != nil {
|
||||
return err
|
||||
} else {
|
||||
d.constraints = append(d.constraints, constraints...)
|
||||
// Determine effective protected policies based on the provided arguments.
|
||||
effectivePodProtections := getEffectivePodProtections(args)
|
||||
|
||||
if err := applyEffectivePodProtections(d, effectivePodProtections, handle); err != nil {
|
||||
return fmt.Errorf("failed to apply effective protected policies: %w", err)
|
||||
}
|
||||
d.constraints = append(d.constraints, evictionConstraintsForLocalStoragePods(args.EvictLocalStoragePods)...)
|
||||
d.constraints = append(d.constraints, evictionConstraintsForDaemonSetPods(args.EvictDaemonSetPods)...)
|
||||
d.constraints = append(d.constraints, evictionConstraintsForPvcPods(args.IgnorePvcPods)...)
|
||||
if constraints, err := evictionConstraintsForLabelSelector(logger, args.LabelSelector); err != nil {
|
||||
return err
|
||||
} else {
|
||||
@@ -107,10 +105,123 @@ func (d *DefaultEvictor) addAllConstraints(logger klog.Logger, handle frameworkt
|
||||
d.constraints = append(d.constraints, constraints...)
|
||||
}
|
||||
d.constraints = append(d.constraints, evictionConstraintsForMinPodAge(args.MinPodAge)...)
|
||||
d.constraints = append(d.constraints, evictionConstraintsForIgnorePodsWithoutPDB(args.IgnorePodsWithoutPDB, handle)...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyEffectivePodProtections configures the evictor with specified Pod protection.
|
||||
func applyEffectivePodProtections(d *DefaultEvictor, podProtections []PodProtection, handle frameworktypes.Handle) error {
|
||||
protectionMap := make(map[PodProtection]bool, len(podProtections))
|
||||
for _, protection := range podProtections {
|
||||
protectionMap[protection] = true
|
||||
}
|
||||
|
||||
// Apply protections
|
||||
if err := applySystemCriticalPodsProtection(d, protectionMap, handle); err != nil {
|
||||
return err
|
||||
}
|
||||
applyFailedBarePodsProtection(d, protectionMap)
|
||||
applyLocalStoragePodsProtection(d, protectionMap)
|
||||
applyDaemonSetPodsProtection(d, protectionMap)
|
||||
applyPvcPodsProtection(d, protectionMap)
|
||||
applyPodsWithoutPDBProtection(d, protectionMap, handle)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyFailedBarePodsProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool) {
|
||||
isProtectionEnabled := protectionMap[FailedBarePods]
|
||||
d.constraints = append(d.constraints, evictionConstraintsForFailedBarePods(d.logger, !isProtectionEnabled)...)
|
||||
}
|
||||
|
||||
func applySystemCriticalPodsProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool, handle frameworktypes.Handle) error {
|
||||
isProtectionEnabled := protectionMap[SystemCriticalPods]
|
||||
constraints, err := evictionConstraintsForSystemCriticalPods(
|
||||
d.logger,
|
||||
!isProtectionEnabled,
|
||||
d.args.PriorityThreshold,
|
||||
handle,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.constraints = append(d.constraints, constraints...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyLocalStoragePodsProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool) {
|
||||
isProtectionEnabled := protectionMap[PodsWithLocalStorage]
|
||||
d.constraints = append(d.constraints, evictionConstraintsForLocalStoragePods(!isProtectionEnabled)...)
|
||||
}
|
||||
|
||||
func applyDaemonSetPodsProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool) {
|
||||
isProtectionEnabled := protectionMap[DaemonSetPods]
|
||||
d.constraints = append(d.constraints, evictionConstraintsForDaemonSetPods(!isProtectionEnabled)...)
|
||||
}
|
||||
|
||||
func applyPvcPodsProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool) {
|
||||
isProtectionEnabled := protectionMap[PodsWithPVC]
|
||||
d.constraints = append(d.constraints, evictionConstraintsForPvcPods(isProtectionEnabled)...)
|
||||
}
|
||||
|
||||
func applyPodsWithoutPDBProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool, handle frameworktypes.Handle) {
|
||||
isProtectionEnabled := protectionMap[PodsWithoutPDB]
|
||||
d.constraints = append(d.constraints, evictionConstraintsForIgnorePodsWithoutPDB(isProtectionEnabled, handle)...)
|
||||
}
|
||||
|
||||
// getEffectivePodProtections determines which policies are currently active.
|
||||
// It supports both new-style (PodProtections) and legacy-style flags.
|
||||
func getEffectivePodProtections(args *DefaultEvictorArgs) []PodProtection {
|
||||
// determine whether to use PodProtections config
|
||||
useNewConfig := len(args.PodProtections.DefaultDisabled) > 0 || len(args.PodProtections.ExtraEnabled) > 0
|
||||
|
||||
if !useNewConfig {
|
||||
// fall back to the Deprecated config
|
||||
return legacyGetPodProtections(args)
|
||||
}
|
||||
|
||||
// effective is the final list of active protection.
|
||||
effective := make([]PodProtection, 0)
|
||||
effective = append(effective, defaultPodProtections...)
|
||||
|
||||
// Remove PodProtections that are in the DefaultDisabled list.
|
||||
effective = slices.DeleteFunc(effective, func(protection PodProtection) bool {
|
||||
return slices.Contains(args.PodProtections.DefaultDisabled, protection)
|
||||
})
|
||||
|
||||
// Add extra enabled in PodProtections
|
||||
effective = append(effective, args.PodProtections.ExtraEnabled...)
|
||||
|
||||
return effective
|
||||
}
|
||||
|
||||
// legacyGetPodProtections returns protections using deprecated boolean flags.
|
||||
func legacyGetPodProtections(args *DefaultEvictorArgs) []PodProtection {
|
||||
var protections []PodProtection
|
||||
|
||||
// defaultDisabled
|
||||
if !args.EvictLocalStoragePods {
|
||||
protections = append(protections, PodsWithLocalStorage)
|
||||
}
|
||||
if !args.EvictDaemonSetPods {
|
||||
protections = append(protections, DaemonSetPods)
|
||||
}
|
||||
if !args.EvictSystemCriticalPods {
|
||||
protections = append(protections, SystemCriticalPods)
|
||||
}
|
||||
if !args.EvictFailedBarePods {
|
||||
protections = append(protections, FailedBarePods)
|
||||
}
|
||||
|
||||
// extraEnabled
|
||||
if args.IgnorePvcPods {
|
||||
protections = append(protections, PodsWithPVC)
|
||||
}
|
||||
if args.IgnorePodsWithoutPDB {
|
||||
protections = append(protections, PodsWithoutPDB)
|
||||
}
|
||||
return protections
|
||||
}
|
||||
|
||||
// Name retrieves the plugin name
|
||||
func (d *DefaultEvictor) Name() string {
|
||||
return PluginName
|
||||
|
||||
@@ -16,14 +16,14 @@ package defaultevictor
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
policyv1 "k8s.io/api/policy/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/informers"
|
||||
@@ -52,6 +52,7 @@ type testCase struct {
|
||||
minPodAge *metav1.Duration
|
||||
result bool
|
||||
ignorePodsWithoutPDB bool
|
||||
podProtections PodProtections
|
||||
noEvictionPolicy NoEvictionPolicy
|
||||
}
|
||||
|
||||
@@ -330,11 +331,13 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
pod.Status.Phase = v1.PodFailed
|
||||
}),
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with no ownerRefs and evictFailedBarePods enabled",
|
||||
pods: []*v1.Pod{test.BuildTestPod("bare_pod", 400, 0, n1.Name, nil)},
|
||||
evictFailedBarePods: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Failed pod eviction with no ownerRefs",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("bare_pod_failed_but_can_be_evicted", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -343,7 +346,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
evictFailedBarePods: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with normal ownerRefs",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -351,7 +355,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with normal ownerRefs and " + evictPodAnnotationKey + " annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p2", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -360,7 +365,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with normal ownerRefs and " + evictionutils.SoftNoEvictionAnnotationKey + " annotation (preference)",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p2", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -371,7 +377,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
evictLocalStoragePods: false,
|
||||
evictSystemCriticalPods: false,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with normal ownerRefs and " + evictionutils.SoftNoEvictionAnnotationKey + " annotation (mandatory)",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p2", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -383,7 +390,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
evictSystemCriticalPods: false,
|
||||
noEvictionPolicy: MandatoryNoEvictionPolicy,
|
||||
result: false,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with replicaSet ownerRefs",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p3", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -391,7 +399,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with replicaSet ownerRefs and " + evictPodAnnotationKey + " annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p4", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -400,7 +409,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with statefulSet ownerRefs",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p18", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -408,7 +418,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Normal pod eviction with statefulSet ownerRefs and " + evictPodAnnotationKey + " annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p19", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -417,7 +428,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod not evicted because it is bound to a PV and evictLocalStoragePods = false",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p5", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -435,7 +447,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it is bound to a PV and evictLocalStoragePods = true",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p6", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -455,7 +468,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
evictLocalStoragePods: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it is bound to a PV and evictLocalStoragePods = false, but it has scheduler.alpha.kubernetes.io/evict annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p7", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -475,7 +489,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod not evicted because it is part of a daemonSet",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p8", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -483,7 +498,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
pod.ObjectMeta.OwnerReferences = test.GetDaemonSetOwnerRefList()
|
||||
}),
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it is part of a daemonSet, but it has scheduler.alpha.kubernetes.io/evict annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p9", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -492,7 +508,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod not evicted because it is a mirror poddsa",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p10", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -500,7 +517,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
pod.Annotations = test.GetMirrorPodAnnotation()
|
||||
}),
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it is a mirror pod, but it has scheduler.alpha.kubernetes.io/evict annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p11", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -510,7 +528,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod not evicted because it has system critical priority",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p12", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -519,7 +538,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
pod.Spec.Priority = &priority
|
||||
}),
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it has system critical priority, but it has scheduler.alpha.kubernetes.io/evict annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p13", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -532,7 +552,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod not evicted because it has a priority higher than the configured priority threshold",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p14", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -541,7 +562,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
priorityThreshold: &lowPriority,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it has a priority higher than the configured priority threshold, but it has scheduler.alpha.kubernetes.io/evict annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p15", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -552,7 +574,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
priorityThreshold: &lowPriority,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it has system critical priority, but evictSystemCriticalPods = true",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p16", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -563,7 +586,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
evictSystemCriticalPods: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it has system critical priority, but evictSystemCriticalPods = true and it has scheduler.alpha.kubernetes.io/evict annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p16", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -575,7 +599,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
evictSystemCriticalPods: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it has a priority higher than the configured priority threshold, but evictSystemCriticalPods = true",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p17", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -586,7 +611,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
evictSystemCriticalPods: true,
|
||||
priorityThreshold: &lowPriority,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod is evicted because it has a priority higher than the configured priority threshold, but evictSystemCriticalPods = true and it has scheduler.alpha.kubernetes.io/evict annotation",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p17", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -598,7 +624,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
evictSystemCriticalPods: true,
|
||||
priorityThreshold: &lowPriority,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod with no tolerations running on normal node, all other nodes tainted, no PreEvictionFilter, should ignore nodeFit",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -627,7 +654,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
nodeFit: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "minReplicas of 2, owner with 2 replicas, evicts",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -641,7 +669,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
minReplicas: 2,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "minReplicas of 3, owner with 2 replicas, no eviction",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -654,7 +683,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
minReplicas: 3,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "minReplicas of 2, multiple owners, no eviction",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -667,7 +697,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
minReplicas: 2,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "minPodAge of 50, pod created 10 minutes ago, no eviction",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -677,7 +708,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
minPodAge: &minPodAge,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "minPodAge of 50, pod created 60 minutes ago, evicts",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -688,7 +720,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
minPodAge: &minPodAge,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "nil minPodAge, pod created 60 minutes ago, evicts",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -698,7 +731,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "ignorePodsWithoutPDB, pod with no PDBs, no eviction",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -709,7 +743,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
ignorePodsWithoutPDB: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "ignorePodsWithoutPDB, pod with PDBs, evicts",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -724,7 +759,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
ignorePodsWithoutPDB: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "ignorePvcPods is set, pod with PVC, not evicts",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p15", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -739,7 +775,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
ignorePvcPods: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "ignorePvcPods is not set, pod with PVC, evicts",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p15", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -755,6 +792,74 @@ func TestDefaultEvictorFilter(t *testing.T) {
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
description: "Pod with local storage is evicted because 'withLocalStorage' is in disabledDefaultPodProtections",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p18", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
pod.Spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: "local-storage", VolumeSource: v1.VolumeSource{
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{},
|
||||
},
|
||||
},
|
||||
}
|
||||
}),
|
||||
},
|
||||
podProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{PodsWithLocalStorage},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
description: "DaemonSet pod is evicted because 'daemonSetPods' is in disabledDefaultPodProtections",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p19", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
pod.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Kind: "DaemonSet",
|
||||
Name: "daemonset-test",
|
||||
UID: "daemonset-uid",
|
||||
},
|
||||
}
|
||||
}),
|
||||
},
|
||||
podProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{DaemonSetPods},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
description: "Pod with PVC is not evicted because 'withPVC' is in extraPodProtections",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p20", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
pod.Spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: "pvc", VolumeSource: v1.VolumeSource{
|
||||
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ClaimName: "foo"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}),
|
||||
},
|
||||
podProtections: PodProtections{
|
||||
ExtraEnabled: []PodProtection{PodsWithPVC},
|
||||
},
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
description: "Pod without PDB is not evicted because 'withoutPDB' is in extraPodProtections",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p21", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
}),
|
||||
},
|
||||
podProtections: PodProtections{
|
||||
ExtraEnabled: []PodProtection{PodsWithoutPDB},
|
||||
},
|
||||
result: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -857,6 +962,7 @@ func initializePlugin(ctx context.Context, test testCase) (frameworktypes.Plugin
|
||||
MinPodAge: test.minPodAge,
|
||||
IgnorePodsWithoutPDB: test.ignorePodsWithoutPDB,
|
||||
NoEvictionPolicy: test.noEvictionPolicy,
|
||||
PodProtections: test.podProtections,
|
||||
}
|
||||
|
||||
evictorPlugin, err := New(
|
||||
@@ -873,3 +979,102 @@ func initializePlugin(ctx context.Context, test testCase) (frameworktypes.Plugin
|
||||
|
||||
return evictorPlugin, nil
|
||||
}
|
||||
|
||||
func TestGetEffectivePodProtections_TableDriven(t *testing.T) {
|
||||
// Prepare the default set for easy reference
|
||||
defaultSet := defaultPodProtections
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args *DefaultEvictorArgs
|
||||
wantResult []PodProtection
|
||||
}{
|
||||
{
|
||||
name: "NewConfig_EmptyConfig_ReturnsDefault",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{},
|
||||
ExtraEnabled: []PodProtection{},
|
||||
},
|
||||
},
|
||||
wantResult: defaultSet,
|
||||
},
|
||||
{
|
||||
name: "NewConfig_DisableOneDefault_ReturnsDefaultMinusOne",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{PodsWithLocalStorage},
|
||||
ExtraEnabled: []PodProtection{},
|
||||
},
|
||||
},
|
||||
wantResult: []PodProtection{DaemonSetPods, SystemCriticalPods, FailedBarePods},
|
||||
},
|
||||
{
|
||||
name: "NewConfig_DisableMultipleDefaults_ReturnsDefaultMinusMultiple",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{DaemonSetPods, SystemCriticalPods},
|
||||
ExtraEnabled: []PodProtection{},
|
||||
},
|
||||
},
|
||||
wantResult: []PodProtection{PodsWithLocalStorage, FailedBarePods},
|
||||
},
|
||||
{
|
||||
name: "NewConfig_EnableOneExtra_ReturnsDefaultPlusOne",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{},
|
||||
ExtraEnabled: []PodProtection{PodsWithPVC},
|
||||
},
|
||||
},
|
||||
wantResult: append(defaultSet, PodsWithPVC),
|
||||
},
|
||||
{
|
||||
name: "NewConfig_EnableMultipleExtra_ReturnsDefaultPlusMultiple",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{},
|
||||
ExtraEnabled: []PodProtection{PodsWithPVC, PodsWithoutPDB},
|
||||
},
|
||||
},
|
||||
wantResult: append(defaultSet, PodsWithPVC, PodsWithoutPDB),
|
||||
},
|
||||
{
|
||||
name: "NewConfig_DisableAndEnable_ReturnsModifiedSet",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{FailedBarePods, DaemonSetPods},
|
||||
ExtraEnabled: []PodProtection{PodsWithPVC},
|
||||
},
|
||||
},
|
||||
wantResult: []PodProtection{PodsWithLocalStorage, SystemCriticalPods, PodsWithPVC},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := getEffectivePodProtections(tt.args)
|
||||
|
||||
if !slicesEqualUnordered(tt.wantResult, got) {
|
||||
t.Errorf("getEffectivePodProtections() = %v, want %v", got, tt.wantResult)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func slicesEqualUnordered(expected, actual []PodProtection) bool {
|
||||
if len(expected) != len(actual) {
|
||||
return false
|
||||
}
|
||||
for _, exp := range expected {
|
||||
if !slices.Contains(actual, exp) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, act := range actual {
|
||||
if !slices.Contains(expected, act) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
utilptr "k8s.io/utils/ptr"
|
||||
|
||||
@@ -25,19 +25,82 @@ import (
|
||||
type DefaultEvictorArgs struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
NodeSelector string `json:"nodeSelector,omitempty"`
|
||||
EvictLocalStoragePods bool `json:"evictLocalStoragePods,omitempty"`
|
||||
EvictDaemonSetPods bool `json:"evictDaemonSetPods,omitempty"`
|
||||
EvictSystemCriticalPods bool `json:"evictSystemCriticalPods,omitempty"`
|
||||
IgnorePvcPods bool `json:"ignorePvcPods,omitempty"`
|
||||
EvictFailedBarePods bool `json:"evictFailedBarePods,omitempty"`
|
||||
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
|
||||
PriorityThreshold *api.PriorityThreshold `json:"priorityThreshold,omitempty"`
|
||||
NodeFit bool `json:"nodeFit,omitempty"`
|
||||
MinReplicas uint `json:"minReplicas,omitempty"`
|
||||
MinPodAge *metav1.Duration `json:"minPodAge,omitempty"`
|
||||
IgnorePodsWithoutPDB bool `json:"ignorePodsWithoutPDB,omitempty"`
|
||||
NoEvictionPolicy NoEvictionPolicy `json:"noEvictionPolicy,omitempty"`
|
||||
NodeSelector string `json:"nodeSelector,omitempty"`
|
||||
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
|
||||
PriorityThreshold *api.PriorityThreshold `json:"priorityThreshold,omitempty"`
|
||||
NodeFit bool `json:"nodeFit,omitempty"`
|
||||
MinReplicas uint `json:"minReplicas,omitempty"`
|
||||
MinPodAge *metav1.Duration `json:"minPodAge,omitempty"`
|
||||
NoEvictionPolicy NoEvictionPolicy `json:"noEvictionPolicy,omitempty"`
|
||||
|
||||
// PodProtections holds the list of enabled and disabled protection policies.
|
||||
// Users can selectively disable certain default protection rules or enable extra ones.
|
||||
PodProtections PodProtections `json:"podProtections,omitempty"`
|
||||
|
||||
// Deprecated: Use DisabledDefaultPodProtection with "PodsWithLocalStorage" instead.
|
||||
EvictLocalStoragePods bool `json:"evictLocalStoragePods,omitempty"`
|
||||
// Deprecated: Use DisabledDefaultPodProtection with "DaemonSetPods" instead.
|
||||
EvictDaemonSetPods bool `json:"evictDaemonSetPods,omitempty"`
|
||||
// Deprecated: Use DisabledDefaultPodProtection with "SystemCriticalPods" instead.
|
||||
EvictSystemCriticalPods bool `json:"evictSystemCriticalPods,omitempty"`
|
||||
// Deprecated: Use ExtraPodProtection with "PodsWithPVC" instead.
|
||||
IgnorePvcPods bool `json:"ignorePvcPods,omitempty"`
|
||||
// Deprecated: Use ExtraPodProtection with "PodsWithoutPDB" instead.
|
||||
IgnorePodsWithoutPDB bool `json:"ignorePodsWithoutPDB,omitempty"`
|
||||
// Deprecated: Use DisabledDefaultPodProtection with "FailedBarePods" instead.
|
||||
EvictFailedBarePods bool `json:"evictFailedBarePods,omitempty"`
|
||||
}
|
||||
|
||||
// PodProtection defines the protection policy for a pod.
|
||||
type PodProtection string
|
||||
|
||||
const (
|
||||
PodsWithLocalStorage PodProtection = "PodsWithLocalStorage"
|
||||
DaemonSetPods PodProtection = "DaemonSetPods"
|
||||
SystemCriticalPods PodProtection = "SystemCriticalPods"
|
||||
FailedBarePods PodProtection = "FailedBarePods"
|
||||
PodsWithPVC PodProtection = "PodsWithPVC"
|
||||
PodsWithoutPDB PodProtection = "PodsWithoutPDB"
|
||||
)
|
||||
|
||||
// PodProtections holds the list of enabled and disabled protection policies.
|
||||
// NOTE: The list of default enabled pod protection policies is subject to change in future versions.
|
||||
// +k8s:deepcopy-gen=true
|
||||
type PodProtections struct {
|
||||
// ExtraEnabled specifies additional protection policies that should be enabled.
|
||||
// Supports: PodsWithPVC, PodsWithoutPDB
|
||||
ExtraEnabled []PodProtection `json:"extraEnabled,omitempty"`
|
||||
|
||||
// DefaultDisabled specifies which default protection policies should be disabled.
|
||||
// Supports: PodsWithLocalStorage, DaemonSetPods, SystemCriticalPods, FailedBarePods
|
||||
DefaultDisabled []PodProtection `json:"defaultDisabled,omitempty"`
|
||||
}
|
||||
|
||||
// defaultPodProtections holds the list of protection policies that are enabled by default.
|
||||
// User can use the 'disabledDefaultPodProtections' evictor arguments (via PodProtections.DefaultDisabled)
|
||||
// to disable any of these default protections.
|
||||
//
|
||||
// The following four policies are included by default:
|
||||
// - PodsWithLocalStorage: Protects pods with local storage.
|
||||
// - DaemonSetPods: Protects DaemonSet managed pods.
|
||||
// - SystemCriticalPods: Protects system-critical pods.
|
||||
// - FailedBarePods: Protects failed bare pods (not part of any controller).
|
||||
var defaultPodProtections = []PodProtection{
|
||||
PodsWithLocalStorage,
|
||||
SystemCriticalPods,
|
||||
FailedBarePods,
|
||||
DaemonSetPods,
|
||||
}
|
||||
|
||||
// extraPodProtections holds a list of protection policies that the user can optionally enable
|
||||
// through the configuration (via PodProtections.ExtraEnabled). These policies are not enabled by default.
|
||||
//
|
||||
// Currently supported extra policies:
|
||||
// - PodsWithPVC: Protects pods using PersistentVolumeClaims.
|
||||
// - PodsWithoutPDB: Protects pods lacking a PodDisruptionBudget.
|
||||
var extraPodProtections = []PodProtection{
|
||||
PodsWithPVC,
|
||||
PodsWithoutPDB,
|
||||
}
|
||||
|
||||
// NoEvictionPolicy dictates whether a no-eviction policy is preferred or mandatory.
|
||||
|
||||
@@ -15,6 +15,7 @@ package defaultevictor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
@@ -38,5 +39,51 @@ func ValidateDefaultEvictorArgs(obj runtime.Object) error {
|
||||
}
|
||||
}
|
||||
|
||||
// check if any deprecated fields are set to true
|
||||
hasDeprecatedFields := args.EvictLocalStoragePods || args.EvictDaemonSetPods ||
|
||||
args.EvictSystemCriticalPods || args.IgnorePvcPods ||
|
||||
args.EvictFailedBarePods || args.IgnorePodsWithoutPDB
|
||||
|
||||
// disallow mixing deprecated fields with PodProtections.ExtraEnabled and PodProtections.DefaultDisabled
|
||||
if hasDeprecatedFields && (len(args.PodProtections.ExtraEnabled) > 0 || len(args.PodProtections.DefaultDisabled) > 0) {
|
||||
return fmt.Errorf("cannot use Deprecated fields alongside PodProtections.ExtraEnabled or PodProtections.DefaultDisabled")
|
||||
}
|
||||
|
||||
if len(args.PodProtections.ExtraEnabled) > 0 || len(args.PodProtections.DefaultDisabled) > 0 {
|
||||
|
||||
for _, policy := range args.PodProtections.ExtraEnabled {
|
||||
if !slices.Contains(extraPodProtections, policy) {
|
||||
return fmt.Errorf("invalid pod protection policy in ExtraEnabled: %q. Valid options are: %v",
|
||||
string(policy), extraPodProtections)
|
||||
}
|
||||
}
|
||||
|
||||
for _, policy := range args.PodProtections.DefaultDisabled {
|
||||
if !slices.Contains(defaultPodProtections, policy) {
|
||||
return fmt.Errorf("invalid pod protection policy in DefaultDisabled: %q. Valid options are: %v",
|
||||
string(policy), defaultPodProtections)
|
||||
}
|
||||
}
|
||||
|
||||
if hasDuplicates(args.PodProtections.DefaultDisabled) {
|
||||
return fmt.Errorf("PodProtections.DefaultDisabled contains duplicate entries")
|
||||
}
|
||||
|
||||
if hasDuplicates(args.PodProtections.ExtraEnabled) {
|
||||
return fmt.Errorf("PodProtections.ExtraEnabled contains duplicate entries")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasDuplicates(slice []PodProtection) bool {
|
||||
seen := make(map[PodProtection]struct{}, len(slice))
|
||||
for _, item := range slice {
|
||||
if _, exists := seen[item]; exists {
|
||||
return true
|
||||
}
|
||||
seen[item] = struct{}{}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -40,13 +40,153 @@ func TestValidateDefaultEvictorArgs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf("priority threshold misconfigured, only one of priorityThreshold fields can be set"),
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "passing invalid no eviction policy",
|
||||
args: &DefaultEvictorArgs{
|
||||
NoEvictionPolicy: "invalid-no-eviction-policy",
|
||||
},
|
||||
errInfo: fmt.Errorf("noEvictionPolicy accepts only %q values", []NoEvictionPolicy{PreferredNoEvictionPolicy, MandatoryNoEvictionPolicy}),
|
||||
},
|
||||
{
|
||||
name: "Valid configuration with no deprecated fields",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{},
|
||||
ExtraEnabled: []PodProtection{},
|
||||
},
|
||||
},
|
||||
errInfo: nil,
|
||||
},
|
||||
{
|
||||
name: "Valid configuration: both Disabled and ExtraEnabled",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{
|
||||
DaemonSetPods,
|
||||
PodsWithLocalStorage,
|
||||
},
|
||||
ExtraEnabled: []PodProtection{
|
||||
PodsWithPVC,
|
||||
},
|
||||
},
|
||||
},
|
||||
errInfo: nil,
|
||||
},
|
||||
{
|
||||
name: "Valid configuration with ExtraEnabled",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
ExtraEnabled: []PodProtection{
|
||||
PodsWithPVC,
|
||||
},
|
||||
},
|
||||
},
|
||||
errInfo: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid configuration: Deprecated field used with Disabled",
|
||||
args: &DefaultEvictorArgs{
|
||||
EvictLocalStoragePods: true,
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{
|
||||
DaemonSetPods,
|
||||
},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf("cannot use Deprecated fields alongside PodProtections.ExtraEnabled or PodProtections.DefaultDisabled"),
|
||||
},
|
||||
{
|
||||
name: "Invalid configuration: Deprecated field used with ExtraPodProtections",
|
||||
args: &DefaultEvictorArgs{
|
||||
EvictDaemonSetPods: true,
|
||||
PodProtections: PodProtections{
|
||||
ExtraEnabled: []PodProtection{
|
||||
PodsWithPVC,
|
||||
},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf("cannot use Deprecated fields alongside PodProtections.ExtraEnabled or PodProtections.DefaultDisabled"),
|
||||
},
|
||||
{
|
||||
name: "MinReplicas warning logged but no error",
|
||||
args: &DefaultEvictorArgs{
|
||||
MinReplicas: 1,
|
||||
},
|
||||
errInfo: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid ExtraEnabled: Unknown policy",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
ExtraEnabled: []PodProtection{"InvalidPolicy"},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf(`invalid pod protection policy in ExtraEnabled: "InvalidPolicy". Valid options are: [PodsWithPVC PodsWithoutPDB]`),
|
||||
},
|
||||
{
|
||||
name: "Invalid ExtraEnabled: Misspelled policy",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
ExtraEnabled: []PodProtection{"PodsWithPVCC"},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf(`invalid pod protection policy in ExtraEnabled: "PodsWithPVCC". Valid options are: [PodsWithPVC PodsWithoutPDB]`),
|
||||
},
|
||||
{
|
||||
name: "Invalid ExtraEnabled: Policy from DefaultDisabled list",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
ExtraEnabled: []PodProtection{DaemonSetPods},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf(`invalid pod protection policy in ExtraEnabled: "DaemonSetPods". Valid options are: [PodsWithPVC PodsWithoutPDB]`),
|
||||
},
|
||||
{
|
||||
name: "Invalid DefaultDisabled: Unknown policy",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{"InvalidPolicy"},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf(`invalid pod protection policy in DefaultDisabled: "InvalidPolicy". Valid options are: [PodsWithLocalStorage SystemCriticalPods FailedBarePods DaemonSetPods]`),
|
||||
},
|
||||
{
|
||||
name: "Invalid DefaultDisabled: Misspelled policy",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{"PodsWithLocalStorag"},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf(`invalid pod protection policy in DefaultDisabled: "PodsWithLocalStorag". Valid options are: [PodsWithLocalStorage SystemCriticalPods FailedBarePods DaemonSetPods]`),
|
||||
},
|
||||
{
|
||||
name: "Invalid DefaultDisabled: Policy from ExtraEnabled list",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{PodsWithPVC},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf(`invalid pod protection policy in DefaultDisabled: "PodsWithPVC". Valid options are: [PodsWithLocalStorage SystemCriticalPods FailedBarePods DaemonSetPods]`),
|
||||
},
|
||||
{
|
||||
name: "Invalid ExtraEnabled duplicate",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
ExtraEnabled: []PodProtection{PodsWithPVC, PodsWithPVC},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf(`PodProtections.ExtraEnabled contains duplicate entries`),
|
||||
},
|
||||
{
|
||||
name: "Invalid DefaultDisabled duplicate",
|
||||
args: &DefaultEvictorArgs{
|
||||
PodProtections: PodProtections{
|
||||
DefaultDisabled: []PodProtection{PodsWithLocalStorage, PodsWithLocalStorage},
|
||||
},
|
||||
},
|
||||
errInfo: fmt.Errorf(`PodProtections.DefaultDisabled contains duplicate entries`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
|
||||
@@ -46,6 +46,7 @@ func (in *DefaultEvictorArgs) DeepCopyInto(out *DefaultEvictorArgs) {
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
in.PodProtections.DeepCopyInto(&out.PodProtections)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -66,3 +67,29 @@ func (in *DefaultEvictorArgs) DeepCopyObject() runtime.Object {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodProtections) DeepCopyInto(out *PodProtections) {
|
||||
*out = *in
|
||||
if in.ExtraEnabled != nil {
|
||||
in, out := &in.ExtraEnabled, &out.ExtraEnabled
|
||||
*out = make([]PodProtection, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.DefaultDisabled != nil {
|
||||
in, out := &in.DefaultDisabled, &out.DefaultDisabled
|
||||
*out = make([]PodProtection, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodProtections.
|
||||
func (in *PodProtections) DeepCopy() *PodProtections {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodProtections)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user