Skip to content

Commit 8cb8853

Browse files
committed
using configmap to inject runtime and csi sideacr
Signed-off-by: jicheng.sk <jicheng.sk@alibaba-inc.com>
1 parent 4d5825c commit 8cb8853

File tree

11 files changed

+1798
-12
lines changed

11 files changed

+1798
-12
lines changed

api/v1alpha1/sandbox_types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ const (
3535
// Note: SandboxSet creates sandboxes with priority 0 by default.
3636
// Sandbox Manager or Sandbox Claim creates high-priority sandboxes by default.
3737
SandboxAnnotationPriority = "agents.kruise.io/sandbox-priority"
38+
39+
// ShouldInjectCsiMount is the annotation key for inject csi mount plugin container.
40+
// If set, the csi sidecar will be injected into the pod when the sandbox is created.
41+
// The csi mount sidecar is used to mount the remote oss/nas storage to the sandbox container.
42+
ShouldInjectCsiMount = "agents.kruise.io/should-inject-csi-mount-plugin"
43+
44+
// ShouldInjectAgentRuntime is the annotation key for inject agent runtime sidecar in init container.
45+
// If set, the agent runtime sidecar will be injected into the pod when the sandbox is created.
46+
// Some binary tools which are contained in the init agent runtime container. These are the basic tools for sandbox running.
47+
ShouldInjectAgentRuntime = "agents.kruise.io/should-inject-agent-runtime"
3848
)
3949

4050
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ require (
2121
golang.org/x/time v0.11.0
2222
google.golang.org/grpc v1.75.1
2323
google.golang.org/protobuf v1.36.11
24+
gopkg.in/evanphx/json-patch.v4 v4.12.0
2425
k8s.io/api v0.33.0
2526
k8s.io/apimachinery v0.33.0
2627
k8s.io/apiserver v0.33.0
@@ -103,7 +104,6 @@ require (
103104
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
104105
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect
105106
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
106-
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
107107
gopkg.in/inf.v0 v0.9.1 // indirect
108108
gopkg.in/yaml.v3 v3.0.1 // indirect
109109
k8s.io/apiextensions-apiserver v0.33.0 // indirect
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package core
2+
3+
import (
4+
corev1 "k8s.io/api/core/v1"
5+
)
6+
7+
const (
8+
KEY_CSI_INJECTION_CONFIG = "csi-config"
9+
KEY_RUNTIME_INJECTION_CONFIG = "agent-runtime-config"
10+
sandboxInjectionConfigName = "sandbox-injection-config"
11+
)
12+
13+
type SidecarInjectConfig struct {
14+
// Configuration injection for the main container (by convention, one container is designated as the main container)
15+
// Injection configuration items for business-specified main containers, such as volumeMount, environment variables, etc. Format: corev1.Container
16+
MainContainer corev1.Container `json:"mainContainer"`
17+
// Support injection for multiple independent sidecar containers; CSI container plugins are all injected from this
18+
Sidecars []corev1.Container `json:"csiSidecar"`
19+
// Support injection for volume mount configurations
20+
Volumes []corev1.Volume `json:"volume"`
21+
}
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
package core
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
corev1 "k8s.io/api/core/v1"
8+
"k8s.io/apimachinery/pkg/api/errors"
9+
"k8s.io/apimachinery/pkg/types"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
logf "sigs.k8s.io/controller-runtime/pkg/log"
12+
13+
agentsv1alpha1 "github.com/openkruise/agents/api/v1alpha1"
14+
"github.com/openkruise/agents/pkg/utils/webhookutils"
15+
)
16+
17+
func enableInjectCsiMountConfig(sandbox *agentsv1alpha1.Sandbox) bool {
18+
return sandbox.Annotations[agentsv1alpha1.ShouldInjectCsiMount] == "true"
19+
}
20+
21+
func enableInjectAgentRuntimeConfig(sandbox *agentsv1alpha1.Sandbox) bool {
22+
return sandbox.Annotations[agentsv1alpha1.ShouldInjectAgentRuntime] == "true"
23+
}
24+
25+
// fetchInjectionConfiguration retrieves the sidecar injection configuration from the ConfigMap.
26+
// It attempts to fetch the ConfigMap named sandboxInjectionConfigName from the sandbox-system namespace.
27+
// If the ConfigMap exists, it returns the data map containing configuration keys like
28+
// KEY_CSI_INJECTION_CONFIG and KEY_RUNTIME_INJECTION_CONFIG. If the ConfigMap is not found
29+
// or any error occurs during retrieval, it returns nil data and no error.
30+
//
31+
// Parameters:
32+
// - ctx: context.Context for the operation
33+
// - client: Kubernetes client.Interface used to retrieve the ConfigMap
34+
//
35+
// Returns:
36+
// - map[string]string: The configuration data from the ConfigMap, or nil if not found or on error
37+
// - error: Always returns nil (errors are logged but not propagated)
38+
func fetchInjectionConfiguration(ctx context.Context, cli client.Client) (map[string]string, error) {
39+
logger := logf.FromContext(ctx)
40+
config := &corev1.ConfigMap{}
41+
err := cli.Get(ctx, types.NamespacedName{
42+
Namespace: webhookutils.GetNamespace(), // Todo considering the security concern and rbac issue
43+
Name: sandboxInjectionConfigName,
44+
}, config)
45+
if err != nil {
46+
if errors.IsNotFound(err) {
47+
logger.Info("no found the injection configuration, using default")
48+
return map[string]string{}, nil
49+
}
50+
return map[string]string{}, err
51+
}
52+
return config.Data, nil
53+
}
54+
55+
// parseCSIMountConfig parses the CSI sidecar injection configuration from raw ConfigMap data.
56+
// It extracts the value associated with KEY_CSI_INJECTION_CONFIG key and unmarshals it into
57+
// a SidecarInjectConfig struct. If the key is not present in the configRaw map, it returns
58+
// an empty SidecarInjectConfig without error. The configuration includes the main container
59+
// settings, CSI sidecar containers list, and volumes to be injected.
60+
//
61+
// Parameters:
62+
// - ctx: context.Context for the operation (currently unused but kept for future extensibility)
63+
// - configRaw: map[string]string containing the raw ConfigMap data with potential CSI config
64+
//
65+
// Returns:
66+
// - SidecarInjectConfig: The parsed configuration containing main container, sidecars, and volumes
67+
// - error: An error if JSON unmarshaling fails, nil otherwise
68+
func parseCSIMountConfig(ctx context.Context, configRaw map[string]string) (SidecarInjectConfig, error) {
69+
log := logf.FromContext(ctx)
70+
csiMountConfig := SidecarInjectConfig{}
71+
72+
configValue, exists := configRaw[KEY_CSI_INJECTION_CONFIG]
73+
if !exists || configValue == "" {
74+
log.Info("csi container config not found or empty, using default configuration")
75+
return csiMountConfig, nil
76+
}
77+
78+
err := json.Unmarshal([]byte(configRaw[KEY_CSI_INJECTION_CONFIG]), &csiMountConfig)
79+
if err != nil {
80+
log.Error(err, "failed to json unmarshal csi mount config")
81+
return csiMountConfig, err
82+
}
83+
return csiMountConfig, nil
84+
}
85+
86+
// parseAgentRuntimeConfig parses the agent runtime injection configuration from raw ConfigMap data.
87+
// It extracts the value associated with KEY_RUNTIME_INJECTION_CONFIG key and unmarshals it into
88+
// a SidecarInjectConfig struct. If the key is not present in the configRaw map, it returns
89+
// an empty SidecarInjectConfig without error. The configuration includes the main container
90+
// settings with lifecycle hooks, init runtime containers list, and volumes to be injected.
91+
//
92+
// Parameters:
93+
// - ctx: context.Context for the operation (currently unused but kept for future extensibility)
94+
// - configRaw: map[string]string containing the raw ConfigMap data with potential runtime config
95+
//
96+
// Returns:
97+
// - SidecarInjectConfig: The parsed configuration containing main container, sidecars (init containers), and volumes
98+
// - error: An error if JSON unmarshaling fails, nil otherwise
99+
func parseAgentRuntimeConfig(ctx context.Context, configRaw map[string]string) (SidecarInjectConfig, error) {
100+
log := logf.FromContext(ctx)
101+
csiMountConfig := SidecarInjectConfig{}
102+
103+
configValue, exists := configRaw[KEY_RUNTIME_INJECTION_CONFIG]
104+
if !exists || configValue == "" {
105+
log.Info("agent runtime config not found or empty, using default configuration")
106+
return csiMountConfig, nil
107+
}
108+
109+
err := json.Unmarshal([]byte(configValue), &csiMountConfig)
110+
if err != nil {
111+
log.Error(err, "failed to json unmarshal agent runtime config")
112+
return csiMountConfig, err
113+
}
114+
return csiMountConfig, nil
115+
}
116+
117+
// setCSIMountContainer injects CSI mount configurations into the SandboxTemplate's pod spec.
118+
// It configures the main container (first container in the spec) with CSI sidecar settings,
119+
// appends additional CSI sidecar containers, and mounts shared volumes.
120+
// Volumes are only added if they don't already exist in the template.
121+
func setCSIMountContainer(ctx context.Context, obj *corev1.PodTemplateSpec, config SidecarInjectConfig) {
122+
log := logf.FromContext(ctx)
123+
124+
// set main container, the first container is the main container
125+
if len(obj.Spec.Containers) == 0 {
126+
log.Info("no found the template containers")
127+
return
128+
}
129+
130+
mainContainer := &obj.Spec.Containers[0]
131+
setMainContainerWhenInjectCSISidecar(mainContainer, config)
132+
133+
// set csi sidecars
134+
for _, csiSidecar := range config.Sidecars {
135+
obj.Spec.Containers = append(obj.Spec.Containers, csiSidecar)
136+
}
137+
138+
// set share volume
139+
if len(config.Volumes) > 0 {
140+
if obj.Spec.Volumes == nil {
141+
obj.Spec.Volumes = make([]corev1.Volume, 0, len(config.Volumes))
142+
}
143+
for _, vol := range config.Volumes {
144+
if findVolumeByName(obj.Spec.Volumes, vol.Name) {
145+
continue
146+
}
147+
obj.Spec.Volumes = append(obj.Spec.Volumes, vol)
148+
}
149+
}
150+
}
151+
152+
// setMainContainerWhenInjectCSISidecar configures the main container with environment variables and volume mounts from the CSI sidecar configuration.
153+
// It appends environment variables and volume mounts to the main container, skipping any that already exist (matched by name) to avoid duplicates.
154+
func setMainContainerWhenInjectCSISidecar(mainContainer *corev1.Container, config SidecarInjectConfig) {
155+
// append some envs in main container when processing csi mount
156+
if mainContainer.Env == nil {
157+
mainContainer.Env = make([]corev1.EnvVar, 0, 1)
158+
}
159+
for _, env := range config.MainContainer.Env {
160+
if findEnvByName(mainContainer.Env, env.Name) {
161+
continue
162+
}
163+
mainContainer.Env = append(mainContainer.Env, env)
164+
}
165+
166+
// append some volumeMounts config in main container
167+
if config.MainContainer.VolumeMounts != nil {
168+
if mainContainer.VolumeMounts == nil {
169+
mainContainer.VolumeMounts = make([]corev1.VolumeMount, 0, len(config.MainContainer.VolumeMounts))
170+
}
171+
for _, volMount := range config.MainContainer.VolumeMounts {
172+
if findVolumeMountByName(mainContainer.VolumeMounts, volMount.Name) {
173+
continue
174+
}
175+
mainContainer.VolumeMounts = append(mainContainer.VolumeMounts, volMount)
176+
}
177+
}
178+
}
179+
180+
// setAgentRuntimeContainer injects agent runtime configurations into the SandboxTemplate's pod spec.
181+
// It appends agent runtime containers as init containers and configures the main container (first container) with runtime settings.
182+
// The init containers run before the main containers to prepare the runtime environment.
183+
func setAgentRuntimeContainer(ctx context.Context, obj *corev1.PodTemplateSpec, config SidecarInjectConfig) {
184+
log := logf.FromContext(ctx)
185+
186+
// append init agent runtime container
187+
if obj.Spec.InitContainers == nil {
188+
obj.Spec.InitContainers = make([]corev1.Container, 0, 1)
189+
}
190+
obj.Spec.InitContainers = append(obj.Spec.InitContainers, config.Sidecars...)
191+
192+
if len(obj.Spec.Containers) == 0 {
193+
log.Info("no found the template container, default main container is the first container")
194+
return
195+
}
196+
mainContainer := &obj.Spec.Containers[0]
197+
setMainContainerConfigWhenInjectRuntimeSidecar(mainContainer, config)
198+
199+
obj.Spec.Volumes = append(obj.Spec.Volumes, config.Volumes...)
200+
}
201+
202+
func setMainContainerConfigWhenInjectRuntimeSidecar(mainContainer *corev1.Container, config SidecarInjectConfig) {
203+
// set main container lifecycle
204+
if mainContainer.Lifecycle == nil {
205+
mainContainer.Lifecycle = &corev1.Lifecycle{}
206+
}
207+
if mainContainer.Lifecycle.PostStart == nil {
208+
mainContainer.Lifecycle.PostStart = &corev1.LifecycleHandler{}
209+
}
210+
211+
// using config to override the main container lifecycle post start
212+
if config.MainContainer.Lifecycle != nil && config.MainContainer.Lifecycle.PostStart != nil {
213+
mainContainer.Lifecycle.PostStart = config.MainContainer.Lifecycle.PostStart
214+
}
215+
216+
// set main container env
217+
if mainContainer.Env == nil {
218+
mainContainer.Env = make([]corev1.EnvVar, 0, len(config.MainContainer.Env))
219+
}
220+
mainContainer.Env = append(mainContainer.Env, config.MainContainer.Env...)
221+
222+
// set main container volumeMounts
223+
if mainContainer.VolumeMounts == nil {
224+
mainContainer.VolumeMounts = make([]corev1.VolumeMount, 0, len(config.MainContainer.VolumeMounts))
225+
}
226+
mainContainer.VolumeMounts = append(mainContainer.VolumeMounts, config.MainContainer.VolumeMounts...)
227+
}
228+
229+
func findVolumeMountByName(volumeMounts []corev1.VolumeMount, name string) bool {
230+
for _, volumeMount := range volumeMounts {
231+
if volumeMount.Name == name {
232+
return true
233+
}
234+
}
235+
return false
236+
}
237+
238+
func findVolumeByName(volumes []corev1.Volume, name string) bool {
239+
for _, volume := range volumes {
240+
if volume.Name == name {
241+
return true
242+
}
243+
}
244+
return false
245+
}
246+
247+
func findEnvByName(envs []corev1.EnvVar, name string) bool {
248+
for _, env := range envs {
249+
if env.Name == name {
250+
return true
251+
}
252+
}
253+
return false
254+
}

0 commit comments

Comments
 (0)