1 package storage
2
3 import (
4 "fmt"
5 "strings"
6
7 "github.com/devfile/library/v2/pkg/devfile/generator"
8 v1 "k8s.io/api/apps/v1"
9 corev1 "k8s.io/api/core/v1"
10 "k8s.io/apimachinery/pkg/api/resource"
11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12 "k8s.io/klog"
13
14 "github.com/redhat-developer/odo/pkg/kclient"
15 odolabels "github.com/redhat-developer/odo/pkg/labels"
16 )
17
18
19 type kubernetesClient struct {
20 generic
21 client kclient.ClientInterface
22
23
24
25
26 deployment *v1.Deployment
27 }
28
29 var _ Client = (*kubernetesClient)(nil)
30
31
32 func (k kubernetesClient) Create(storage Storage) error {
33
34 if k.componentName == "" || k.appName == "" {
35 return fmt.Errorf("the component name and the app name should be provided")
36 }
37
38 pvcName, err := generatePVCName(storage.Name, k.componentName, k.appName)
39 if err != nil {
40 return err
41 }
42
43 labels := odolabels.GetLabels(k.componentName, k.appName, k.runtime, odolabels.ComponentDevMode, false)
44 odolabels.AddStorageInfo(labels, storage.Name, strings.Contains(storage.Name, OdoSourceVolume))
45
46 objectMeta := generator.GetObjectMeta(pvcName, k.client.GetCurrentNamespace(), labels, nil)
47
48 quantity, err := resource.ParseQuantity(storage.Spec.Size)
49 if err != nil {
50 return fmt.Errorf("unable to parse size: %v: %w", storage.Spec.Size, err)
51 }
52
53 pvcParams := generator.PVCParams{
54 ObjectMeta: objectMeta,
55 Quantity: quantity,
56 }
57 pvc := generator.GetPVC(pvcParams)
58
59
60 klog.V(2).Infof("Creating a PVC with name %v and labels %v", pvcName, labels)
61 _, err = k.client.CreatePVC(*pvc)
62 if err != nil {
63 return fmt.Errorf("unable to create PVC: %w", err)
64 }
65 return nil
66 }
67
68
69 func (k kubernetesClient) Delete(name string) error {
70 pvcName, err := getPVCNameFromStorageName(k.client, name)
71 if err != nil {
72 return err
73 }
74
75
76 err = k.client.DeletePVC(pvcName)
77 if err != nil {
78 return fmt.Errorf("unable to delete PVC %v: %w", pvcName, err)
79 }
80
81 return nil
82 }
83
84
85 func (k kubernetesClient) List() (StorageList, error) {
86 if k.deployment == nil {
87 var err error
88 selector := odolabels.GetSelector(k.componentName, k.appName, odolabels.ComponentAnyMode, true)
89 k.deployment, err = k.client.GetOneDeploymentFromSelector(selector)
90 if err != nil {
91 if _, ok := err.(*kclient.DeploymentNotFoundError); ok {
92 return StorageList{}, nil
93 }
94 return StorageList{}, err
95 }
96 }
97
98 initContainerVolumeMounts := make(map[string]bool)
99 for _, container := range k.deployment.Spec.Template.Spec.InitContainers {
100 for _, volumeMount := range container.VolumeMounts {
101 initContainerVolumeMounts[volumeMount.Name] = true
102 }
103 }
104
105 containerVolumeMounts := make(map[string]bool)
106 for _, container := range k.deployment.Spec.Template.Spec.Containers {
107 for _, volumeMount := range container.VolumeMounts {
108 containerVolumeMounts[volumeMount.Name] = true
109 }
110 }
111
112 var storage []Storage
113 var volumeMounts []Storage
114 for _, container := range k.deployment.Spec.Template.Spec.Containers {
115 for _, volumeMount := range container.VolumeMounts {
116
117
118
119
120
121 _, initOK := initContainerVolumeMounts[volumeMount.Name]
122 _, ok := containerVolumeMounts[volumeMount.Name]
123 if (!ok && initOK) ||
124 volumeMount.Name == OdoSourceVolume ||
125 volumeMount.Name == SharedDataVolumeName ||
126 strings.HasPrefix(volumeMount.Name, "auto-") {
127 continue
128 }
129
130 volumeMounts = append(volumeMounts, Storage{
131 ObjectMeta: metav1.ObjectMeta{Name: volumeMount.Name},
132 Spec: StorageSpec{
133 Path: volumeMount.MountPath,
134 ContainerName: container.Name,
135 },
136 })
137
138 }
139 }
140
141 if len(volumeMounts) <= 0 {
142 return StorageList{}, nil
143 }
144
145 selector := odolabels.SelectorBuilder().WithComponent(k.componentName).WithoutSourcePVC(OdoSourceVolume).Selector()
146 pvcs, err := k.client.ListPVCs(selector)
147 if err != nil {
148 return StorageList{}, fmt.Errorf("unable to get PVC using selector %q: %w", selector, err)
149 }
150
151
152 validVolumeMounts := make(map[string]bool)
153
154 for _, pvc := range pvcs {
155 found := false
156 for _, volumeMount := range volumeMounts {
157 if volumeMount.Name == pvc.Name+"-vol" {
158
159 validVolumeMounts[volumeMount.Name] = true
160
161 found = true
162 size := pvc.Spec.Resources.Requests[corev1.ResourceStorage]
163 storage = append(storage, NewStorageWithContainer(odolabels.GetDevfileStorageName(pvc.Labels), size.String(), volumeMount.Spec.Path, volumeMount.Spec.ContainerName, nil))
164 }
165 }
166 if !found {
167 return StorageList{}, fmt.Errorf("mount path for pvc %s not found", pvc.Name)
168 }
169 }
170
171
172 for _, volume := range k.deployment.Spec.Template.Spec.Volumes {
173 if volume.PersistentVolumeClaim == nil {
174 validVolumeMounts[volume.Name] = true
175 }
176 }
177
178 for _, volumeMount := range volumeMounts {
179 if _, ok := validVolumeMounts[volumeMount.Name]; !ok {
180 return StorageList{}, fmt.Errorf("pvc not found for mount path %s", volumeMount.Name)
181 }
182 }
183
184 return StorageList{Items: storage}, nil
185 }
186
View as plain text