forked from openkruise/agents
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsidecar_config_inject.go
More file actions
234 lines (207 loc) · 9.18 KB
/
sidecar_config_inject.go
File metadata and controls
234 lines (207 loc) · 9.18 KB
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package core
import (
"context"
"encoding/json"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
agentsv1alpha1 "github.com/openkruise/agents/api/v1alpha1"
"github.com/openkruise/agents/pkg/utils/webhookutils"
)
func enableInjectCsiMountConfig(sandbox *agentsv1alpha1.Sandbox) bool {
return sandbox.Annotations[agentsv1alpha1.ShouldInjectCsiMount] == "true"
}
func enableInjectAgentRuntimeConfig(sandbox *agentsv1alpha1.Sandbox) bool {
return sandbox.Annotations[agentsv1alpha1.ShouldInjectAgentRuntime] == "true"
}
// fetchInjectionConfiguration retrieves the sidecar injection configuration from the ConfigMap.
// It attempts to fetch the ConfigMap named sandboxInjectionConfigName from the sandbox-system namespace.
// If the ConfigMap exists, it returns the data map containing configuration keys like
// KEY_CSI_INJECTION_CONFIG and KEY_RUNTIME_INJECTION_CONFIG. If the ConfigMap is not found
// or any error occurs during retrieval, it returns nil data and no error.
//
// Parameters:
// - ctx: context.Context for the operation
// - client: Kubernetes client.Interface used to retrieve the ConfigMap
//
// Returns:
// - map[string]string: The configuration data from the ConfigMap, or nil if not found or on error
// - error: Always returns nil (errors are logged but not propagated)
func fetchInjectionConfiguration(ctx context.Context, cli client.Client) (map[string]string, error) {
logger := logf.FromContext(ctx)
config := &corev1.ConfigMap{}
err := cli.Get(ctx, types.NamespacedName{
Namespace: webhookutils.GetNamespace(), // Todo considering the security concern and rbac issue
Name: sandboxInjectionConfigName,
}, config)
if err != nil {
if errors.IsNotFound(err) {
logger.Info("injection configuration not found, skip injection")
return map[string]string{}, nil
}
return map[string]string{}, err
}
return config.Data, nil
}
// parseInjectConfig parses the sidecar injection configuration from raw ConfigMap data for a specific config key.
// It extracts the value associated with the given configKey (e.g., KEY_CSI_INJECTION_CONFIG or KEY_RUNTIME_INJECTION_CONFIG)
// and unmarshals it into a SidecarInjectConfig struct. If the key is not present in the configRaw map or the value
// is empty, it returns an empty SidecarInjectConfig without error. The configuration includes the main container
// settings, sidecar containers list (CSI sidecars or runtime init containers), and volumes to be injected.
//
// Parameters:
// - ctx: context.Context for the operation, used for logging
// - configKey: string representing the configuration key to look up (KEY_CSI_INJECTION_CONFIG or KEY_RUNTIME_INJECTION_CONFIG)
// - configRaw: map[string]string containing the raw ConfigMap data with potential injection config
//
// Returns:
// - SidecarInjectConfig: The parsed configuration containing main container, sidecars, and volumes
// - error: An error if JSON unmarshaling fails, nil otherwise or when config key is missing/empty
func parseInjectConfig(ctx context.Context, configKey string, configRaw map[string]string) (SidecarInjectConfig, error) {
log := logf.FromContext(ctx)
sidecarConfig := SidecarInjectConfig{}
configValue, exists := configRaw[configKey]
if !exists || configValue == "" {
log.Info("config key not found or empty, using default configuration")
return sidecarConfig, nil
}
err := json.Unmarshal([]byte(configRaw[configKey]), &sidecarConfig)
if err != nil {
log.Error(err, "failed to unmarshal sidecar config for the %v", configKey)
return sidecarConfig, err
}
return sidecarConfig, nil
}
// setCSIMountContainer injects CSI mount configurations into the SandboxTemplate's pod spec.
// It configures the main container (first container in the spec) with CSI sidecar settings,
// appends additional CSI sidecar containers, and mounts shared volumes.
// Volumes are only added if they don't already exist in the template.
func setCSIMountContainer(ctx context.Context, template *corev1.PodTemplateSpec, config SidecarInjectConfig) {
log := logf.FromContext(ctx)
// set main container, the first container is the main container
if len(template.Spec.Containers) == 0 {
log.Info("no container found in sidecar template")
return
}
mainContainer := &template.Spec.Containers[0]
setMainContainerWhenInjectCSISidecar(mainContainer, config)
// set csi sidecars
for _, csiSidecar := range config.Sidecars {
template.Spec.Containers = append(template.Spec.Containers, csiSidecar)
}
// set share volume
if len(config.Volumes) > 0 {
if template.Spec.Volumes == nil {
template.Spec.Volumes = make([]corev1.Volume, 0, len(config.Volumes))
}
for _, vol := range config.Volumes {
if findVolumeByName(template.Spec.Volumes, vol.Name) {
continue
}
template.Spec.Volumes = append(template.Spec.Volumes, vol)
}
}
}
// setMainContainerWhenInjectCSISidecar configures the main container with environment variables and volume mounts from the CSI sidecar configuration.
// It appends environment variables and volume mounts to the main container, skipping any that already exist (matched by name) to avoid duplicates.
func setMainContainerWhenInjectCSISidecar(mainContainer *corev1.Container, config SidecarInjectConfig) {
// append some envs in main container when processing csi mount
if mainContainer.Env == nil {
mainContainer.Env = make([]corev1.EnvVar, 0, 1)
}
for _, env := range config.MainContainer.Env {
if findEnvByName(mainContainer.Env, env.Name) {
continue
}
mainContainer.Env = append(mainContainer.Env, env)
}
// append some volumeMounts config in main container
if config.MainContainer.VolumeMounts != nil {
if mainContainer.VolumeMounts == nil {
mainContainer.VolumeMounts = make([]corev1.VolumeMount, 0, len(config.MainContainer.VolumeMounts))
}
for _, volMount := range config.MainContainer.VolumeMounts {
if findVolumeMountByName(mainContainer.VolumeMounts, volMount.Name) {
continue
}
mainContainer.VolumeMounts = append(mainContainer.VolumeMounts, volMount)
}
}
}
// setAgentRuntimeContainer injects agent runtime configurations into the SandboxTemplate's pod spec.
// It appends agent runtime containers as init containers and configures the main container (first container) with runtime settings.
// The init containers run before the main containers to prepare the runtime environment.
func setAgentRuntimeContainer(ctx context.Context, template *corev1.PodTemplateSpec, config SidecarInjectConfig) {
log := logf.FromContext(ctx)
// append init agent runtime container
if template.Spec.InitContainers == nil {
template.Spec.InitContainers = make([]corev1.Container, 0, 1)
}
template.Spec.InitContainers = append(template.Spec.InitContainers, config.Sidecars...)
if len(template.Spec.Containers) == 0 {
log.Info("no container found in sidecar template for agent runtime")
return
}
mainContainer := &template.Spec.Containers[0]
setMainContainerConfigWhenInjectRuntimeSidecar(ctx, mainContainer, config)
template.Spec.Volumes = append(template.Spec.Volumes, config.Volumes...)
}
func setMainContainerConfigWhenInjectRuntimeSidecar(ctx context.Context, mainContainer *corev1.Container, config SidecarInjectConfig) {
log := logf.FromContext(ctx)
// Check if main container already has a postStart hook
if mainContainer.Lifecycle != nil && mainContainer.Lifecycle.PostStart != nil {
if config.MainContainer.Lifecycle != nil && config.MainContainer.Lifecycle.PostStart != nil {
log.Error(nil, "conflicting postStart hooks detected, main container already has a postStart hook defined",
"existingHook", mainContainer.Lifecycle.PostStart,
"injectedHook", config.MainContainer.Lifecycle.PostStart)
}
} else {
// set main container lifecycle
if mainContainer.Lifecycle == nil {
mainContainer.Lifecycle = &corev1.Lifecycle{}
}
if mainContainer.Lifecycle.PostStart == nil {
mainContainer.Lifecycle.PostStart = &corev1.LifecycleHandler{}
}
// Main container doesn't have postStart, apply config if available
if config.MainContainer.Lifecycle != nil && config.MainContainer.Lifecycle.PostStart != nil {
mainContainer.Lifecycle.PostStart = config.MainContainer.Lifecycle.PostStart
}
}
// set main container env
if mainContainer.Env == nil {
mainContainer.Env = make([]corev1.EnvVar, 0, len(config.MainContainer.Env))
}
mainContainer.Env = append(mainContainer.Env, config.MainContainer.Env...)
// set main container volumeMounts
if mainContainer.VolumeMounts == nil {
mainContainer.VolumeMounts = make([]corev1.VolumeMount, 0, len(config.MainContainer.VolumeMounts))
}
mainContainer.VolumeMounts = append(mainContainer.VolumeMounts, config.MainContainer.VolumeMounts...)
}
func findVolumeMountByName(volumeMounts []corev1.VolumeMount, name string) bool {
for _, volumeMount := range volumeMounts {
if volumeMount.Name == name {
return true
}
}
return false
}
func findVolumeByName(volumes []corev1.Volume, name string) bool {
for _, volume := range volumes {
if volume.Name == name {
return true
}
}
return false
}
func findEnvByName(envs []corev1.EnvVar, name string) bool {
for _, env := range envs {
if env.Name == name {
return true
}
}
return false
}