diff --git a/pkg/mounter/oss/oss_fuse_manager.go b/pkg/mounter/oss/oss_fuse_manager.go index 6478aa5c9..c7257427a 100644 --- a/pkg/mounter/oss/oss_fuse_manager.go +++ b/pkg/mounter/oss/oss_fuse_manager.go @@ -51,6 +51,11 @@ const ( // Options contains options for target oss type Options struct { + // DirectAssigned indicates the volume should be directly assigned to the pod. + // When DirectAssigned is True, it means the runtime is either rund or coco: + // - Controller server should skip the controller publish phase + // - Node server should check the skipAttach field to determine if it's rund (skipAttach=true) or coco (skipAttach=false) + // Otherwise, the runtime is runc DirectAssigned bool CNFSName string diff --git a/pkg/oss/controllerserver.go b/pkg/oss/controllerserver.go index d083b9163..6bf742418 100644 --- a/pkg/oss/controllerserver.go +++ b/pkg/oss/controllerserver.go @@ -90,6 +90,10 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol func (cs *controllerServer) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) { klog.Infof("ControllerUnpublishVolume: volume %s on node %s", req.VolumeId, req.NodeId) + // Note: For scenarios with mixed deployment of rund/coco and runc, + // since we cannot determine by req parameters, + // all need to attempt pod deletion for compatibility + // To maintain the compatibility, all kinds of fuseType Pod share the same globalmount path as ossfs. if err := cs.fusePodManagers[unifiedFsType].Delete(&mounter.FusePodContext{ Context: ctx, @@ -123,6 +127,14 @@ func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *cs if err := setCNFSOptions(ctx, cs.cnfsGetter, opts); err != nil { return nil, err } + + // Note: Skip controller publish for direct=true. + // The actual mounting of these volumes will be handled by rund/coco. + if opts.DirectAssigned { + klog.Infof("ControllerPublishVolume: skip DirectVolume: %s", req.VolumeId) + return &csi.ControllerPublishVolumeResponse{}, nil + } + // options validation if err := checkOssOptions(opts, cs.fusePodManagers[opts.FuseType]); err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) @@ -134,12 +146,6 @@ func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *cs } // make pod template config ptCfg := makePodTemplateConfig(opts) - // Skip controller publish for PVs with attribute direct=true. - // The actual mounting of these volumes will be handled by rund. - if opts.DirectAssigned { - klog.Infof("ControllerPublishVolume: skip DirectVolume: %s", req.VolumeId) - return &csi.ControllerPublishVolumeResponse{}, nil - } // make mount options controllerPublishPath := mounter.GetAttachPath(req.VolumeId) diff --git a/pkg/oss/csi_agent.go b/pkg/oss/csi_agent.go index dfcf26df8..b89ac3695 100644 --- a/pkg/oss/csi_agent.go +++ b/pkg/oss/csi_agent.go @@ -34,7 +34,7 @@ func NewCSIAgent(m metadata.MetadataProvider, socketPath string) *CSIAgent { metadata: m, locks: utils.NewVolumeLocks(), rawMounter: mountutils.NewWithoutSystemd(""), - skipAttach: true, + skipAttach: true, // label for csi-agent environment fusePodManagers: fusePodManagers, ossfsPaths: map[string]string{ OssFsType: ossfsExecPath, diff --git a/pkg/oss/nodeserver.go b/pkg/oss/nodeserver.go index 8a4029c41..72ccf0e98 100644 --- a/pkg/oss/nodeserver.go +++ b/pkg/oss/nodeserver.go @@ -107,6 +107,15 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis return &csi.NodePublishVolumeResponse{}, nil } + // rund 3.0 protocol + // Note: In rund 3.0 node server (non csi-agent), skip all parameter validation and exit directly + if features.FunctionalMutableFeatureGate.Enabled(features.RundCSIProtocol3) { + if ns.clientset != nil && utils.GetPodRunTime(ctx, req, ns.clientset) == utils.RundRunTimeTag { + klog.Infof("NodePublishVolume: skip as %s enabled", features.RundCSIProtocol3) + return &csi.NodePublishVolumeResponse{}, nil + } + } + // parse options // ensure fuseType is not empty 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 return nil, status.Error(codes.InvalidArgument, err.Error()) } - // PVs with attribute direct=true will by mounted in rund guest os. - // csi need only to prepare the direct-volume mountinfo in /run/kata-containers/shared/direct-volumes directory. - if opts.DirectAssigned { + // Note: In non-csi-agent environment (!ns.skipAttach), + // if DirectAssigned is True, it's a confidential container scenario (coco) + if opts.DirectAssigned && !ns.skipAttach { return ns.publishDirectVolume(ctx, req, opts) } @@ -140,14 +149,6 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis } mountOptions = ns.fusePodManagers[opts.FuseType].AddDefaultMountOptions(mountOptions) - // rund 3.0 protocol - if features.FunctionalMutableFeatureGate.Enabled(features.RundCSIProtocol3) { - if ns.clientset != nil && utils.GetPodRunTime(ctx, req, ns.clientset) == utils.RundRunTimeTag { - klog.Infof("NodePublishVolume: skip as %s enabled", features.RundCSIProtocol3) - return &csi.NodePublishVolumeResponse{}, nil - } - } - // get mount proxy socket path socketPath := req.PublishContext[mountProxySocket] if socketPath == "" && !ns.skipAttach { diff --git a/pkg/oss/utils.go b/pkg/oss/utils.go index a37f36ba9..439a4eb95 100644 --- a/pkg/oss/utils.go +++ b/pkg/oss/utils.go @@ -69,10 +69,11 @@ func parseOptions(volOptions, secrets map[string]string, volCaps []*csi.VolumeCa } opts := &oss.Options{ - UseSharedPath: true, - Path: "/", - AkID: strings.TrimSpace(secrets[AkID]), - AkSecret: strings.TrimSpace(secrets[AkSecret]), + UseSharedPath: true, + Path: "/", + AkID: strings.TrimSpace(secrets[AkID]), + AkSecret: strings.TrimSpace(secrets[AkSecret]), + DirectAssigned: getDirectAssignedValue(), } var volumeAsSubpath bool @@ -444,3 +445,29 @@ func makeMountOptions(opt *oss.Options, fpm *oss.OSSFusePodManager, m metadata.M mountOptions = append(mountOptions, ops...) return } + +// getDirectAssignedValue returns the default value for DirectAssigned option based on the runtime class. +// The function reads DEFAULT_RUNTIME_CLASS environment variable and determines the appropriate default value: +// - For "rund" runtime, returns true (direct assignment enabled by default) +// - For "runc" runtime or empty value, returns false (direct assignment disabled by default) +// - For any other value, logs a warning and returns false +func getDirectAssignedValue() bool { + // Get runtime class from environment variable + runtimeClass := os.Getenv("DEFAULT_RUNTIME_CLASS") + + // Validate runtime class, only allow "runc", "rund" or empty (treated as "runc") + // Note: Do not consider the confidential container scenario (coco), + // because in this scenario, PV.attributes must explicitly specify whether directAssigned + switch strings.ToLower(runtimeClass) { + case strings.ToLower(utils.RundRunTimeTag): + // For rund, default value is true + return true + case strings.ToLower(utils.RuncRunTimeTag), "": + // For runc or empty (default to runc), default value is false + return false + default: + // Invalid runtime class, see as rund and return error + klog.Warningf("invalid DEFAULT_RUNTIME_CLASS value: %q, only %s and %s are allowed", runtimeClass, utils.RuncRunTimeTag, utils.RundRunTimeTag) + return false + } +} diff --git a/pkg/oss/utils_test.go b/pkg/oss/utils_test.go index a370887da..dac4de146 100644 --- a/pkg/oss/utils_test.go +++ b/pkg/oss/utils_test.go @@ -740,3 +740,124 @@ func TestMakePodTemplateConfig(t *testing.T) { assert.Equal(t, &mounter.PodTemplateConfig{}, makePodTemplateConfig(&oss.Options{})) } + +func TestGetDirectAssignedValue(t *testing.T) { + tests := []struct { + name string + runtimeClass string + expected bool + }{ + { + name: "Test with rund runtime class", + runtimeClass: "rund", + expected: true, + }, + { + name: "Test with runc runtime class", + runtimeClass: "runc", + expected: false, + }, + { + name: "Test with empty runtime class", + runtimeClass: "", + expected: false, + }, + { + name: "Test with invalid runtime class", + runtimeClass: "invalid", + expected: false, + }, + { + name: "Test with Rund runtime class (case insensitive)", + runtimeClass: "Rund", + expected: true, + }, + { + name: "Test with runC runtime class (case insensitive)", + runtimeClass: "runC", + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Setenv("DEFAULT_RUNTIME_CLASS", tt.runtimeClass) + result := getDirectAssignedValue() + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestParseOptions_DirectAssigned(t *testing.T) { + tests := []struct { + name string + default_runtime_class string + volOptions map[string]string + wantDirectAssigned bool + }{ + { + name: "empty runtime, empty volOptions", + default_runtime_class: "", + volOptions: map[string]string{}, + wantDirectAssigned: false, + }, + { + name: "invalid runtime, empty volOptions", + default_runtime_class: "invalid", + volOptions: map[string]string{}, + wantDirectAssigned: false, + }, + { + name: "default rund, empty volOptions", + default_runtime_class: "rund", + volOptions: map[string]string{}, + wantDirectAssigned: true, + }, + { + name: "default runc, empty volOptions", + default_runtime_class: "runc", + volOptions: map[string]string{}, + wantDirectAssigned: false, + }, + { + name: "empty runtime, vol true", + default_runtime_class: "", + volOptions: map[string]string{ + optDirectAssigned: "true", + }, + wantDirectAssigned: true, + }, + { + name: "invalid runtime, vol false", + default_runtime_class: "invalid", + volOptions: map[string]string{ + optDirectAssigned: "false", + }, + wantDirectAssigned: false, + }, + { + name: "default rund, invalid volOptions", + default_runtime_class: "rund", + volOptions: map[string]string{ + optDirectAssigned: "invalid", + }, + wantDirectAssigned: true, + }, + { + name: "default runc, vol true", + default_runtime_class: "runc", + volOptions: map[string]string{ + optDirectAssigned: "true", + }, + wantDirectAssigned: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeMeta := metadata.NewMetadata() + t.Setenv("DEFAULT_RUNTIME_CLASS", tt.default_runtime_class) + opt := parseOptions(tt.volOptions, nil, nil, false, "", false, fakeMeta) + assert.Equal(t, tt.wantDirectAssigned, opt.DirectAssigned) + }) + } +}