• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubevirt / kubevirt / 233433ab-b0ba-4540-82e9-5448c4285cf5

26 Jun 2025 02:54PM UTC coverage: 70.492%. Remained the same
233433ab-b0ba-4540-82e9-5448c4285cf5

push

prow

web-flow
Merge pull request #14882 from awels/migration-controller-decentralize

Decentralized live migration core functionality.

408 of 936 new or added lines in 18 files covered. (43.59%)

1917 existing lines in 17 files now uncovered.

66894 of 94896 relevant lines covered (70.49%)

0.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.23
/pkg/synchronization-controller/synchronization-controller.go
1
/*
2
 * This file is part of the KubeVirt project
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.  * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 *
15
 * Copyright The KubeVirt Authors.
16
 *
17
 */
18

19
package synchronization
20

21
import (
22
        "crypto/tls"
23
        "encoding/json"
24
        "fmt"
25
        "io"
26
        "net"
27
        "os"
28
        "strconv"
29
        "sync"
30
        "time"
31

32
        apiequality "k8s.io/apimachinery/pkg/api/equality"
33
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34
        "k8s.io/apimachinery/pkg/types"
35
        "k8s.io/apimachinery/pkg/util/wait"
36
        "k8s.io/client-go/tools/cache"
37
        "k8s.io/client-go/util/workqueue"
38

39
        virtv1 "kubevirt.io/api/core/v1"
40

41
        "kubevirt.io/kubevirt/pkg/apimachinery/patch"
42
        "kubevirt.io/kubevirt/pkg/controller"
43

44
        context "golang.org/x/net/context"
45
        "google.golang.org/grpc"
46
        "google.golang.org/grpc/credentials"
47
        "kubevirt.io/client-go/kubecli"
48
        "kubevirt.io/client-go/log"
49

50
        syncv1 "kubevirt.io/kubevirt/pkg/synchronizer-com/synchronization/v1"
51
)
52

53
const (
54
        defaultTimeout = 30
55

56
        MyPodIP = "MY_POD_IP"
57

58
        noSourceStatusErrorMsg               = "must pass source status"
59
        noTargetStatusErrorMsg               = "must pass target status"
60
        unableToLocateVMIMigrationIDErrorMsg = "unable to locate VMI for migrationID %s"
61

62
        successMessage = "success"
63

64
        maxCloseRetries = 10
65
)
66

67
type SynchronizationController struct {
68
        client   kubecli.KubevirtClient
69
        connChan chan io.ReadWriteCloser
70

71
        vmiInformer       cache.SharedIndexInformer
72
        migrationInformer cache.SharedIndexInformer
73

74
        listener        net.Listener
75
        bindAddress     string
76
        bindPort        int
77
        clientTLSConfig *tls.Config
78
        serverTLSConfig *tls.Config
79
        timeout         int
80

81
        queue     workqueue.TypedRateLimitingInterface[string]
82
        hasSynced func() bool
83

84
        syncOutboundConnectionMap  *sync.Map
85
        syncReceivingConnectionMap *sync.Map
86
        failedCloseConnections     *sync.Map
87
        grpcServer                 *grpc.Server
88
}
89

90
func NewSynchronizationController(
91
        client kubecli.KubevirtClient,
92
        vmiInformer cache.SharedIndexInformer,
93
        migrationInformer cache.SharedIndexInformer,
94
        clientTLSConfig,
95
        serverTLSConfig *tls.Config,
96
        bindAddress string,
97
        bindPort int,
98
) (*SynchronizationController, error) {
1✔
99
        syncController := &SynchronizationController{
1✔
100
                vmiInformer:       vmiInformer,
1✔
101
                migrationInformer: migrationInformer,
1✔
102
                clientTLSConfig:   clientTLSConfig,
1✔
103
                serverTLSConfig:   serverTLSConfig,
1✔
104
                timeout:           defaultTimeout,
1✔
105
                bindAddress:       bindAddress,
1✔
106
                bindPort:          bindPort,
1✔
107
                client:            client,
1✔
108
        }
1✔
109

1✔
110
        queue := workqueue.NewTypedRateLimitingQueueWithConfig[string](
1✔
111
                workqueue.DefaultTypedControllerRateLimiter[string](),
1✔
112
                workqueue.TypedRateLimitingQueueConfig[string]{Name: "sync-vmi-status"},
1✔
113
        )
1✔
114
        syncController.queue = queue
1✔
115

1✔
116
        syncController.hasSynced = func() bool {
1✔
NEW
117
                return vmiInformer.HasSynced() && migrationInformer.HasSynced()
×
UNCOV
118
        }
×
119

120
        syncController.syncOutboundConnectionMap = &sync.Map{}
1✔
121
        syncController.syncReceivingConnectionMap = &sync.Map{}
1✔
122
        syncController.failedCloseConnections = &sync.Map{}
1✔
123

1✔
124
        _, err := vmiInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
1✔
125
                AddFunc:    syncController.addVmiFunc,
1✔
126
                DeleteFunc: syncController.deleteVmiFunc,
1✔
127
                UpdateFunc: syncController.updateVmiFunc,
1✔
128
        })
1✔
129
        if err != nil {
1✔
UNCOV
130
                return nil, err
×
UNCOV
131
        }
×
132

133
        if err := syncController.migrationInformer.AddIndexers(map[string]cache.IndexFunc{
1✔
134
                "byUID":               indexByMigrationUID,
1✔
135
                "byVMIName":           indexByVmiName,
1✔
136
                "byTargetMigrationID": indexByTargetMigrationID,
1✔
137
                "bySourceMigrationID": indexBySourceMigrationID,
1✔
138
        }); err != nil {
1✔
UNCOV
139
                return nil, err
×
UNCOV
140
        }
×
141

142
        if _, err := syncController.migrationInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
1✔
143
                AddFunc:    syncController.addMigrationFunc,
1✔
144
                DeleteFunc: syncController.deleteMigrationFunc,
1✔
145
                UpdateFunc: syncController.updateMigrationFunc,
1✔
146
        }); err != nil {
1✔
UNCOV
147
                return nil, err
×
UNCOV
148
        }
×
149

150
        syncController.grpcServer = grpc.NewServer(grpc.Creds(credentials.NewTLS(serverTLSConfig)))
1✔
151
        syncv1.RegisterSynchronizeServer(syncController.grpcServer, syncController)
1✔
152

1✔
153
        return syncController, nil
1✔
154
}
155

UNCOV
156
func (s *SynchronizationController) addVmiFunc(addObj interface{}) {
×
UNCOV
157
        s.enqueueVirtualMachineInstance(addObj)
×
UNCOV
158
}
×
159

160
func (s *SynchronizationController) deleteVmiFunc(addObj interface{}) {
×
161
        s.enqueueVirtualMachineInstance(addObj)
×
162
}
×
163

164
func (s *SynchronizationController) updateVmiFunc(_, curr interface{}) {
×
165
        s.enqueueVirtualMachineInstance(curr)
×
166
}
×
167

168
func (s *SynchronizationController) enqueueVirtualMachineInstance(obj interface{}) {
×
169
        vmi, ok := obj.(*virtv1.VirtualMachineInstance)
×
170
        if ok {
×
UNCOV
171
                key, err := controller.KeyFunc(vmi)
×
172
                if err != nil {
×
173
                        log.Log.Object(vmi).Reason(err).Error("failed to extract key from virtualmachine.")
×
174
                        return
×
175
                }
×
176
                s.queue.Add(key)
×
177
        }
178
}
179

180
func (s *SynchronizationController) addMigrationFunc(addObj interface{}) {
1✔
181
        s.enqueueVirtualMachineInstanceFromMigration(addObj)
1✔
182
}
1✔
183

184
func (s *SynchronizationController) deleteMigrationFunc(delObj interface{}) {
1✔
185
        // Clean up any synchronization connections in the map.
1✔
186
        s.enqueueVirtualMachineInstanceFromMigration(delObj)
1✔
187
        // Close any connections associated with this migration.
1✔
188
        migration, ok := delObj.(*virtv1.VirtualMachineInstanceMigration)
1✔
189
        if ok {
2✔
190
                if !migration.IsDecentralized() {
1✔
UNCOV
191
                        return
×
UNCOV
192
                }
×
193
                if migration.Spec.Receive != nil {
2✔
194
                        if err := s.closeConnectionForMigrationID(s.syncReceivingConnectionMap, migration.Spec.Receive.MigrationID); err != nil {
1✔
NEW
195
                                log.Log.Reason(err).Infof("unable to close connection for migrationID %s, possibly leaked connection", migration.Spec.Receive.MigrationID)
×
NEW
196
                        }
×
197
                } else if migration.Spec.SendTo != nil {
2✔
198
                        if err := s.closeConnectionForMigrationID(s.syncOutboundConnectionMap, migration.Spec.SendTo.MigrationID); err != nil {
1✔
NEW
199
                                log.Log.Reason(err).Infof("unable to close connection for migrationID %s, possibly leaked connection", migration.Spec.SendTo.MigrationID)
×
NEW
200
                        }
×
201
                }
202
        }
203
}
204

205
func (s *SynchronizationController) closeConnectionForMigrationID(syncMap *sync.Map, migrationID string) error {
1✔
206
        obj, loaded := syncMap.LoadAndDelete(migrationID)
1✔
207
        if loaded {
2✔
208
                log.Log.V(4).Infof("closing connection associated with migrationID %s", migrationID)
1✔
209
                outboundConnection, ok := obj.(*SynchronizationConnection)
1✔
210
                if ok {
2✔
211
                        if err := outboundConnection.Close(); err != nil {
1✔
UNCOV
212
                                log.Log.Warningf("unable to close connection for migrationID %s, %v", migrationID, err)
×
NEW
213
                                s.failedCloseConnections.Store(outboundConnection, 0)
×
NEW
214
                                return err
×
UNCOV
215
                        }
×
216
                } else {
1✔
217
                        log.Log.Warningf("unable to close connection for migrationID %s, type is %v", migrationID, obj)
1✔
218
                        return fmt.Errorf("unknown type %v", obj)
1✔
219
                }
1✔
220
        }
221
        return nil
1✔
222
}
223

224
func (s *SynchronizationController) updateMigrationFunc(_, curr interface{}) {
1✔
225
        s.enqueueVirtualMachineInstanceFromMigration(curr)
1✔
226
}
1✔
227

228
func (s *SynchronizationController) enqueueVirtualMachineInstanceFromMigration(obj interface{}) {
1✔
229
        migration, ok := obj.(*virtv1.VirtualMachineInstanceMigration)
1✔
230
        if ok {
2✔
231
                key := controller.NamespacedKey(migration.Namespace, migration.Spec.VMIName)
1✔
232
                s.queue.Add(key)
1✔
233
        }
1✔
234
}
235

UNCOV
236
func (s *SynchronizationController) Run(threadiness int, stopCh <-chan struct{}) error {
×
UNCOV
237
        defer controller.HandlePanic()
×
UNCOV
238
        defer s.queue.ShutDown()
×
UNCOV
239
        defer s.closeConnections()
×
UNCOV
240

×
UNCOV
241
        log.Log.Info("starting vmi status synchronization controller.")
×
UNCOV
242

×
UNCOV
243
        // Wait for cache sync before we start the pod controller
×
UNCOV
244
        cache.WaitForCacheSync(stopCh, s.hasSynced)
×
UNCOV
245

×
UNCOV
246
        // Start the actual work
×
UNCOV
247
        for i := 0; i < threadiness; i++ {
×
248
                go wait.Until(s.runWorker, time.Second, stopCh)
×
249
        }
×
NEW
250
        go wait.Until(s.runConnectionCleanup, 5*time.Second, stopCh)
×
251

×
252
        conn, err := s.createTcpListener()
×
253
        if err != nil {
×
254
                log.Log.Criticalf("received error %v, exiting", err)
×
255
                return err
×
256
        } else {
×
257
                go func() {
×
258
                        s.grpcServer.Serve(conn)
×
259
                }()
×
260
        }
NEW
261
        if err := s.rebuildConnectionsAndUpdateSyncAddress(); err != nil {
×
NEW
262
                return err
×
NEW
263
        }
×
264

265
        log.Log.Info("waiting on stop signal")
×
266
        <-stopCh
×
267
        log.Log.Info("normally stopping vmi status synchronization controller.")
×
268
        return nil
×
269
}
270

271
func (s *SynchronizationController) closeConnections() {
1✔
272
        log.Log.V(1).Info("closing listener and grpcserver")
1✔
273
        if s.listener != nil {
2✔
274
                s.listener.Close()
1✔
275
        }
1✔
276
        if s.grpcServer != nil {
2✔
277
                s.grpcServer.GracefulStop()
1✔
278
        }
1✔
279
        log.Log.V(1).Infof("closing outbound connections")
1✔
280
        s.syncOutboundConnectionMap.Range(closeMapConnections)
1✔
281
        log.Log.V(1).Infof("closing inbound connections")
1✔
282
        s.syncReceivingConnectionMap.Range(closeMapConnections)
1✔
283
}
284

285
func closeMapConnections(k, obj interface{}) bool {
1✔
286
        outboundConnection, ok := obj.(*SynchronizationConnection)
1✔
287
        if ok && outboundConnection != nil {
2✔
288
                log.Log.V(1).Infof("closing connection for migration ID: %s", outboundConnection.migrationID)
1✔
289
                if err := outboundConnection.Close(); err != nil {
1✔
UNCOV
290
                        log.Log.Warningf("unable to close connection for VMI %s during shutdown, %v", k, err)
×
UNCOV
291
                }
×
UNCOV
292
        } else {
×
UNCOV
293
                log.Log.Warningf("unable to close connection for VMI %s during shutdown", k)
×
UNCOV
294
        }
×
295
        return true
1✔
296
}
297

UNCOV
298
func (s *SynchronizationController) runWorker() {
×
UNCOV
299
        for s.Execute() {
×
UNCOV
300
        }
×
301
}
302

303
func (s *SynchronizationController) Execute() bool {
1✔
304
        key, quit := s.queue.Get()
1✔
305
        if quit {
1✔
306
                return false
×
307
        }
×
308

309
        defer s.queue.Done(key)
1✔
310
        err := s.execute(key)
1✔
311

1✔
312
        if err != nil {
1✔
UNCOV
313
                log.Log.Reason(err).Infof("reenqueuing VirtualMachineInstance %v", key)
×
314
                s.queue.AddRateLimited(key)
×
315
        } else {
1✔
316
                log.Log.V(4).Infof("processed VirtualMachineInstance %v", key)
1✔
317
                s.queue.Forget(key)
1✔
318
        }
1✔
319
        return true
1✔
320
}
321

322
func (s *SynchronizationController) execute(key string) error {
1✔
323
        // Fetch the latest VMI state from cache
1✔
324
        obj, exists, _ := s.vmiInformer.GetStore().GetByKey(key)
1✔
325
        if !exists {
1✔
UNCOV
326
                return nil
×
UNCOV
327
        }
×
328
        vmi := obj.(*virtv1.VirtualMachineInstance)
1✔
329

1✔
330
        migration, err := s.getMigrationForVMI(vmi)
1✔
331
        if err != nil {
1✔
UNCOV
332
                return err
×
UNCOV
333
        }
×
334
        if migration != nil && migration.IsDecentralized() {
2✔
335
                if migration.IsDecentralizedSource() {
2✔
336
                        if err := s.handleSourceState(vmi.DeepCopy(), migration); err != nil {
1✔
UNCOV
337
                                return err
×
UNCOV
338
                        }
×
339
                }
340
                if migration.IsDecentralizedTarget() {
2✔
341
                        return s.handleTargetState(vmi.DeepCopy(), migration)
1✔
342
                }
1✔
343
                return nil
1✔
344
        } else {
1✔
345
                // No migration found don't do anything
1✔
346
                log.Log.Object(vmi).V(4).Info("no decentralized migration found for VMI")
1✔
347
                return nil
1✔
348
        }
1✔
349
}
350

351
func (s *SynchronizationController) getMigrationIDFromUID(migrationUID types.UID) (string, error) {
1✔
352
        objs, err := s.migrationInformer.GetIndexer().ByIndex("byUID", string(migrationUID))
1✔
353
        if err != nil {
1✔
354
                return "", err
×
UNCOV
355
        }
×
356
        if len(objs) > 1 {
1✔
UNCOV
357
                return "", fmt.Errorf("found more than one migration with same UID")
×
UNCOV
358
        }
×
359
        if len(objs) == 0 {
1✔
UNCOV
360
                return "", nil
×
UNCOV
361
        }
×
362
        migration, ok := objs[0].(*virtv1.VirtualMachineInstanceMigration)
1✔
363
        if !ok {
1✔
UNCOV
364
                return "", fmt.Errorf("found unknown object in migration cache")
×
UNCOV
365
        }
×
366
        var migrationID string
1✔
367
        if migration.Spec.Receive != nil {
2✔
368
                migrationID = migration.Spec.Receive.MigrationID
1✔
369
        }
1✔
370
        if migration.Spec.SendTo != nil {
2✔
371
                migrationID = migration.Spec.SendTo.MigrationID
1✔
372
        }
1✔
373
        return migrationID, nil
1✔
374
}
375

376
func (s *SynchronizationController) getOutboundSourceConnection(vmi *virtv1.VirtualMachineInstance, migrationState *virtv1.VirtualMachineInstanceMigrationState) (*SynchronizationConnection, error) {
1✔
377
        if migrationState.TargetState == nil || migrationState.TargetState.SyncAddress == nil || *migrationState.TargetState.SyncAddress == "" {
2✔
378
                return nil, nil
1✔
379
        }
1✔
380
        return s.getOutboundConnection(vmi, migrationState.SourceState.MigrationUID, *migrationState.TargetState.SyncAddress, s.syncOutboundConnectionMap)
1✔
381
}
382

383
func (s *SynchronizationController) getOutboundTargetConnection(vmi *virtv1.VirtualMachineInstance, migrationState *virtv1.VirtualMachineInstanceMigrationState) (*SynchronizationConnection, error) {
1✔
384
        if migrationState.SourceState == nil || migrationState.SourceState.SyncAddress == nil || *migrationState.SourceState.SyncAddress == "" {
2✔
385
                return nil, nil
1✔
386
        }
1✔
387
        return s.getOutboundConnection(vmi, migrationState.TargetState.MigrationUID, *migrationState.SourceState.SyncAddress, s.syncReceivingConnectionMap)
1✔
388
}
389

390
func (s *SynchronizationController) getOutboundConnection(vmi *virtv1.VirtualMachineInstance, migrationUID types.UID, syncAddress string, connectionMap *sync.Map) (*SynchronizationConnection, error) {
1✔
391
        if migrationUID == "" {
1✔
UNCOV
392
                return nil, nil
×
UNCOV
393
        }
×
394
        migrationID, err := s.getMigrationIDFromUID(migrationUID)
1✔
395
        if err != nil {
1✔
UNCOV
396
                return nil, err
×
UNCOV
397
        }
×
398
        log.Log.Object(vmi).V(4).Infof("found migration ID %s", migrationID)
1✔
399
        obj, ok := connectionMap.Load(migrationID)
1✔
400
        if !ok {
2✔
401
                grpcClientConnection, err := s.createOutboundConnection(syncAddress)
1✔
402
                if err != nil {
1✔
UNCOV
403
                        return nil, err
×
UNCOV
404
                }
×
405
                conn := &SynchronizationConnection{
1✔
406
                        migrationID:          migrationID,
1✔
407
                        grpcClientConnection: grpcClientConnection,
1✔
408
                }
1✔
409
                connectionMap.Store(migrationID, conn)
1✔
410
                return conn, nil
1✔
411
        }
412
        outboundSyncConnection, ok := obj.(*SynchronizationConnection)
1✔
413
        if !ok {
1✔
UNCOV
414
                return nil, fmt.Errorf("found unknown object in outbound connection cache %#v", outboundSyncConnection)
×
UNCOV
415
        }
×
416
        return outboundSyncConnection, nil
1✔
417
}
418

419
func (s *SynchronizationController) handleSourceState(vmi *virtv1.VirtualMachineInstance, migration *virtv1.VirtualMachineInstanceMigration) error {
1✔
420
        var outboundConnection *SynchronizationConnection
1✔
421
        var err error
1✔
422
        if vmi.Status.MigrationState == nil {
2✔
423
                // No migration state, don't do anything
1✔
424
                return nil
1✔
425
        }
1✔
426
        if vmi.Status.MigrationState.SourceState == nil || vmi.Status.MigrationState.TargetState == nil {
2✔
427
                // No migration state, don't do anything
1✔
428
                return nil
1✔
429
        }
1✔
430
        if migration.IsFinal() {
1✔
NEW
431
                // Migration completed already, no need to synchronize anymore.
×
NEW
432
                return nil
×
NEW
433
        }
×
434

435
        sourceState := vmi.Status.MigrationState.SourceState
1✔
436
        if sourceState.SyncAddress == nil || *sourceState.SyncAddress == "" {
2✔
437
                syncAddress, err := s.getLocalSynchronizationAddress()
1✔
438
                if err != nil {
1✔
UNCOV
439
                        return err
×
UNCOV
440
                }
×
441
                sourceState.SyncAddress = &syncAddress
1✔
442
        }
443
        targetState := vmi.Status.MigrationState.TargetState
1✔
444
        if targetState.SyncAddress != nil && sourceState.MigrationUID != "" {
2✔
445
                if outboundConnection, err = s.getOutboundSourceConnection(vmi, vmi.Status.MigrationState); err != nil {
1✔
UNCOV
446
                        return err
×
UNCOV
447
                }
×
448
        }
449
        if outboundConnection == nil {
1✔
UNCOV
450
                log.Log.Object(vmi).V(4).Info("no synchronization connection found for source, doing nothing")
×
UNCOV
451
                return nil
×
452
        }
×
453
        vmiStatusJson, err := json.Marshal(vmi.Status)
1✔
454
        if err != nil {
1✔
UNCOV
455
                return err
×
UNCOV
456
        }
×
457
        client := syncv1.NewSynchronizeClient(outboundConnection.grpcClientConnection)
1✔
458
        ctx, cancel := context.WithTimeout(context.Background(), time.Duration(s.timeout)*time.Second)
1✔
459
        defer cancel()
1✔
460

1✔
461
        if _, err := client.SyncSourceMigrationStatus(ctx, &syncv1.VMIStatusRequest{
1✔
462
                MigrationID: outboundConnection.migrationID,
1✔
463
                VmiStatus: &syncv1.VMIStatus{
1✔
464
                        VmiStatusJson: vmiStatusJson,
1✔
465
                },
1✔
466
        }); err != nil {
2✔
467
                return err
1✔
468
        }
1✔
469
        if migration.IsFinal() {
1✔
NEW
470
                if migration.Spec.SendTo != nil {
×
NEW
471
                        log.Log.Object(migration).Infof("completed migration for VMI %s/%s, closing outbound connections", migration.Namespace, migration.Spec.VMIName)
×
NEW
472
                        s.closeConnectionForMigrationID(s.syncOutboundConnectionMap, migration.Spec.SendTo.MigrationID)
×
NEW
473
                }
×
474
        }
475

476
        return nil
1✔
477
}
478

479
func (s *SynchronizationController) handleTargetState(vmi *virtv1.VirtualMachineInstance, migration *virtv1.VirtualMachineInstanceMigration) error {
1✔
480
        if vmi.Status.MigrationState == nil {
2✔
481
                // No migration state, don't do anything
1✔
482
                return nil
1✔
483
        }
1✔
484
        if vmi.Status.MigrationState.TargetState == nil || vmi.Status.MigrationState.SourceState == nil {
2✔
485
                // No migration state, don't do anything
1✔
486
                return nil
1✔
487
        }
1✔
488
        if migration.IsFinal() {
1✔
NEW
489
                // Migration completed already, no need to synchronize anymore.
×
NEW
490
                return nil
×
NEW
491
        }
×
492

493
        var outboundConnection *SynchronizationConnection
1✔
494
        var err error
1✔
495
        sourceState := vmi.Status.MigrationState.SourceState
1✔
496
        targetState := vmi.Status.MigrationState.TargetState
1✔
497
        if targetState.SyncAddress == nil || *targetState.SyncAddress == "" {
2✔
498
                syncAddress, err := s.getLocalSynchronizationAddress()
1✔
499
                if err != nil {
1✔
UNCOV
500
                        return err
×
UNCOV
501
                }
×
502
                targetState.SyncAddress = &syncAddress
1✔
503
        }
504

505
        if sourceState.SyncAddress != nil && targetState.MigrationUID != "" {
2✔
506
                if outboundConnection, err = s.getOutboundTargetConnection(vmi, vmi.Status.MigrationState); err != nil {
1✔
UNCOV
507
                        return err
×
UNCOV
508
                }
×
509
        }
510
        if outboundConnection == nil {
1✔
UNCOV
511
                log.Log.Object(vmi).V(4).Info("no synchronization connection found for target, doing nothing")
×
UNCOV
512
                return nil
×
UNCOV
513
        }
×
514

515
        vmiStatusJson, err := json.Marshal(vmi.Status)
1✔
516
        if err != nil {
1✔
UNCOV
517
                return err
×
UNCOV
518
        }
×
519
        client := syncv1.NewSynchronizeClient(outboundConnection.grpcClientConnection)
1✔
520
        ctx, cancel := context.WithTimeout(context.Background(), time.Duration(s.timeout)*time.Second)
1✔
521
        defer cancel()
1✔
522

1✔
523
        _, err = client.SyncTargetMigrationStatus(ctx, &syncv1.VMIStatusRequest{
1✔
524
                MigrationID: outboundConnection.migrationID,
1✔
525
                VmiStatus: &syncv1.VMIStatus{
1✔
526
                        VmiStatusJson: vmiStatusJson,
1✔
527
                },
1✔
528
        })
1✔
529
        if err != nil {
2✔
530
                return err
1✔
531
        }
1✔
532
        if migration.IsFinal() {
1✔
NEW
533
                if migration.Spec.Receive != nil {
×
NEW
534
                        log.Log.Object(migration).Infof("completed migration for VMI %s/%s, closing receiving connections", migration.Namespace, migration.Spec.VMIName)
×
NEW
535
                        s.closeConnectionForMigrationID(s.syncReceivingConnectionMap, migration.Spec.Receive.MigrationID)
×
NEW
536
                }
×
537
        }
538

539
        return nil
1✔
540
}
541

542
func (s *SynchronizationController) getMigrationForVMI(vmi *virtv1.VirtualMachineInstance) (*virtv1.VirtualMachineInstanceMigration, error) {
1✔
543
        objects, err := s.migrationInformer.GetIndexer().ByIndex("byVMIName", vmi.Name)
1✔
544
        if err != nil {
1✔
UNCOV
545
                return nil, err
×
UNCOV
546
        }
×
547
        if len(objects) > 0 {
2✔
548
                count := 0
1✔
549
                var res *virtv1.VirtualMachineInstanceMigration
1✔
550
                for _, migrationObj := range objects {
2✔
551
                        migration, ok := migrationObj.(*virtv1.VirtualMachineInstanceMigration)
1✔
552
                        if !ok {
1✔
UNCOV
553
                                return nil, fmt.Errorf("not a virtual machine instance migration")
×
554
                        }
×
555
                        if migration.Namespace == vmi.Namespace {
2✔
556
                                count++
1✔
557
                                res = migration
1✔
558
                        }
1✔
559
                }
560
                if count > 1 {
1✔
561
                        return nil, fmt.Errorf("found more than one migration pointing to same VMI")
×
562
                } else if count == 0 {
1✔
UNCOV
563
                        return nil, nil
×
UNCOV
564
                }
×
565
                return res, nil
1✔
566
        }
567
        return nil, nil
1✔
568
}
569

570
func (s *SynchronizationController) rebuildConnectionsAndUpdateSyncAddress() error {
1✔
571
        // Go and find all active migration resources, if they are decentralized rebuild either
1✔
572
        // the incoming or outbound connections, and call sync to update the remote with the new
1✔
573
        // address.
1✔
574
        objs := s.migrationInformer.GetStore().List()
1✔
575
        log.Log.V(4).Infof("rebuilding any connections, and updating remote VMIs, found %d migrations", len(objs))
1✔
576
        for _, obj := range objs {
2✔
577
                migration, ok := obj.(*virtv1.VirtualMachineInstanceMigration)
1✔
578
                if !ok {
1✔
NEW
579
                        return fmt.Errorf("unknown object in migration store %v", obj)
×
NEW
580
                }
×
581
                if isOnGoingMigration(migration) {
2✔
582
                        vmi, err := s.getVMIFromMigration(migration)
1✔
583
                        if err != nil {
1✔
NEW
584
                                return err
×
NEW
585
                        }
×
586
                        if vmi == nil {
2✔
587
                                // No VMI found, can't update it, so skip it.
1✔
588
                                continue
1✔
589
                        }
590
                        // ongoing migration.
591
                        if migration.Spec.Receive != nil {
2✔
592
                                // We are the target
1✔
593
                                log.Log.Object(migration).Object(vmi).Info("found ongoing target migration for vmi, rebuilding connection")
1✔
594
                                if err := s.rebuildTargetConnection(migration, vmi); err != nil {
1✔
NEW
595
                                        return err
×
NEW
596
                                }
×
597
                        } else if migration.Spec.SendTo != nil {
2✔
598
                                // We are the source
1✔
599
                                log.Log.Object(migration).Object(vmi).Info("found ongoing source migration for vmi, rebuilding connection")
1✔
600
                                if err := s.rebuildSourceConnection(migration, vmi); err != nil {
1✔
NEW
601
                                        return err
×
NEW
602
                                }
×
603
                        }
604
                }
605
        }
606
        return nil
1✔
607
}
608

609
func isOnGoingMigration(migration *virtv1.VirtualMachineInstanceMigration) bool {
1✔
610
        return migration.IsDecentralized() && migration.Status.Phase != virtv1.MigrationFailed && migration.Status.Phase != virtv1.MigrationSucceeded
1✔
611
}
1✔
612

613
func (s *SynchronizationController) rebuildTargetConnection(migration *virtv1.VirtualMachineInstanceMigration, vmi *virtv1.VirtualMachineInstance) error {
1✔
614
        conn, err := s.getOutboundTargetConnection(vmi, vmi.Status.MigrationState)
1✔
615
        if err != nil {
1✔
NEW
616
                return err
×
NEW
617
        }
×
618
        if conn == nil {
2✔
619
                return nil
1✔
620
        }
1✔
621
        s.syncReceivingConnectionMap.Store(migration.Spec.Receive.MigrationID, conn)
1✔
622
        if vmi.Status.MigrationState != nil && vmi.Status.MigrationState.TargetState != nil {
2✔
623
                url, err := s.getLocalSynchronizationAddress()
1✔
624
                if err != nil {
1✔
NEW
625
                        return err
×
NEW
626
                }
×
627
                origVMI := vmi.DeepCopy()
1✔
628
                vmi.Status.MigrationState.TargetState.SyncAddress = &url
1✔
629
                // patching will cause reconcile loop to connect to remote to update
1✔
630
                if err := s.patchVMI(context.Background(), origVMI, vmi); err != nil {
1✔
NEW
631
                        return err
×
NEW
632
                }
×
633
        }
634
        return nil
1✔
635
}
636

637
func (s *SynchronizationController) rebuildSourceConnection(migration *virtv1.VirtualMachineInstanceMigration, vmi *virtv1.VirtualMachineInstance) error {
1✔
638
        conn, err := s.getOutboundSourceConnection(vmi, vmi.Status.MigrationState)
1✔
639
        if err != nil {
1✔
NEW
640
                return err
×
NEW
641
        }
×
642
        if conn == nil {
2✔
643
                return nil
1✔
644
        }
1✔
645
        s.syncOutboundConnectionMap.Store(migration.Spec.SendTo.MigrationID, conn)
1✔
646
        if vmi.Status.MigrationState != nil && vmi.Status.MigrationState.SourceState != nil {
2✔
647
                url, err := s.getLocalSynchronizationAddress()
1✔
648
                if err != nil {
1✔
NEW
649
                        return err
×
NEW
650
                }
×
651
                origVMI := vmi.DeepCopy()
1✔
652
                vmi.Status.MigrationState.SourceState.SyncAddress = &url
1✔
653
                // patching will cause reconcile loop to connect to remote to update
1✔
654
                if err := s.patchVMI(context.Background(), origVMI, vmi); err != nil {
1✔
NEW
655
                        return err
×
NEW
656
                }
×
657
        }
658
        return nil
1✔
659
}
660

661
func (s *SynchronizationController) getVMIFromMigration(migration *virtv1.VirtualMachineInstanceMigration) (*virtv1.VirtualMachineInstance, error) {
1✔
662
        key := controller.NamespacedKey(migration.Namespace, migration.Spec.VMIName)
1✔
663
        obj, exists, err := s.vmiInformer.GetStore().GetByKey(key)
1✔
664
        if err != nil {
1✔
NEW
665
                return nil, err
×
NEW
666
        }
×
667
        if !exists {
2✔
668
                return nil, nil
1✔
669
        }
1✔
670
        return obj.(*virtv1.VirtualMachineInstance).DeepCopy(), nil
1✔
671
}
672

673
func (s *SynchronizationController) getLocalSynchronizationAddress() (string, error) {
1✔
674
        myIp := os.Getenv(MyPodIP)
1✔
675
        if myIp != "" {
1✔
UNCOV
676
                names, err := net.LookupAddr(myIp)
×
UNCOV
677
                if err != nil {
×
NEW
678
                        log.Log.Errorf("Error from lookupAddr %v", err)
×
679
                }
×
680
                for _, name := range names {
×
681
                        log.Log.V(4).Infof("found DNS name for my IP address: %s", name)
×
682
                        return fmt.Sprintf("%s:%d", name, s.bindPort), nil
×
UNCOV
683
                }
×
NEW
684
                log.Log.Info("No names from DNS, returning my ip address")
×
UNCOV
685
                return fmt.Sprintf("%s:%d", myIp, s.bindPort), nil
×
686
        }
687
        if s.listener == nil {
1✔
UNCOV
688
                return fmt.Sprintf("%s:%d", s.bindAddress, s.bindPort), nil
×
UNCOV
689
        }
×
690
        // TODO figure out how to get my URL with or without submariner (url changes based on export)
691
        return s.listener.Addr().String(), nil
1✔
692
}
693

694
func (s *SynchronizationController) createOutboundConnection(connectionURL string) (*grpc.ClientConn, error) {
1✔
695
        logger := log.Log.With("outbound", connectionURL)
1✔
696
        logger.Info("creating new synchronization grpc connection")
1✔
697

1✔
698
        client, err := grpc.NewClient(connectionURL, grpc.WithTransportCredentials(credentials.NewTLS(s.clientTLSConfig)))
1✔
699
        return client, err
1✔
700
}
1✔
701

702
func (s *SynchronizationController) createTcpListener() (net.Listener, error) {
1✔
703
        if s.listener != nil {
1✔
UNCOV
704
                return s.listener, nil
×
UNCOV
705
        }
×
706
        var ln net.Listener
1✔
707
        var err error
1✔
708
        addr := net.JoinHostPort(s.bindAddress, strconv.Itoa(s.bindPort))
1✔
709
        ln, err = net.Listen("tcp", addr)
1✔
710
        if err != nil {
1✔
711
                log.Log.Reason(err).Error("failed to create tcp listener")
×
UNCOV
712
                return nil, err
×
UNCOV
713
        }
×
714
        s.listener = ln
1✔
715
        return ln, nil
1✔
716
}
717

718
func (s *SynchronizationController) findTargetMigrationFromMigrationID(migrationID string) (*virtv1.VirtualMachineInstanceMigration, error) {
1✔
719
        return s.findMigrationFromMigrationIDByIndex("byTargetMigrationID", migrationID)
1✔
720
}
1✔
721

722
func (s *SynchronizationController) findSourceMigrationFromMigrationID(migrationID string) (*virtv1.VirtualMachineInstanceMigration, error) {
1✔
723
        return s.findMigrationFromMigrationIDByIndex("bySourceMigrationID", migrationID)
1✔
724
}
1✔
725

726
func (s *SynchronizationController) findMigrationFromMigrationIDByIndex(indexName, migrationID string) (*virtv1.VirtualMachineInstanceMigration, error) {
1✔
727
        objs, err := s.migrationInformer.GetIndexer().ByIndex(indexName, migrationID)
1✔
728
        if err != nil {
1✔
UNCOV
729
                return nil, err
×
UNCOV
730
        }
×
731
        if len(objs) > 1 {
1✔
732
                log.Log.Warningf("found multiple migrations for migrationID %s, picking first one", migrationID)
×
UNCOV
733
        }
×
734
        for _, obj := range objs {
2✔
735
                migration, _ := obj.(*virtv1.VirtualMachineInstanceMigration)
1✔
736
                return migration, nil
1✔
737
        }
1✔
738
        return nil, nil
1✔
739
}
740

741
func (s *SynchronizationController) SyncSourceMigrationStatus(ctx context.Context, request *syncv1.VMIStatusRequest) (*syncv1.VMIStatusResponse, error) {
1✔
742
        if request.VmiStatus == nil || len(request.VmiStatus.VmiStatusJson) == 0 {
2✔
743
                return &syncv1.VMIStatusResponse{
1✔
744
                        Message: noSourceStatusErrorMsg,
1✔
745
                }, fmt.Errorf(noSourceStatusErrorMsg)
1✔
746
        }
1✔
747
        migration, err := s.findTargetMigrationFromMigrationID(request.MigrationID)
1✔
748
        if migration == nil {
2✔
749
                return &syncv1.VMIStatusResponse{
1✔
750
                        Message: fmt.Sprintf(unableToLocateVMIMigrationIDErrorMsg, request.MigrationID),
1✔
751
                }, fmt.Errorf(unableToLocateVMIMigrationIDErrorMsg, request.MigrationID)
1✔
752
        }
1✔
753
        key := controller.NamespacedKey(migration.Namespace, migration.Spec.VMIName)
1✔
754
        log.Log.Object(migration).V(5).Infof("looking up VMI %s", key)
1✔
755
        obj, exists, err := s.vmiInformer.GetStore().GetByKey(key)
1✔
756
        if err != nil || !exists {
2✔
757
                if err == nil {
2✔
758
                        err = fmt.Errorf(unableToLocateVMIMigrationIDErrorMsg, request.MigrationID)
1✔
759
                }
1✔
760
                return &syncv1.VMIStatusResponse{
1✔
761
                        Message: fmt.Sprintf(unableToLocateVMIMigrationIDErrorMsg, request.MigrationID),
1✔
762
                }, err
1✔
763
        }
764
        vmi := obj.(*virtv1.VirtualMachineInstance)
1✔
765

1✔
766
        remoteStatus := &virtv1.VirtualMachineInstanceStatus{}
1✔
767
        if err := json.Unmarshal(request.VmiStatus.VmiStatusJson, remoteStatus); err != nil {
2✔
768
                return &syncv1.VMIStatusResponse{
1✔
769
                        Message: fmt.Sprintf("unable to unmarshal vmistatus for migrationID %s", request.MigrationID),
1✔
770
                }, err
1✔
771
        }
1✔
772
        if remoteStatus.MigrationState == nil {
2✔
773
                return &syncv1.VMIStatusResponse{
1✔
774
                        Message: noSourceStatusErrorMsg,
1✔
775
                }, fmt.Errorf(noSourceStatusErrorMsg)
1✔
776
        }
1✔
777
        newVMI := vmi.DeepCopy()
1✔
778
        if newVMI.Status.MigrationState == nil {
2✔
779
                newVMI.Status.MigrationState = &virtv1.VirtualMachineInstanceMigrationState{}
1✔
780
        }
1✔
781
        log.Log.Object(newVMI).V(5).Infof("vmi migration source state: %#v", newVMI.Status.MigrationState.SourceState)
1✔
782
        log.Log.Object(newVMI).V(5).Infof("remote migration source state: %#v", remoteStatus.MigrationState.SourceState)
1✔
783
        newVMI.Status.MigrationState.SourceState = remoteStatus.MigrationState.SourceState.DeepCopy()
1✔
784
        copyLegacySourceFields(newVMI, remoteStatus.MigrationState)
1✔
785
        newVMI.Status.MigratedVolumes = remoteStatus.MigratedVolumes
1✔
786
        newVMI.Status.MigrationMethod = remoteStatus.MigrationMethod
1✔
787
        if !apiequality.Semantic.DeepEqual(vmi.Status, newVMI.Status) {
2✔
788
                if err := s.patchVMI(ctx, vmi, newVMI); err != nil {
2✔
789
                        return &syncv1.VMIStatusResponse{
1✔
790
                                Message: fmt.Sprintf("unable to synchronize VMI for migrationID %s", request.MigrationID),
1✔
791
                        }, err
1✔
792
                }
1✔
793
                log.Log.Object(newVMI).With("MigrationID", request.MigrationID).V(5).Info("successfully patched VMI with source state")
1✔
794
        }
795
        log.Log.Object(newVMI).V(5).Info("returning success to grpc caller, source")
1✔
796
        return &syncv1.VMIStatusResponse{
1✔
797
                Message: successMessage,
1✔
798
        }, nil
1✔
799
}
800

801
func (s *SynchronizationController) SyncTargetMigrationStatus(ctx context.Context, request *syncv1.VMIStatusRequest) (*syncv1.VMIStatusResponse, error) {
1✔
802
        if request.VmiStatus == nil || len(request.VmiStatus.VmiStatusJson) == 0 {
2✔
803
                return &syncv1.VMIStatusResponse{
1✔
804
                        Message: noTargetStatusErrorMsg,
1✔
805
                }, fmt.Errorf(noTargetStatusErrorMsg)
1✔
806
        }
1✔
807

808
        migration, err := s.findSourceMigrationFromMigrationID(request.MigrationID)
1✔
809
        if migration == nil {
2✔
810
                return &syncv1.VMIStatusResponse{
1✔
811
                        Message: fmt.Sprintf(unableToLocateVMIMigrationIDErrorMsg, request.MigrationID),
1✔
812
                }, fmt.Errorf(unableToLocateVMIMigrationIDErrorMsg, request.MigrationID)
1✔
813
        }
1✔
814

815
        key := controller.NamespacedKey(migration.Namespace, migration.Spec.VMIName)
1✔
816
        obj, exists, err := s.vmiInformer.GetStore().GetByKey(key)
1✔
817
        if err != nil || !exists {
2✔
818
                if err == nil {
2✔
819
                        err = fmt.Errorf(unableToLocateVMIMigrationIDErrorMsg, request.MigrationID)
1✔
820
                }
1✔
821
                return &syncv1.VMIStatusResponse{
1✔
822
                        Message: fmt.Sprintf(unableToLocateVMIMigrationIDErrorMsg, request.MigrationID),
1✔
823
                }, err
1✔
824
        }
825
        vmi := obj.(*virtv1.VirtualMachineInstance)
1✔
826
        remoteStatus := &virtv1.VirtualMachineInstanceStatus{}
1✔
827
        if err := json.Unmarshal(request.VmiStatus.VmiStatusJson, remoteStatus); err != nil {
2✔
828
                return &syncv1.VMIStatusResponse{
1✔
829
                        Message: fmt.Sprintf("unable to unmarshal vmistatus for migrationID %s", request.MigrationID),
1✔
830
                }, err
1✔
831
        }
1✔
832
        if remoteStatus.MigrationState == nil {
2✔
833
                return &syncv1.VMIStatusResponse{
1✔
834
                        Message: noTargetStatusErrorMsg,
1✔
835
                }, fmt.Errorf(noTargetStatusErrorMsg)
1✔
836
        }
1✔
837
        newVMI := vmi.DeepCopy()
1✔
838
        if newVMI.Status.MigrationState == nil {
2✔
839
                newVMI.Status.MigrationState = &virtv1.VirtualMachineInstanceMigrationState{}
1✔
840
        }
1✔
841

842
        log.Log.Object(newVMI).V(5).Infof("vmi migration target state: %#v", newVMI.Status.MigrationState.TargetState)
1✔
843
        log.Log.Object(newVMI).V(5).Infof("remote migration target state: %#v", remoteStatus.MigrationState.TargetState)
1✔
844
        newVMI.Status.MigrationState.TargetState = remoteStatus.MigrationState.TargetState.DeepCopy()
1✔
845
        copyLegacyTargetFields(newVMI, remoteStatus.MigrationState)
1✔
846
        if !apiequality.Semantic.DeepEqual(vmi.Status.MigrationState, newVMI.Status.MigrationState) {
2✔
847
                if err := s.patchVMI(ctx, vmi, newVMI); err != nil {
2✔
848
                        return &syncv1.VMIStatusResponse{
1✔
849
                                Message: fmt.Sprintf("unable to synchronize VMI for migrationID %s", request.MigrationID),
1✔
850
                        }, err
1✔
851
                }
1✔
852
                log.Log.Object(newVMI).With("MigrationID", request.MigrationID).V(5).Info("successfully patched VMI with target state")
1✔
853
        }
854
        log.Log.Object(newVMI).V(5).Info("returning success to grpc caller, target")
1✔
855
        return &syncv1.VMIStatusResponse{
1✔
856
                Message: successMessage,
1✔
857
        }, nil
1✔
858
}
859

860
func (s *SynchronizationController) patchVMI(ctx context.Context, origVMI, newVMI *virtv1.VirtualMachineInstance) error {
1✔
861
        if origVMI.Status.MigrationState != nil && origVMI.Status.MigrationState.Completed {
1✔
NEW
862
                log.Log.Object(origVMI).V(3).Infof("VMI is completed, skipping patch")
×
NEW
863
                return nil
×
NEW
864
        }
×
865

866
        patchSet := patch.New()
1✔
867

1✔
868
        if !apiequality.Semantic.DeepEqual(origVMI.Labels, newVMI.Labels) {
1✔
NEW
869
                if len(origVMI.Labels) == 0 {
×
NEW
870
                        patchSet.AddOption(
×
NEW
871
                                patch.WithAdd("/metadata/labels", newVMI.Labels))
×
NEW
872
                } else {
×
NEW
873
                        patchSet.AddOption(
×
NEW
874
                                patch.WithTest("/metadata/labels", origVMI.Labels),
×
NEW
875
                                patch.WithReplace("/metadata/labels", newVMI.Labels),
×
NEW
876
                        )
×
877
                }
×
878
        }
879

880
        if !apiequality.Semantic.DeepEqual(origVMI.Status.MigrationMethod, newVMI.Status.MigrationMethod) {
1✔
NEW
881
                if origVMI.Status.MigrationMethod == "" {
×
NEW
882
                        patchSet.AddOption(
×
NEW
883
                                patch.WithAdd("/status/migrationMethod", newVMI.Status.MigrationMethod))
×
NEW
884
                } else {
×
NEW
885
                        patchSet.AddOption(
×
NEW
886
                                patch.WithTest("/status/migrationMethod", origVMI.Status.MigrationMethod),
×
NEW
887
                                patch.WithReplace("/status/migrationMethod", newVMI.Status.MigrationMethod),
×
NEW
888
                        )
×
NEW
889
                }
×
890
        }
891

892
        if !apiequality.Semantic.DeepEqual(origVMI.Status.MigratedVolumes, newVMI.Status.MigratedVolumes) {
1✔
NEW
893
                if origVMI.Status.MigratedVolumes == nil {
×
NEW
894
                        patchSet.AddOption(
×
NEW
895
                                patch.WithAdd("/status/migratedVolumes", newVMI.Status.MigratedVolumes))
×
NEW
896
                } else {
×
NEW
897
                        patchSet.AddOption(
×
NEW
898
                                patch.WithTest("/status/migratedVolumes", origVMI.Status.MigratedVolumes),
×
NEW
899
                                patch.WithReplace("/status/migratedVolumes", newVMI.Status.MigratedVolumes),
×
NEW
900
                        )
×
UNCOV
901
                }
×
902
        }
903

904
        if !apiequality.Semantic.DeepEqual(origVMI.Status.MigrationState, newVMI.Status.MigrationState) {
2✔
905
                if origVMI.Status.MigrationState == nil {
2✔
906
                        patchSet.AddOption(
1✔
907
                                patch.WithAdd("/status/migrationState", newVMI.Status.MigrationState))
1✔
908
                } else {
2✔
909
                        patchSet.AddOption(
1✔
910
                                patch.WithTest("/status/migrationState", origVMI.Status.MigrationState),
1✔
911
                                patch.WithReplace("/status/migrationState", newVMI.Status.MigrationState),
1✔
912
                        )
1✔
913
                }
1✔
914
        }
915
        if !patchSet.IsEmpty() {
2✔
916
                patchBytes, err := patchSet.GeneratePayload()
1✔
917
                if err != nil {
1✔
UNCOV
918
                        return err
×
UNCOV
919
                }
×
920
                log.Log.Object(origVMI).V(3).Infof("patch VMI with %s", string(patchBytes))
1✔
921
                if _, err := s.client.VirtualMachineInstance(origVMI.Namespace).Patch(ctx, origVMI.Name, types.JSONPatchType, patchBytes, metav1.PatchOptions{}); err != nil {
2✔
922
                        return err
1✔
923
                }
1✔
924
        }
925
        return nil
1✔
926
}
927

928
func indexByMigrationUID(obj interface{}) ([]string, error) {
1✔
929
        migration, ok := obj.(*virtv1.VirtualMachineInstanceMigration)
1✔
930
        if !ok {
2✔
931
                return nil, nil
1✔
932
        }
1✔
933
        return []string{string(migration.UID)}, nil
1✔
934
}
935

936
func indexByVmiName(obj interface{}) ([]string, error) {
1✔
937
        migration, ok := obj.(*virtv1.VirtualMachineInstanceMigration)
1✔
938
        if !ok {
2✔
939
                return nil, nil
1✔
940
        }
1✔
941
        return []string{migration.Spec.VMIName}, nil
1✔
942
}
943

944
func indexByTargetMigrationID(obj interface{}) ([]string, error) {
1✔
945
        migration, ok := obj.(*virtv1.VirtualMachineInstanceMigration)
1✔
946
        if !ok {
2✔
947
                return nil, nil
1✔
948
        }
1✔
949
        if migration.Spec.Receive != nil {
2✔
950
                return []string{migration.Spec.Receive.MigrationID}, nil
1✔
951
        }
1✔
952
        return []string{}, nil
1✔
953
}
954

955
func indexBySourceMigrationID(obj interface{}) ([]string, error) {
1✔
956
        migration, ok := obj.(*virtv1.VirtualMachineInstanceMigration)
1✔
957
        if !ok {
2✔
958
                return nil, nil
1✔
959
        }
1✔
960
        if migration.Spec.SendTo != nil {
2✔
961
                return []string{migration.Spec.SendTo.MigrationID}, nil
1✔
962
        }
1✔
963
        return []string{}, nil
1✔
964
}
965

966
func copyLegacyTargetFields(vmi *virtv1.VirtualMachineInstance, migrationState *virtv1.VirtualMachineInstanceMigrationState) {
1✔
967
        targetState := migrationState.TargetState
1✔
968
        vmi.Status.MigrationState.TargetNode = targetState.Node
1✔
969
        if targetState.AttachmentPodUID != nil {
1✔
NEW
970
                vmi.Status.MigrationState.TargetAttachmentPodUID = *targetState.AttachmentPodUID
×
NEW
971
        }
×
972
        vmi.Status.MigrationState.TargetCPUSet = targetState.CPUSet
1✔
973
        vmi.Status.MigrationState.TargetDirectMigrationNodePorts = targetState.DirectMigrationNodePorts
1✔
974
        if targetState.NodeAddress != nil {
1✔
NEW
975
                vmi.Status.MigrationState.TargetNodeAddress = *targetState.NodeAddress
×
NEW
976
        }
×
977
        vmi.Status.MigrationState.TargetNodeDomainDetected = targetState.DomainDetected
1✔
978
        vmi.Status.MigrationState.TargetNodeDomainReadyTimestamp = targetState.DomainReadyTimestamp
1✔
979
        if targetState.NodeTopology != nil {
1✔
NEW
980
                vmi.Status.MigrationState.TargetNodeTopology = *targetState.NodeTopology
×
NEW
981
        }
×
982
        if targetState.PersistentStatePVCName != nil {
1✔
NEW
983
                vmi.Status.MigrationState.TargetPersistentStatePVCName = *targetState.PersistentStatePVCName
×
NEW
984
        }
×
985
        vmi.Status.MigrationState.TargetPod = targetState.Pod
1✔
986
        copyCommonLegacyFields(vmi.Status.MigrationState, migrationState)
1✔
987
        vmi.Status.MigrationState.Completed = migrationState.Completed
1✔
988
        vmi.Status.MigrationState.Failed = migrationState.Failed
1✔
989
}
990

991
func copyLegacySourceFields(vmi *virtv1.VirtualMachineInstance, migrationState *virtv1.VirtualMachineInstanceMigrationState) {
1✔
992
        vmi.Status.MigrationState.SourceNode = migrationState.SourceState.Node
1✔
993
        if migrationState.SourceState.PersistentStatePVCName != nil {
1✔
NEW
994
                vmi.Status.MigrationState.SourcePersistentStatePVCName = *migrationState.SourceState.PersistentStatePVCName
×
NEW
995
        }
×
996
        vmi.Status.MigrationState.SourcePod = migrationState.SourceState.Pod
1✔
997
        copyCommonLegacyFields(vmi.Status.MigrationState, migrationState)
1✔
998
}
999

1000
func copyCommonLegacyFields(targetMigrationState, sourceMigrationState *virtv1.VirtualMachineInstanceMigrationState) {
1✔
1001
        // Copy regular fields.
1✔
1002
        if sourceMigrationState.MigrationPolicyName != nil {
1✔
NEW
1003
                targetMigrationState.MigrationPolicyName = sourceMigrationState.MigrationPolicyName
×
NEW
1004
        }
×
1005
        if sourceMigrationState.MigrationConfiguration != nil {
1✔
NEW
1006
                targetMigrationState.MigrationConfiguration = sourceMigrationState.MigrationConfiguration
×
NEW
1007
        }
×
1008
        if sourceMigrationState.StartTimestamp != nil {
1✔
NEW
1009
                targetMigrationState.StartTimestamp = sourceMigrationState.StartTimestamp
×
NEW
1010
        }
×
1011
        if sourceMigrationState.EndTimestamp != nil {
1✔
NEW
1012
                targetMigrationState.EndTimestamp = sourceMigrationState.StartTimestamp
×
NEW
1013
        }
×
1014
}
1015

NEW
1016
func (s *SynchronizationController) runConnectionCleanup() {
×
NEW
1017
        s.failedCloseConnections.Range(func(k, v interface{}) bool {
×
NEW
1018
                retryCount, ok := v.(int)
×
NEW
1019
                if !ok {
×
NEW
1020
                        log.Log.Warningf("invalid retry count type during connection cleanup: %v", v)
×
NEW
1021
                        s.failedCloseConnections.Delete(k)
×
NEW
1022
                        return true
×
NEW
1023
                }
×
NEW
1024
                if retryCount >= maxCloseRetries {
×
NEW
1025
                        log.Log.Warningf("connection for migrationID %s failed to close after %d retries, not attempting to close again", k, retryCount)
×
NEW
1026
                        s.failedCloseConnections.Delete(k)
×
NEW
1027
                }
×
NEW
1028
                outboundConnection, ok := k.(*SynchronizationConnection)
×
NEW
1029
                if !ok {
×
NEW
1030
                        log.Log.Warningf("invalid outbound connection type during connection cleanup: %v", k)
×
NEW
1031
                        s.failedCloseConnections.Delete(k)
×
NEW
1032
                        return true
×
NEW
1033
                }
×
NEW
1034
                if err := outboundConnection.Close(); err != nil {
×
NEW
1035
                        log.Log.Warningf("unable to close connection for migrationID, trying again: %s, %v", outboundConnection.migrationID, err)
×
NEW
1036
                        s.failedCloseConnections.Store(outboundConnection, retryCount+1)
×
NEW
1037
                } else {
×
NEW
1038
                        s.failedCloseConnections.Delete(k)
×
NEW
1039
                }
×
NEW
1040
                return true
×
1041
        })
1042
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc