-
Notifications
You must be signed in to change notification settings - Fork 480
/
helpers.go
202 lines (172 loc) · 7.87 KB
/
helpers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License 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 worker
import (
"fmt"
"strings"
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
machinev1alpha1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
corev1 "k8s.io/api/core/v1"
)
var (
trueMachineDeploymentConditionTypes = []machinev1alpha1.MachineDeploymentConditionType{
machinev1alpha1.MachineDeploymentAvailable,
}
trueOptionalMachineDeploymentConditionTypes = []machinev1alpha1.MachineDeploymentConditionType{
machinev1alpha1.MachineDeploymentProgressing,
}
falseMachineDeploymentConditionTypes = []machinev1alpha1.MachineDeploymentConditionType{
machinev1alpha1.MachineDeploymentReplicaFailure,
machinev1alpha1.MachineDeploymentFrozen,
}
)
// CheckMachineDeployment checks whether the given MachineDeployment is healthy.
// A MachineDeployment is considered healthy if its controller observed its current revision and if
// its desired number of replicas is equal to its updated replicas.
func CheckMachineDeployment(deployment *machinev1alpha1.MachineDeployment) error {
if deployment.Status.ObservedGeneration < deployment.Generation {
return fmt.Errorf("observed generation outdated (%d/%d)", deployment.Status.ObservedGeneration, deployment.Generation)
}
for _, trueConditionType := range trueMachineDeploymentConditionTypes {
condition := getMachineDeploymentCondition(deployment.Status.Conditions, trueConditionType)
if condition == nil {
return requiredConditionMissing(trueConditionType)
}
if err := checkConditionStatus(condition, machinev1alpha1.ConditionTrue); err != nil {
return err
}
}
for _, trueOptionalConditionType := range trueOptionalMachineDeploymentConditionTypes {
condition := getMachineDeploymentCondition(deployment.Status.Conditions, trueOptionalConditionType)
if condition == nil {
continue
}
if err := checkConditionStatus(condition, machinev1alpha1.ConditionTrue); err != nil {
return err
}
}
for _, falseConditionType := range falseMachineDeploymentConditionTypes {
condition := getMachineDeploymentCondition(deployment.Status.Conditions, falseConditionType)
if condition == nil {
continue
}
if err := checkConditionStatus(condition, machinev1alpha1.ConditionFalse); err != nil {
return err
}
}
return nil
}
func checkMachineDeploymentsHealthy(machineDeployments []machinev1alpha1.MachineDeployment) (bool, error) {
for _, deployment := range machineDeployments {
for _, failedMachine := range deployment.Status.FailedMachines {
return false, fmt.Errorf("machine %q failed: %s", failedMachine.Name, failedMachine.LastOperation.Description)
}
if err := CheckMachineDeployment(&deployment); err != nil {
return false, fmt.Errorf("machine deployment %q in namespace %q is unhealthy: %v", deployment.Name, deployment.Namespace, err)
}
}
return true, nil
}
func checkNodesScalingUp(machineList *machinev1alpha1.MachineList, readyNodes, desiredMachines int) (gardencorev1beta1.ConditionStatus, error) {
if readyNodes == desiredMachines {
return gardencorev1beta1.ConditionTrue, nil
}
if machineObjects := len(machineList.Items); machineObjects < desiredMachines {
return gardencorev1beta1.ConditionFalse, fmt.Errorf("not enough machine objects created yet (%d/%d)", machineObjects, desiredMachines)
}
var pendingMachines, erroneousMachines int
for _, machine := range machineList.Items {
switch machine.Status.CurrentStatus.Phase {
case machinev1alpha1.MachineRunning, machinev1alpha1.MachineAvailable:
// machine is already running fine
continue
case machinev1alpha1.MachinePending, "": // https://github.com/gardener/machine-controller-manager/issues/466
// machine is in the process of being created
pendingMachines++
default:
// undesired machine phase
erroneousMachines++
}
}
if erroneousMachines > 0 {
return gardencorev1beta1.ConditionFalse, fmt.Errorf("%s erroneous", cosmeticMachineMessage(erroneousMachines))
}
if pendingMachines == 0 {
return gardencorev1beta1.ConditionFalse, fmt.Errorf("not enough ready worker nodes registered in the cluster (%d/%d)", readyNodes, desiredMachines)
}
return gardencorev1beta1.ConditionProgressing, fmt.Errorf("%s provisioning and should join the cluster soon", cosmeticMachineMessage(pendingMachines))
}
func checkNodesScalingDown(machineList *machinev1alpha1.MachineList, nodeList *corev1.NodeList, registeredNodes, desiredMachines int) (gardencorev1beta1.ConditionStatus, error) {
if registeredNodes == desiredMachines {
return gardencorev1beta1.ConditionTrue, nil
}
// Check if all nodes that are cordoned map to machines with a deletion timestamp. This might be the case during
// a rolling update.
nodeNameToMachine := map[string]machinev1alpha1.Machine{}
for _, machine := range machineList.Items {
if machine.Labels != nil && machine.Labels["node"] != "" {
nodeNameToMachine[machine.Labels["node"]] = machine
}
}
var cordonedNodes int
for _, node := range nodeList.Items {
if node.Spec.Unschedulable {
machine, ok := nodeNameToMachine[node.Name]
if !ok {
return gardencorev1beta1.ConditionFalse, fmt.Errorf("machine object for cordoned node %q not found", node.Name)
}
if machine.DeletionTimestamp == nil {
return gardencorev1beta1.ConditionFalse, fmt.Errorf("cordoned node %q found but corresponding machine object does not have a deletion timestamp", node.Name)
}
cordonedNodes++
}
}
// If there are still more nodes than desired then report an error.
if registeredNodes-cordonedNodes != desiredMachines {
return gardencorev1beta1.ConditionFalse, fmt.Errorf("too many worker nodes are registered. Exceeding maximum desired machine count (%d/%d)", registeredNodes, desiredMachines)
}
return gardencorev1beta1.ConditionProgressing, fmt.Errorf("%s waiting to be completely drained from pods. If this persists, check your pod disruption budgets and pending finalizers. Please note, that nodes that fail to be drained will be deleted automatically", cosmeticMachineMessage(cordonedNodes))
}
func getDesiredMachineCount(machineDeployments []machinev1alpha1.MachineDeployment) int {
desiredMachines := 0
for _, deployment := range machineDeployments {
if deployment.DeletionTimestamp == nil {
desiredMachines += int(deployment.Spec.Replicas)
}
}
return desiredMachines
}
func checkConditionStatus(condition *machinev1alpha1.MachineDeploymentCondition, expectedStatus machinev1alpha1.ConditionStatus) error {
if condition.Status != expectedStatus {
return fmt.Errorf("%s (%s)", strings.Trim(condition.Message, "."), condition.Reason)
}
return nil
}
func getMachineDeploymentCondition(conditions []machinev1alpha1.MachineDeploymentCondition, conditionType machinev1alpha1.MachineDeploymentConditionType) *machinev1alpha1.MachineDeploymentCondition {
for _, condition := range conditions {
if condition.Type == conditionType {
return &condition
}
}
return nil
}
func requiredConditionMissing(conditionType machinev1alpha1.MachineDeploymentConditionType) error {
return fmt.Errorf("condition %q is missing", conditionType)
}
func cosmeticMachineMessage(numberOfMachines int) string {
if numberOfMachines == 1 {
return fmt.Sprintf("%d machine is", numberOfMachines)
}
return fmt.Sprintf("%d machines are", numberOfMachines)
}