Skip to content

Commit 050b12e

Browse files
Merge pull request #1287 from theobarberbany/featuregated-machineset-paused-condition
OCPCLOUD-2565: Adds paused condition to MachineSet
2 parents ed764c1 + 6fc9d1d commit 050b12e

File tree

13 files changed

+267
-44
lines changed

13 files changed

+267
-44
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ require (
2929
k8s.io/kubectl v0.30.0
3030
k8s.io/utils v0.0.0-20240310230437-4693a0247e57
3131
sigs.k8s.io/cluster-api v1.6.1
32-
sigs.k8s.io/controller-runtime v0.18.3
32+
sigs.k8s.io/controller-runtime v0.18.4
3333
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240522175850-2e9781e9fc60
3434
sigs.k8s.io/yaml v1.4.0
3535
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,8 +778,8 @@ mvdan.cc/unparam v0.0.0-20240427195214-063aff900ca1 h1:Nykk7fggxChwLK4rUPYESzeIw
778778
mvdan.cc/unparam v0.0.0-20240427195214-063aff900ca1/go.mod h1:ZzZjEpJDOmx8TdVU6umamY3Xy0UAQUI2DHbf05USVbI=
779779
sigs.k8s.io/cluster-api v1.6.1 h1:I34p/fwgRlEhs+o9cUhKXDwNNfPS3no0yJsd2bJyQVc=
780780
sigs.k8s.io/cluster-api v1.6.1/go.mod h1:DaxwruDvSaEYq5q6FREDaGzX6UsAVUCA99Sp8vfMHyQ=
781-
sigs.k8s.io/controller-runtime v0.18.3 h1:B5Wmmo8WMWK7izei+2LlXLVDGzMwAHBNLX68lwtlSR4=
782-
sigs.k8s.io/controller-runtime v0.18.3/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg=
781+
sigs.k8s.io/controller-runtime v0.18.4 h1:87+guW1zhvuPLh1PHybKdYFLU0YJp4FhJRmiHvm5BZw=
782+
sigs.k8s.io/controller-runtime v0.18.4/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg=
783783
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240522175850-2e9781e9fc60 h1:ihaeBTCFuEYPL1T1/FqAavDY7z5UcKSnWpnb+I3DYeM=
784784
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240522175850-2e9781e9fc60/go.mod h1:4+4tM2Es0ycqPedATtzPer5RTrUq3Xab59BYogt0mCE=
785785
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=

pkg/controller/machine/machine_controller_test.go

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ import (
3131
"github.com/openshift/cluster-control-plane-machine-set-operator/test/e2e/framework"
3232
testutils "github.com/openshift/machine-api-operator/pkg/util/testing"
3333

34-
apierrs "k8s.io/apimachinery/pkg/api/errors"
35-
utilnet "k8s.io/apimachinery/pkg/util/net"
3634
"sigs.k8s.io/controller-runtime/pkg/client"
3735
"sigs.k8s.io/controller-runtime/pkg/envtest/komega"
3836
"sigs.k8s.io/controller-runtime/pkg/manager"
@@ -144,11 +142,6 @@ var _ = Describe("Machine Reconciler", func() {
144142
HaveField("Status", Equal(corev1.ConditionTrue)),
145143
))))
146144

147-
By("Transitioning the AuthoritativeAPI though 'Migrating' to MachineAPI")
148-
Eventually(k.UpdateStatus(instance, func() {
149-
instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMigrating
150-
})).Should(Succeed())
151-
152145
// The condition should remain true whilst transitioning through 'Migrating'
153146
// Run this in a goroutine so we don't block
154147

@@ -164,7 +157,7 @@ var _ = Describe("Machine Reconciler", func() {
164157

165158
localInstance := instanceCopy.DeepCopy()
166159
if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil {
167-
return g.Expect(err).Should(WithTransform(isRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err)
160+
return g.Expect(err).Should(WithTransform(testutils.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err)
168161
}
169162

170163
return g.Expect(localInstance.Status.Conditions).Should(ContainElement(SatisfyAll(
@@ -179,13 +172,18 @@ var _ = Describe("Machine Reconciler", func() {
179172

180173
localInstance := instanceCopy.DeepCopy()
181174
if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil {
182-
return g.Expect(err).Should(WithTransform(isRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err)
175+
return g.Expect(err).Should(WithTransform(testutils.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err)
183176
}
184177

185-
return g.Expect(localInstance.Status.AuthoritativeAPI).ToNot(Equal(machinev1.MachineAuthorityMachineAPI))
178+
return g.Expect(localInstance.Status.AuthoritativeAPI).To(Equal(machinev1.MachineAuthorityMachineAPI))
186179
})
187180
}()
188181

182+
By("Transitioning the AuthoritativeAPI though 'Migrating' to MachineAPI")
183+
Eventually(k.UpdateStatus(instance, func() {
184+
instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMigrating
185+
})).Should(Succeed())
186+
189187
By("Updating the AuthoritativeAPI from Migrating to MachineAPI")
190188
Eventually(k.UpdateStatus(instance, func() {
191189
instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMachineAPI
@@ -220,21 +218,3 @@ func cleanResources(namespace string) error {
220218

221219
return nil
222220
}
223-
224-
// isRetryableAPIError returns whether an API error is retryable or not.
225-
// inspired by: k8s.io/kubernetes/test/utils.
226-
func isRetryableAPIError(err error) bool {
227-
// These errors may indicate a transient error that we can retry in tests.
228-
if apierrs.IsInternalError(err) || apierrs.IsTimeout(err) || apierrs.IsServerTimeout(err) ||
229-
apierrs.IsTooManyRequests(err) || utilnet.IsProbableEOF(err) || utilnet.IsConnectionReset(err) ||
230-
utilnet.IsHTTP2ConnectionLost(err) {
231-
return true
232-
}
233-
234-
// If the error sends the Retry-After header, we respect it as an explicit confirmation we should retry.
235-
if _, shouldRetry := apierrs.SuggestsClientDelay(err); shouldRetry {
236-
return true
237-
}
238-
239-
return false
240-
}

pkg/controller/machineset/controller.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ import (
2525
"sync"
2626
"time"
2727

28+
openshiftfeatures "github.com/openshift/api/features"
2829
machinev1 "github.com/openshift/api/machine/v1beta1"
2930
"github.com/openshift/machine-api-operator/pkg/util"
31+
"github.com/openshift/machine-api-operator/pkg/util/conditions"
3032
corev1 "k8s.io/api/core/v1"
3133
apierrors "k8s.io/apimachinery/pkg/api/errors"
3234
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -45,6 +47,14 @@ import (
4547
"sigs.k8s.io/controller-runtime/pkg/source"
4648
)
4749

50+
const (
51+
PausedCondition machinev1.ConditionType = "Paused"
52+
53+
PausedConditionReason = "AuthoritativeAPI is not set to MachineAPI"
54+
55+
NotPausedConditionReason = "AuthoritativeAPI is set to MachineAPI"
56+
)
57+
4858
var (
4959
controllerKind = machinev1.SchemeGroupVersion.WithKind("MachineSet")
5060

@@ -170,6 +180,42 @@ func (r *ReconcileMachineSet) Reconcile(ctx context.Context, request reconcile.R
170180
return reconcile.Result{}, nil
171181
}
172182

183+
if r.gate.Enabled(featuregate.Feature(openshiftfeatures.FeatureGateMachineAPIMigration)) {
184+
machineSetCopy := machineSet.DeepCopy()
185+
// Check Status.AuthoritativeAPI. If it's not set to MachineAPI. Set the
186+
// paused condition true and return early.
187+
//
188+
// Once we have a webhook, we want to remove the check that the AuthoritativeAPI
189+
// field is populated.
190+
if machineSet.Status.AuthoritativeAPI != "" &&
191+
machineSet.Status.AuthoritativeAPI != machinev1.MachineAuthorityMachineAPI {
192+
conditions.Set(machineSetCopy, conditions.TrueConditionWithReason(
193+
PausedCondition,
194+
PausedConditionReason,
195+
"The AuthoritativeAPI is set to %s", machineSet.Status.AuthoritativeAPI,
196+
))
197+
198+
_, err := updateMachineSetStatus(r.Client, machineSet, machineSetCopy.Status)
199+
if err != nil {
200+
klog.Errorf("%v: error updating status: %v", machineSet.Name, err)
201+
}
202+
203+
klog.Infof("%v: machine is paused, taking no further action", machineSet.Name)
204+
return reconcile.Result{}, nil
205+
}
206+
207+
conditions.Set(machineSetCopy, conditions.FalseCondition(
208+
PausedCondition,
209+
NotPausedConditionReason,
210+
"The AuthoritativeAPI is set to %s", string(machineSet.Status.AuthoritativeAPI),
211+
))
212+
_, err := updateMachineSetStatus(r.Client, machineSet, machineSetCopy.Status)
213+
if err != nil {
214+
klog.Errorf("%v: error updating status: %v", machineSet.Name, err)
215+
}
216+
klog.Infof("%v: setting paused to false and continuing reconcile", machineSet.Name)
217+
}
218+
173219
result, err := r.reconcile(ctx, machineSet)
174220
if err != nil {
175221
klog.Errorf("Failed to reconcile MachineSet %q: %v", request.NamespacedName, err)

pkg/controller/machineset/controller_test.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ func TestMachineSetToMachines(t *testing.T) {
5757
},
5858
},
5959
},
60+
Status: machinev1.MachineSetStatus{
61+
AuthoritativeAPI: machinev1.MachineAuthorityMachineAPI,
62+
},
6063
},
6164
},
6265
}
@@ -153,7 +156,11 @@ func TestShouldExcludeMachine(t *testing.T) {
153156
expected bool
154157
}{
155158
{
156-
machineSet: machinev1.MachineSet{},
159+
machineSet: machinev1.MachineSet{
160+
Status: machinev1.MachineSetStatus{
161+
AuthoritativeAPI: machinev1.MachineAuthorityMachineAPI,
162+
},
163+
},
157164
machine: machinev1.Machine{
158165
ObjectMeta: metav1.ObjectMeta{
159166
Name: "withNoMatchingOwnerRef",
@@ -178,6 +185,9 @@ func TestShouldExcludeMachine(t *testing.T) {
178185
},
179186
},
180187
},
188+
Status: machinev1.MachineSetStatus{
189+
AuthoritativeAPI: machinev1.MachineAuthorityMachineAPI,
190+
},
181191
},
182192
machine: machinev1.Machine{
183193
ObjectMeta: metav1.ObjectMeta{
@@ -191,7 +201,11 @@ func TestShouldExcludeMachine(t *testing.T) {
191201
expected: false,
192202
},
193203
{
194-
machineSet: machinev1.MachineSet{},
204+
machineSet: machinev1.MachineSet{
205+
Status: machinev1.MachineSetStatus{
206+
AuthoritativeAPI: machinev1.MachineAuthorityMachineAPI,
207+
},
208+
},
195209
machine: machinev1.Machine{
196210
ObjectMeta: metav1.ObjectMeta{
197211
Name: "withDeletionTimestamp",
@@ -225,6 +239,9 @@ func TestAdoptOrphan(t *testing.T) {
225239
ObjectMeta: metav1.ObjectMeta{
226240
Name: "adoptOrphanMachine",
227241
},
242+
Status: machinev1.MachineSetStatus{
243+
AuthoritativeAPI: machinev1.MachineAuthorityMachineAPI,
244+
},
228245
}
229246
controller := true
230247
blockOwnerDeletion := true
@@ -306,7 +323,11 @@ var _ = Describe("MachineSet Reconcile", func() {
306323
},
307324
Spec: machinev1.MachineSetSpec{
308325
Template: machinev1.MachineTemplateSpec{},
309-
}}
326+
},
327+
Status: machinev1.MachineSetStatus{
328+
AuthoritativeAPI: machinev1.MachineAuthorityMachineAPI,
329+
},
330+
}
310331

311332
r.Client = fake.NewClientBuilder().WithScheme(scheme.Scheme).WithRuntimeObjects(ms).WithStatusSubresource(&machinev1.MachineSet{}).Build()
312333
})
@@ -335,6 +356,9 @@ var _ = Describe("MachineSet Reconcile", func() {
335356
Spec: machinev1.MachineSetSpec{
336357
Replicas: &replicas,
337358
},
359+
Status: machinev1.MachineSetStatus{
360+
AuthoritativeAPI: machinev1.MachineAuthorityMachineAPI,
361+
},
338362
}
339363

340364
ms.Spec.Selector.MatchLabels = map[string]string{

pkg/controller/machineset/machineset_controller_suite_test.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ package machineset
1919
import (
2020
"context"
2121
"flag"
22+
"log"
23+
"os"
2224
"path/filepath"
2325
"testing"
2426
"time"
2527

2628
. "github.com/onsi/ginkgo/v2"
2729
. "github.com/onsi/gomega"
30+
configv1 "github.com/openshift/api/config/v1"
2831
machinev1 "github.com/openshift/api/machine/v1beta1"
32+
2933
"k8s.io/client-go/kubernetes/scheme"
3034
"k8s.io/client-go/rest"
3135
"k8s.io/klog/v2/textlogger"
@@ -38,9 +42,6 @@ func init() {
3842
textLoggerConfig := textlogger.NewConfig()
3943
textLoggerConfig.AddFlags(flag.CommandLine)
4044
logf.SetLogger(textlogger.NewLogger(textLoggerConfig))
41-
42-
// Register required object kinds with global scheme.
43-
_ = machinev1.Install(scheme.Scheme)
4445
}
4546

4647
const (
@@ -62,18 +63,38 @@ func TestMachinesetController(t *testing.T) {
6263

6364
var _ = BeforeSuite(func(ctx SpecContext) {
6465
By("bootstrapping test environment")
66+
6567
testEnv = &envtest.Environment{
66-
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "install")},
68+
69+
CRDInstallOptions: envtest.CRDInstallOptions{
70+
Paths: []string{
71+
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "machine", "v1beta1", "zz_generated.crd-manifests", "0000_10_machine-api_01_machines-CustomNoUpgrade.crd.yaml"),
72+
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "machine", "v1beta1", "zz_generated.crd-manifests", "0000_10_machine-api_01_machinesets-CustomNoUpgrade.crd.yaml"),
73+
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1", "zz_generated.crd-manifests", "0000_00_cluster-version-operator_01_clusterversions-CustomNoUpgrade.crd.yaml"),
74+
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1", "zz_generated.crd-manifests", "0000_10_config-operator_01_featuregates.crd.yaml"),
75+
},
76+
},
6777
}
6878

6979
var err error
7080
cfg, err = testEnv.Start()
7181
Expect(err).ToNot(HaveOccurred())
7282
Expect(cfg).ToNot(BeNil())
73-
7483
})
7584

7685
var _ = AfterSuite(func() {
7786
By("tearing down the test environment")
7887
Expect(testEnv.Stop()).To(Succeed())
7988
})
89+
90+
func TestMain(m *testing.M) {
91+
// Register required object kinds with global scheme.
92+
if err := machinev1.Install(scheme.Scheme); err != nil {
93+
log.Fatalf("cannot add scheme: %v", err)
94+
}
95+
if err := configv1.Install(scheme.Scheme); err != nil {
96+
log.Fatalf("cannot add scheme: %v", err)
97+
}
98+
exitVal := m.Run()
99+
os.Exit(exitVal)
100+
}

0 commit comments

Comments
 (0)