1
16
17 package deployment
18
19 import (
20 "context"
21 "sort"
22 "strconv"
23
24 appsv1 "k8s.io/api/apps/v1"
25 corev1 "k8s.io/api/core/v1"
26 apiequality "k8s.io/apimachinery/pkg/api/equality"
27 "k8s.io/apimachinery/pkg/api/meta"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/runtime"
30 intstrutil "k8s.io/apimachinery/pkg/util/intstr"
31 runtimeresource "k8s.io/cli-runtime/pkg/resource"
32 appsclient "k8s.io/client-go/kubernetes/typed/apps/v1"
33 )
34
35 const (
36
37 RevisionAnnotation = "deployment.kubernetes.io/revision"
38
39 RevisionHistoryAnnotation = "deployment.kubernetes.io/revision-history"
40
41
42
43 DesiredReplicasAnnotation = "deployment.kubernetes.io/desired-replicas"
44
45
46
47 MaxReplicasAnnotation = "deployment.kubernetes.io/max-replicas"
48
49 RollbackRevisionNotFound = "DeploymentRollbackRevisionNotFound"
50
51 RollbackTemplateUnchanged = "DeploymentRollbackTemplateUnchanged"
52
53 RollbackDone = "DeploymentRollback"
54
55
56 TimedOutReason = "ProgressDeadlineExceeded"
57 )
58
59
60 func GetDeploymentCondition(status appsv1.DeploymentStatus, condType appsv1.DeploymentConditionType) *appsv1.DeploymentCondition {
61 for i := range status.Conditions {
62 c := status.Conditions[i]
63 if c.Type == condType {
64 return &c
65 }
66 }
67 return nil
68 }
69
70
71 func Revision(obj runtime.Object) (int64, error) {
72 acc, err := meta.Accessor(obj)
73 if err != nil {
74 return 0, err
75 }
76 v, ok := acc.GetAnnotations()[RevisionAnnotation]
77 if !ok {
78 return 0, nil
79 }
80 return strconv.ParseInt(v, 10, 64)
81 }
82
83
84
85
86
87 func GetAllReplicaSets(deployment *appsv1.Deployment, c appsclient.AppsV1Interface) ([]*appsv1.ReplicaSet, []*appsv1.ReplicaSet, *appsv1.ReplicaSet, error) {
88 rsList, err := listReplicaSets(deployment, rsListFromClient(c), nil)
89 if err != nil {
90 return nil, nil, nil, err
91 }
92 newRS := findNewReplicaSet(deployment, rsList)
93 oldRSes, allOldRSes := findOldReplicaSets(deployment, rsList, newRS)
94 return oldRSes, allOldRSes, newRS, nil
95 }
96
97
98
99
100
101
102 func GetAllReplicaSetsInChunks(deployment *appsv1.Deployment, c appsclient.AppsV1Interface, chunkSize int64) ([]*appsv1.ReplicaSet, []*appsv1.ReplicaSet, *appsv1.ReplicaSet, error) {
103 rsList, err := listReplicaSets(deployment, rsListFromClient(c), &chunkSize)
104 if err != nil {
105 return nil, nil, nil, err
106 }
107 newRS := findNewReplicaSet(deployment, rsList)
108 oldRSes, allOldRSes := findOldReplicaSets(deployment, rsList, newRS)
109 return oldRSes, allOldRSes, newRS, nil
110 }
111
112
113 func rsListFromClient(c appsclient.AppsV1Interface) rsListFunc {
114 return func(namespace string, initialOpts metav1.ListOptions) ([]*appsv1.ReplicaSet, error) {
115 rsList := &appsv1.ReplicaSetList{}
116 err := runtimeresource.FollowContinue(&initialOpts,
117 func(opts metav1.ListOptions) (runtime.Object, error) {
118 newRs, err := c.ReplicaSets(namespace).List(context.TODO(), opts)
119 if err != nil {
120 return nil, runtimeresource.EnhanceListError(err, opts, "replicasets")
121 }
122 rsList.Items = append(rsList.Items, newRs.Items...)
123 return newRs, nil
124 })
125 if err != nil {
126 return nil, err
127 }
128 var ret []*appsv1.ReplicaSet
129 for i := range rsList.Items {
130 ret = append(ret, &rsList.Items[i])
131 }
132 return ret, err
133 }
134 }
135
136
137 type rsListFunc func(string, metav1.ListOptions) ([]*appsv1.ReplicaSet, error)
138
139
140
141
142
143 func listReplicaSets(deployment *appsv1.Deployment, getRSList rsListFunc, chunkSize *int64) ([]*appsv1.ReplicaSet, error) {
144
145
146 namespace := deployment.Namespace
147 selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
148 if err != nil {
149 return nil, err
150 }
151 options := metav1.ListOptions{LabelSelector: selector.String()}
152 if chunkSize != nil {
153 options.Limit = *chunkSize
154 }
155 all, err := getRSList(namespace, options)
156 if err != nil {
157 return nil, err
158 }
159
160 owned := make([]*appsv1.ReplicaSet, 0, len(all))
161 for _, rs := range all {
162 if metav1.IsControlledBy(rs, deployment) {
163 owned = append(owned, rs)
164 }
165 }
166 return owned, nil
167 }
168
169
170
171
172
173
174 func equalIgnoreHash(template1, template2 *corev1.PodTemplateSpec) bool {
175 t1Copy := template1.DeepCopy()
176 t2Copy := template2.DeepCopy()
177
178 delete(t1Copy.Labels, appsv1.DefaultDeploymentUniqueLabelKey)
179 delete(t2Copy.Labels, appsv1.DefaultDeploymentUniqueLabelKey)
180 return apiequality.Semantic.DeepEqual(t1Copy, t2Copy)
181 }
182
183
184 func findNewReplicaSet(deployment *appsv1.Deployment, rsList []*appsv1.ReplicaSet) *appsv1.ReplicaSet {
185 sort.Sort(replicaSetsByCreationTimestamp(rsList))
186 for i := range rsList {
187 if equalIgnoreHash(&rsList[i].Spec.Template, &deployment.Spec.Template) {
188
189
190
191
192 return rsList[i]
193 }
194 }
195
196 return nil
197 }
198
199
200 type replicaSetsByCreationTimestamp []*appsv1.ReplicaSet
201
202 func (o replicaSetsByCreationTimestamp) Len() int { return len(o) }
203 func (o replicaSetsByCreationTimestamp) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
204 func (o replicaSetsByCreationTimestamp) Less(i, j int) bool {
205 if o[i].CreationTimestamp.Equal(&o[j].CreationTimestamp) {
206 return o[i].Name < o[j].Name
207 }
208 return o[i].CreationTimestamp.Before(&o[j].CreationTimestamp)
209 }
210
211
212
213 func findOldReplicaSets(deployment *appsv1.Deployment, rsList []*appsv1.ReplicaSet, newRS *appsv1.ReplicaSet) ([]*appsv1.ReplicaSet, []*appsv1.ReplicaSet) {
214 var requiredRSs []*appsv1.ReplicaSet
215 var allRSs []*appsv1.ReplicaSet
216 for _, rs := range rsList {
217
218 if newRS != nil && rs.UID == newRS.UID {
219 continue
220 }
221 allRSs = append(allRSs, rs)
222 if *(rs.Spec.Replicas) != 0 {
223 requiredRSs = append(requiredRSs, rs)
224 }
225 }
226 return requiredRSs, allRSs
227 }
228
229
230
231
232
233
234
235
236
237
238 func ResolveFenceposts(maxSurge, maxUnavailable *intstrutil.IntOrString, desired int32) (int32, int32, error) {
239 surge, err := intstrutil.GetScaledValueFromIntOrPercent(intstrutil.ValueOrDefault(maxSurge, intstrutil.FromInt32(0)), int(desired), true)
240 if err != nil {
241 return 0, 0, err
242 }
243 unavailable, err := intstrutil.GetScaledValueFromIntOrPercent(intstrutil.ValueOrDefault(maxUnavailable, intstrutil.FromInt32(0)), int(desired), false)
244 if err != nil {
245 return 0, 0, err
246 }
247
248 if surge == 0 && unavailable == 0 {
249
250
251
252
253 unavailable = 1
254 }
255
256 return int32(surge), int32(unavailable), nil
257 }
258
View as plain text