Skip to content

Commit 68900e9

Browse files
author
Phillip Wittrock
authored
Merge pull request kubernetes-sigs#2 from droot/machineset-adoption
added machinedeployment rollout and machineset adoption logic
2 parents 74997b5 + 0b268fb commit 68900e9

File tree

3 files changed

+390
-49
lines changed

3 files changed

+390
-49
lines changed

pkg/controller/machinedeployment/controller.go

+122-48
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package machinedeployment
1818

1919
import (
2020
"context"
21+
"fmt"
2122
"reflect"
2223

2324
"github.com/golang/glog"
@@ -26,7 +27,8 @@ import (
2627
"k8s.io/apimachinery/pkg/labels"
2728
"k8s.io/apimachinery/pkg/runtime"
2829
"k8s.io/apimachinery/pkg/types"
29-
clusterv1alpha1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
30+
"sigs.k8s.io/cluster-api/pkg/apis/cluster/common"
31+
"sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
3032
"sigs.k8s.io/controller-runtime/pkg/client"
3133
"sigs.k8s.io/controller-runtime/pkg/controller"
3234
"sigs.k8s.io/controller-runtime/pkg/handler"
@@ -36,35 +38,50 @@ import (
3638
)
3739

3840
// controllerKind contains the schema.GroupVersionKind for this controller type.
39-
var controllerKind = clusterv1alpha1.SchemeGroupVersion.WithKind("MachineDeployment")
41+
var controllerKind = v1alpha1.SchemeGroupVersion.WithKind("MachineDeployment")
4042

41-
// Add creates a new MachineDeployment Controller and adds it to the Manager with default RBAC.
42-
func Add(mgr manager.Manager) error {
43-
return add(mgr, newReconciler(mgr))
43+
// ReconcileMachineDeployment reconciles a MachineDeployment object
44+
type ReconcileMachineDeployment struct {
45+
client.Client
46+
scheme *runtime.Scheme
4447
}
4548

4649
// newReconciler returns a new reconcile.Reconciler
47-
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
50+
func newReconciler(mgr manager.Manager) *ReconcileMachineDeployment {
4851
return &ReconcileMachineDeployment{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
4952
}
5053

54+
// Add creates a new MachineDeployment Controller and adds it to the Manager with default RBAC.
55+
func Add(mgr manager.Manager) error {
56+
r := newReconciler(mgr)
57+
return add(mgr, newReconciler(mgr), r.MachineSetToDeployments)
58+
}
59+
5160
// add adds a new Controller to mgr with r as the reconcile.Reconciler
52-
func add(mgr manager.Manager, r reconcile.Reconciler) error {
61+
func add(mgr manager.Manager, r reconcile.Reconciler, mapFn handler.ToRequestsFunc) error {
5362
// Create a new controller
5463
c, err := controller.New("machinedeployment-controller", mgr, controller.Options{Reconciler: r})
5564
if err != nil {
5665
return err
5766
}
5867

5968
// Watch for changes to MachineDeployment
60-
err = c.Watch(&source.Kind{Type: &clusterv1alpha1.MachineDeployment{}}, &handler.EnqueueRequestForObject{})
69+
err = c.Watch(&source.Kind{Type: &v1alpha1.MachineDeployment{}}, &handler.EnqueueRequestForObject{})
6170
if err != nil {
6271
return err
6372
}
6473

6574
// Watch for changes to MachineSet and reconcile the owner MachineDeployment
66-
err = c.Watch(&source.Kind{Type: &clusterv1alpha1.MachineSet{}},
67-
&handler.EnqueueRequestForOwner{OwnerType: &clusterv1alpha1.MachineDeployment{}, IsController: true})
75+
err = c.Watch(&source.Kind{Type: &v1alpha1.MachineSet{}},
76+
&handler.EnqueueRequestForOwner{OwnerType: &v1alpha1.MachineDeployment{}, IsController: true})
77+
if err != nil {
78+
return err
79+
}
80+
81+
// Map MachineSet changes to MachineDeployment
82+
err = c.Watch(
83+
&source.Kind{Type: &v1alpha1.MachineSet{}},
84+
&handler.EnqueueRequestsFromMapFunc{ToRequests: mapFn})
6885
if err != nil {
6986
return err
7087
}
@@ -74,18 +91,49 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
7491

7592
var _ reconcile.Reconciler = &ReconcileMachineDeployment{}
7693

77-
// ReconcileMachineDeployment reconciles a MachineDeployment object
78-
type ReconcileMachineDeployment struct {
79-
client.Client
80-
scheme *runtime.Scheme
94+
func (r *ReconcileMachineDeployment) getMachineSetsForDeployment(d *v1alpha1.MachineDeployment) ([]*v1alpha1.MachineSet, error) {
95+
// List all MachineSets to find those we own but that no longer match our
96+
// selector.
97+
machineSets := &v1alpha1.MachineSetList{}
98+
err := r.List(context.Background(), client.InNamespace(d.Namespace), machineSets)
99+
if err != nil {
100+
return nil, err
101+
}
102+
103+
// TODO: flush out machine set adoption.
104+
105+
var filteredMS []*v1alpha1.MachineSet
106+
for idx, _ := range machineSets.Items {
107+
ms := &machineSets.Items[idx]
108+
if metav1.GetControllerOf(ms) == nil || (metav1.GetControllerOf(ms) != nil && !metav1.IsControlledBy(ms, d)) {
109+
glog.V(4).Infof("%s not controlled by %v", ms.Name, d.Name)
110+
continue
111+
}
112+
selector, err := metav1.LabelSelectorAsSelector(&d.Spec.Selector)
113+
if err != nil {
114+
glog.Errorf("Skipping machineset %v, failed to get label selector from spec selector.", ms.Name)
115+
continue
116+
}
117+
// If a deployment with a nil or empty selector creeps in, it should match nothing, not everything.
118+
if selector.Empty() {
119+
glog.Warningf("Skipping machineset %v as the selector is empty.", ms.Name)
120+
continue
121+
}
122+
if !selector.Matches(labels.Set(ms.Labels)) {
123+
glog.V(4).Infof("Skipping machineset %v, label mismatch.", ms.Name)
124+
continue
125+
}
126+
filteredMS = append(filteredMS, ms)
127+
}
128+
return filteredMS, nil
81129
}
82130

83131
// Reconcile reads that state of the cluster for a MachineDeployment object and makes changes based on the state read
84132
// and what is in the MachineDeployment.Spec
85133
// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machinedeployments,verbs=get;list;watch;create;update;patch;delete
86134
func (r *ReconcileMachineDeployment) Reconcile(request reconcile.Request) (reconcile.Result, error) {
87135
// Fetch the MachineDeployment instance
88-
d := &clusterv1alpha1.MachineDeployment{}
136+
d := &v1alpha1.MachineDeployment{}
89137
err := r.Get(context.TODO(), request.NamespacedName, d)
90138
if err != nil {
91139
if errors.IsNotFound(err) {
@@ -96,6 +144,7 @@ func (r *ReconcileMachineDeployment) Reconcile(request reconcile.Request) (recon
96144
// Error reading the object - requeue the request.
97145
return reconcile.Result{}, err
98146
}
147+
99148
everything := metav1.LabelSelector{}
100149
if reflect.DeepEqual(d.Spec.Selector, &everything) {
101150
if d.Status.ObservedGeneration < d.Generation {
@@ -126,55 +175,50 @@ func (r *ReconcileMachineDeployment) Reconcile(request reconcile.Request) (recon
126175
return reconcile.Result{}, r.sync(d, msList, machineMap)
127176
}
128177

129-
return reconcile.Result{}, nil
178+
switch d.Spec.Strategy.Type {
179+
case common.RollingUpdateMachineDeploymentStrategyType:
180+
return reconcile.Result{}, r.rolloutRolling(d, msList, machineMap)
181+
}
182+
183+
return reconcile.Result{}, fmt.Errorf("unexpected deployment strategy type: %s", d.Spec.Strategy.Type)
130184
}
131185

132-
func (r *ReconcileMachineDeployment) getMachineSetsForDeployment(d *clusterv1alpha1.MachineDeployment) ([]*clusterv1alpha1.MachineSet, error) {
133-
// List all MachineSets to find those we own but that no longer match our
134-
// selector.
135-
machineSets := &clusterv1alpha1.MachineSetList{}
136-
err := r.List(context.Background(),
137-
// TODO(droot): check if we need to specify labels for fetching
138-
// everything ?
139-
// client.InNamespace(d.Namespace).MatchingLabels(labels.Everything()),
140-
client.InNamespace(d.Namespace), machineSets)
141-
if err != nil {
142-
return nil, err
186+
// getMachineDeploymentsForMachineSet returns a list of Deployments that potentially
187+
// match a MachineSet.
188+
func (r *ReconcileMachineDeployment) getMachineDeploymentsForMachineSet(ms *v1alpha1.MachineSet) []*v1alpha1.MachineDeployment {
189+
if len(ms.Labels) == 0 {
190+
glog.Warningf("no machine deployments found for MachineSet %v because it has no labels", ms.Name)
191+
return nil
143192
}
144193

145-
// TODO: flush out machine set adoption.
194+
dList := &v1alpha1.MachineDeploymentList{}
195+
err := r.Client.List(context.Background(), client.InNamespace(ms.Namespace), dList)
196+
if err != nil {
197+
glog.Warningf("failed to list machine deployments, %v", err)
198+
return nil
199+
}
146200

147-
var filteredMS []*clusterv1alpha1.MachineSet
148-
for idx := range machineSets.Items {
149-
ms := &machineSets.Items[idx]
150-
if metav1.GetControllerOf(ms) == nil || (metav1.GetControllerOf(ms) != nil && !metav1.IsControlledBy(ms, d)) {
151-
glog.V(4).Infof("%s not controlled by %v", ms.Name, d.Name)
152-
continue
153-
}
201+
var deployments []*v1alpha1.MachineDeployment
202+
for idx, d := range dList.Items {
154203
selector, err := metav1.LabelSelectorAsSelector(&d.Spec.Selector)
155204
if err != nil {
156-
glog.Errorf("Skipping machineset %v, failed to get label selector from spec selector.", ms.Name)
157205
continue
158206
}
159207
// If a deployment with a nil or empty selector creeps in, it should match nothing, not everything.
160-
if selector.Empty() {
161-
glog.Warningf("Skipping machineset %v as the selector is empty.", ms.Name)
162-
continue
163-
}
164-
if !selector.Matches(labels.Set(ms.Labels)) {
165-
glog.V(4).Infof("Skipping machineset %v, label mismatch.", ms.Name)
208+
if selector.Empty() || !selector.Matches(labels.Set(ms.Labels)) {
166209
continue
167210
}
168-
filteredMS = append(filteredMS, ms)
211+
deployments = append(deployments, &dList.Items[idx])
169212
}
170-
return filteredMS, nil
213+
214+
return deployments
171215
}
172216

173217
// getMachineMapForDeployment returns the Machines managed by a Deployment.
174218
//
175219
// It returns a map from MachineSet UID to a list of Machines controlled by that MS,
176220
// according to the Machine's ControllerRef.
177-
func (r *ReconcileMachineDeployment) getMachineMapForDeployment(d *clusterv1alpha1.MachineDeployment, msList []*clusterv1alpha1.MachineSet) (map[types.UID]*clusterv1alpha1.MachineList, error) {
221+
func (r *ReconcileMachineDeployment) getMachineMapForDeployment(d *v1alpha1.MachineDeployment, msList []*v1alpha1.MachineSet) (map[types.UID]*v1alpha1.MachineList, error) {
178222
// TODO(droot): double check if previous selector maps correctly to new one.
179223
// _, err := metav1.LabelSelectorAsSelector(&d.Spec.Selector)
180224

@@ -183,15 +227,15 @@ func (r *ReconcileMachineDeployment) getMachineMapForDeployment(d *clusterv1alph
183227
if err != nil {
184228
return nil, err
185229
}
186-
machines := &clusterv1alpha1.MachineList{}
230+
machines := &v1alpha1.MachineList{}
187231
err = r.List(context.Background(), client.InNamespace(d.Namespace).MatchingLabels(selector), machines)
188232
if err != nil {
189233
return nil, err
190234
}
191235
// Group Machines by their controller (if it's in msList).
192-
machineMap := make(map[types.UID]*clusterv1alpha1.MachineList, len(msList))
236+
machineMap := make(map[types.UID]*v1alpha1.MachineList, len(msList))
193237
for _, ms := range msList {
194-
machineMap[ms.UID] = &clusterv1alpha1.MachineList{}
238+
machineMap[ms.UID] = &v1alpha1.MachineList{}
195239
}
196240
for idx := range machines.Items {
197241
machine := &machines.Items[idx]
@@ -208,3 +252,33 @@ func (r *ReconcileMachineDeployment) getMachineMapForDeployment(d *clusterv1alph
208252
}
209253
return machineMap, nil
210254
}
255+
256+
func (r *ReconcileMachineDeployment) MachineSetToDeployments(o handler.MapObject) []reconcile.Request {
257+
result := []reconcile.Request{}
258+
ms := &v1alpha1.MachineSet{}
259+
key := client.ObjectKey{Namespace: o.Meta.GetNamespace(), Name: o.Meta.GetNamespace()}
260+
err := r.Client.Get(context.Background(), key, ms)
261+
if err != nil {
262+
glog.Errorf("Unable to retrieve Machineset %v from store: %v", key, err)
263+
return nil
264+
}
265+
266+
for _, ref := range ms.ObjectMeta.OwnerReferences {
267+
if ref.Controller != nil && *ref.Controller {
268+
return result
269+
}
270+
}
271+
272+
mds := r.getMachineDeploymentsForMachineSet(ms)
273+
if len(mds) == 0 {
274+
glog.V(4).Infof("Found no machine set for machine: %v", ms.Name)
275+
return nil
276+
}
277+
278+
for _, md := range mds {
279+
result = append(result, reconcile.Request{
280+
NamespacedName: client.ObjectKey{Namespace: md.Namespace, Name: md.Name}})
281+
}
282+
283+
return result
284+
}

pkg/controller/machinedeployment/machinedeployment_controller_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ func TestReconcile(t *testing.T) {
5050
c = mgr.GetClient()
5151

5252
recFn, requests := SetupTestReconcile(newReconciler(mgr))
53-
g.Expect(add(mgr, recFn)).NotTo(gomega.HaveOccurred())
53+
r := &ReconcileMachineDeployment{}
54+
g.Expect(add(mgr, recFn, r.MachineSetToDeployments)).NotTo(gomega.HaveOccurred())
5455
defer close(StartTestManager(mgr, g))
5556

5657
// Create the MachineDeployment object and expect the Reconcile and Deployment to be created

0 commit comments

Comments
 (0)