|
1 | 1 | package framework |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
4 | 5 | "fmt" |
| 6 | + "time" |
5 | 7 |
|
6 | 8 | . "github.com/onsi/ginkgo/v2" |
7 | 9 | . "github.com/onsi/gomega" |
8 | 10 | corev1 "k8s.io/api/core/v1" |
9 | 11 | apierrors "k8s.io/apimachinery/pkg/api/errors" |
10 | 12 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 13 | + "k8s.io/apimachinery/pkg/runtime/schema" |
| 14 | + "k8s.io/apimachinery/pkg/util/wait" |
| 15 | + "k8s.io/client-go/discovery" |
| 16 | + "k8s.io/client-go/dynamic" |
| 17 | + "k8s.io/client-go/rest" |
| 18 | + "k8s.io/client-go/scale" |
| 19 | + "k8s.io/klog" |
11 | 20 | "k8s.io/utils/pointer" |
12 | 21 | clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" |
13 | 22 | "sigs.k8s.io/controller-runtime/pkg/client" |
| 23 | + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" |
| 24 | + "sigs.k8s.io/controller-runtime/pkg/client/config" |
14 | 25 | ) |
15 | 26 |
|
16 | 27 | type machineSetParams struct { |
@@ -188,3 +199,106 @@ func GetMachinesFromMachineSet(cl client.Client, machineSet *clusterv1.MachineSe |
188 | 199 | } |
189 | 200 | return machinesForSet, nil |
190 | 201 | } |
| 202 | + |
| 203 | +// GetLatestMachineFromMachineSet returns the new created machine by a given machineSet. |
| 204 | +func GetLatestMachineFromMachineSet(cl client.Client, machineSet *clusterv1.MachineSet) (*clusterv1.Machine, error) { |
| 205 | + machines, err := GetMachinesFromMachineSet(cl, machineSet) |
| 206 | + if err != nil { |
| 207 | + return nil, fmt.Errorf("error getting machines: %w", err) |
| 208 | + } |
| 209 | + |
| 210 | + var machine *clusterv1.Machine |
| 211 | + |
| 212 | + newest := time.Date(2020, 0, 1, 12, 0, 0, 0, time.UTC) |
| 213 | + |
| 214 | + for key := range machines { |
| 215 | + time := machines[key].CreationTimestamp.Time |
| 216 | + if time.After(newest) { |
| 217 | + newest = time |
| 218 | + machine = machines[key] |
| 219 | + } |
| 220 | + } |
| 221 | + |
| 222 | + return machine, nil |
| 223 | +} |
| 224 | + |
| 225 | +// DeleteMachines deletes the specified machines and returns an error on failure. |
| 226 | +func DeleteMachines(cl client.Client, machines ...*clusterv1.Machine) error { |
| 227 | + return wait.PollUntilContextTimeout(ctx, RetryShort, time.Minute, true, func(ctx context.Context) (bool, error) { |
| 228 | + for _, machine := range machines { |
| 229 | + if err := cl.Delete(ctx, machine); err != nil { |
| 230 | + klog.Errorf("Error querying api for machine object %q: %v, retrying...", machine.Name, err) |
| 231 | + return false, err |
| 232 | + } |
| 233 | + } |
| 234 | + |
| 235 | + return true, nil |
| 236 | + }) |
| 237 | +} |
| 238 | + |
| 239 | +// WaitForMachinesDeleted polls until the given Machines are not found. |
| 240 | +func WaitForMachinesDeleted(cl client.Client, machines ...*clusterv1.Machine) { |
| 241 | + Eventually(func() bool { |
| 242 | + for _, m := range machines { |
| 243 | + if err := cl.Get(context.Background(), client.ObjectKey{ |
| 244 | + Name: m.GetName(), |
| 245 | + Namespace: m.GetNamespace(), |
| 246 | + }, &clusterv1.Machine{}); !apierrors.IsNotFound(err) { |
| 247 | + return false // Not deleted, or other error. |
| 248 | + } |
| 249 | + } |
| 250 | + |
| 251 | + return true // Everything was deleted. |
| 252 | + }, WaitLong, RetryMedium).Should(BeTrue(), "error encountered while waiting for Machines to be deleted.") |
| 253 | +} |
| 254 | + |
| 255 | +// ScaleMachineSet scales a machineSet with a given name to the given number of replicas. |
| 256 | +func ScaleMachineSet(name string, replicas int) error { |
| 257 | + scaleClient, err := getScaleClient() |
| 258 | + if err != nil { |
| 259 | + return fmt.Errorf("error calling getScaleClient %w", err) |
| 260 | + } |
| 261 | + |
| 262 | + scale, err := scaleClient.Scales(CAPINamespace).Get(ctx, schema.GroupResource{Group: "cluster.x-k8s.io", Resource: "MachineSet"}, name, metav1.GetOptions{}) |
| 263 | + if err != nil { |
| 264 | + return fmt.Errorf("error calling scaleClient.Scales get: %w", err) |
| 265 | + } |
| 266 | + |
| 267 | + scaleUpdate := scale.DeepCopy() |
| 268 | + scaleUpdate.Spec.Replicas = int32(replicas) |
| 269 | + |
| 270 | + _, err = scaleClient.Scales(CAPINamespace).Update(ctx, schema.GroupResource{Group: "cluster.x-k8s.io", Resource: "MachineSet"}, scaleUpdate, metav1.UpdateOptions{}) |
| 271 | + if err != nil { |
| 272 | + return fmt.Errorf("error calling scaleClient.Scales update: %w", err) |
| 273 | + } |
| 274 | + |
| 275 | + return nil |
| 276 | +} |
| 277 | + |
| 278 | +// getScaleClient returns a ScalesGetter object to manipulate scale subresources. |
| 279 | +func getScaleClient() (scale.ScalesGetter, error) { |
| 280 | + cfg, err := config.GetConfig() |
| 281 | + if err != nil { |
| 282 | + return nil, fmt.Errorf("error getting config %w", err) |
| 283 | + } |
| 284 | + |
| 285 | + httpClient, err := rest.HTTPClientFor(cfg) |
| 286 | + if err != nil { |
| 287 | + return nil, fmt.Errorf("error calling rest.HTTPClientFor %w", err) |
| 288 | + } |
| 289 | + |
| 290 | + mapper, err := apiutil.NewDynamicRESTMapper(cfg, httpClient) |
| 291 | + if err != nil { |
| 292 | + return nil, fmt.Errorf("error calling NewDiscoveryRESTMapper %w", err) |
| 293 | + } |
| 294 | + |
| 295 | + discovery := discovery.NewDiscoveryClientForConfigOrDie(cfg) |
| 296 | + scaleKindResolver := scale.NewDiscoveryScaleKindResolver(discovery) |
| 297 | + |
| 298 | + scaleClient, err := scale.NewForConfig(cfg, mapper, dynamic.LegacyAPIPathResolverFunc, scaleKindResolver) |
| 299 | + if err != nil { |
| 300 | + return nil, fmt.Errorf("error calling building scale client %w", err) |
| 301 | + } |
| 302 | + |
| 303 | + return scaleClient, nil |
| 304 | +} |
0 commit comments