Skip to content

Commit 54481a5

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 54481a5

File tree

11 files changed

+1768
-12
lines changed

11 files changed

+1768
-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: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
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+
// parseInjectConfig parses the sidecar injection configuration from raw ConfigMap data for a specific config key.
56+
// It extracts the value associated with the given configKey (e.g., KEY_CSI_INJECTION_CONFIG or KEY_RUNTIME_INJECTION_CONFIG)
57+
// and unmarshals it into a SidecarInjectConfig struct. If the key is not present in the configRaw map or the value
58+
// is empty, it returns an empty SidecarInjectConfig without error. The configuration includes the main container
59+
// settings, sidecar containers list (CSI sidecars or runtime init containers), and volumes to be injected.
60+
//
61+
// Parameters:
62+
// - ctx: context.Context for the operation, used for logging
63+
// - configKey: string representing the configuration key to look up (KEY_CSI_INJECTION_CONFIG or KEY_RUNTIME_INJECTION_CONFIG)
64+
// - configRaw: map[string]string containing the raw ConfigMap data with potential injection config
65+
//
66+
// Returns:
67+
// - SidecarInjectConfig: The parsed configuration containing main container, sidecars, and volumes
68+
// - error: An error if JSON unmarshaling fails, nil otherwise or when config key is missing/empty
69+
func parseInjectConfig(ctx context.Context, configKey string, configRaw map[string]string) (SidecarInjectConfig, error) {
70+
log := logf.FromContext(ctx)
71+
csiMountConfig := SidecarInjectConfig{}
72+
73+
configValue, exists := configRaw[configKey]
74+
if !exists || configValue == "" {
75+
log.Info("config key not found or empty, using default configuration")
76+
return csiMountConfig, nil
77+
}
78+
79+
err := json.Unmarshal([]byte(configRaw[configKey]), &csiMountConfig)
80+
if err != nil {
81+
log.Error(err, "failed to json unmarshal csi mount config")
82+
return csiMountConfig, err
83+
}
84+
return csiMountConfig, nil
85+
}
86+
87+
// setCSIMountContainer injects CSI mount configurations into the SandboxTemplate's pod spec.
88+
// It configures the main container (first container in the spec) with CSI sidecar settings,
89+
// appends additional CSI sidecar containers, and mounts shared volumes.
90+
// Volumes are only added if they don't already exist in the template.
91+
func setCSIMountContainer(ctx context.Context, obj *corev1.PodTemplateSpec, config SidecarInjectConfig) {
92+
log := logf.FromContext(ctx)
93+
94+
// set main container, the first container is the main container
95+
if len(obj.Spec.Containers) == 0 {
96+
log.Info("no found the template containers")
97+
return
98+
}
99+
100+
mainContainer := &obj.Spec.Containers[0]
101+
setMainContainerWhenInjectCSISidecar(mainContainer, config)
102+
103+
// set csi sidecars
104+
for _, csiSidecar := range config.Sidecars {
105+
obj.Spec.Containers = append(obj.Spec.Containers, csiSidecar)
106+
}
107+
108+
// set share volume
109+
if len(config.Volumes) > 0 {
110+
if obj.Spec.Volumes == nil {
111+
obj.Spec.Volumes = make([]corev1.Volume, 0, len(config.Volumes))
112+
}
113+
for _, vol := range config.Volumes {
114+
if findVolumeByName(obj.Spec.Volumes, vol.Name) {
115+
continue
116+
}
117+
obj.Spec.Volumes = append(obj.Spec.Volumes, vol)
118+
}
119+
}
120+
}
121+
122+
// setMainContainerWhenInjectCSISidecar configures the main container with environment variables and volume mounts from the CSI sidecar configuration.
123+
// It appends environment variables and volume mounts to the main container, skipping any that already exist (matched by name) to avoid duplicates.
124+
func setMainContainerWhenInjectCSISidecar(mainContainer *corev1.Container, config SidecarInjectConfig) {
125+
// append some envs in main container when processing csi mount
126+
if mainContainer.Env == nil {
127+
mainContainer.Env = make([]corev1.EnvVar, 0, 1)
128+
}
129+
for _, env := range config.MainContainer.Env {
130+
if findEnvByName(mainContainer.Env, env.Name) {
131+
continue
132+
}
133+
mainContainer.Env = append(mainContainer.Env, env)
134+
}
135+
136+
// append some volumeMounts config in main container
137+
if config.MainContainer.VolumeMounts != nil {
138+
if mainContainer.VolumeMounts == nil {
139+
mainContainer.VolumeMounts = make([]corev1.VolumeMount, 0, len(config.MainContainer.VolumeMounts))
140+
}
141+
for _, volMount := range config.MainContainer.VolumeMounts {
142+
if findVolumeMountByName(mainContainer.VolumeMounts, volMount.Name) {
143+
continue
144+
}
145+
mainContainer.VolumeMounts = append(mainContainer.VolumeMounts, volMount)
146+
}
147+
}
148+
}
149+
150+
// setAgentRuntimeContainer injects agent runtime configurations into the SandboxTemplate's pod spec.
151+
// It appends agent runtime containers as init containers and configures the main container (first container) with runtime settings.
152+
// The init containers run before the main containers to prepare the runtime environment.
153+
func setAgentRuntimeContainer(ctx context.Context, obj *corev1.PodTemplateSpec, config SidecarInjectConfig) {
154+
log := logf.FromContext(ctx)
155+
156+
// append init agent runtime container
157+
if obj.Spec.InitContainers == nil {
158+
obj.Spec.InitContainers = make([]corev1.Container, 0, 1)
159+
}
160+
obj.Spec.InitContainers = append(obj.Spec.InitContainers, config.Sidecars...)
161+
162+
if len(obj.Spec.Containers) == 0 {
163+
log.Info("no found the template container, default main container is the first container")
164+
return
165+
}
166+
mainContainer := &obj.Spec.Containers[0]
167+
setMainContainerConfigWhenInjectRuntimeSidecar(mainContainer, config)
168+
169+
obj.Spec.Volumes = append(obj.Spec.Volumes, config.Volumes...)
170+
}
171+
172+
func setMainContainerConfigWhenInjectRuntimeSidecar(mainContainer *corev1.Container, config SidecarInjectConfig) {
173+
// set main container lifecycle
174+
if mainContainer.Lifecycle == nil {
175+
mainContainer.Lifecycle = &corev1.Lifecycle{}
176+
}
177+
if mainContainer.Lifecycle.PostStart == nil {
178+
mainContainer.Lifecycle.PostStart = &corev1.LifecycleHandler{}
179+
}
180+
181+
// using config to override the main container lifecycle post start
182+
if config.MainContainer.Lifecycle != nil && config.MainContainer.Lifecycle.PostStart != nil {
183+
mainContainer.Lifecycle.PostStart = config.MainContainer.Lifecycle.PostStart
184+
}
185+
186+
// set main container env
187+
if mainContainer.Env == nil {
188+
mainContainer.Env = make([]corev1.EnvVar, 0, len(config.MainContainer.Env))
189+
}
190+
mainContainer.Env = append(mainContainer.Env, config.MainContainer.Env...)
191+
192+
// set main container volumeMounts
193+
if mainContainer.VolumeMounts == nil {
194+
mainContainer.VolumeMounts = make([]corev1.VolumeMount, 0, len(config.MainContainer.VolumeMounts))
195+
}
196+
mainContainer.VolumeMounts = append(mainContainer.VolumeMounts, config.MainContainer.VolumeMounts...)
197+
}
198+
199+
func findVolumeMountByName(volumeMounts []corev1.VolumeMount, name string) bool {
200+
for _, volumeMount := range volumeMounts {
201+
if volumeMount.Name == name {
202+
return true
203+
}
204+
}
205+
return false
206+
}
207+
208+
func findVolumeByName(volumes []corev1.Volume, name string) bool {
209+
for _, volume := range volumes {
210+
if volume.Name == name {
211+
return true
212+
}
213+
}
214+
return false
215+
}
216+
217+
func findEnvByName(envs []corev1.EnvVar, name string) bool {
218+
for _, env := range envs {
219+
if env.Name == name {
220+
return true
221+
}
222+
}
223+
return false
224+
}

0 commit comments

Comments
 (0)