1 package configAutomount
2
3 import (
4 "fmt"
5 "path/filepath"
6 "sort"
7 "strconv"
8
9 "github.com/redhat-developer/odo/pkg/kclient"
10 )
11
12 const (
13 labelMountName = "devfile.io/auto-mount"
14 labelMountValue = "true"
15
16 annotationMountPathName = "devfile.io/mount-path"
17 annotationMountAsName = "devfile.io/mount-as"
18 annotationReadOnlyName = "devfile.io/read-only"
19 annotationMountAccessMode = "devfile.io/mount-access-mode"
20 )
21
22 type KubernetesClient struct {
23 kubeClient kclient.ClientInterface
24 }
25
26 func NewKubernetesClient(kubeClient kclient.ClientInterface) KubernetesClient {
27 return KubernetesClient{
28 kubeClient: kubeClient,
29 }
30 }
31
32 func (o KubernetesClient) GetAutomountingVolumes() ([]AutomountInfo, error) {
33 var result []AutomountInfo
34
35 pvcs, err := o.getAutomountingPVCs()
36 if err != nil {
37 return nil, err
38 }
39 result = append(result, pvcs...)
40
41 secrets, err := o.getAutomountingSecrets()
42 if err != nil {
43 return nil, err
44 }
45 result = append(result, secrets...)
46
47 cms, err := o.getAutomountingConfigmaps()
48 if err != nil {
49 return nil, err
50 }
51 result = append(result, cms...)
52
53 return result, nil
54 }
55
56 func (o KubernetesClient) getAutomountingPVCs() ([]AutomountInfo, error) {
57 pvcs, err := o.kubeClient.ListPVCs(labelMountName + "=" + labelMountValue)
58 if err != nil {
59 return nil, err
60 }
61
62 var result []AutomountInfo
63 for _, pvc := range pvcs {
64 mountPath := filepath.ToSlash(filepath.Join("/", "tmp", pvc.Name))
65 if val, found := getMountPathFromAnnotation(pvc.Annotations); found {
66 mountPath = val
67 }
68 result = append(result, AutomountInfo{
69 VolumeType: VolumeTypePVC,
70 VolumeName: pvc.Name,
71 MountPath: mountPath,
72 MountAs: MountAsFile,
73 ReadOnly: pvc.Annotations[annotationReadOnlyName] == "true",
74 })
75 }
76 return result, nil
77 }
78
79 func (o KubernetesClient) getAutomountingSecrets() ([]AutomountInfo, error) {
80 secrets, err := o.kubeClient.ListSecrets(labelMountName + "=" + labelMountValue)
81 if err != nil {
82 return nil, err
83 }
84
85 var result []AutomountInfo
86 for _, secret := range secrets {
87 mountAs := getMountAsFromAnnotation(secret.Annotations)
88 mountPath := filepath.ToSlash(filepath.Join("/", "etc", "secret", secret.Name))
89 var keys []string
90 if val, found := getMountPathFromAnnotation(secret.Annotations); found {
91 mountPath = val
92 }
93 if mountAs == MountAsEnv {
94 mountPath = ""
95 }
96 if mountAs == MountAsSubpath {
97 for k := range secret.Data {
98 keys = append(keys, k)
99 }
100 sort.Strings(keys)
101 }
102
103 mountAccessMode, err := getMountAccessModeFromAnnotation(secret.Annotations)
104 if err != nil {
105 return nil, err
106 }
107
108 result = append(result, AutomountInfo{
109 VolumeType: VolumeTypeSecret,
110 VolumeName: secret.Name,
111 MountPath: mountPath,
112 MountAs: mountAs,
113 ReadOnly: secret.Annotations[annotationReadOnlyName] == "true",
114 Keys: keys,
115 MountAccessMode: mountAccessMode,
116 })
117 }
118 return result, nil
119 }
120
121 func (o KubernetesClient) getAutomountingConfigmaps() ([]AutomountInfo, error) {
122 cms, err := o.kubeClient.ListConfigMaps(labelMountName + "=" + labelMountValue)
123 if err != nil {
124 return nil, err
125 }
126
127 var result []AutomountInfo
128 for _, cm := range cms {
129 mountAs := getMountAsFromAnnotation(cm.Annotations)
130 mountPath := filepath.ToSlash(filepath.Join("/", "etc", "config", cm.Name))
131 var keys []string
132 if val, found := getMountPathFromAnnotation(cm.Annotations); found {
133 mountPath = val
134 }
135 if mountAs == MountAsEnv {
136 mountPath = ""
137 }
138 if mountAs == MountAsSubpath {
139 for k := range cm.Data {
140 keys = append(keys, k)
141 }
142 sort.Strings(keys)
143 }
144
145 mountAccessMode, err := getMountAccessModeFromAnnotation(cm.Annotations)
146 if err != nil {
147 return nil, err
148 }
149
150 result = append(result, AutomountInfo{
151 VolumeType: VolumeTypeConfigmap,
152 VolumeName: cm.Name,
153 MountPath: mountPath,
154 MountAs: mountAs,
155 ReadOnly: cm.Annotations[annotationReadOnlyName] == "true",
156 Keys: keys,
157 MountAccessMode: mountAccessMode,
158 })
159 }
160 return result, nil
161 }
162
163 func getMountPathFromAnnotation(annotations map[string]string) (string, bool) {
164 val, found := annotations[annotationMountPathName]
165 return val, found
166 }
167
168 func getMountAsFromAnnotation(annotations map[string]string) MountAs {
169 switch annotations[annotationMountAsName] {
170 case "subpath":
171 return MountAsSubpath
172 case "env":
173 return MountAsEnv
174 default:
175 return MountAsFile
176 }
177 }
178
179 func getMountAccessModeFromAnnotation(annotations map[string]string) (*int32, error) {
180 accessModeStr, found := annotations[annotationMountAccessMode]
181 if !found {
182 return nil, nil
183 }
184 accessMode64, err := strconv.ParseInt(accessModeStr, 0, 32)
185 if err != nil {
186 return nil, err
187 }
188
189 if accessMode64 < 0 || accessMode64 > 0777 {
190 return nil, fmt.Errorf("invalid access mode annotation: value '%s' parsed to %o (octal). Must be in range 0000-0777", accessModeStr, accessMode64)
191 }
192
193 accessMode32 := int32(accessMode64)
194 return &accessMode32, nil
195 }
196
View as plain text