Skip to content

Commit 4f8338c

Browse files
committed
Support mixed deployment of runc and rund runtimes: enhance DirectAssigned logic and improve runtime detection
1 parent 298a44f commit 4f8338c

File tree

8 files changed

+194
-22
lines changed

8 files changed

+194
-22
lines changed

deploy/charts/alibaba-cloud-csi-driver/templates/controller.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,10 @@ spec:
510510
- name: ENABLE_NAS_SUBPATH_FINALIZER
511511
value: "true"
512512
{{- end }}
513+
{{- if .Values.deploy.defaultRuntimeClass }}
514+
- name: DEFAULT_RUNTIME_CLASS
515+
value: {{ .Values.deploy.defaultRuntimeClass | quote }}
516+
{{- end }}
513517
{{- include "akEnv" .Values.deploy.accessKey | nindent 12 }}
514518
livenessProbe:
515519
httpGet:

deploy/charts/alibaba-cloud-csi-driver/values.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ deploy:
9999
idKey: id # the key of AccessKey id in the k8s Secret
100100
secretkey: secret # the key of AccessKey secret in the k8s Secret
101101

102+
# Default runtime class to use when
103+
# volume does not declare,
104+
# and cannot be fetched from podinfo.
105+
# Possible values:
106+
# - runc (default)
107+
# - rund
108+
defaultRuntimeClass: null
109+
102110
images:
103111
registry: null
104112
registryVPC: null

pkg/mounter/oss/oss_fuse_manager.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ const (
5151

5252
// Options contains options for target oss
5353
type Options struct {
54+
// DirectAssigned indicates the volume should be directly assigned to the pod.
55+
// When DirectAssigned is True, it means the runtime is either rund or coco:
56+
// - Controller server should skip the controller publish phase
57+
// - Node server should check the skipAttach field to determine if it's rund (skipAttach=true) or coco (skipAttach=false)
58+
// Otherwise, the runtime is runc
5459
DirectAssigned bool
5560
CNFSName string
5661

pkg/oss/controllerserver.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
9090

9191
func (cs *controllerServer) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
9292
klog.Infof("ControllerUnpublishVolume: volume %s on node %s", req.VolumeId, req.NodeId)
93+
// Note: For scenarios with mixed deployment of rund/coco and runc,
94+
// since we cannot determine by req parameters,
95+
// all need to attempt pod deletion for compatibility
96+
9397
// To maintain the compatibility, all kinds of fuseType Pod share the same globalmount path as ossfs.
9498
if err := cs.fusePodManagers[unifiedFsType].Delete(&mounter.FusePodContext{
9599
Context: ctx,
@@ -123,6 +127,14 @@ func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *cs
123127
if err := setCNFSOptions(ctx, cs.cnfsGetter, opts); err != nil {
124128
return nil, err
125129
}
130+
131+
// Note: Skip controller publish for direct=true.
132+
// The actual mounting of these volumes will be handled by rund/coco.
133+
if opts.DirectAssigned {
134+
klog.Infof("ControllerPublishVolume: skip DirectVolume: %s", req.VolumeId)
135+
return &csi.ControllerPublishVolumeResponse{}, nil
136+
}
137+
126138
// options validation
127139
if err := checkOssOptions(opts, cs.fusePodManagers[opts.FuseType]); err != nil {
128140
return nil, status.Error(codes.InvalidArgument, err.Error())
@@ -134,12 +146,6 @@ func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *cs
134146
}
135147
// make pod template config
136148
ptCfg := makePodTemplateConfig(opts)
137-
// Skip controller publish for PVs with attribute direct=true.
138-
// The actual mounting of these volumes will be handled by rund.
139-
if opts.DirectAssigned {
140-
klog.Infof("ControllerPublishVolume: skip DirectVolume: %s", req.VolumeId)
141-
return &csi.ControllerPublishVolumeResponse{}, nil
142-
}
143149
// make mount options
144150
controllerPublishPath := mounter.GetAttachPath(req.VolumeId)
145151

pkg/oss/csi_agent.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func NewCSIAgent(m metadata.MetadataProvider, socketPath string) *CSIAgent {
3434
metadata: m,
3535
locks: utils.NewVolumeLocks(),
3636
rawMounter: mountutils.NewWithoutSystemd(""),
37-
skipAttach: true,
37+
skipAttach: true, // label for csi-agent environment
3838
fusePodManagers: fusePodManagers,
3939
ossfsPaths: map[string]string{
4040
OssFsType: ossfsExecPath,

pkg/oss/nodeserver.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
107107
return &csi.NodePublishVolumeResponse{}, nil
108108
}
109109

110+
// rund 3.0 protocol
111+
// Note: In rund 3.0 node server (non csi-agent), skip all parameter validation and exit directly
112+
if features.FunctionalMutableFeatureGate.Enabled(features.RundCSIProtocol3) {
113+
if ns.clientset != nil && utils.GetPodRunTime(ctx, req, ns.clientset) == utils.RundRunTimeTag {
114+
klog.Infof("NodePublishVolume: skip as %s enabled", features.RundCSIProtocol3)
115+
return &csi.NodePublishVolumeResponse{}, nil
116+
}
117+
}
118+
110119
// parse options
111120
// ensure fuseType is not empty
112121
opts := parseOptions(req.GetVolumeContext(), req.GetSecrets(), []*csi.VolumeCapability{req.GetVolumeCapability()}, req.GetReadonly(), "", true, ns.metadata)
@@ -125,9 +134,9 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
125134
return nil, status.Error(codes.InvalidArgument, err.Error())
126135
}
127136

128-
// PVs with attribute direct=true will by mounted in rund guest os.
129-
// csi need only to prepare the direct-volume mountinfo in /run/kata-containers/shared/direct-volumes directory.
130-
if opts.DirectAssigned {
137+
// Note: In non-csi-agent environment (!ns.skipAttach),
138+
// if DirectAssigned is True, it's a confidential container scenario (coco)
139+
if opts.DirectAssigned && !ns.skipAttach {
131140
return ns.publishDirectVolume(ctx, req, opts)
132141
}
133142

@@ -140,14 +149,6 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
140149
}
141150
mountOptions = ns.fusePodManagers[opts.FuseType].AddDefaultMountOptions(mountOptions)
142151

143-
// rund 3.0 protocol
144-
if features.FunctionalMutableFeatureGate.Enabled(features.RundCSIProtocol3) {
145-
if ns.clientset != nil && utils.GetPodRunTime(ctx, req, ns.clientset) == utils.RundRunTimeTag {
146-
klog.Infof("NodePublishVolume: skip as %s enabled", features.RundCSIProtocol3)
147-
return &csi.NodePublishVolumeResponse{}, nil
148-
}
149-
}
150-
151152
// get mount proxy socket path
152153
socketPath := req.PublishContext[mountProxySocket]
153154
if socketPath == "" && !ns.skipAttach {

pkg/oss/utils.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,11 @@ func parseOptions(volOptions, secrets map[string]string, volCaps []*csi.VolumeCa
6969
}
7070

7171
opts := &oss.Options{
72-
UseSharedPath: true,
73-
Path: "/",
74-
AkID: strings.TrimSpace(secrets[AkID]),
75-
AkSecret: strings.TrimSpace(secrets[AkSecret]),
72+
UseSharedPath: true,
73+
Path: "/",
74+
AkID: strings.TrimSpace(secrets[AkID]),
75+
AkSecret: strings.TrimSpace(secrets[AkSecret]),
76+
DirectAssigned: getDirectAssignedValue(),
7677
}
7778

7879
var volumeAsSubpath bool
@@ -444,3 +445,29 @@ func makeMountOptions(opt *oss.Options, fpm *oss.OSSFusePodManager, m metadata.M
444445
mountOptions = append(mountOptions, ops...)
445446
return
446447
}
448+
449+
// getDirectAssignedValue returns the default value for DirectAssigned option based on the runtime class.
450+
// The function reads DEFAULT_RUNTIME_CLASS environment variable and determines the appropriate default value:
451+
// - For "rund" runtime, returns true (direct assignment enabled by default)
452+
// - For "runc" runtime or empty value, returns false (direct assignment disabled by default)
453+
// - For any other value, logs a warning and returns false
454+
func getDirectAssignedValue() bool {
455+
// Get runtime class from environment variable
456+
runtimeClass := os.Getenv("DEFAULT_RUNTIME_CLASS")
457+
458+
// Validate runtime class, only allow "runc", "rund" or empty (treated as "runc")
459+
// Note: Do not consider the confidential container scenario (coco),
460+
// because in this scenario, PV.attributes must explicitly specify whether directAssigned
461+
switch strings.ToLower(runtimeClass) {
462+
case strings.ToLower(utils.RundRunTimeTag):
463+
// For rund, default value is true
464+
return true
465+
case strings.ToLower(utils.RuncRunTimeTag), "":
466+
// For runc or empty (default to runc), default value is false
467+
return false
468+
default:
469+
// Invalid runtime class, see as rund and return error
470+
klog.Warningf("invalid DEFAULT_RUNTIME_CLASS value: %q, only %s and %s are allowed", runtimeClass, utils.RuncRunTimeTag, utils.RundRunTimeTag)
471+
return false
472+
}
473+
}

pkg/oss/utils_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,3 +740,124 @@ func TestMakePodTemplateConfig(t *testing.T) {
740740

741741
assert.Equal(t, &mounter.PodTemplateConfig{}, makePodTemplateConfig(&oss.Options{}))
742742
}
743+
744+
func TestGetDirectAssignedValue(t *testing.T) {
745+
tests := []struct {
746+
name string
747+
runtimeClass string
748+
expected bool
749+
}{
750+
{
751+
name: "Test with rund runtime class",
752+
runtimeClass: "rund",
753+
expected: true,
754+
},
755+
{
756+
name: "Test with runc runtime class",
757+
runtimeClass: "runc",
758+
expected: false,
759+
},
760+
{
761+
name: "Test with empty runtime class",
762+
runtimeClass: "",
763+
expected: false,
764+
},
765+
{
766+
name: "Test with invalid runtime class",
767+
runtimeClass: "invalid",
768+
expected: false,
769+
},
770+
{
771+
name: "Test with RUNd runtime class (case insensitive)",
772+
runtimeClass: "RUNd",
773+
expected: true,
774+
},
775+
{
776+
name: "Test with RUNc runtime class (case insensitive)",
777+
runtimeClass: "RUNc",
778+
expected: false,
779+
},
780+
}
781+
782+
for _, tt := range tests {
783+
t.Run(tt.name, func(t *testing.T) {
784+
t.Setenv("DEFAULT_RUNTIME_CLASS", tt.runtimeClass)
785+
result := getDirectAssignedValue()
786+
assert.Equal(t, tt.expected, result)
787+
})
788+
}
789+
}
790+
791+
func TestParseOptions_DirectAssigned(t *testing.T) {
792+
tests := []struct {
793+
name string
794+
default_runtime_class string
795+
volOptions map[string]string
796+
wantDirectAssigned bool
797+
}{
798+
{
799+
name: "empty runtime, empty volOptions",
800+
default_runtime_class: "",
801+
volOptions: map[string]string{},
802+
wantDirectAssigned: false,
803+
},
804+
{
805+
name: "invalid runtime, empty volOptions",
806+
default_runtime_class: "invalid",
807+
volOptions: map[string]string{},
808+
wantDirectAssigned: false,
809+
},
810+
{
811+
name: "default rund, empty volOptions",
812+
default_runtime_class: "rund",
813+
volOptions: map[string]string{},
814+
wantDirectAssigned: true,
815+
},
816+
{
817+
name: "default runc, empty volOptions",
818+
default_runtime_class: "runc",
819+
volOptions: map[string]string{},
820+
wantDirectAssigned: false,
821+
},
822+
{
823+
name: "empty runtime, vol true",
824+
default_runtime_class: "",
825+
volOptions: map[string]string{
826+
optDirectAssigned: "true",
827+
},
828+
wantDirectAssigned: true,
829+
},
830+
{
831+
name: "invalid runtime, vol false",
832+
default_runtime_class: "invalid",
833+
volOptions: map[string]string{
834+
optDirectAssigned: "false",
835+
},
836+
wantDirectAssigned: false,
837+
},
838+
{
839+
name: "default rund, invalid volOptions",
840+
default_runtime_class: "rund",
841+
volOptions: map[string]string{
842+
optDirectAssigned: "invalid",
843+
},
844+
wantDirectAssigned: true,
845+
},
846+
{
847+
name: "default runc, vol true",
848+
default_runtime_class: "runc",
849+
volOptions: map[string]string{
850+
optDirectAssigned: "true",
851+
},
852+
wantDirectAssigned: true,
853+
},
854+
}
855+
for _, tt := range tests {
856+
t.Run(tt.name, func(t *testing.T) {
857+
fakeMeta := metadata.NewMetadata()
858+
t.Setenv("DEFAULT_RUNTIME_CLASS", tt.default_runtime_class)
859+
opt := parseOptions(tt.volOptions, nil, nil, false, "", false, fakeMeta)
860+
assert.Equal(t, tt.wantDirectAssigned, opt.DirectAssigned)
861+
})
862+
}
863+
}

0 commit comments

Comments
 (0)