Skip to content

Commit 1541a73

Browse files
authored
Add safe guard to reduce risk when a large number of nodes are tainted (#2008)
* Add safe guard to reduce risk when a large number of nodes are tainted
1 parent 62cc41a commit 1541a73

11 files changed

+999
-1240
lines changed

api/v1beta2/foundationdbcluster_types.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1beta2
1818

1919
import (
2020
"fmt"
21+
"k8s.io/apimachinery/pkg/util/intstr"
2122
"math"
2223
"math/rand"
2324
"regexp"
@@ -1257,6 +1258,16 @@ type AutomaticReplacementOptions struct {
12571258
// TaintReplacementOption controls which taint label the operator will react to.
12581259
// +kubebuilder:validation:MaxItems=32
12591260
TaintReplacementOptions []TaintReplacementOption `json:"taintReplacementOptions,omitempty"`
1261+
1262+
// MaxFaultDomainsWithTaintedProcessGroups defines how many fault domains in the cluster can have process groups
1263+
// with the NodeTaintReplacing condition and still allow the operator to automatically replace those process groups.
1264+
// If more fault domains contain process groups with the NodeTaintReplacing condition, the operator will not
1265+
// automatically replace those process groups. This is a safeguard in addition to MaxConcurrentReplacements to make
1266+
// sure the operator is not replacing too many process groups if a large number of nodes are tainted. A absolute number
1267+
// of fault domains or a percentage can be provided.
1268+
// Defaults to 10% of the fault domains or at least 1.
1269+
// +kubebuilder:validation:XIntOrString
1270+
MaxFaultDomainsWithTaintedProcessGroups *intstr.IntOrString `json:"maxFaultDomainsWithTaintedProcessGroups,omitempty"`
12601271
}
12611272

12621273
// ProcessSettings defines process-level settings.
@@ -2528,7 +2539,7 @@ func (cluster *FoundationDBCluster) GetIgnoreTerminatingPodsSeconds() int {
25282539
return pointer.IntDeref(cluster.Spec.AutomationOptions.IgnoreTerminatingPodsSeconds, int((10 * time.Minute).Seconds()))
25292540
}
25302541

2531-
// GetProcessGroupsToRemove will returns the list of Process Group IDs that must be added to the ProcessGroupsToRemove
2542+
// GetProcessGroupsToRemove will return the list of Process Group IDs that must be added to the ProcessGroupsToRemove
25322543
// it will filter out all Process Group IDs that are already marked for removal to make sure those are clean up. If a
25332544
// provided process group ID doesn't exit it will be ignored.
25342545
func (cluster *FoundationDBCluster) GetProcessGroupsToRemove(processGroupIDs []ProcessGroupID) []ProcessGroupID {
@@ -2926,8 +2937,25 @@ func (cluster *FoundationDBCluster) ProcessSharesDC(process FoundationDBStatusPr
29262937
if cluster == nil || cluster.Spec.DataCenter == "" {
29272938
return true
29282939
}
2940+
29292941
if cluster.Spec.DataCenter == process.Locality[FDBLocalityDCIDKey] {
29302942
return true
29312943
}
2944+
29322945
return false
29332946
}
2947+
2948+
// GetMaxFaultDomainsWithTaintedProcessGroups returns the maximum fault domains that can hold pods on tainted nodes to still
2949+
// allow the operator to automatically replace those pods on the tainted nodes automatically.
2950+
func (cluster *FoundationDBCluster) GetMaxFaultDomainsWithTaintedProcessGroups(faultDomainCnt int) (int, error) {
2951+
maxAllowed, err := intstr.GetScaledValueFromIntOrPercent(intstr.ValueOrDefault(cluster.Spec.AutomationOptions.Replacements.MaxFaultDomainsWithTaintedProcessGroups, intstr.IntOrString{Type: intstr.String, StrVal: "10%"}), faultDomainCnt, false)
2952+
if err != nil {
2953+
return -1, err
2954+
}
2955+
2956+
if maxAllowed < 1 {
2957+
return 1, nil
2958+
}
2959+
2960+
return maxAllowed, nil
2961+
}

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10202,6 +10202,11 @@ spec:
1020210202
default: 1
1020310203
minimum: 0
1020410204
type: integer
10205+
maxFaultDomainsWithTaintedProcessGroups:
10206+
anyOf:
10207+
- type: integer
10208+
- type: string
10209+
x-kubernetes-int-or-string: true
1020510210
taintReplacementOptions:
1020610211
items:
1020710212
properties:

controllers/cluster_controller_test.go

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"regexp"
2828
"sort"
2929
"strings"
30-
"time"
3130

3231
"github.com/FoundationDB/fdb-kubernetes-operator/pkg/fdbadminclient/mock"
3332

@@ -2654,30 +2653,17 @@ var _ = Describe("cluster_controller", func() {
26542653

26552654
BeforeEach(func() {
26562655
pods := &corev1.PodList{}
2657-
err = k8sClient.List(context.TODO(), pods, getListOptions(cluster)...)
2658-
Expect(err).NotTo(HaveOccurred())
2656+
Expect(k8sClient.List(context.TODO(), pods, getListOptions(cluster)...)).NotTo(HaveOccurred())
26592657

26602658
recreatedPod = pods.Items[0]
2661-
err = k8sClient.SetPodIntoFailed(context.Background(), &recreatedPod, "NodeAffinity")
2662-
Expect(err).NotTo(HaveOccurred())
2663-
// We have to sleep 1 second otherwise the creation timestamp will be the same
2664-
time.Sleep(1 * time.Second)
2665-
2659+
Expect(k8sClient.SetPodIntoFailed(context.Background(), &recreatedPod, "NodeAffinity")).NotTo(HaveOccurred())
26662660
generationGap = 0
26672661
})
26682662

26692663
It("should recreate the Pod", func() {
2670-
pods := &corev1.PodList{}
2671-
err = k8sClient.List(context.TODO(), pods, getListOptions(cluster)...)
2672-
Expect(err).NotTo(HaveOccurred())
2673-
2674-
for _, pod := range pods.Items {
2675-
if pod.GetName() != recreatedPod.GetName() {
2676-
continue
2677-
}
2678-
2679-
Expect(recreatedPod.CreationTimestamp.UnixNano()).To(BeNumerically("<", pod.CreationTimestamp.UnixNano()))
2680-
}
2664+
pod := &corev1.Pod{}
2665+
Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Namespace: recreatedPod.Namespace, Name: recreatedPod.Name}, pod)).NotTo(HaveOccurred())
2666+
Expect(pod.UID).NotTo(Equal(recreatedPod.UID))
26812667
})
26822668
})
26832669

0 commit comments

Comments
 (0)