sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/alpha/machinedeployment.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package alpha 18 19 import ( 20 "context" 21 "fmt" 22 "strconv" 23 "time" 24 25 "github.com/pkg/errors" 26 "k8s.io/apimachinery/pkg/api/meta" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/labels" 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/apimachinery/pkg/types" 31 "sigs.k8s.io/controller-runtime/pkg/client" 32 33 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 34 "sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster" 35 logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log" 36 ) 37 38 // getMachineDeployment retrieves the MachineDeployment object corresponding to the name and namespace specified. 39 func getMachineDeployment(ctx context.Context, proxy cluster.Proxy, name, namespace string) (*clusterv1.MachineDeployment, error) { 40 mdObj := &clusterv1.MachineDeployment{} 41 c, err := proxy.NewClient(ctx) 42 if err != nil { 43 return nil, err 44 } 45 mdObjKey := client.ObjectKey{ 46 Namespace: namespace, 47 Name: name, 48 } 49 if err := c.Get(ctx, mdObjKey, mdObj); err != nil { 50 return nil, errors.Wrapf(err, "failed to get MachineDeployment %s/%s", 51 mdObjKey.Namespace, mdObjKey.Name) 52 } 53 return mdObj, nil 54 } 55 56 // setRolloutAfterOnMachineDeployment sets MachineDeployment.spec.rolloutAfter. 57 func setRolloutAfterOnMachineDeployment(ctx context.Context, proxy cluster.Proxy, name, namespace string) error { 58 patch := client.RawPatch(types.MergePatchType, []byte(fmt.Sprintf(`{"spec":{"rolloutAfter":"%v"}}`, time.Now().Format(time.RFC3339)))) 59 return patchMachineDeployment(ctx, proxy, name, namespace, patch) 60 } 61 62 // patchMachineDeployment applies a patch to a machinedeployment. 63 func patchMachineDeployment(ctx context.Context, proxy cluster.Proxy, name, namespace string, patch client.Patch) error { 64 cFrom, err := proxy.NewClient(ctx) 65 if err != nil { 66 return err 67 } 68 mdObj := &clusterv1.MachineDeployment{} 69 mdObjKey := client.ObjectKey{ 70 Namespace: namespace, 71 Name: name, 72 } 73 if err := cFrom.Get(ctx, mdObjKey, mdObj); err != nil { 74 return errors.Wrapf(err, "failed to get MachineDeployment %s/%s", mdObj.GetNamespace(), mdObj.GetName()) 75 } 76 77 if err := cFrom.Patch(ctx, mdObj, patch); err != nil { 78 return errors.Wrapf(err, "failed while patching MachineDeployment %s/%s", mdObj.GetNamespace(), mdObj.GetName()) 79 } 80 return nil 81 } 82 83 // findMachineDeploymentRevision finds the specific revision in the machine sets. 84 func findMachineDeploymentRevision(toRevision int64, allMSs []*clusterv1.MachineSet) (*clusterv1.MachineSet, error) { 85 var ( 86 latestMachineSet *clusterv1.MachineSet 87 latestRevision = int64(-1) 88 previousMachineSet *clusterv1.MachineSet 89 previousRevision = int64(-1) 90 ) 91 for _, ms := range allMSs { 92 if v, err := revision(ms); err == nil { 93 if toRevision == 0 { 94 if latestRevision < v { 95 // newest one we've seen so far 96 previousRevision = latestRevision 97 previousMachineSet = latestMachineSet 98 latestRevision = v 99 latestMachineSet = ms 100 } else if previousRevision < v { 101 // second newest one we've seen so far 102 previousRevision = v 103 previousMachineSet = ms 104 } 105 } else if toRevision == v { 106 return ms, nil 107 } 108 } 109 } 110 111 if toRevision > 0 { 112 return nil, errors.Errorf("unable to find specified MachineDeployment revision: %v", toRevision) 113 } 114 115 if previousMachineSet == nil { 116 return nil, errors.Errorf("no rollout history found for MachineDeployment") 117 } 118 return previousMachineSet, nil 119 } 120 121 // getMachineSetsForDeployment returns a list of MachineSets associated with a MachineDeployment. 122 func getMachineSetsForDeployment(ctx context.Context, proxy cluster.Proxy, md *clusterv1.MachineDeployment) ([]*clusterv1.MachineSet, error) { 123 log := logf.Log 124 c, err := proxy.NewClient(ctx) 125 if err != nil { 126 return nil, err 127 } 128 // List all MachineSets to find those we own but that no longer match our selector. 129 machineSets := &clusterv1.MachineSetList{} 130 if err := c.List(ctx, machineSets, client.InNamespace(md.Namespace)); err != nil { 131 return nil, err 132 } 133 134 filtered := make([]*clusterv1.MachineSet, 0, len(machineSets.Items)) 135 for idx := range machineSets.Items { 136 ms := &machineSets.Items[idx] 137 138 // Skip this MachineSet if its controller ref is not pointing to this MachineDeployment 139 if !metav1.IsControlledBy(ms, md) { 140 log.V(5).Info("Skipping MachineSet, controller ref does not match MachineDeployment", "machineset", ms.Name) 141 continue 142 } 143 144 selector, err := metav1.LabelSelectorAsSelector(&md.Spec.Selector) 145 if err != nil { 146 log.V(5).Info("Skipping MachineSet, failed to get label selector from spec selector", "machineset", ms.Name) 147 continue 148 } 149 // If a MachineDeployment with a nil or empty selector creeps in, it should match nothing, not everything. 150 if selector.Empty() { 151 log.V(5).Info("Skipping MachineSet as the selector is empty", "machineset", ms.Name) 152 continue 153 } 154 // Skip this MachineSet if selector does not match 155 if !selector.Matches(labels.Set(ms.Labels)) { 156 log.V(5).Info("Skipping MachineSet, label mismatch", "machineset", ms.Name) 157 continue 158 } 159 filtered = append(filtered, ms) 160 } 161 162 return filtered, nil 163 } 164 165 func revision(obj runtime.Object) (int64, error) { 166 acc, err := meta.Accessor(obj) 167 if err != nil { 168 return 0, err 169 } 170 v, ok := acc.GetAnnotations()[clusterv1.RevisionAnnotation] 171 if !ok { 172 return 0, nil 173 } 174 return strconv.ParseInt(v, 10, 64) 175 }