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

kubevirt / kubevirt / 1ed85bd5-e134-414d-9a03-c2bb1f90bd83

02 Jul 2025 04:52PM UTC coverage: 70.256% (+0.03%) from 70.231%
1ed85bd5-e134-414d-9a03-c2bb1f90bd83

push

prow

web-flow
Merge pull request #15022 from awels/find_migration_network_ip

Use migration network IP adress in synchronization controller

29 of 58 new or added lines in 6 files covered. (50.0%)

4 existing lines in 4 files now uncovered.

67820 of 96533 relevant lines covered (70.26%)

0.78 hits per line

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

68.27
/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
        ip              string
78
        clientTLSConfig *tls.Config
79
        serverTLSConfig *tls.Config
80
        timeout         int
81

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

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

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

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

1✔
119
        syncController.hasSynced = func() bool {
1✔
120
                return vmiInformer.HasSynced() && migrationInformer.HasSynced()
×
121
        }
×
122

123
        syncController.syncOutboundConnectionMap = &sync.Map{}
1✔
124
        syncController.syncReceivingConnectionMap = &sync.Map{}
1✔
125
        syncController.failedCloseConnections = &sync.Map{}
1✔
126

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

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

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

153
        syncController.grpcServer = grpc.NewServer(grpc.Creds(credentials.NewTLS(serverTLSConfig)))
1✔
154
        syncv1.RegisterSynchronizeServer(syncController.grpcServer, syncController)
1✔
155

1✔
156
        return syncController, nil
1✔
157
}
158

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

163
func (s *SynchronizationController) deleteVmiFunc(addObj interface{}) {
×
164
        s.enqueueVirtualMachineInstance(addObj)
×
165
}
×
166

167
func (s *SynchronizationController) updateVmiFunc(_, curr interface{}) {
×
168
        s.enqueueVirtualMachineInstance(curr)
×
169
}
×
170

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

183
func (s *SynchronizationController) addMigrationFunc(addObj interface{}) {
1✔
184
        s.enqueueVirtualMachineInstanceFromMigration(addObj)
1✔
185
}
1✔
186

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

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

227
func (s *SynchronizationController) updateMigrationFunc(_, curr interface{}) {
1✔
228
        s.enqueueVirtualMachineInstanceFromMigration(curr)
1✔
229
}
1✔
230

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

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

×
244
        log.Log.Info("starting vmi status synchronization controller.")
×
245

×
246
        // Wait for cache sync before we start the pod controller
×
247
        cache.WaitForCacheSync(stopCh, s.hasSynced)
×
248

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

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

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

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

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

301
func (s *SynchronizationController) runWorker() {
×
302
        for s.Execute() {
×
303
        }
×
304
}
305

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

312
        defer s.queue.Done(key)
1✔
313
        err := s.execute(key)
1✔
314

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

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

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

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

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

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

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

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

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

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

479
        return nil
1✔
480
}
481

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

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

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

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

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

542
        return nil
1✔
543
}
544

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

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

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

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

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

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

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

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

1✔
701
        client, err := grpc.NewClient(connectionURL, grpc.WithTransportCredentials(credentials.NewTLS(s.clientTLSConfig)))
1✔
702
        return client, err
1✔
703
}
1✔
704

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

721
func (s *SynchronizationController) findTargetMigrationFromMigrationID(migrationID string) (*virtv1.VirtualMachineInstanceMigration, error) {
1✔
722
        return s.findMigrationFromMigrationIDByIndex("byTargetMigrationID", migrationID)
1✔
723
}
1✔
724

725
func (s *SynchronizationController) findSourceMigrationFromMigrationID(migrationID string) (*virtv1.VirtualMachineInstanceMigration, error) {
1✔
726
        return s.findMigrationFromMigrationIDByIndex("bySourceMigrationID", migrationID)
1✔
727
}
1✔
728

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

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

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

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

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

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

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

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

869
        patchSet := patch.New()
1✔
870

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

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

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

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

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

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

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

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

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

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

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

1019
func (s *SynchronizationController) runConnectionCleanup() {
×
1020
        s.failedCloseConnections.Range(func(k, v interface{}) bool {
×
1021
                retryCount, ok := v.(int)
×
1022
                if !ok {
×
1023
                        log.Log.Warningf("invalid retry count type during connection cleanup: %v", v)
×
1024
                        s.failedCloseConnections.Delete(k)
×
1025
                        return true
×
1026
                }
×
1027
                if retryCount >= maxCloseRetries {
×
1028
                        log.Log.Warningf("connection for migrationID %s failed to close after %d retries, not attempting to close again", k, retryCount)
×
1029
                        s.failedCloseConnections.Delete(k)
×
1030
                }
×
1031
                outboundConnection, ok := k.(*SynchronizationConnection)
×
1032
                if !ok {
×
1033
                        log.Log.Warningf("invalid outbound connection type during connection cleanup: %v", k)
×
1034
                        s.failedCloseConnections.Delete(k)
×
1035
                        return true
×
1036
                }
×
1037
                if err := outboundConnection.Close(); err != nil {
×
1038
                        log.Log.Warningf("unable to close connection for migrationID, trying again: %s, %v", outboundConnection.migrationID, err)
×
1039
                        s.failedCloseConnections.Store(outboundConnection, retryCount+1)
×
1040
                } else {
×
1041
                        s.failedCloseConnections.Delete(k)
×
1042
                }
×
1043
                return true
×
1044
        })
1045
}
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