1+ /*
2+ Copyright 2023.
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+
117package controlplane
218
319import (
@@ -16,6 +32,7 @@ import (
1632 "k8s.io/apimachinery/pkg/types"
1733 "k8s.io/client-go/kubernetes"
1834 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
35+ "sigs.k8s.io/cluster-api/controllers/external"
1936 "sigs.k8s.io/cluster-api/util/collections"
2037 ctrl "sigs.k8s.io/controller-runtime"
2138 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -24,6 +41,10 @@ import (
2441 cpv1beta1 "github.com/k0sproject/k0smotron/api/controlplane/v1beta1"
2542)
2643
44+ const (
45+ etcdMemberConditionTypeJoined = "Joined"
46+ )
47+
2748func (c * K0sController ) createMachine (ctx context.Context , name string , cluster * clusterv1.Cluster , kcp * cpv1beta1.K0sControlPlane , infraRef corev1.ObjectReference , failureDomain * string ) (* clusterv1.Machine , error ) {
2849 machine , err := c .generateMachine (ctx , name , cluster , kcp , infraRef , failureDomain )
2950 if err != nil {
@@ -100,6 +121,21 @@ func (c *K0sController) generateMachine(_ context.Context, name string, cluster
100121 return machine , nil
101122}
102123
124+ func (c * K0sController ) getInfraMachines (ctx context.Context , machines collections.Machines ) (map [string ]* unstructured.Unstructured , error ) {
125+ result := map [string ]* unstructured.Unstructured {}
126+ for _ , m := range machines {
127+ infraMachine , err := external .Get (ctx , c .Client , & m .Spec .InfrastructureRef , m .Namespace )
128+ if err != nil {
129+ if apierrors .IsNotFound (err ) {
130+ continue
131+ }
132+ return nil , fmt .Errorf ("failed to retrieve infra machine for machine object %s: %w" , m .Name , err )
133+ }
134+ result [m .Name ] = infraMachine
135+ }
136+ return result , nil
137+ }
138+
103139func (c * K0sController ) createMachineFromTemplate (ctx context.Context , name string , cluster * clusterv1.Cluster , kcp * cpv1beta1.K0sControlPlane ) (* unstructured.Unstructured , error ) {
104140 machineFromTemplate , err := c .generateMachineFromTemplate (ctx , name , cluster , kcp )
105141 if err != nil {
@@ -211,15 +247,62 @@ func (c *K0sController) generateMachineFromTemplate(ctx context.Context, name st
211247 return machine , nil
212248}
213249
250+ func matchesTemplateClonedFrom (infraMachines map [string ]* unstructured.Unstructured , kcp * cpv1beta1.K0sControlPlane , machine * clusterv1.Machine ) bool {
251+ if machine == nil {
252+ return false
253+ }
254+ infraMachine , found := infraMachines [machine .Name ]
255+ if ! found {
256+ return false
257+ }
258+
259+ clonedFromName := infraMachine .GetAnnotations ()[clusterv1 .TemplateClonedFromNameAnnotation ]
260+ clonedFromGroupKind := infraMachine .GetAnnotations ()[clusterv1 .TemplateClonedFromGroupKindAnnotation ]
261+
262+ return clonedFromName == kcp .Spec .MachineTemplate .InfrastructureRef .Name &&
263+ clonedFromGroupKind == kcp .Spec .MachineTemplate .InfrastructureRef .GroupVersionKind ().GroupKind ().String ()
264+ }
265+
266+ func (c * K0sController ) checkMachineLeft (ctx context.Context , name string , clientset * kubernetes.Clientset ) (bool , error ) {
267+ var etcdMember unstructured.Unstructured
268+ err := clientset .RESTClient ().
269+ Get ().
270+ AbsPath ("/apis/etcd.k0sproject.io/v1beta1/etcdmembers/" + name ).
271+ Do (ctx ).
272+ Into (& etcdMember )
273+
274+ if err != nil {
275+ if apierrors .IsNotFound (err ) {
276+ return true , nil
277+ }
278+ return false , fmt .Errorf ("error getting etcd member: %w" , err )
279+ }
280+
281+ conditions , _ , err := unstructured .NestedSlice (etcdMember .Object , "status" , "conditions" )
282+ if err != nil {
283+ return false , fmt .Errorf ("error getting etcd member conditions: %w" , err )
284+ }
285+
286+ for _ , condition := range conditions {
287+ conditionMap := condition .(map [string ]interface {})
288+ if conditionMap ["type" ] == etcdMemberConditionTypeJoined && conditionMap ["status" ] == "False" {
289+ return true , nil
290+ }
291+ }
292+ return false , nil
293+ }
294+
214295func (c * K0sController ) markChildControlNodeToLeave (ctx context.Context , name string , clientset * kubernetes.Clientset ) error {
215296 if clientset == nil {
216297 return nil
217298 }
299+
218300 logger := log .FromContext (ctx ).WithValues ("controlNode" , name )
301+
219302 err := clientset .RESTClient ().
220303 Patch (types .MergePatchType ).
221304 AbsPath ("/apis/etcd.k0sproject.io/v1beta1/etcdmembers/" + name ).
222- Body ([]byte (`{"spec":{"leave":true}}` )).
305+ Body ([]byte (`{"spec":{"leave":true}, "metadata": {"annotations": {"k0smotron.io/marked-to-leave-at": "` + time . Now (). String () + `"}} }` )).
223306 Do (ctx ).
224307 Error ()
225308 if err != nil {
0 commit comments