English
In-depth Analysis of container-storage-interface/spec: The Go Implementation of CSI

Understand the structure, core types, and usage of the CSI spec library in one article
Preface
If you are developing a Kubernetes CSI driver, or want to understand the underlying implementation of CSI plugins,github.com/container-storage-interface/spec This library is a dependency you can't avoid. It is the Go implementation of the CSI specification, defining all gRPC interfaces, message structures, and enum types.
This article will proceed from Actual use from the perspective of, take you through this library in full.
I. Installation and Import of Libraries
bash
go get github.com/container-storage-interface/[email protected]Import in Go code:
go
import (
"github.com/container-storage-interface/spec/lib/go/csi"
)Note: The import path is lib/go/csi, not the root directory.
II. Directory Structure Analysis
github.com/container-storage-interface/spec/
├── csi.proto # Original protobuf definition (canonical source)
├── spec.md # Specification document (human-readable)
├── VERSION # Current version number (currently 1.13)
├── lib/
│ └── go/
│ └── csi/
│ ├── csi.pb.go # Go definitions for all messages and enums
│ └── csi_grpc.pb.go # gRPC server/client interface
└── Makefile # Used to regenerate the pb.go fileThe core file is csi.pb.go and csi_grpc.pb.go, the former is a data structure, and the latter is a service interface.
III. Detailed Explanation of Core Types (Most Commonly Used)
3.1 Volume— roll object
go
type Volume struct {
VolumeId string
CapacityBytes int64
VolumeContext map[string]string
ContentSource *VolumeContentSource
AccessibleTopology []*Topology
}This is CreateVolume the core field of the return value, representing a created storage volume.
3.2 VolumeCapability—— Access capability (top priority)
go
type VolumeCapability struct {
// Access mode (required)
AccessMode *VolumeCapability_AccessMode
// Choose one: Mount (filesystem) or Block (raw block device)
Mount *VolumeCapability_MountVolume
Block *VolumeCapability_BlockVolume
}Access mode enumeration
go
type VolumeCapability_AccessMode_Mode int32
const (
VolumeCapability_AccessMode_SINGLE_NODE_WRITER_ONLY VolumeCapability_AccessMode_Mode = 0
VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY VolumeCapability_AccessMode_Mode = 1
VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY VolumeCapability_AccessMode_Mode = 2
VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER VolumeCapability_AccessMode_Mode = 3
VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER VolumeCapability_AccessMode_Mode = 4
)Correspondence:
| Mode | meaning | Use cases |
|---|---|---|
SINGLE_NODE_WRITER_ONLY | Single-node read/write | The most common RWO |
SINGLE_NODE_READER_ONLY | Single-node read-only | Read-only data volume |
MULTI_NODE_READER_ONLY | Multi-node read-only | Shared read-only data (such as configuration) |
MULTI_NODE_SINGLE_WRITER | Multi-node single-write | Shared storage, but only one write is allowed |
MULTI_NODE_MULTI_WRITER | Multi-node multi-write | Shared read/write; the application must handle conflicts itself. |
MULTI_NODE_MULTI_WRITER It is a multi-node read-write mode and is widely used in distributed file systems and shared storage CSI drivers.
3.3 Topology—— topology information
go
type Topology struct {
Segments map[string]string
}Used to tell Kubernetes which region/availability zone a volume is in, so the scheduler can schedule Pods accordingly.
IV. Three Major gRPC Service Interfaces
4.1 Identity Service — Plugin Self-Introduction
go
type IdentityServer interface {
GetPluginInfo(ctx context.Context, req *GetPluginInfoRequest) (*GetPluginInfoResponse, error)
GetPluginCapabilities(ctx context.Context, req *GetPluginCapabilitiesRequest) (*GetPluginCapabilitiesResponse, error)
Probe(ctx context.Context, req *ProbeRequest) (*ProbeResponse, error)
}| RPC | effect |
|---|---|
GetPluginInfo | Return the driver name and version number. |
GetPluginCapabilities | Return the capabilities supported by the plugin. |
Probe | Health check, Kubernetes will call it periodically. |
4.2 Controller Service — Volume Lifecycle Management
go
type ControllerServer interface {
CreateVolume(...) (*CreateVolumeResponse, error)
DeleteVolume(...) (*DeleteVolumeResponse, error)
ControllerPublishVolume(...) (*ControllerPublishVolumeResponse, error) // Publish the volume to the node.
ControllerUnpublishVolume(...) (*ControllerUnpublishVolumeResponse, error)
ValidateVolumeCapabilities(...) (*ValidateVolumeCapabilitiesResponse, error)
ListVolumes(...) (*ListVolumesResponse, error)
GetCapacity(...) (*GetCapacityResponse, error)
ControllerGetCapabilities(...) (*ControllerGetCapabilitiesResponse, error)
CreateSnapshot(...) (*CreateSnapshotResponse, error)
DeleteSnapshot(...) (*DeleteSnapshotResponse, error)
ListSnapshots(...) (*ListSnapshotsResponse, error)
ControllerExpandVolume(...) (*ControllerExpandVolumeResponse, error) // Added in v1.1+
ControllerGetVolume(...) (*ControllerGetVolumeResponse, error) // New in v1.9+
}critical call chain:
CreateVolume: Called when Kubernetes creates a PVC → You need to actually create the storage on the backendControllerPublishVolume: Called when a Pod is scheduled to a node → attach the volume to that node (e.g., Attach a cloud disk)ControllerExpandVolume: Called when a PVC is expanded → expand backend storage capacity
4.3 Node Service — Mount Operations on Nodes
go
type NodeServer interface {
NodeStageVolume(...) (*NodeStageVolumeResponse, error) // Format + mount to a temporary path
NodeUnstageVolume(...) (*NodeUnstageVolumeResponse, error)
NodePublishVolume(...) (*NodePublishVolumeResponse, error) // Mount to the Pod target path
NodeUnpublishVolume(...) (*NodeUnpublishVolumeResponse, error)
NodeGetInfo(...) (*NodeGetInfoResponse, error) // Return node ID and topology information
NodeGetCapabilities(...) (*NodeGetCapabilitiesResponse, error)
NodeGetVolumeStats(...) (*NodeGetVolumeStatsResponse, error) // v1.0+ Get volume statistics
NodeExpandVolume(...) (*NodeExpandVolumeResponse, error) // v1.1+ Node-side scaling
}The difference between Stage vs Publish:
| Stage | Operation | Description |
|---|---|---|
NodeStageVolume | Format + mount to /var/lib/kubelet/plugins/.../volumes/ | One-time preparatory action |
NodePublishVolume | Bind mount from the Stage path to the Pod directory | Each Pod is mounted separately. |
Simple understanding:Stage Make physical preparations,Publish Perform logical mounting.
V. Capability Statement (Capabilities)
CSI requires plugins to explicitly declare which capabilities they support, and Kubernetes external sidecars decide whether to call the corresponding RPC based on these capabilities.
5.1 Controller Capabilities
go
type ControllerServiceCapability_RPC_Type int32
const (
ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME = 0
ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME = 1
ControllerServiceCapability_RPC_LIST_VOLUMES = 2
ControllerServiceCapability_RPC_GET_CAPACITY = 3
ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT = 4
ControllerServiceCapability_RPC_LIST_SNAPSHOTS = 5
ControllerServiceCapability_RPC_CLONE_VOLUME = 6
ControllerServiceCapability_RPC_EXPAND_VOLUME = 7 // v1.1+
ControllerServiceCapability_RPC_GET_VOLUME = 8 // v1.9+
// ... more
)5.2 Node Capabilities
go
type NodeServiceCapability_RPC_Type int32
const (
NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME = 0
NodeServiceCapability_RPC_GET_VOLUME_STATS = 1
NodeServiceCapability_RPC_EXPAND_VOLUME = 2 // v1.1+
NodeServiceCapability_RPC_GET_VOLUME_ATTRIBUTES = 3 // v1.13+
)Actual CSI driver project Examples of capabilities registered in:
go
// Controller side
csiDriver.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME, // Create/Delete Volume
csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME, // Mount/Unmount
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME, // Capacity expansion
})
// Node side
csiDriver.AddNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{
csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
})This means the driver implements: create/delete volumes, mount/unmount volumes, and expand volumes (expansion is supported on both the Controller and Node sides).
6. Usage in an Actual CSI Driver (Code Example)
6.1 Initializing the Driver
go
// Generic CSI Driver initialization example
func NewDriver(nodeID, endpoint, driverName, version string) *MyDriver {
d := &MyDriver{endpoint: endpoint}
// Create a CSI Driver instance
csiDriver := csiprovider.NewCSIDriver(driverName, version, nodeID)
// Register Controller-side capabilities
csiDriver.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME,
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
})
// Register Node-side capabilities
csiDriver.AddNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{
csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
})
d.csiDriver = csiDriver
return d
}6.2 Implement the skeleton of CreateVolume
go
func (d *MyDriver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
// 1. Extract parameters from req
name := req.GetName()
size := req.GetCapacityRange().GetRequiredBytes()
caps := req.GetVolumeCapabilities()
// 2. Validate VolumeCapability
for _, cap := range caps {
if !isValidCapability(cap) {
return nil, status.Error(codes.InvalidArgument, "unsupported volume capability")
}
}
// 3. Call the backend storage API to actually create the volume (pseudocode)
// volID, err := d.storageClient.CreateVolume(ctx, name, size)
// if err != nil { return nil, err }
// 4. Construct the response
return &csi.CreateVolumeResponse{
Volume: &csi.Volume{
VolumeId: "vol-xxxxxxxx",
CapacityBytes: size,
VolumeContext: map[string]string{
"createdBy": "my-csi-driver",
},
},
}, nil
}6.3 Implement the skeleton of NodePublishVolume
go
func (d *MyDriver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
targetPath := req.GetTargetPath()
volID := req.GetVolumeId()
volumeCap := req.GetVolumeCapability()
// 1. Determine whether it is Mount or Block
if mountCap := volumeCap.GetMount(); mountCap != nil {
// File system mount
// fsType := mountCap.GetFsType()
// err := d.mountDevice(volID, targetPath, fsType)
} else if blockCap := volumeCap.GetBlock(); blockCap != nil {
// raw block device
// err := d.mapBlockDevice(volID, targetPath)
} else {
return nil, status.Error(codes.InvalidArgument, "missing mount or block capability")
}
return &csi.NodePublishVolumeResponse{}, nil
}VII. Version Evolution (as Seen from the VERSION File)
Current version:v1.13.0(October 2025)
| Version | Key new features |
|---|---|
| v1.0.0 | Basic Specification (2019) |
| v1.1.0 | EXPAND_VOLUME(Volume expansion) |
| v1.2.0 | Volume Clone (Clone) |
| v1.5.0 | Snapshots |
| v1.9.0 | ControllerGetVolume,GET_VOLUME_ATTRIBUTES |
| v1.13.0 | Snapshot Topology(Latest submission as of July 2026) |
Latest commit b516e92(2026.7.22) introduced Snapshot Topology, allowing snapshots to also have topology-awareness capabilities.
VIII. Common Code Snippets Quick Reference
Validate VolumeCapability
go
func isValidCapability(cap *csi.VolumeCapability) bool {
mode := cap.GetAccessMode().GetMode()
if mode != csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER {
return false
}
if cap.GetMount() == nil && cap.GetBlock() == nil {
return false
}
return true
}Extract parameters
go
func getParameter(req *csi.CreateVolumeRequest, key string) string {
if req.GetParameters() == nil {
return ""
}
return req.GetParameters()[key]
}Construction error
go
import "google.golang.org/grpc/codes"
import "google.golang.org/grpc/status"
func invalidArgument(msg string) error {
return status.Error(codes.InvalidArgument, msg)
}
func notFound(msg string) error {
return status.Error(codes.NotFound, msg)
}IX. Summary
| What do you want to do? | Look at this. |
|---|---|
| Understanding the CSI service interface | IdentityServer,ControllerServer,NodeServer |
| Processing volume access mode | VolumeCapability+AccessMode |
| Declare plugin capabilities | ControllerServiceCapability+NodeServiceCapability |
| Create/Delete Volume | CreateVolume/DeleteVolume |
| Mount volume to node | ControllerPublishVolume+NodeStageVolume+NodePublishVolume |
| Volume expansion | ControllerExpandVolume+NodeExpandVolume |
The core value of this library is Defined a standard interface, enabling storage driver developers to integrate with container orchestration systems such as Kubernetes simply by implementing these interfaces.