podman

Форк
0
325 строк · 10.4 Кб
1
//go:build !remote
2

3
package kube
4

5
import (
6
	"errors"
7
	"fmt"
8
	"io/fs"
9
	"os"
10

11
	"github.com/containers/common/pkg/parse"
12
	"github.com/containers/common/pkg/secrets"
13
	"github.com/containers/podman/v5/libpod"
14
	v1 "github.com/containers/podman/v5/pkg/k8s.io/api/core/v1"
15
	"github.com/containers/storage/pkg/fileutils"
16

17
	"github.com/sirupsen/logrus"
18
	"sigs.k8s.io/yaml"
19
)
20

21
const (
22
	// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
23
	kubeDirectoryPermission = 0755
24
	// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
25
	kubeFilePermission = 0644
26
)
27

28
//nolint:revive
29
type KubeVolumeType int
30

31
const (
32
	KubeVolumeTypeBindMount KubeVolumeType = iota
33
	KubeVolumeTypeNamed
34
	KubeVolumeTypeConfigMap
35
	KubeVolumeTypeBlockDevice
36
	KubeVolumeTypeCharDevice
37
	KubeVolumeTypeSecret
38
	KubeVolumeTypeEmptyDir
39
	KubeVolumeTypeEmptyDirTmpfs
40
)
41

42
//nolint:revive
43
type KubeVolume struct {
44
	// Type of volume to create
45
	Type KubeVolumeType
46
	// Path for bind mount or volume name for named volume
47
	Source string
48
	// Items to add to a named volume created where the key is the file name and the value is the data
49
	// This is only used when there are volumes in the yaml that refer to a configmap
50
	// Example: if configmap has data "SPECIAL_LEVEL: very" then the file name is "SPECIAL_LEVEL" and the
51
	// data in that file is "very".
52
	Items map[string][]byte
53
	// If the volume is optional, we can move on if it is not found
54
	// Only used when there are volumes in a yaml that refer to a configmap
55
	Optional bool
56
	// DefaultMode sets the permissions on files created for the volume
57
	// This is optional and defaults to 0644
58
	DefaultMode int32
59
}
60

61
// Create a KubeVolume from an HostPathVolumeSource
62
func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource, mountLabel string) (*KubeVolume, error) {
63
	if hostPath.Type != nil {
64
		switch *hostPath.Type {
65
		case v1.HostPathDirectoryOrCreate:
66
			if err := os.MkdirAll(hostPath.Path, kubeDirectoryPermission); err != nil {
67
				return nil, err
68
			}
69
			// Label a newly created volume
70
			if err := libpod.LabelVolumePath(hostPath.Path, mountLabel); err != nil {
71
				return nil, fmt.Errorf("giving %s a label: %w", hostPath.Path, err)
72
			}
73
		case v1.HostPathFileOrCreate:
74
			if err := fileutils.Exists(hostPath.Path); errors.Is(err, fs.ErrNotExist) {
75
				f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission)
76
				if err != nil {
77
					return nil, fmt.Errorf("creating HostPath: %w", err)
78
				}
79
				if err := f.Close(); err != nil {
80
					logrus.Warnf("Error in closing newly created HostPath file: %v", err)
81
				}
82
			}
83
			// unconditionally label a newly created volume
84

85
			if err := libpod.LabelVolumePath(hostPath.Path, mountLabel); err != nil {
86
				return nil, fmt.Errorf("giving %s a label: %w", hostPath.Path, err)
87
			}
88
		case v1.HostPathSocket:
89
			st, err := os.Stat(hostPath.Path)
90
			if err != nil {
91
				return nil, fmt.Errorf("checking HostPathSocket: %w", err)
92
			}
93
			if st.Mode()&os.ModeSocket != os.ModeSocket {
94
				return nil, fmt.Errorf("checking HostPathSocket: path %s is not a socket", hostPath.Path)
95
			}
96
		case v1.HostPathBlockDev:
97
			dev, err := os.Stat(hostPath.Path)
98
			if err != nil {
99
				return nil, fmt.Errorf("checking HostPathBlockDevice: %w", err)
100
			}
101
			if dev.Mode()&os.ModeCharDevice == os.ModeCharDevice {
102
				return nil, fmt.Errorf("checking HostPathDevice: path %s is not a block device", hostPath.Path)
103
			}
104
			return &KubeVolume{
105
				Type:   KubeVolumeTypeBlockDevice,
106
				Source: hostPath.Path,
107
			}, nil
108
		case v1.HostPathCharDev:
109
			dev, err := os.Stat(hostPath.Path)
110
			if err != nil {
111
				return nil, fmt.Errorf("checking HostPathCharDevice: %w", err)
112
			}
113
			if dev.Mode()&os.ModeCharDevice != os.ModeCharDevice {
114
				return nil, fmt.Errorf("checking HostPathCharDevice: path %s is not a character device", hostPath.Path)
115
			}
116
			return &KubeVolume{
117
				Type:   KubeVolumeTypeCharDevice,
118
				Source: hostPath.Path,
119
			}, nil
120
		case v1.HostPathDirectory:
121
		case v1.HostPathFile:
122
		case v1.HostPathUnset:
123
			// do nothing here because we will verify the path exists in validateVolumeHostDir
124
			break
125
		default:
126
			return nil, fmt.Errorf("invalid HostPath type %v", hostPath.Type)
127
		}
128
	}
129

130
	if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil {
131
		return nil, fmt.Errorf("in parsing HostPath in YAML: %w", err)
132
	}
133

134
	return &KubeVolume{
135
		Type:   KubeVolumeTypeBindMount,
136
		Source: hostPath.Path,
137
	}, nil
138
}
139

140
// VolumeFromSecret creates a new kube volume from a kube secret.
141
func VolumeFromSecret(secretSource *v1.SecretVolumeSource, secretsManager *secrets.SecretsManager) (*KubeVolume, error) {
142
	kv := &KubeVolume{
143
		Type:        KubeVolumeTypeSecret,
144
		Source:      secretSource.SecretName,
145
		Items:       map[string][]byte{},
146
		DefaultMode: v1.SecretVolumeSourceDefaultMode,
147
	}
148
	// Set the defaultMode if set in the kube yaml
149
	validMode, err := isValidDefaultMode(secretSource.DefaultMode)
150
	if err != nil {
151
		return nil, fmt.Errorf("invalid DefaultMode for secret %q: %w", secretSource.SecretName, err)
152
	}
153
	if validMode {
154
		kv.DefaultMode = *secretSource.DefaultMode
155
	}
156

157
	// returns a byte array of a kube secret data, meaning this needs to go into a string map
158
	_, secretByte, err := secretsManager.LookupSecretData(secretSource.SecretName)
159
	if err != nil {
160
		if errors.Is(err, secrets.ErrNoSuchSecret) && secretSource.Optional != nil && *secretSource.Optional {
161
			kv.Optional = true
162
			return kv, nil
163
		}
164
		return nil, err
165
	}
166

167
	secret := &v1.Secret{}
168

169
	err = yaml.Unmarshal(secretByte, secret)
170
	if err != nil {
171
		return nil, err
172
	}
173

174
	// If there are Items specified in the volumeSource, that overwrites the Data from the Secret
175
	if len(secretSource.Items) > 0 {
176
		for _, item := range secretSource.Items {
177
			if val, ok := secret.Data[item.Key]; ok {
178
				kv.Items[item.Path] = val
179
			} else if val, ok := secret.StringData[item.Key]; ok {
180
				kv.Items[item.Path] = []byte(val)
181
			}
182
		}
183
	} else {
184
		// add key: value pairs to the items array
185
		for key, entry := range secret.Data {
186
			kv.Items[key] = entry
187
		}
188

189
		for key, entry := range secret.StringData {
190
			kv.Items[key] = []byte(entry)
191
		}
192
	}
193

194
	return kv, nil
195
}
196

197
// Create a KubeVolume from a PersistentVolumeClaimVolumeSource
198
func VolumeFromPersistentVolumeClaim(claim *v1.PersistentVolumeClaimVolumeSource) (*KubeVolume, error) {
199
	return &KubeVolume{
200
		Type:   KubeVolumeTypeNamed,
201
		Source: claim.ClaimName,
202
	}, nil
203
}
204

205
func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, configMaps []v1.ConfigMap) (*KubeVolume, error) {
206
	var configMap *v1.ConfigMap
207
	kv := &KubeVolume{
208
		Type:        KubeVolumeTypeConfigMap,
209
		Items:       map[string][]byte{},
210
		DefaultMode: v1.ConfigMapVolumeSourceDefaultMode,
211
	}
212
	for _, cm := range configMaps {
213
		if cm.Name == configMapVolumeSource.Name {
214
			matchedCM := cm
215
			// Set the source to the config map name
216
			kv.Source = cm.Name
217
			configMap = &matchedCM
218
			break
219
		}
220
	}
221
	// Set the defaultMode if set in the kube yaml
222
	validMode, err := isValidDefaultMode(configMapVolumeSource.DefaultMode)
223
	if err != nil {
224
		return nil, fmt.Errorf("invalid DefaultMode for configMap %q: %w", configMapVolumeSource.Name, err)
225
	}
226
	if validMode {
227
		kv.DefaultMode = *configMapVolumeSource.DefaultMode
228
	}
229

230
	if configMap == nil {
231
		// If the volumeSource was optional, move on even if a matching configmap wasn't found
232
		if configMapVolumeSource.Optional != nil && *configMapVolumeSource.Optional {
233
			kv.Source = configMapVolumeSource.Name
234
			kv.Optional = *configMapVolumeSource.Optional
235
			return kv, nil
236
		}
237
		return nil, fmt.Errorf("no such ConfigMap %q", configMapVolumeSource.Name)
238
	}
239

240
	// don't allow keys from "data" and "binaryData" to overlap
241
	for k := range configMap.Data {
242
		if _, ok := configMap.BinaryData[k]; ok {
243
			return nil, fmt.Errorf("the ConfigMap %q is invalid: duplicate key %q present in data and binaryData", configMap.Name, k)
244
		}
245
	}
246

247
	// If there are Items specified in the volumeSource, that overwrites the Data from the configmap
248
	if len(configMapVolumeSource.Items) > 0 {
249
		for _, item := range configMapVolumeSource.Items {
250
			if val, ok := configMap.Data[item.Key]; ok {
251
				kv.Items[item.Path] = []byte(val)
252
			} else if val, ok := configMap.BinaryData[item.Key]; ok {
253
				kv.Items[item.Path] = val
254
			}
255
		}
256
	} else {
257
		for k, v := range configMap.Data {
258
			kv.Items[k] = []byte(v)
259
		}
260
		for k, v := range configMap.BinaryData {
261
			kv.Items[k] = v
262
		}
263
	}
264
	return kv, nil
265
}
266

267
// Create a kubeVolume for an emptyDir volume
268
func VolumeFromEmptyDir(emptyDirVolumeSource *v1.EmptyDirVolumeSource, name string) (*KubeVolume, error) {
269
	if emptyDirVolumeSource.Medium == v1.StorageMediumMemory {
270
		return &KubeVolume{
271
			Type:   KubeVolumeTypeEmptyDirTmpfs,
272
			Source: name,
273
		}, nil
274
	} else {
275
		return &KubeVolume{
276
			Type:   KubeVolumeTypeEmptyDir,
277
			Source: name,
278
		}, nil
279
	}
280
}
281

282
// Create a KubeVolume from one of the supported VolumeSource
283
func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager, volName, mountLabel string) (*KubeVolume, error) {
284
	switch {
285
	case volumeSource.HostPath != nil:
286
		return VolumeFromHostPath(volumeSource.HostPath, mountLabel)
287
	case volumeSource.PersistentVolumeClaim != nil:
288
		return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim)
289
	case volumeSource.ConfigMap != nil:
290
		return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps)
291
	case volumeSource.Secret != nil:
292
		return VolumeFromSecret(volumeSource.Secret, secretsManager)
293
	case volumeSource.EmptyDir != nil:
294
		return VolumeFromEmptyDir(volumeSource.EmptyDir, volName)
295
	default:
296
		return nil, errors.New("HostPath, ConfigMap, EmptyDir, Secret, and PersistentVolumeClaim are currently the only supported VolumeSource")
297
	}
298
}
299

300
// Create a map of volume name to KubeVolume
301
func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager, mountLabel string) (map[string]*KubeVolume, error) {
302
	volumes := make(map[string]*KubeVolume)
303

304
	for _, specVolume := range specVolumes {
305
		volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager, specVolume.Name, mountLabel)
306
		if err != nil {
307
			return nil, fmt.Errorf("failed to create volume %q: %w", specVolume.Name, err)
308
		}
309

310
		volumes[specVolume.Name] = volume
311
	}
312

313
	return volumes, nil
314
}
315

316
// isValidDefaultMode returns true if mode is between 0 and 0777
317
func isValidDefaultMode(mode *int32) (bool, error) {
318
	if mode == nil {
319
		return false, nil
320
	}
321
	if *mode >= 0 && *mode <= int32(os.ModePerm) {
322
		return true, nil
323
	}
324
	return false, errors.New("must be between 0000 and 0777")
325
}
326

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.