createDV 被 DataVolumeController 处理。代码入口为 /cmd/cdi-operator/operator.go : main()
/cmd/cdi-controller.go main() - startLeaderElection() - start(ctx, cfg)
在 start 函数中将所有 controller 与 manager 绑定,包括 DatavolumeController。
通过NewDatavolumeController
创建DVController,其中 addDatavolumeControllerWatches,addCloneWithoutSourceWatch 会监听 pod、 pvc、 dv、 sc 的状态变化,将相关dv.name+dv.ns.name加入到reconcile.Request队列中。
其中reconciler处理queue中的dv,使其seq与status一致。该段代码需要根据dv 决定是否要进行删除操作等如果dv.source.pvc != nil 则会进入clone流程reconcileClone

首先获取相关的 dv.obj 若dv.DeletionTimestamp != nil 则删除相关的所有 pvc pod dv 以及相关的ObjectTransfer。
如果 dv 设置了 sourceRef 会将 dv.Spec.Source 填充为相应的 pvc。sourceRef是一种使用最新 datasource 的模式,在创建时使用最新 pvc。
1
2
3
|
dv.Spec.Source = &cdiv1.DataVolumeSource{
PVC: dataSource.Spec.Source.PVC,
}
|
尝试获取dv同名的的PVC。
1
2
3
4
5
6
7
|
pvc := &corev1.PersistentVolumeClaim{}
if err := r.client.Get(context.TODO(), types.NamespacedName{Namespace: datavolume.Namespace, Name: datavolume.Name}, pvc); err != nil {
// If the resource doesn't exist, we'll create it
if k8serrors.IsNotFound(err) {
pvc = nil
}
...
|
若存在则检查该pvc 相关 ann 是否为了 dvname ,并确认是否该pvc 的 owner已经设置了,如果未设置则进行设置。
这时如果 dv 的 source.pvc != nil 则进入 reconcileclone 克隆流程,其中参数包括 dvPrePopulated。
dvPrePopulated 这个概念在 \staging\src\kubevirt.io\containerized-data-importer-api\pkg\apis\core\v1alpha1\utils.go 中有介绍,dv 对应的 target pvc 在 dv status 为 succeeded 后和未被 owned 的时候为 dvPrePopulated ==true。
1
2
3
4
5
|
_, dvPrePopulated := datavolume.Annotations[AnnPrePopulated]
if isClone := datavolume.Spec.Source.PVC != nil; isClone {
return r.reconcileClone(log, datavolume, pvc, pvcSpec, transferName, dvPrePopulated, pvcPopulated)
}
|
若isNotClone 且 dvPrePopulated 为 false 则进入准备,如创建 dv 同名的 pvc 等操作。
在 reconcileClone 流程中会进行一系列的检查并且确定克隆策略,根据策略进入 reconcileSmartClonePvc 的流程中。

首先检查检查源pvc是否存在dv和源pvc ContentType是否一致dv size是否符合 。
接着查看dv的克隆策略默认 snapshot 源 pvc 的 storgeclass 根据 name 获得storageProfile.Status.CloneStrategy 若无特殊设定则返回默认策略。
根据源pvc尝试设置目标pvc spec的size。
进入smartClone的reconcile。
该段代码主要判断是否需要进入跨 ns 克隆流程,并且根据源 pvc 创建 snapshot。可以注意到这时创建了snapshot 但是 target pvc 还未创建,个人认为后续逻辑在 smart-clone controller 中进行。

smart-clone-controller 的主要逻辑也在 /pkg/controller/smart-clone-controller.go。 reconcile中这段代码主要逻辑是判断 dv 的 target PVC 是否存在,如果存在则进入 reconcilePVC 如果不存在再去检查 snapshot 是否存在,从上面的步骤中我们可以知道 snapshot 是存在的 pvc 还未建立,这时候进入 reconcileSnapshot 的逻辑。然后创建 target pvc。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
func (r *SmartCloneReconciler) reconcileSnapshot(log logr.Logger, snapshot *snapshotv1.VolumeSnapshot) (reconcile.Result, error) {
log.WithValues("snapshot.Name", snapshot.Name).
WithValues("snapshot.Namespace", snapshot.Namespace).
Info("Reconciling snapshot")
// …
// pvc may have been transferred
//这里如果pvc isnotfound会返回nil 不会返回err进入后续代码
targetPVC, err := r.getTargetPVC(dataVolume)
if err != nil {
return reconcile.Result{}, err
}
if targetPVC != nil {
return reconcile.Result{}, nil
}
if snapshot.Status == nil || snapshot.Status.ReadyToUse == nil || !*snapshot.Status.ReadyToUse {
// wait for ready to use
return reconcile.Result{}, nil
}
//dv.spec.pvc or dv.spec.storage
targetPvcSpec, err := RenderPvcSpec(r.client, r.recorder, r.log, dataVolume)
if err != nil {
return reconcile.Result{}, err
}
newPvc, err := newPvcFromSnapshot(snapshot, targetPvcSpec)
if err != nil {
return reconcile.Result{}, err
}
util.SetRecommendedLabels(newPvc, r.installerLabels, "cdi-controller")
if err := setAnnOwnedByDataVolume(newPvc, dataVolume); err != nil {
return reconcile.Result{}, err
}
if len(dataVolume.GetAnnotations()) > 0 {
for k, v := range dataVolume.GetAnnotations() {
if !strings.Contains(k, common.CDIAnnKey) {
newPvc.Annotations[k] = v
}
}
}
if snapshot.Spec.Source.PersistentVolumeClaimName != nil {
event := &DataVolumeEvent{
eventType: corev1.EventTypeNormal,
reason: SmartClonePVCInProgress,
message: fmt.Sprintf(MessageSmartClonePVCInProgress, snapshot.Namespace, *snapshot.Spec.Source.PersistentVolumeClaimName),
}
r.emitEvent(snapshot, event)
}
log.V(3).Info("Creating PVC from snapshot", "pvc.Namespace", newPvc.Namespace, "pvc.Name", newPvc.Name)
//创建pvc 应该整个smart-clone的流程走完了。
if err := r.client.Create(context.TODO(), newPvc); err != nil {
if errQuotaExceeded(err) {
event := &DataVolumeEvent{
eventType: corev1.EventTypeWarning,
reason: ErrExceededQuota,
message: err.Error(),
}
r.emitEvent(snapshot, event)
}
log.Error(err, "error creating pvc from snapshot")
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
|