windows node support

windows node support for smb/iscsi (csi-proxy)
better logging
support (for testing) generating volume_id from name
wait for chown/chmod jobs to complete (freenas)
general improvement to smb behavior throughout

Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
This commit is contained in:
Travis Glenn Hansen 2022-04-17 10:57:57 -06:00
parent 68d0d57a99
commit 3f51f5b7a6
44 changed files with 5204 additions and 998 deletions

View File

@ -250,7 +250,7 @@ jobs:
run: |
export ARCH=$([ $(uname -m) = "x86_64" ] && echo "amd64" || echo "arm64")
mkdir -p ~/.docker/cli-plugins/
wget -qO ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.5.1/buildx-v0.5.1.linux-${ARCH}
wget -qO ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.8.2/buildx-v0.8.2.linux-${ARCH}
chmod a+x ~/.docker/cli-plugins/docker-buildx
docker info
docker buildx version

View File

@ -136,6 +136,14 @@ let operationLock = new Set();
async function requestHandlerProxy(call, callback, serviceMethodName) {
const cleansedCall = JSON.parse(stringify(call));
delete cleansedCall.call;
delete cleansedCall.canceled;
for (const key in cleansedCall) {
if (key.startsWith("_")) {
delete cleansedCall[key];
}
}
for (const key in cleansedCall.request) {
if (key.includes("secret")) {
cleansedCall.request[key] = "redacted";
@ -165,6 +173,17 @@ async function requestHandlerProxy(call, callback, serviceMethodName) {
});
}
// for testing purposes
//await GeneralUtils.sleep(10000);
// for CI/testing purposes
if (["NodePublishVolume", "NodeStageVolume"].includes(serviceMethodName)) {
await driver.setVolumeContextCache(
call.request.volume_id,
call.request.volume_context
);
}
let response;
let responseError;
try {
@ -190,12 +209,21 @@ async function requestHandlerProxy(call, callback, serviceMethodName) {
throw responseError;
}
// for CI/testing purposes
if (serviceMethodName == "CreateVolume") {
await driver.setVolumeContextCache(
response.volume.volume_id,
response.volume.volume_context
);
}
logger.info(
"new response - driver: %s method: %s response: %j",
driver.constructor.name,
serviceMethodName,
response
);
callback(null, response);
} catch (e) {
let message;
@ -205,7 +233,7 @@ async function requestHandlerProxy(call, callback, serviceMethodName) {
message += ` ${e.stack}`;
}
} else {
message = JSON.stringify(e);
message = stringify(e);
}
logger.error(

View File

@ -0,0 +1,111 @@
syntax = "proto3";
package v1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1";
service Disk {
// ListDiskLocations returns locations <Adapter, Bus, Target, LUN ID> of all
// disk devices enumerated by the host.
rpc ListDiskLocations(ListDiskLocationsRequest) returns (ListDiskLocationsResponse) {}
// PartitionDisk initializes and partitions a disk device with the GPT partition style
// (if the disk has not been partitioned already) and returns the resulting volume device ID.
rpc PartitionDisk(PartitionDiskRequest) returns (PartitionDiskResponse) {}
// Rescan refreshes the host's storage cache.
rpc Rescan(RescanRequest) returns (RescanResponse) {}
// ListDiskIDs returns a map of DiskID objects where the key is the disk number.
rpc ListDiskIDs(ListDiskIDsRequest) returns (ListDiskIDsResponse) {}
// GetDiskStats returns the stats of a disk (currently it returns the disk size).
rpc GetDiskStats(GetDiskStatsRequest) returns (GetDiskStatsResponse) {}
// SetDiskState sets the offline/online state of a disk.
rpc SetDiskState(SetDiskStateRequest) returns (SetDiskStateResponse) {}
// GetDiskState gets the offline/online state of a disk.
rpc GetDiskState(GetDiskStateRequest) returns (GetDiskStateResponse) {}
}
message ListDiskLocationsRequest {
// Intentionally empty.
}
message DiskLocation {
string Adapter = 1;
string Bus = 2;
string Target = 3;
string LUNID = 4;
}
message ListDiskLocationsResponse {
// Map of disk number and <adapter, bus, target, lun ID> associated with each disk device.
map <uint32, DiskLocation> disk_locations = 1;
}
message PartitionDiskRequest {
// Disk device number of the disk to partition.
uint32 disk_number = 1;
}
message PartitionDiskResponse {
// Intentionally empty.
}
message RescanRequest {
// Intentionally empty.
}
message RescanResponse {
// Intentionally empty.
}
message ListDiskIDsRequest {
// Intentionally empty.
}
message DiskIDs {
// The disk page83 id.
string page83 = 1;
// The disk serial number.
string serial_number = 2;
}
message ListDiskIDsResponse {
// Map of disk numbers and disk identifiers associated with each disk device.
map <uint32, DiskIDs> diskIDs = 1; // the case is intentional for protoc to generate the field as DiskIDs
}
message GetDiskStatsRequest {
// Disk device number of the disk to get the stats from.
uint32 disk_number = 1;
}
message GetDiskStatsResponse {
// Total size of the volume.
int64 total_bytes = 1;
}
message SetDiskStateRequest {
// Disk device number of the disk.
uint32 disk_number = 1;
// Online state to set for the disk. true for online, false for offline.
bool is_online = 2;
}
message SetDiskStateResponse {
// Intentionally empty.
}
message GetDiskStateRequest {
// Disk device number of the disk.
uint32 disk_number = 1;
}
message GetDiskStateResponse {
// Online state of the disk. true for online, false for offline.
bool is_online = 1;
}

View File

@ -0,0 +1,62 @@
syntax = "proto3";
package v1alpha1;
service Disk {
// ListDiskLocations returns locations <Adapter, Bus, Target, LUN ID> of all
// disk devices enumerated by the host
rpc ListDiskLocations(ListDiskLocationsRequest) returns (ListDiskLocationsResponse) {}
// PartitionDisk initializes and partitions a disk device (if the disk has not
// been partitioned already) and returns the resulting volume device ID
rpc PartitionDisk(PartitionDiskRequest) returns (PartitionDiskResponse) {}
// Rescan refreshes the host's storage cache
rpc Rescan(RescanRequest) returns (RescanResponse) {}
// GetDiskNumberByName returns disk number based on the passing disk name information
rpc GetDiskNumberByName(GetDiskNumberByNameRequest) returns (GetDiskNumberByNameResponse) {}
}
message ListDiskLocationsRequest {
// Intentionally empty
}
message DiskLocation {
string Adapter = 1;
string Bus = 2;
string Target = 3;
string LUNID = 4;
}
message ListDiskLocationsResponse {
// Map of disk device IDs and <adapter, bus, target, lun ID> associated with each disk device
map <string, DiskLocation> disk_locations = 1;
}
message PartitionDiskRequest {
// Disk device ID of the disk to partition
string diskID = 1;
}
message PartitionDiskResponse {
// Intentionally empty
}
message RescanRequest {
// Intentionally empty
}
message RescanResponse {
// Intentionally empty
}
message GetDiskNumberByNameRequest {
// Disk ID
string disk_name = 1;
}
message GetDiskNumberByNameResponse {
// Disk number
string disk_number = 1;
}

View File

@ -0,0 +1,81 @@
syntax = "proto3";
package v1beta1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1beta1";
service Disk {
// ListDiskLocations returns locations <Adapter, Bus, Target, LUN ID> of all
// disk devices enumerated by the host
rpc ListDiskLocations(ListDiskLocationsRequest) returns (ListDiskLocationsResponse) {}
// PartitionDisk initializes and partitions a disk device (if the disk has not
// been partitioned already) and returns the resulting volume device ID
rpc PartitionDisk(PartitionDiskRequest) returns (PartitionDiskResponse) {}
// Rescan refreshes the host's storage cache
rpc Rescan(RescanRequest) returns (RescanResponse) {}
// ListDiskIDs returns a map of DiskID objects where the key is the disk number
rpc ListDiskIDs(ListDiskIDsRequest) returns (ListDiskIDsResponse) {}
// DiskStats returns the stats for the disk
rpc DiskStats(DiskStatsRequest) returns (DiskStatsResponse) {}
}
message ListDiskLocationsRequest {
// Intentionally empty
}
message DiskLocation {
string Adapter = 1;
string Bus = 2;
string Target = 3;
string LUNID = 4;
}
message ListDiskLocationsResponse {
// Map of disk device IDs and <adapter, bus, target, lun ID> associated with each disk device
map <string, DiskLocation> disk_locations = 1;
}
message PartitionDiskRequest {
// Disk device ID of the disk to partition
string diskID = 1;
}
message PartitionDiskResponse {
// Intentionally empty
}
message RescanRequest {
// Intentionally empty
}
message RescanResponse {
// Intentionally empty
}
message ListDiskIDsRequest {
// Intentionally empty
}
message DiskIDs {
// Map of Disk ID types and Disk ID values
map <string, string> identifiers = 1;
}
message ListDiskIDsResponse {
// Map of disk device numbers and IDs <page83> associated with each disk device
map <string, DiskIDs> diskIDs = 1;
}
message DiskStatsRequest {
// Disk device ID of the disk to get the size from
string diskID = 1;
}
message DiskStatsResponse {
//Total size of the volume
int64 diskSize = 1;
}

View File

@ -0,0 +1,109 @@
syntax = "proto3";
package v1beta2;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1beta2";
service Disk {
// ListDiskLocations returns locations <Adapter, Bus, Target, LUN ID> of all
// disk devices enumerated by the host
rpc ListDiskLocations(ListDiskLocationsRequest) returns (ListDiskLocationsResponse) {}
// PartitionDisk initializes and partitions a disk device (if the disk has not
// been partitioned already) and returns the resulting volume device ID
rpc PartitionDisk(PartitionDiskRequest) returns (PartitionDiskResponse) {}
// Rescan refreshes the host's storage cache
rpc Rescan(RescanRequest) returns (RescanResponse) {}
// ListDiskIDs returns a map of DiskID objects where the key is the disk number
rpc ListDiskIDs(ListDiskIDsRequest) returns (ListDiskIDsResponse) {}
// DiskStats returns the stats for the disk
rpc DiskStats(DiskStatsRequest) returns (DiskStatsResponse) {}
// SetAttachState sets the offline/online state of a disk
rpc SetAttachState(SetAttachStateRequest) returns (SetAttachStateResponse) {}
// GetAttachState gets the offline/online state of a disk
rpc GetAttachState(GetAttachStateRequest) returns (GetAttachStateResponse) {}
}
message ListDiskLocationsRequest {
// Intentionally empty
}
message DiskLocation {
string Adapter = 1;
string Bus = 2;
string Target = 3;
string LUNID = 4;
}
message ListDiskLocationsResponse {
// Map of disk device IDs and <adapter, bus, target, lun ID> associated with each disk device
map <string, DiskLocation> disk_locations = 1;
}
message PartitionDiskRequest {
// Disk device ID of the disk to partition
string diskID = 1;
}
message PartitionDiskResponse {
// Intentionally empty
}
message RescanRequest {
// Intentionally empty
}
message RescanResponse {
// Intentionally empty
}
message ListDiskIDsRequest {
// Intentionally empty
}
message DiskIDs {
// Map of Disk ID types and Disk ID values
map <string, string> identifiers = 1;
}
message ListDiskIDsResponse {
// Map of disk device numbers and IDs <page83> associated with each disk device
map <string, DiskIDs> diskIDs = 1;
}
message DiskStatsRequest {
// Disk device ID of the disk to get the size from
string diskID = 1;
}
message DiskStatsResponse {
//Total size of the volume
int64 diskSize = 1;
}
message SetAttachStateRequest {
// Disk device ID (number) of the disk which state will change
string diskID = 1;
// Online state to set for the disk. true for online, false for offline
bool isOnline = 2;
}
message SetAttachStateResponse {
}
message GetAttachStateRequest {
// Disk device ID (number) of the disk
string diskID = 1;
}
message GetAttachStateResponse {
// Online state of the disk. true for online, false for offline
bool isOnline = 1;
}

View File

@ -0,0 +1,111 @@
syntax = "proto3";
package v1beta3;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1beta3";
service Disk {
// ListDiskLocations returns locations <Adapter, Bus, Target, LUN ID> of all
// disk devices enumerated by the host.
rpc ListDiskLocations(ListDiskLocationsRequest) returns (ListDiskLocationsResponse) {}
// PartitionDisk initializes and partitions a disk device with the GPT partition style
// (if the disk has not been partitioned already) and returns the resulting volume device ID.
rpc PartitionDisk(PartitionDiskRequest) returns (PartitionDiskResponse) {}
// Rescan refreshes the host's storage cache.
rpc Rescan(RescanRequest) returns (RescanResponse) {}
// ListDiskIDs returns a map of DiskID objects where the key is the disk number.
rpc ListDiskIDs(ListDiskIDsRequest) returns (ListDiskIDsResponse) {}
// GetDiskStats returns the stats of a disk (currently it returns the disk size).
rpc GetDiskStats(GetDiskStatsRequest) returns (GetDiskStatsResponse) {}
// SetDiskState sets the offline/online state of a disk.
rpc SetDiskState(SetDiskStateRequest) returns (SetDiskStateResponse) {}
// GetDiskState gets the offline/online state of a disk.
rpc GetDiskState(GetDiskStateRequest) returns (GetDiskStateResponse) {}
}
message ListDiskLocationsRequest {
// Intentionally empty.
}
message DiskLocation {
string Adapter = 1;
string Bus = 2;
string Target = 3;
string LUNID = 4;
}
message ListDiskLocationsResponse {
// Map of disk number and <adapter, bus, target, lun ID> associated with each disk device.
map <uint32, DiskLocation> disk_locations = 1;
}
message PartitionDiskRequest {
// Disk device number of the disk to partition.
uint32 disk_number = 1;
}
message PartitionDiskResponse {
// Intentionally empty.
}
message RescanRequest {
// Intentionally empty.
}
message RescanResponse {
// Intentionally empty.
}
message ListDiskIDsRequest {
// Intentionally empty.
}
message DiskIDs {
// The disk page83 id.
string page83 = 1;
// The disk serial number.
string serial_number = 2;
}
message ListDiskIDsResponse {
// Map of disk numbers and disk identifiers associated with each disk device.
map <uint32, DiskIDs> diskIDs = 1; // the case is intentional for protoc to generate the field as DiskIDs
}
message GetDiskStatsRequest {
// Disk device number of the disk to get the stats from.
uint32 disk_number = 1;
}
message GetDiskStatsResponse {
// Total size of the volume.
int64 total_bytes = 1;
}
message SetDiskStateRequest {
// Disk device number of the disk.
uint32 disk_number = 1;
// Online state to set for the disk. true for online, false for offline.
bool is_online = 2;
}
message SetDiskStateResponse {
// Intentionally empty.
}
message GetDiskStateRequest {
// Disk device number of the disk.
uint32 disk_number = 1;
}
message GetDiskStateResponse {
// Online state of the disk. true for online, false for offline.
bool is_online = 1;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
package api;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api";
// CommandError details errors yielded by cmdlet calls.
message CmdletError {
// Name of the cmdlet that errored out.
string cmdlet_name = 1;
// Error code that got returned.
uint32 code = 2;
// Human-readable error message - can be empty.
string message = 3;
}

View File

@ -0,0 +1,136 @@
syntax = "proto3";
package v1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/filesystem/v1";
service Filesystem {
// PathExists checks if the requested path exists in the host filesystem.
rpc PathExists(PathExistsRequest) returns (PathExistsResponse) {}
// Mkdir creates a directory at the requested path in the host filesystem.
rpc Mkdir(MkdirRequest) returns (MkdirResponse) {}
// Rmdir removes the directory at the requested path in the host filesystem.
// This may be used for unlinking a symlink created through CreateSymlink.
rpc Rmdir(RmdirRequest) returns (RmdirResponse) {}
// CreateSymlink creates a symbolic link called target_path that points to source_path
// in the host filesystem (target_path is the name of the symbolic link created,
// source_path is the existing path).
rpc CreateSymlink(CreateSymlinkRequest) returns (CreateSymlinkResponse) {}
// IsSymlink checks if a given path is a symlink.
rpc IsSymlink(IsSymlinkRequest) returns (IsSymlinkResponse) {}
}
message PathExistsRequest {
// The path whose existence we want to check in the host's filesystem
string path = 1;
}
message PathExistsResponse {
// Indicates whether the path in PathExistsRequest exists in the host's filesystem
bool exists = 1;
}
message MkdirRequest {
// The path to create in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
// Non-existent parent directories in the path will be automatically created.
// Directories will be created with Read and Write privileges of the Windows
// User account under which csi-proxy is started (typically LocalSystem).
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// The path parameter cannot already exist in the host's filesystem.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Maximum path length will be capped to 260 characters.
string path = 1;
}
message MkdirResponse {
// Intentionally empty.
}
message RmdirRequest {
// The path to remove in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Path cannot be a file of type symlink.
// Maximum path length will be capped to 260 characters.
string path = 1;
// Force remove all contents under path (if any).
bool force = 2;
}
message RmdirResponse {
// Intentionally empty.
}
message CreateSymlinkRequest {
// The path of the existing directory to be linked.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs needs to match the paths specified as
// kubelet-csi-plugins-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// source_path cannot already exist in the host filesystem.
// Maximum path length will be capped to 260 characters.
string source_path = 1;
// Target path is the location of the new directory entry to be created in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs to match the paths specified as
// kubelet-pod-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// target_path needs to exist as a directory in the host that is empty.
// target_path cannot be a symbolic link.
// Maximum path length will be capped to 260 characters.
string target_path = 2;
}
message CreateSymlinkResponse {
// Intentionally empty.
}
message IsSymlinkRequest {
// The path whose existence as a symlink we want to check in the host's filesystem.
string path = 1;
}
message IsSymlinkResponse {
// Indicates whether the path in IsSymlinkRequest is a symlink.
bool is_symlink = 1;
}

View File

@ -0,0 +1,168 @@
syntax = "proto3";
package v1alpha1;
service Filesystem {
// PathExists checks if the requested path exists in the host's filesystem
rpc PathExists(PathExistsRequest) returns (PathExistsResponse) {}
// Mkdir creates a directory at the requested path in the host's filesystem
rpc Mkdir(MkdirRequest) returns (MkdirResponse) {}
// Rmdir removes the directory at the requested path in the host's filesystem.
// This may be used for unlinking a symlink created through LinkPath
rpc Rmdir(RmdirRequest) returns (RmdirResponse) {}
// LinkPath creates a local directory symbolic link between a source path
// and target path in the host's filesystem
rpc LinkPath(LinkPathRequest) returns (LinkPathResponse) {}
//IsMountPoint checks if a given path is mount or not
rpc IsMountPoint(IsMountPointRequest) returns (IsMountPointResponse) {}
}
// Context of the paths used for path prefix validation
enum PathContext {
// Indicates the kubelet-csi-plugins-path parameter of csi-proxy be used as
// the path context. This may be used while handling NodeStageVolume where
// a volume may need to be mounted at a plugin-specific path like:
// kubelet\plugins\kubernetes.io\csi\pv\<pv-name>\globalmount
PLUGIN = 0;
// Indicates the kubelet-pod-path parameter of csi-proxy be used as the path
// context. This may be used while handling NodePublishVolume where a staged
// volume may be need to be symlinked to a pod-specific path like:
// kubelet\pods\<pod-uuid>\volumes\kubernetes.io~csi\<pvc-name>\mount
POD = 1;
}
message PathExistsRequest {
// The path whose existence we want to check in the host's filesystem
string path = 1;
// Context of the path parameter.
// This is used to validate prefix for absolute paths passed
PathContext context = 2;
}
message PathExistsResponse {
// Error message if any. Empty string indicates success
string error = 1;
// Indicates whether the path in PathExistsRequest exists in the host's filesystem
bool exists = 2;
}
message MkdirRequest {
// The path to create in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
// Non-existent parent directories in the path will be automatically created.
// Directories will be created with Read and Write privileges of the Windows
// User account under which csi-proxy is started (typically LocalSystem).
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// The path parameter cannot already exist in the host's filesystem.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Maximum path length will be capped to 260 characters.
string path = 1;
// Context of the path parameter.
// This is used to validate prefix for absolute paths passed
PathContext context = 2;
}
message MkdirResponse {
// Error message if any. Empty string indicates success
string error = 1;
}
message RmdirRequest {
// The path to remove in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Path cannot be a file of type symlink.
// Maximum path length will be capped to 260 characters.
string path = 1;
// Context of the path parameter.
// This is used to validate prefix for absolute paths passed
PathContext context = 2;
// Force remove all contents under path (if any).
bool force = 3;
}
message RmdirResponse {
// Error message if any. Empty string indicates success
string error = 1;
}
message LinkPathRequest {
// The path where the symlink is created in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs needs to match the paths specified as
// kubelet-csi-plugins-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// source_path cannot already exist in the host filesystem.
// Maximum path length will be capped to 260 characters.
string source_path = 1;
// Target path in the host's filesystem used for the symlink creation.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs to match the paths specified as
// kubelet-pod-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// target_path needs to exist as a directory in the host that is empty.
// target_path cannot be a symbolic link.
// Maximum path length will be capped to 260 characters.
string target_path = 2;
}
message LinkPathResponse {
// Error message if any. Empty string indicates success
string error = 1;
}
message IsMountPointRequest {
// The path whose existence we want to check in the host's filesystem
string path = 1;
}
message IsMountPointResponse {
// Error message if any. Empty string indicates success
string error = 1;
// Indicates whether the path in PathExistsRequest exists in the host's filesystem
bool is_mount_point = 2;
}

View File

@ -0,0 +1,168 @@
syntax = "proto3";
package v1beta1;
service Filesystem {
// PathExists checks if the requested path exists in the host's filesystem
rpc PathExists(PathExistsRequest) returns (PathExistsResponse) {}
// Mkdir creates a directory at the requested path in the host's filesystem
rpc Mkdir(MkdirRequest) returns (MkdirResponse) {}
// Rmdir removes the directory at the requested path in the host's filesystem.
// This may be used for unlinking a symlink created through LinkPath
rpc Rmdir(RmdirRequest) returns (RmdirResponse) {}
// LinkPath creates a local directory symbolic link between a source path
// and target path in the host's filesystem
rpc LinkPath(LinkPathRequest) returns (LinkPathResponse) {}
//IsMountPoint checks if a given path is mount or not
rpc IsMountPoint(IsMountPointRequest) returns (IsMountPointResponse) {}
}
// Context of the paths used for path prefix validation
enum PathContext {
// Indicates the kubelet-csi-plugins-path parameter of csi-proxy be used as
// the path context. This may be used while handling NodeStageVolume where
// a volume may need to be mounted at a plugin-specific path like:
// kubelet\plugins\kubernetes.io\csi\pv\<pv-name>\globalmount
PLUGIN = 0;
// Indicates the kubelet-pod-path parameter of csi-proxy be used as the path
// context. This may be used while handling NodePublishVolume where a staged
// volume may be need to be symlinked to a pod-specific path like:
// kubelet\pods\<pod-uuid>\volumes\kubernetes.io~csi\<pvc-name>\mount
POD = 1;
}
message PathExistsRequest {
// The path whose existence we want to check in the host's filesystem
string path = 1;
// Context of the path parameter.
// This is used to validate prefix for absolute paths passed
PathContext context = 2;
}
message PathExistsResponse {
// Error message if any. Empty string indicates success
string error = 1;
// Indicates whether the path in PathExistsRequest exists in the host's filesystem
bool exists = 2;
}
message MkdirRequest {
// The path to create in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
// Non-existent parent directories in the path will be automatically created.
// Directories will be created with Read and Write privileges of the Windows
// User account under which csi-proxy is started (typically LocalSystem).
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// The path parameter cannot already exist in the host's filesystem.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Maximum path length will be capped to 260 characters.
string path = 1;
// Context of the path parameter.
// This is used to validate prefix for absolute paths passed
PathContext context = 2;
}
message MkdirResponse {
// Error message if any. Empty string indicates success
string error = 1;
}
message RmdirRequest {
// The path to remove in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Path cannot be a file of type symlink.
// Maximum path length will be capped to 260 characters.
string path = 1;
// Context of the path parameter.
// This is used to validate prefix for absolute paths passed
PathContext context = 2;
// Force remove all contents under path (if any).
bool force = 3;
}
message RmdirResponse {
// Error message if any. Empty string indicates success
string error = 1;
}
message LinkPathRequest {
// The path where the symlink is created in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs needs to match the paths specified as
// kubelet-csi-plugins-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// source_path cannot already exist in the host filesystem.
// Maximum path length will be capped to 260 characters.
string source_path = 1;
// Target path in the host's filesystem used for the symlink creation.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs to match the paths specified as
// kubelet-pod-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// target_path needs to exist as a directory in the host that is empty.
// target_path cannot be a symbolic link.
// Maximum path length will be capped to 260 characters.
string target_path = 2;
}
message LinkPathResponse {
// Error message if any. Empty string indicates success
string error = 1;
}
message IsMountPointRequest {
// The path whose existence we want to check in the host's filesystem
string path = 1;
}
message IsMountPointResponse {
// Error message if any. Empty string indicates success
string error = 1;
// Indicates whether the path in PathExistsRequest exists in the host's filesystem
bool is_mount_point = 2;
}

View File

@ -0,0 +1,136 @@
syntax = "proto3";
package v1beta2;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/filesystem/v1beta2";
service Filesystem {
// PathExists checks if the requested path exists in the host filesystem.
rpc PathExists(PathExistsRequest) returns (PathExistsResponse) {}
// Mkdir creates a directory at the requested path in the host filesystem.
rpc Mkdir(MkdirRequest) returns (MkdirResponse) {}
// Rmdir removes the directory at the requested path in the host filesystem.
// This may be used for unlinking a symlink created through CreateSymlink.
rpc Rmdir(RmdirRequest) returns (RmdirResponse) {}
// CreateSymlink creates a symbolic link called target_path that points to source_path
// in the host filesystem (target_path is the name of the symbolic link created,
// source_path is the existing path).
rpc CreateSymlink(CreateSymlinkRequest) returns (CreateSymlinkResponse) {}
// IsSymlink checks if a given path is a symlink.
rpc IsSymlink(IsSymlinkRequest) returns (IsSymlinkResponse) {}
}
message PathExistsRequest {
// The path whose existence we want to check in the host's filesystem
string path = 1;
}
message PathExistsResponse {
// Indicates whether the path in PathExistsRequest exists in the host's filesystem
bool exists = 1;
}
message MkdirRequest {
// The path to create in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
// Non-existent parent directories in the path will be automatically created.
// Directories will be created with Read and Write privileges of the Windows
// User account under which csi-proxy is started (typically LocalSystem).
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// The path parameter cannot already exist in the host's filesystem.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Maximum path length will be capped to 260 characters.
string path = 1;
}
message MkdirResponse {
// Intentionally empty.
}
message RmdirRequest {
// The path to remove in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Path cannot be a file of type symlink.
// Maximum path length will be capped to 260 characters.
string path = 1;
// Force remove all contents under path (if any).
bool force = 2;
}
message RmdirResponse {
// Intentionally empty.
}
message CreateSymlinkRequest {
// The path of the existing directory to be linked.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs needs to match the paths specified as
// kubelet-csi-plugins-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// source_path cannot already exist in the host filesystem.
// Maximum path length will be capped to 260 characters.
string source_path = 1;
// Target path is the location of the new directory entry to be created in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs to match the paths specified as
// kubelet-pod-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// target_path needs to exist as a directory in the host that is empty.
// target_path cannot be a symbolic link.
// Maximum path length will be capped to 260 characters.
string target_path = 2;
}
message CreateSymlinkResponse {
// Intentionally empty.
}
message IsSymlinkRequest {
// The path whose existence as a symlink we want to check in the host's filesystem.
string path = 1;
}
message IsSymlinkResponse {
// Indicates whether the path in IsSymlinkRequest is a symlink.
bool is_symlink = 1;
}

View File

@ -0,0 +1,163 @@
syntax = "proto3";
package v2alpha1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/filesystem/v2alpha1";
service Filesystem {
// PathExists checks if the requested path exists in the host filesystem.
rpc PathExists(PathExistsRequest) returns (PathExistsResponse) {}
// Mkdir creates a directory at the requested path in the host filesystem.
rpc Mkdir(MkdirRequest) returns (MkdirResponse) {}
// Rmdir removes the directory at the requested path in the host filesystem.
// This may be used for unlinking a symlink created through CreateSymlink.
rpc Rmdir(RmdirRequest) returns (RmdirResponse) {}
// RmdirContents removes the contents of a directory in the host filesystem.
// Unlike Rmdir it won't delete the requested path, it'll only delete its contents.
rpc RmdirContents(RmdirContentsRequest) returns (RmdirContentsResponse) {}
// CreateSymlink creates a symbolic link called target_path that points to source_path
// in the host filesystem (target_path is the name of the symbolic link created,
// source_path is the existing path).
rpc CreateSymlink(CreateSymlinkRequest) returns (CreateSymlinkResponse) {}
// IsSymlink checks if a given path is a symlink.
rpc IsSymlink(IsSymlinkRequest) returns (IsSymlinkResponse) {}
}
message PathExistsRequest {
// The path whose existence we want to check in the host's filesystem
string path = 1;
}
message PathExistsResponse {
// Indicates whether the path in PathExistsRequest exists in the host's filesystem
bool exists = 1;
}
message MkdirRequest {
// The path to create in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
// Non-existent parent directories in the path will be automatically created.
// Directories will be created with Read and Write privileges of the Windows
// User account under which csi-proxy is started (typically LocalSystem).
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// The path parameter cannot already exist in the host's filesystem.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Maximum path length will be capped to 260 characters.
string path = 1;
}
message MkdirResponse {
// Intentionally empty.
}
message RmdirRequest {
// The path to remove in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Path cannot be a file of type symlink.
// Maximum path length will be capped to 260 characters.
string path = 1;
// Force remove all contents under path (if any).
bool force = 2;
}
message RmdirResponse {
// Intentionally empty.
}
message RmdirContentsRequest {
// The path whose contents will be removed in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// Depending on the context parameter of this function, the path prefix needs
// to match the paths specified either as kubelet-csi-plugins-path
// or as kubelet-pod-path parameters of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// Path cannot be a file of type symlink.
// Maximum path length will be capped to 260 characters.
string path = 1;
}
message RmdirContentsResponse {
// Intentionally empty.
}
message CreateSymlinkRequest {
// The path of the existing directory to be linked.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs needs to match the paths specified as
// kubelet-csi-plugins-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// source_path cannot already exist in the host filesystem.
// Maximum path length will be capped to 260 characters.
string source_path = 1;
// Target path is the location of the new directory entry to be created in the host's filesystem.
// All special characters allowed by Windows in path names will be allowed
// except for restrictions noted below. For details, please check:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
//
// Restrictions:
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
// The path prefix needs to match the paths specified as
// kubelet-pod-path parameter of csi-proxy.
// UNC paths of the form "\\server\share\path\file" are not allowed.
// All directory separators need to be backslash character: "\".
// Characters: .. / : | ? * in the path are not allowed.
// target_path needs to exist as a directory in the host that is empty.
// target_path cannot be a symbolic link.
// Maximum path length will be capped to 260 characters.
string target_path = 2;
}
message CreateSymlinkResponse {
// Intentionally empty.
}
message IsSymlinkRequest {
// The path whose existence as a symlink we want to check in the host's filesystem.
string path = 1;
}
message IsSymlinkResponse {
// Indicates whether the path in IsSymlinkRequest is a symlink.
bool is_symlink = 1;
}

View File

@ -0,0 +1,153 @@
syntax = "proto3";
package v1alpha1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1";
service Iscsi {
// AddTargetPortal registers an iSCSI target network address for later
// discovery.
// AddTargetPortal currently does not support selecting different NICs or
// a different iSCSI initiator (e.g a hardware initiator). This means that
// Windows will select the initiator NIC and instance on its own.
rpc AddTargetPortal(AddTargetPortalRequest)
returns (AddTargetPortalResponse) {}
// DiscoverTargetPortal initiates discovery on an iSCSI target network address
// and returns discovered IQNs.
rpc DiscoverTargetPortal(DiscoverTargetPortalRequest)
returns (DiscoverTargetPortalResponse) {}
// RemoveTargetPortal removes an iSCSI target network address registration.
rpc RemoveTargetPortal(RemoveTargetPortalRequest)
returns (RemoveTargetPortalResponse) {}
// ListTargetPortal lists all currently registered iSCSI target network
// addresses.
rpc ListTargetPortals(ListTargetPortalsRequest)
returns (ListTargetPortalsResponse) {}
// ConnectTarget connects to an iSCSI Target
rpc ConnectTarget(ConnectTargetRequest) returns (ConnectTargetResponse) {}
// DisconnectTarget disconnects from an iSCSI Target
rpc DisconnectTarget(DisconnectTargetRequest)
returns (DisconnectTargetResponse) {}
// GetTargetDisks returns the disk addresses that correspond to an iSCSI
// target
rpc GetTargetDisks(GetTargetDisksRequest) returns (GetTargetDisksResponse) {}
}
// TargetPortal is an address and port pair for a specific iSCSI storage
// target.
message TargetPortal {
// iSCSI Target (server) address
string target_address = 1;
// iSCSI Target port (default iSCSI port is 3260)
uint32 target_port = 2;
}
message AddTargetPortalRequest {
// iSCSI Target Portal to register in the initiator
TargetPortal target_portal = 1;
}
message AddTargetPortalResponse {
// Intentionally empty
}
message DiscoverTargetPortalRequest {
// iSCSI Target Portal on which to initiate discovery
TargetPortal target_portal = 1;
}
message DiscoverTargetPortalResponse {
// List of discovered IQN addresses
// follows IQN format: iqn.yyyy-mm.naming-authority:unique-name
repeated string iqns = 1;
}
message RemoveTargetPortalRequest {
// iSCSI Target Portal
TargetPortal target_portal = 1;
}
message RemoveTargetPortalResponse {
// Intentionally empty
}
message ListTargetPortalsRequest {
// Intentionally empty
}
message ListTargetPortalsResponse {
// A list of Target Portals currently registered in the initiator
repeated TargetPortal target_portals = 1;
}
enum AuthenticationType {
// No authentication is used
NONE = 0;
// One way CHAP authentication. The target authenticates the initiator.
ONE_WAY_CHAP = 1;
// Mutual CHAP authentication. The target and initiator authenticate each
// other.
MUTUAL_CHAP = 2;
}
message ConnectTargetRequest {
// Target portal to which the initiator will connect
TargetPortal target_portal = 1;
// IQN of the iSCSI Target
string iqn = 2;
// Connection authentication type, None by default
//
// One Way Chap uses the chap_username and chap_secret
// fields mentioned below to authenticate the initiator.
//
// Mutual Chap uses both the user/secret mentioned below
// and the Initiator Chap Secret to authenticate the target and initiator.
AuthenticationType auth_type = 3;
// CHAP Username used to authenticate the initiator
string chap_username = 4;
// CHAP password used to authenticate the initiator
string chap_secret = 5;
}
message ConnectTargetResponse {
// Intentionally empty
}
message GetTargetDisksRequest {
// Target portal whose disks will be queried
TargetPortal target_portal = 1;
// IQN of the iSCSI Target
string iqn = 2;
}
message GetTargetDisksResponse {
// List composed of disk ids (numbers) that are associated with the
// iSCSI target
repeated string diskIDs = 1;
}
message DisconnectTargetRequest {
// Target portal from which initiator will disconnect
TargetPortal target_portal = 1;
// IQN of the iSCSI Target
string iqn = 2;
}
message DisconnectTargetResponse {
// Intentionally empty
}

View File

@ -0,0 +1,175 @@
syntax = "proto3";
package v1alpha2;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha2";
service Iscsi {
// AddTargetPortal registers an iSCSI target network address for later
// discovery.
// AddTargetPortal currently does not support selecting different NICs or
// a different iSCSI initiator (e.g a hardware initiator). This means that
// Windows will select the initiator NIC and instance on its own.
rpc AddTargetPortal(AddTargetPortalRequest)
returns (AddTargetPortalResponse) {}
// DiscoverTargetPortal initiates discovery on an iSCSI target network address
// and returns discovered IQNs.
rpc DiscoverTargetPortal(DiscoverTargetPortalRequest)
returns (DiscoverTargetPortalResponse) {}
// RemoveTargetPortal removes an iSCSI target network address registration.
rpc RemoveTargetPortal(RemoveTargetPortalRequest)
returns (RemoveTargetPortalResponse) {}
// ListTargetPortal lists all currently registered iSCSI target network
// addresses.
rpc ListTargetPortals(ListTargetPortalsRequest)
returns (ListTargetPortalsResponse) {}
// ConnectTarget connects to an iSCSI Target
rpc ConnectTarget(ConnectTargetRequest) returns (ConnectTargetResponse) {}
// DisconnectTarget disconnects from an iSCSI Target
rpc DisconnectTarget(DisconnectTargetRequest)
returns (DisconnectTargetResponse) {}
// GetTargetDisks returns the disk addresses that correspond to an iSCSI
// target
rpc GetTargetDisks(GetTargetDisksRequest) returns (GetTargetDisksResponse) {}
// SetMutualChapSecret sets the default CHAP secret that all initiators on
// this machine (node) use to authenticate the target on mutual CHAP
// authentication.
// NOTE: This method affects global node state and should only be used
// with consideration to other CSI drivers that run concurrently.
rpc SetMutualChapSecret(SetMutualChapSecretRequest)
returns (SetMutualChapSecretResponse) {}
}
// TargetPortal is an address and port pair for a specific iSCSI storage
// target.
message TargetPortal {
// iSCSI Target (server) address
string target_address = 1;
// iSCSI Target port (default iSCSI port is 3260)
uint32 target_port = 2;
}
message AddTargetPortalRequest {
// iSCSI Target Portal to register in the initiator
TargetPortal target_portal = 1;
}
message AddTargetPortalResponse {
// Intentionally empty
}
message DiscoverTargetPortalRequest {
// iSCSI Target Portal on which to initiate discovery
TargetPortal target_portal = 1;
}
message DiscoverTargetPortalResponse {
// List of discovered IQN addresses
// follows IQN format: iqn.yyyy-mm.naming-authority:unique-name
repeated string iqns = 1;
}
message RemoveTargetPortalRequest {
// iSCSI Target Portal
TargetPortal target_portal = 1;
}
message RemoveTargetPortalResponse {
// Intentionally empty
}
message ListTargetPortalsRequest {
// Intentionally empty
}
message ListTargetPortalsResponse {
// A list of Target Portals currently registered in the initiator
repeated TargetPortal target_portals = 1;
}
// iSCSI logon authentication type
enum AuthenticationType {
// No authentication is used
NONE = 0;
// One way CHAP authentication. The target authenticates the initiator.
ONE_WAY_CHAP = 1;
// Mutual CHAP authentication. The target and initiator authenticate each
// other.
MUTUAL_CHAP = 2;
}
message ConnectTargetRequest {
// Target portal to which the initiator will connect
TargetPortal target_portal = 1;
// IQN of the iSCSI Target
string iqn = 2;
// Connection authentication type, None by default
//
// One Way Chap uses the chap_username and chap_secret
// fields mentioned below to authenticate the initiator.
//
// Mutual Chap uses both the user/secret mentioned below
// and the Initiator Chap Secret (See `SetMutualChapSecret`)
// to authenticate the target and initiator.
AuthenticationType auth_type = 3;
// CHAP Username used to authenticate the initiator
string chap_username = 4;
// CHAP password used to authenticate the initiator
string chap_secret = 5;
}
message ConnectTargetResponse {
// Intentionally empty
}
message GetTargetDisksRequest {
// Target portal whose disks will be queried
TargetPortal target_portal = 1;
// IQN of the iSCSI Target
string iqn = 2;
}
message GetTargetDisksResponse {
// List composed of disk ids (numbers) that are associated with the
// iSCSI target
repeated string diskIDs = 1;
}
message DisconnectTargetRequest {
// Target portal from which initiator will disconnect
TargetPortal target_portal = 1;
// IQN of the iSCSI Target
string iqn = 2;
}
message DisconnectTargetResponse {
// Intentionally empty
}
message SetMutualChapSecretRequest {
// the default CHAP secret that all initiators on this machine (node) use to
// authenticate the target on mutual CHAP authentication.
// Must be at least 12 byte long for non-Ipsec connections, at least one
// byte long for Ipsec connections, and at most 16 bytes long.
string MutualChapSecret = 1;
}
message SetMutualChapSecretResponse {
// Intentionally empty
}

View File

@ -0,0 +1,58 @@
syntax = "proto3";
package v1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/smb/v1";
service Smb {
// NewSmbGlobalMapping creates an SMB mapping on the SMB client to an SMB share.
rpc NewSmbGlobalMapping(NewSmbGlobalMappingRequest) returns (NewSmbGlobalMappingResponse) {}
// RemoveSmbGlobalMapping removes the SMB mapping to an SMB share.
rpc RemoveSmbGlobalMapping(RemoveSmbGlobalMappingRequest) returns (RemoveSmbGlobalMappingResponse) {}
}
message NewSmbGlobalMappingRequest {
// A remote SMB share to mount
// All unicode characters allowed in SMB server name specifications are
// permitted except for restrictions below
//
// Restrictions:
// SMB remote path specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename
// If not an IP address, share name has to be a valid DNS name.
// UNC specifications to local paths or prefix: \\?\ is not allowed.
// Characters: + [ ] " / : ; | < > , ? * = $ are not allowed.
string remote_path = 1;
// Optional local path to mount the smb on
string local_path = 2;
// Username credential associated with the share
string username = 3;
// Password credential associated with the share
string password = 4;
}
message NewSmbGlobalMappingResponse {
// Intentionally empty.
}
message RemoveSmbGlobalMappingRequest {
// A remote SMB share mapping to remove
// All unicode characters allowed in SMB server name specifications are
// permitted except for restrictions below
//
// Restrictions:
// SMB share specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename
// If not an IP address, share name has to be a valid DNS name.
// UNC specifications to local paths or prefix: \\?\ is not allowed.
// Characters: + [ ] " / : ; | < > , ? * = $ are not allowed.
string remote_path = 1;
}
message RemoveSmbGlobalMappingResponse {
// Intentionally empty.
}

View File

@ -0,0 +1,59 @@
syntax = "proto3";
package v1alpha1;
service Smb {
// NewSmbGlobalMapping creates an SMB mapping on the SMB client to an SMB share.
rpc NewSmbGlobalMapping(NewSmbGlobalMappingRequest) returns (NewSmbGlobalMappingResponse) {}
// RemoveSmbGlobalMapping removes the SMB mapping to an SMB share.
rpc RemoveSmbGlobalMapping(RemoveSmbGlobalMappingRequest) returns (RemoveSmbGlobalMappingResponse) {}
}
message NewSmbGlobalMappingRequest {
// A remote SMB share to mount
// All unicode characters allowed in SMB server name specifications are
// permitted except for restrictions below
//
// Restrictions:
// SMB remote path specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename
// If not an IP address, share name has to be a valid DNS name.
// UNC specifications to local paths or prefix: \\?\ is not allowed.
// Characters: + [ ] " / : ; | < > , ? * = $ are not allowed.
string remote_path = 1;
// Optional local path to mount the smb on
string local_path = 2;
// Username credential associated with the share
string username = 3;
// Password credential associated with the share
string password = 4;
}
message NewSmbGlobalMappingResponse {
// Windows error code
// Success is represented as 0
string error = 1;
}
message RemoveSmbGlobalMappingRequest {
// A remote SMB share mapping to remove
// All unicode characters allowed in SMB server name specifications are
// permitted except for restrictions below
//
// Restrictions:
// SMB share specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename
// If not an IP address, share name has to be a valid DNS name.
// UNC specifications to local paths or prefix: \\?\ is not allowed.
// Characters: + [ ] " / : ; | < > , ? * = $ are not allowed.
string remote_path = 1;
}
message RemoveSmbGlobalMappingResponse {
// Windows error code
// Success is represented as 0
string error = 1;
}

View File

@ -0,0 +1,59 @@
syntax = "proto3";
package v1beta1;
service Smb {
// NewSmbGlobalMapping creates an SMB mapping on the SMB client to an SMB share.
rpc NewSmbGlobalMapping(NewSmbGlobalMappingRequest) returns (NewSmbGlobalMappingResponse) {}
// RemoveSmbGlobalMapping removes the SMB mapping to an SMB share.
rpc RemoveSmbGlobalMapping(RemoveSmbGlobalMappingRequest) returns (RemoveSmbGlobalMappingResponse) {}
}
message NewSmbGlobalMappingRequest {
// A remote SMB share to mount
// All unicode characters allowed in SMB server name specifications are
// permitted except for restrictions below
//
// Restrictions:
// SMB remote path specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename
// If not an IP address, share name has to be a valid DNS name.
// UNC specifications to local paths or prefix: \\?\ is not allowed.
// Characters: + [ ] " / : ; | < > , ? * = $ are not allowed.
string remote_path = 1;
// Optional local path to mount the smb on
string local_path = 2;
// Username credential associated with the share
string username = 3;
// Password credential associated with the share
string password = 4;
}
message NewSmbGlobalMappingResponse {
// Windows error code
// Success is represented as 0
string error = 1;
}
message RemoveSmbGlobalMappingRequest {
// A remote SMB share mapping to remove
// All unicode characters allowed in SMB server name specifications are
// permitted except for restrictions below
//
// Restrictions:
// SMB share specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename
// If not an IP address, share name has to be a valid DNS name.
// UNC specifications to local paths or prefix: \\?\ is not allowed.
// Characters: + [ ] " / : ; | < > , ? * = $ are not allowed.
string remote_path = 1;
}
message RemoveSmbGlobalMappingResponse {
// Windows error code
// Success is represented as 0
string error = 1;
}

View File

@ -0,0 +1,58 @@
syntax = "proto3";
package v1beta2;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/smb/v1beta2";
service Smb {
// NewSmbGlobalMapping creates an SMB mapping on the SMB client to an SMB share.
rpc NewSmbGlobalMapping(NewSmbGlobalMappingRequest) returns (NewSmbGlobalMappingResponse) {}
// RemoveSmbGlobalMapping removes the SMB mapping to an SMB share.
rpc RemoveSmbGlobalMapping(RemoveSmbGlobalMappingRequest) returns (RemoveSmbGlobalMappingResponse) {}
}
message NewSmbGlobalMappingRequest {
// A remote SMB share to mount
// All unicode characters allowed in SMB server name specifications are
// permitted except for restrictions below
//
// Restrictions:
// SMB remote path specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename
// If not an IP address, share name has to be a valid DNS name.
// UNC specifications to local paths or prefix: \\?\ is not allowed.
// Characters: + [ ] " / : ; | < > , ? * = $ are not allowed.
string remote_path = 1;
// Optional local path to mount the smb on
string local_path = 2;
// Username credential associated with the share
string username = 3;
// Password credential associated with the share
string password = 4;
}
message NewSmbGlobalMappingResponse {
// Intentionally empty.
}
message RemoveSmbGlobalMappingRequest {
// A remote SMB share mapping to remove
// All unicode characters allowed in SMB server name specifications are
// permitted except for restrictions below
//
// Restrictions:
// SMB share specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename
// If not an IP address, share name has to be a valid DNS name.
// UNC specifications to local paths or prefix: \\?\ is not allowed.
// Characters: + [ ] " / : ; | < > , ? * = $ are not allowed.
string remote_path = 1;
}
message RemoveSmbGlobalMappingResponse {
// Intentionally empty.
}

View File

@ -0,0 +1,93 @@
syntax = "proto3";
package v1alpha1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/system/v1alpha1";
service System {
// GetBIOSSerialNumber returns the device's serial number
rpc GetBIOSSerialNumber(GetBIOSSerialNumberRequest)
returns (GetBIOSSerialNumberResponse) {}
// StartService starts a Windows service
// NOTE: This method affects global node state and should only be used
// with consideration to other CSI drivers that run concurrently.
rpc StartService(StartServiceRequest) returns (StartServiceResponse) {}
// StopService stops a Windows service
// NOTE: This method affects global node state and should only be used
// with consideration to other CSI drivers that run concurrently.
rpc StopService(StopServiceRequest) returns (StopServiceResponse) {}
// GetService queries a Windows service state
rpc GetService(GetServiceRequest) returns (GetServiceResponse) {}
}
message GetBIOSSerialNumberRequest {
// Intentionally empty
}
message GetBIOSSerialNumberResponse {
// Serial number
string serial_number = 1;
}
message StartServiceRequest {
// Service name (as listed in System\CCS\Services keys)
string name = 1;
}
message StartServiceResponse {
// Intentionally empty
}
message StopServiceRequest {
// Service name (as listed in System\CCS\Services keys)
string name = 1;
// Forces stopping of services that has dependent services
bool force = 2;
}
message StopServiceResponse {
// Intentionally empty
}
// https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_status#members
enum ServiceStatus {
UNKNOWN = 0;
STOPPED = 1;
START_PENDING = 2;
STOP_PENDING = 3;
RUNNING = 4;
CONTINUE_PENDING = 5;
PAUSE_PENDING = 6;
PAUSED = 7;
}
// https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-changeserviceconfiga
enum StartType {
BOOT = 0;
SYSTEM = 1;
AUTOMATIC = 2;
MANUAL = 3;
DISABLED = 4;
}
message GetServiceRequest {
// Service name (as listed in System\CCS\Services keys)
string name = 1;
}
message GetServiceResponse {
// Service display name
string display_name = 1;
// Service start type.
// Used to control whether a service will start on boot, and if so on which
// boot phase.
StartType start_type = 2;
// Service status, e.g. stopped, running, paused
ServiceStatus status = 3;
}

View File

@ -0,0 +1,143 @@
syntax = "proto3";
package v1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/volume/v1";
service Volume {
// ListVolumesOnDisk returns the volume IDs (in \\.\Volume{GUID} format) for all volumes from a
// given disk number and partition number (optional)
rpc ListVolumesOnDisk(ListVolumesOnDiskRequest) returns (ListVolumesOnDiskResponse) {}
// MountVolume mounts the volume at the requested global staging path.
rpc MountVolume(MountVolumeRequest) returns (MountVolumeResponse) {}
// UnmountVolume flushes data cache to disk and removes the global staging path.
rpc UnmountVolume(UnmountVolumeRequest) returns (UnmountVolumeResponse) {}
// IsVolumeFormatted checks if a volume is formatted.
rpc IsVolumeFormatted(IsVolumeFormattedRequest) returns (IsVolumeFormattedResponse) {}
// FormatVolume formats a volume with NTFS.
rpc FormatVolume(FormatVolumeRequest) returns (FormatVolumeResponse) {}
// ResizeVolume performs resizing of the partition and file system for a block based volume.
rpc ResizeVolume(ResizeVolumeRequest) returns (ResizeVolumeResponse) {}
// GetVolumeStats gathers total bytes and used bytes for a volume.
rpc GetVolumeStats(GetVolumeStatsRequest) returns (GetVolumeStatsResponse) {}
// GetDiskNumberFromVolumeID gets the disk number of the disk where the volume is located.
rpc GetDiskNumberFromVolumeID(GetDiskNumberFromVolumeIDRequest) returns (GetDiskNumberFromVolumeIDResponse ) {}
// GetVolumeIDFromTargetPath gets the volume id for a given target path.
rpc GetVolumeIDFromTargetPath(GetVolumeIDFromTargetPathRequest) returns (GetVolumeIDFromTargetPathResponse) {}
// WriteVolumeCache write volume cache to disk.
rpc WriteVolumeCache(WriteVolumeCacheRequest) returns (WriteVolumeCacheResponse) {}
}
message ListVolumesOnDiskRequest {
// Disk device number of the disk to query for volumes.
uint32 disk_number = 1;
// The partition number (optional), by default it uses the first partition of the disk.
uint32 partition_number = 2;
}
message ListVolumesOnDiskResponse {
// Volume device IDs of volumes on the specified disk.
repeated string volume_ids = 1;
}
message MountVolumeRequest {
// Volume device ID of the volume to mount.
string volume_id = 1;
// Path in the host's file system where the volume needs to be mounted.
string target_path = 2;
}
message MountVolumeResponse {
// Intentionally empty.
}
message UnmountVolumeRequest {
// Volume device ID of the volume to dismount.
string volume_id = 1;
// Path where the volume has been mounted.
string target_path = 2;
}
message UnmountVolumeResponse {
// Intentionally empty.
}
message IsVolumeFormattedRequest {
// Volume device ID of the volume to check.
string volume_id = 1;
}
message IsVolumeFormattedResponse {
// Is the volume formatted with NTFS.
bool formatted = 1;
}
message FormatVolumeRequest {
// Volume device ID of the volume to format.
string volume_id = 1;
}
message FormatVolumeResponse {
// Intentionally empty.
}
message ResizeVolumeRequest {
// Volume device ID of the volume to resize.
string volume_id = 1;
// New size in bytes of the volume.
int64 size_bytes = 2;
}
message ResizeVolumeResponse {
// Intentionally empty.
}
message GetVolumeStatsRequest{
// Volume device Id of the volume to get the stats for.
string volume_id = 1;
}
message GetVolumeStatsResponse{
// Total bytes
int64 total_bytes = 1;
// Used bytes
int64 used_bytes = 2;
}
message GetDiskNumberFromVolumeIDRequest {
// Volume device ID of the volume to get the disk number for.
string volume_id = 1;
}
message GetDiskNumberFromVolumeIDResponse {
// Corresponding disk number.
uint32 disk_number = 1;
}
message GetVolumeIDFromTargetPathRequest {
// The target path.
string target_path = 1;
}
message GetVolumeIDFromTargetPathResponse {
// The volume device ID.
string volume_id = 1;
}
message WriteVolumeCacheRequest {
// Volume device ID of the volume to flush the cache.
string volume_id = 1;
}
message WriteVolumeCacheResponse {
// Intentionally empty.
}

View File

@ -0,0 +1,69 @@
syntax = "proto3";
package v1alpha1;
service Volume {
// ListVolumesOnDisk returns the volume IDs (in \\.\Volume{GUID} format) for
// all volumes on a Disk device
rpc ListVolumesOnDisk(ListVolumesOnDiskRequest) returns (ListVolumesOnDiskResponse) {}
// MountVolume mounts the volume at the requested global staging path
rpc MountVolume(MountVolumeRequest) returns (MountVolumeResponse) {}
// DismountVolume gracefully dismounts a volume
rpc DismountVolume(DismountVolumeRequest) returns (DismountVolumeResponse) {}
// IsVolumeFormatted checks if a volume is formatted with NTFS
rpc IsVolumeFormatted(IsVolumeFormattedRequest) returns (IsVolumeFormattedResponse) {}
// FormatVolume formats a volume with the provided file system
rpc FormatVolume(FormatVolumeRequest) returns (FormatVolumeResponse) {}
// ResizeVolume performs resizing of the partition and file system for a block based volume
rpc ResizeVolume(ResizeVolumeRequest) returns (ResizeVolumeResponse) {}
}
message ListVolumesOnDiskRequest {
// Disk device ID of the disk to query for volumes
string disk_id = 1;
}
message ListVolumesOnDiskResponse {
// Volume device IDs of volumes on the specified disk
repeated string volume_ids = 1;
}
message MountVolumeRequest {
// Volume device ID of the volume to mount
string volume_id = 1;
// Path in the host's file system where the volume needs to be mounted
string path = 2;
}
message MountVolumeResponse {
// Intentionally empty
}
message DismountVolumeRequest {
// Volume device ID of the volume to dismount
string volume_id = 1;
// Path where the volume has been mounted.
string path = 2;
}
message DismountVolumeResponse {
// Intentionally empty
}
message IsVolumeFormattedRequest {
// Volume device ID of the volume to check
string volume_id = 1;
}
message IsVolumeFormattedResponse {
// Is the volume formatted with NTFS
bool formatted = 1;
}
message FormatVolumeRequest {
// Volume device ID of the volume to format
string volume_id = 1;
}
message FormatVolumeResponse {
// Intentionally empty
}
message ResizeVolumeRequest {
// Volume device ID of the volume to dismount
string volume_id = 1;
// New size of the volume
int64 size = 2;
}
message ResizeVolumeResponse {
// Intentionally empty
}

View File

@ -0,0 +1,121 @@
syntax = "proto3";
package v1beta1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/volume/v1beta1";
service Volume {
// ListVolumesOnDisk returns the volume IDs (in \\.\Volume{GUID} format) for
// all volumes on a Disk device
rpc ListVolumesOnDisk(ListVolumesOnDiskRequest) returns (ListVolumesOnDiskResponse) {}
// MountVolume mounts the volume at the requested global staging path
rpc MountVolume(MountVolumeRequest) returns (MountVolumeResponse) {}
// DismountVolume gracefully dismounts a volume
rpc DismountVolume(DismountVolumeRequest) returns (DismountVolumeResponse) {}
// IsVolumeFormatted checks if a volume is formatted with NTFS
rpc IsVolumeFormatted(IsVolumeFormattedRequest) returns (IsVolumeFormattedResponse) {}
// FormatVolume formats a volume with the provided file system
rpc FormatVolume(FormatVolumeRequest) returns (FormatVolumeResponse) {}
// ResizeVolume performs resizing of the partition and file system for a block based volume
rpc ResizeVolume(ResizeVolumeRequest) returns (ResizeVolumeResponse) {}
// VolumeStats gathers DiskSize, VolumeSize and VolumeUsedSize for a volume
rpc VolumeStats(VolumeStatsRequest) returns (VolumeStatsResponse) {}
// GetVolumeDiskNumber gets the disk number of the disk where the volume is located
rpc GetVolumeDiskNumber(VolumeDiskNumberRequest) returns (VolumeDiskNumberResponse) {}
// GetVolumeIDFromMount gets the volume id for a given mount
rpc GetVolumeIDFromMount(VolumeIDFromMountRequest) returns (VolumeIDFromMountResponse) {}
}
message ListVolumesOnDiskRequest {
// Disk device ID of the disk to query for volumes
string disk_id = 1;
}
message ListVolumesOnDiskResponse {
// Volume device IDs of volumes on the specified disk
repeated string volume_ids = 1;
}
message MountVolumeRequest {
// Volume device ID of the volume to mount
string volume_id = 1;
// Path in the host's file system where the volume needs to be mounted
string path = 2;
}
message MountVolumeResponse {
// Intentionally empty
}
message DismountVolumeRequest {
// Volume device ID of the volume to dismount
string volume_id = 1;
// Path where the volume has been mounted.
string path = 2;
}
message DismountVolumeResponse {
// Intentionally empty
}
message IsVolumeFormattedRequest {
// Volume device ID of the volume to check
string volume_id = 1;
}
message IsVolumeFormattedResponse {
// Is the volume formatted with NTFS
bool formatted = 1;
}
message FormatVolumeRequest {
// Volume device ID of the volume to format
string volume_id = 1;
}
message FormatVolumeResponse {
// Intentionally empty
}
message ResizeVolumeRequest {
// Volume device ID of the volume to dismount
string volume_id = 1;
// New size of the volume
int64 size = 2;
}
message ResizeVolumeResponse {
// Intentionally empty
}
message VolumeStatsRequest{
// Volume device Id of the volume to get the stats for
string volume_id = 1;
}
message VolumeStatsResponse{
// Capacity of the volume
int64 volumeSize = 1;
// Used bytes
int64 volumeUsedSize = 2;
}
message VolumeDiskNumberRequest{
// Volume device Id of the volume to get the disk number for
string volume_id = 1;
}
message VolumeDiskNumberResponse{
// Corresponding disk number
int64 diskNumber = 1;
}
message VolumeIDFromMountRequest {
// Mount
string mount = 1;
}
message VolumeIDFromMountResponse {
// Mount
string volume_id = 1;
}

View File

@ -0,0 +1,132 @@
syntax = "proto3";
package v1beta2;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/volume/v1beta2";
service Volume {
// ListVolumesOnDisk returns the volume IDs (in \\.\Volume{GUID} format) for
// all volumes on a Disk device
rpc ListVolumesOnDisk(ListVolumesOnDiskRequest) returns (ListVolumesOnDiskResponse) {}
// MountVolume mounts the volume at the requested global staging path
rpc MountVolume(MountVolumeRequest) returns (MountVolumeResponse) {}
// DismountVolume gracefully dismounts a volume
rpc DismountVolume(DismountVolumeRequest) returns (DismountVolumeResponse) {}
// IsVolumeFormatted checks if a volume is formatted with NTFS
rpc IsVolumeFormatted(IsVolumeFormattedRequest) returns (IsVolumeFormattedResponse) {}
// FormatVolume formats a volume with the provided file system
rpc FormatVolume(FormatVolumeRequest) returns (FormatVolumeResponse) {}
// ResizeVolume performs resizing of the partition and file system for a block based volume
rpc ResizeVolume(ResizeVolumeRequest) returns (ResizeVolumeResponse) {}
// VolumeStats gathers DiskSize, VolumeSize and VolumeUsedSize for a volume
rpc VolumeStats(VolumeStatsRequest) returns (VolumeStatsResponse) {}
// GetVolumeDiskNumber gets the disk number of the disk where the volume is located
rpc GetVolumeDiskNumber(VolumeDiskNumberRequest) returns (VolumeDiskNumberResponse) {}
// GetVolumeIDFromMount gets the volume id for a given mount
rpc GetVolumeIDFromMount(VolumeIDFromMountRequest) returns (VolumeIDFromMountResponse) {}
// WriteVolumeCache write volume cache to disk
rpc WriteVolumeCache(WriteVolumeCacheRequest) returns (WriteVolumeCacheResponse) {}
}
message ListVolumesOnDiskRequest {
// Disk device ID of the disk to query for volumes
string disk_id = 1;
}
message ListVolumesOnDiskResponse {
// Volume device IDs of volumes on the specified disk
repeated string volume_ids = 1;
}
message MountVolumeRequest {
// Volume device ID of the volume to mount
string volume_id = 1;
// Path in the host's file system where the volume needs to be mounted
string path = 2;
}
message MountVolumeResponse {
// Intentionally empty
}
message DismountVolumeRequest {
// Volume device ID of the volume to dismount
string volume_id = 1;
// Path where the volume has been mounted.
string path = 2;
}
message DismountVolumeResponse {
// Intentionally empty
}
message IsVolumeFormattedRequest {
// Volume device ID of the volume to check
string volume_id = 1;
}
message IsVolumeFormattedResponse {
// Is the volume formatted with NTFS
bool formatted = 1;
}
message FormatVolumeRequest {
// Volume device ID of the volume to format
string volume_id = 1;
}
message FormatVolumeResponse {
// Intentionally empty
}
message ResizeVolumeRequest {
// Volume device ID of the volume to dismount
string volume_id = 1;
// New size of the volume
int64 size = 2;
}
message ResizeVolumeResponse {
// Intentionally empty
}
message VolumeStatsRequest{
// Volume device Id of the volume to get the stats for
string volume_id = 1;
}
message VolumeStatsResponse{
// Capacity of the volume
int64 volumeSize = 1;
// Used bytes
int64 volumeUsedSize = 2;
}
message VolumeDiskNumberRequest{
// Volume device Id of the volume to get the disk number for
string volume_id = 1;
}
message VolumeDiskNumberResponse{
// Corresponding disk number
int64 diskNumber = 1;
}
message VolumeIDFromMountRequest {
// Mount
string mount = 1;
}
message VolumeIDFromMountResponse {
// Mount
string volume_id = 1;
}
message WriteVolumeCacheRequest {
// Volume device ID of the volume to flush the cache
string volume_id = 1;
}
message WriteVolumeCacheResponse {
// Intentionally empty
}

View File

@ -0,0 +1,143 @@
syntax = "proto3";
package v1beta3;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/volume/v1beta3";
service Volume {
// ListVolumesOnDisk returns the volume IDs (in \\.\Volume{GUID} format) for all volumes from a
// given disk number and partition number (optional)
rpc ListVolumesOnDisk(ListVolumesOnDiskRequest) returns (ListVolumesOnDiskResponse) {}
// MountVolume mounts the volume at the requested global staging path.
rpc MountVolume(MountVolumeRequest) returns (MountVolumeResponse) {}
// UnmountVolume flushes data cache to disk and removes the global staging path.
rpc UnmountVolume(UnmountVolumeRequest) returns (UnmountVolumeResponse) {}
// IsVolumeFormatted checks if a volume is formatted.
rpc IsVolumeFormatted(IsVolumeFormattedRequest) returns (IsVolumeFormattedResponse) {}
// FormatVolume formats a volume with NTFS.
rpc FormatVolume(FormatVolumeRequest) returns (FormatVolumeResponse) {}
// ResizeVolume performs resizing of the partition and file system for a block based volume.
rpc ResizeVolume(ResizeVolumeRequest) returns (ResizeVolumeResponse) {}
// GetVolumeStats gathers total bytes and used bytes for a volume.
rpc GetVolumeStats(GetVolumeStatsRequest) returns (GetVolumeStatsResponse) {}
// GetDiskNumberFromVolumeID gets the disk number of the disk where the volume is located.
rpc GetDiskNumberFromVolumeID(GetDiskNumberFromVolumeIDRequest) returns (GetDiskNumberFromVolumeIDResponse ) {}
// GetVolumeIDFromTargetPath gets the volume id for a given target path.
rpc GetVolumeIDFromTargetPath(GetVolumeIDFromTargetPathRequest) returns (GetVolumeIDFromTargetPathResponse) {}
// WriteVolumeCache write volume cache to disk.
rpc WriteVolumeCache(WriteVolumeCacheRequest) returns (WriteVolumeCacheResponse) {}
}
message ListVolumesOnDiskRequest {
// Disk device number of the disk to query for volumes.
uint32 disk_number = 1;
// The partition number (optional), by default it uses the first partition of the disk.
uint32 partition_number = 2;
}
message ListVolumesOnDiskResponse {
// Volume device IDs of volumes on the specified disk.
repeated string volume_ids = 1;
}
message MountVolumeRequest {
// Volume device ID of the volume to mount.
string volume_id = 1;
// Path in the host's file system where the volume needs to be mounted.
string target_path = 2;
}
message MountVolumeResponse {
// Intentionally empty.
}
message UnmountVolumeRequest {
// Volume device ID of the volume to dismount.
string volume_id = 1;
// Path where the volume has been mounted.
string target_path = 2;
}
message UnmountVolumeResponse {
// Intentionally empty.
}
message IsVolumeFormattedRequest {
// Volume device ID of the volume to check.
string volume_id = 1;
}
message IsVolumeFormattedResponse {
// Is the volume formatted with NTFS.
bool formatted = 1;
}
message FormatVolumeRequest {
// Volume device ID of the volume to format.
string volume_id = 1;
}
message FormatVolumeResponse {
// Intentionally empty.
}
message ResizeVolumeRequest {
// Volume device ID of the volume to resize.
string volume_id = 1;
// New size in bytes of the volume.
int64 size_bytes = 2;
}
message ResizeVolumeResponse {
// Intentionally empty.
}
message GetVolumeStatsRequest{
// Volume device Id of the volume to get the stats for.
string volume_id = 1;
}
message GetVolumeStatsResponse{
// Total bytes
int64 total_bytes = 1;
// Used bytes
int64 used_bytes = 2;
}
message GetDiskNumberFromVolumeIDRequest {
// Volume device ID of the volume to get the disk number for.
string volume_id = 1;
}
message GetDiskNumberFromVolumeIDResponse {
// Corresponding disk number.
uint32 disk_number = 1;
}
message GetVolumeIDFromTargetPathRequest {
// The target path.
string target_path = 1;
}
message GetVolumeIDFromTargetPathResponse {
// The volume device ID.
string volume_id = 1;
}
message WriteVolumeCacheRequest {
// Volume device ID of the volume to flush the cache.
string volume_id = 1;
}
message WriteVolumeCacheResponse {
// Intentionally empty.
}

View File

@ -0,0 +1,158 @@
syntax = "proto3";
package v2alpha1;
option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/volume/v2alpha1";
service Volume {
// ListVolumesOnDisk returns the volume IDs (in \\.\Volume{GUID} format) for all volumes from a
// given disk number and partition number (optional)
rpc ListVolumesOnDisk(ListVolumesOnDiskRequest) returns (ListVolumesOnDiskResponse) {}
// MountVolume mounts the volume at the requested global staging path.
rpc MountVolume(MountVolumeRequest) returns (MountVolumeResponse) {}
// UnmountVolume flushes data cache to disk and removes the global staging path.
rpc UnmountVolume(UnmountVolumeRequest) returns (UnmountVolumeResponse) {}
// IsVolumeFormatted checks if a volume is formatted.
rpc IsVolumeFormatted(IsVolumeFormattedRequest) returns (IsVolumeFormattedResponse) {}
// FormatVolume formats a volume with NTFS.
rpc FormatVolume(FormatVolumeRequest) returns (FormatVolumeResponse) {}
// ResizeVolume performs resizing of the partition and file system for a block based volume.
rpc ResizeVolume(ResizeVolumeRequest) returns (ResizeVolumeResponse) {}
// GetVolumeStats gathers total bytes and used bytes for a volume.
rpc GetVolumeStats(GetVolumeStatsRequest) returns (GetVolumeStatsResponse) {}
// GetDiskNumberFromVolumeID gets the disk number of the disk where the volume is located.
rpc GetDiskNumberFromVolumeID(GetDiskNumberFromVolumeIDRequest) returns (GetDiskNumberFromVolumeIDResponse ) {}
// GetVolumeIDFromTargetPath gets the volume id for a given target path.
rpc GetVolumeIDFromTargetPath(GetVolumeIDFromTargetPathRequest) returns (GetVolumeIDFromTargetPathResponse) {}
// GetClosestVolumeIDFromTargetPath gets the closest volume id for a given target path
// by following symlinks and moving up in the filesystem, if after moving up in the filesystem
// we get to a DriveLetter then the volume corresponding to this drive letter is returned instead.
rpc GetClosestVolumeIDFromTargetPath(GetClosestVolumeIDFromTargetPathRequest) returns (GetClosestVolumeIDFromTargetPathResponse) {}
// WriteVolumeCache write volume cache to disk.
rpc WriteVolumeCache(WriteVolumeCacheRequest) returns (WriteVolumeCacheResponse) {}
}
message ListVolumesOnDiskRequest {
// Disk device number of the disk to query for volumes.
uint32 disk_number = 1;
// The partition number (optional), by default it uses the first partition of the disk.
uint32 partition_number = 2;
}
message ListVolumesOnDiskResponse {
// Volume device IDs of volumes on the specified disk.
repeated string volume_ids = 1;
}
message MountVolumeRequest {
// Volume device ID of the volume to mount.
string volume_id = 1;
// Path in the host's file system where the volume needs to be mounted.
string target_path = 2;
}
message MountVolumeResponse {
// Intentionally empty.
}
message UnmountVolumeRequest {
// Volume device ID of the volume to dismount.
string volume_id = 1;
// Path where the volume has been mounted.
string target_path = 2;
}
message UnmountVolumeResponse {
// Intentionally empty.
}
message IsVolumeFormattedRequest {
// Volume device ID of the volume to check.
string volume_id = 1;
}
message IsVolumeFormattedResponse {
// Is the volume formatted with NTFS.
bool formatted = 1;
}
message FormatVolumeRequest {
// Volume device ID of the volume to format.
string volume_id = 1;
}
message FormatVolumeResponse {
// Intentionally empty.
}
message ResizeVolumeRequest {
// Volume device ID of the volume to resize.
string volume_id = 1;
// New size in bytes of the volume.
int64 size_bytes = 2;
}
message ResizeVolumeResponse {
// Intentionally empty.
}
message GetVolumeStatsRequest{
// Volume device Id of the volume to get the stats for.
string volume_id = 1;
}
message GetVolumeStatsResponse{
// Total bytes
int64 total_bytes = 1;
// Used bytes
int64 used_bytes = 2;
}
message GetDiskNumberFromVolumeIDRequest {
// Volume device ID of the volume to get the disk number for.
string volume_id = 1;
}
message GetDiskNumberFromVolumeIDResponse {
// Corresponding disk number.
uint32 disk_number = 1;
}
message GetVolumeIDFromTargetPathRequest {
// The target path.
string target_path = 1;
}
message GetVolumeIDFromTargetPathResponse {
// The volume device ID.
string volume_id = 1;
}
message GetClosestVolumeIDFromTargetPathRequest {
// The target path.
string target_path = 1;
}
message GetClosestVolumeIDFromTargetPathResponse {
// The volume device ID.
string volume_id = 1;
}
message WriteVolumeCacheRequest {
// Volume device ID of the volume to flush the cache.
string volume_id = 1;
}
message WriteVolumeCacheResponse {
// Intentionally empty.
}

View File

@ -43,6 +43,8 @@ zfs:
datasetPermissionsMode: "0777"
datasetPermissionsUser: 0
datasetPermissionsGroup: 0
# not supported yet
#datasetPermissionsAcls:
#- "-m everyone@:full_set:allow"
#- "-m u:kube:full_set:allow"

View File

@ -34,9 +34,10 @@ zfs:
# "org.freenas:test": "{{ parameters.foo }}"
# "org.freenas:test2": "some value"
datasetProperties:
aclmode: restricted
casesensitivity: mixed
# these are managed automatically via the volume creation process when flagged as an smb volume
#datasetProperties:
# aclmode: restricted
# casesensitivity: mixed
datasetParentName: tank/k8s/a/vols
# do NOT make datasetParentName and detachedSnapshotsDatasetParentName overlap
@ -47,8 +48,10 @@ zfs:
datasetPermissionsMode: "0777"
datasetPermissionsUser: 0
datasetPermissionsGroup: 0
datasetPermissionsAcls:
- "-m everyone@:full_set:allow"
# not supported yet in api
#datasetPermissionsAcls:
#- "-m everyone@:full_set:allow"
#- "-m u:kube:full_set:allow"
smb:

View File

@ -46,7 +46,9 @@ zfs:
datasetProperties:
aclmode: restricted
casesensitivity: mixed
aclinherit: passthrough
acltype: nfsv4
casesensitivity: insensitive
datasetParentName: tank/k8s/a/vols
# do NOT make datasetParentName and detachedSnapshotsDatasetParentName overlap
@ -54,12 +56,41 @@ zfs:
detachedSnapshotsDatasetParentName: tank/k8s/a/snaps
datasetEnableQuotas: true
datasetEnableReservation: false
datasetPermissionsMode: "0777"
datasetPermissionsUser: nobody
datasetPermissionsGroup: nobody
datasetPermissionsMode: "0770"
# as appropriate create a dedicated user for smb connections
# and set this
datasetPermissionsUser: 65534
datasetPermissionsGroup: 65534
# CORE
#datasetPermissionsAclsBinary: setfacl
# SCALE
#datasetPermissionsAclsBinary: nfs4xdr_setfacl
# if using a user other than guest/nobody comment the 'everyone@' acl
# and uncomment the appropriate block below
datasetPermissionsAcls:
- "-m everyone@:full_set:allow"
#- "-m u:kube:full_set:allow"
- "-m everyone@:full_set:fd:allow"
# CORE
# in CORE you cannot have multiple entries for the same principle
# or said differently, they are declarative so using -m will replace
# whatever the current value is for the principle rather than adding a
# entry in the acl list
#- "-m g:builtin_users:full_set:fd:allow"
#- "-m group@:modify_set:fd:allow"
#- "-m owner@:full_set:fd:allow"
# SCALE
# https://www.truenas.com/community/threads/get-setfacl-on-scale-with-nfsv4-acls.95231/
# -s replaces everything
# so we put this in specific order to mimic the defaults of SCALE when using the api
#- -s group:builtin_users:full_set:fd:allow
#- -a group:builtin_users:modify_set:fd:allow
#- -a group@:modify_set:fd:allow
#- -a owner@:full_set:fd:allow
smb:
shareHost: server address
@ -77,7 +108,7 @@ smb:
shareAllowedHosts: []
shareDeniedHosts: []
#shareDefaultPermissions: true
shareGuestOk: true
shareGuestOk: false
#shareGuestOnly: true
#shareShowHiddenFiles: true
shareRecycleBin: true

View File

@ -2,6 +2,9 @@
node:
mount:
# predominantly used to facilitate testing
# mount_flags should generally be defined in storage classes, etc
mount_flags: ""
# should fsck be executed before mounting the fs
checkFilesystem:
xfs:

18
examples/private.yaml Normal file
View File

@ -0,0 +1,18 @@
#
# these SHOULD NOT be used
# they are here for documentation purposes only and are likely to:
# - be removed
# - break things
#
_private:
csi:
volume:
derivedContext:
# driver left blank is used to auto select
driver: memory # strictly to facilitate testing
#driver: kubernetes
idHash:
strategy: crc16
#strategy: crc32
#strategy: md5

181
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "democratic-csi",
"version": "1.6.3",
"version": "1.7.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "democratic-csi",
"version": "1.6.3",
"version": "1.7.0",
"license": "MIT",
"dependencies": {
"@grpc/grpc-js": "^1.5.7",
@ -70,9 +70,9 @@
}
},
"node_modules/@grpc/grpc-js": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.2.tgz",
"integrity": "sha512-9+89Ne1K8F9u86T+l1yIV2DS+dWHYVK61SsDZN4MFTFehOOaJ4rHxa1cW8Lwdn2/6tOx7N3+SY/vfcjztOHopA==",
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.5.tgz",
"integrity": "sha512-h0KSwgLiF5rmSAU6qnzK1aoD1MNqOw9HJK96N8VW3dR5FHMpq+0JNdLQFP//NcaIWVB7I7vkHl4JmU9hUw82Aw==",
"dependencies": {
"@grpc/proto-loader": "^0.6.4",
"@types/node": ">=12.12.47"
@ -315,9 +315,9 @@
}
},
"node_modules/@types/node": {
"version": "17.0.23",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
"integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw=="
"version": "17.0.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz",
"integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g=="
},
"node_modules/@types/request": {
"version": "2.48.8",
@ -356,9 +356,9 @@
}
},
"node_modules/@types/tough-cookie": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
"integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz",
"integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw=="
},
"node_modules/@types/underscore": {
"version": "1.11.4",
@ -465,9 +465,9 @@
}
},
"node_modules/async": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
"integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
"integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
},
"node_modules/async-mutex": {
"version": "0.3.2",
@ -947,9 +947,9 @@
}
},
"node_modules/eslint": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz",
"integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==",
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz",
"integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==",
"dev": true,
"dependencies": {
"@eslint/eslintrc": "^1.2.1",
@ -1163,9 +1163,9 @@
"dev": true
},
"node_modules/fecha": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
"integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
},
"node_modules/file-entry-cache": {
"version": "6.0.1",
@ -1757,9 +1757,9 @@
}
},
"node_modules/lru-cache": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.0.tgz",
"integrity": "sha512-AmXqneQZL3KZMIgBpaPTeI6pfwh+xQ2vutMsyqOu1TBdEXFZgpG/80wuJ531w2ZN7TI0/oc8CPxzh/DKQudZqg==",
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
"engines": {
"node": ">=12"
}
@ -2127,18 +2127,18 @@
}
},
"node_modules/prompt": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/prompt/-/prompt-1.2.2.tgz",
"integrity": "sha512-XNXhNv3PUHJDcDkISpCwSJxtw9Bor4FZnlMUDW64N/KCPdxhfVlpD5+YUXI/Z8a9QWmOhs9KSiVtR8nzPS0BYA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz",
"integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==",
"dependencies": {
"@colors/colors": "1.5.0",
"async": "~0.9.0",
"async": "3.2.3",
"read": "1.0.x",
"revalidator": "0.1.x",
"winston": "2.x"
},
"engines": {
"node": ">= 0.6.6"
"node": ">= 6.0.0"
}
},
"node_modules/prompt/node_modules/winston": {
@ -2440,17 +2440,28 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0"
"node": ">=10"
}
},
"node_modules/semver/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/shebang-command": {
@ -2761,9 +2772,9 @@
}
},
"node_modules/uglify-js": {
"version": "3.15.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz",
"integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==",
"version": "3.15.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz",
"integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==",
"optional": true,
"bin": {
"uglifyjs": "bin/uglifyjs"
@ -2864,11 +2875,6 @@
"node": ">= 6.4.0"
}
},
"node_modules/winston/node_modules/async": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
"integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
},
"node_modules/word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@ -2938,9 +2944,9 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yargs": {
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz",
"integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==",
"version": "17.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz",
"integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==",
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
@ -2997,9 +3003,9 @@
}
},
"@grpc/grpc-js": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.2.tgz",
"integrity": "sha512-9+89Ne1K8F9u86T+l1yIV2DS+dWHYVK61SsDZN4MFTFehOOaJ4rHxa1cW8Lwdn2/6tOx7N3+SY/vfcjztOHopA==",
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.5.tgz",
"integrity": "sha512-h0KSwgLiF5rmSAU6qnzK1aoD1MNqOw9HJK96N8VW3dR5FHMpq+0JNdLQFP//NcaIWVB7I7vkHl4JmU9hUw82Aw==",
"requires": {
"@grpc/proto-loader": "^0.6.4",
"@types/node": ">=12.12.47"
@ -3216,9 +3222,9 @@
}
},
"@types/node": {
"version": "17.0.23",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
"integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw=="
"version": "17.0.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz",
"integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g=="
},
"@types/request": {
"version": "2.48.8",
@ -3257,9 +3263,9 @@
}
},
"@types/tough-cookie": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
"integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz",
"integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw=="
},
"@types/underscore": {
"version": "1.11.4",
@ -3339,9 +3345,9 @@
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"async": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
"integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
"integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
},
"async-mutex": {
"version": "0.3.2",
@ -3720,9 +3726,9 @@
"dev": true
},
"eslint": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz",
"integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==",
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz",
"integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==",
"dev": true,
"requires": {
"@eslint/eslintrc": "^1.2.1",
@ -3884,9 +3890,9 @@
"dev": true
},
"fecha": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
"integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
},
"file-entry-cache": {
"version": "6.0.1",
@ -4329,9 +4335,9 @@
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
},
"lru-cache": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.0.tgz",
"integrity": "sha512-AmXqneQZL3KZMIgBpaPTeI6pfwh+xQ2vutMsyqOu1TBdEXFZgpG/80wuJ531w2ZN7TI0/oc8CPxzh/DKQudZqg=="
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg=="
},
"make-error": {
"version": "1.3.6",
@ -4610,12 +4616,12 @@
"dev": true
},
"prompt": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/prompt/-/prompt-1.2.2.tgz",
"integrity": "sha512-XNXhNv3PUHJDcDkISpCwSJxtw9Bor4FZnlMUDW64N/KCPdxhfVlpD5+YUXI/Z8a9QWmOhs9KSiVtR8nzPS0BYA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz",
"integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==",
"requires": {
"@colors/colors": "1.5.0",
"async": "~0.9.0",
"async": "3.2.3",
"read": "1.0.x",
"revalidator": "0.1.x",
"winston": "2.x"
@ -4842,11 +4848,21 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
},
"dependencies": {
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
}
}
},
"shebang-command": {
@ -5073,9 +5089,9 @@
"dev": true
},
"uglify-js": {
"version": "3.15.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz",
"integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==",
"version": "3.15.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz",
"integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==",
"optional": true
},
"underscore": {
@ -5140,13 +5156,6 @@
"stack-trace": "0.0.x",
"triple-beam": "^1.3.0",
"winston-transport": "^4.5.0"
},
"dependencies": {
"async": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
"integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
}
}
},
"winston-transport": {
@ -5202,9 +5211,9 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yargs": {
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz",
"integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==",
"version": "17.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz",
"integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==",
"requires": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",

View File

@ -1,6 +1,6 @@
{
"name": "democratic-csi",
"version": "1.6.3",
"version": "1.7.0",
"description": "kubernetes csi driver framework",
"main": "bin/democratic-csi",
"scripts": {

View File

@ -3,7 +3,7 @@ const { GrpcError, grpc } = require("../../utils/grpc");
const registry = require("../../utils/registry");
const SynologyHttpClient = require("./http").SynologyHttpClient;
const semver = require("semver");
const sleep = require("../../utils/general").sleep;
const GeneralUtils = require("../../utils/general");
const __REGISTRY_NS__ = "ControllerSynologyDriver";
@ -171,7 +171,9 @@ class ControllerSynologyDriver extends CsiBaseDriver {
if (
capability.mount.fs_type &&
!["nfs", "cifs"].includes(capability.mount.fs_type)
!GeneralUtils.default_supported_file_filesystems().includes(
capability.mount.fs_type
)
) {
message = `invalid fs_type ${capability.mount.fs_type}`;
return false;
@ -198,7 +200,7 @@ class ControllerSynologyDriver extends CsiBaseDriver {
if (capability.access_type == "mount") {
if (
capability.mount.fs_type &&
!["btrfs", "ext3", "ext4", "ext4dev", "xfs"].includes(
!GeneralUtils.default_supported_block_filesystems().includes(
capability.mount.fs_type
)
) {
@ -609,12 +611,12 @@ class ControllerSynologyDriver extends CsiBaseDriver {
let waitTimeBetweenChecks = settleSeconds * 1000;
await sleep(waitTimeBetweenChecks);
await GeneralUtils.sleep(waitTimeBetweenChecks);
lun_uuid = await httpClient.GetLunUUIDByName(iscsiName);
while (currentCheck <= settleMaxRetries && lun_uuid) {
currentCheck++;
await sleep(waitTimeBetweenChecks);
await GeneralUtils.sleep(waitTimeBetweenChecks);
lun_uuid = await httpClient.GetLunUUIDByName(iscsiName);
}

View File

@ -1,6 +1,7 @@
const _ = require("lodash");
const { ControllerZfsBaseDriver } = require("../controller-zfs");
const { GrpcError, grpc } = require("../../utils/grpc");
const GeneralUtils = require("../../utils/general");
const LocalCliExecClient = require("./exec").LocalCliClient;
const registry = require("../../utils/registry");
const { Zetabyte } = require("../../utils/zfs");
@ -95,7 +96,7 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
case "filesystem":
return ["zfs"];
case "volume":
return ["btrfs", "ext3", "ext4", "ext4dev", "xfs"];
return GeneralUtils.default_supported_block_filesystems();
}
}

View File

@ -1,7 +1,7 @@
const _ = require("lodash");
const { CsiBaseDriver } = require("../index");
const { GrpcError, grpc } = require("../../utils/grpc");
const sleep = require("../../utils/general").sleep;
const GeneralUtils = require("../../utils/general");
const getLargestNumber = require("../../utils/general").getLargestNumber;
const Handlebars = require("handlebars");
@ -201,9 +201,9 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
const driverZfsResourceType = this.getDriverZfsResourceType();
switch (driverZfsResourceType) {
case "filesystem":
return ["nfs", "cifs"];
return GeneralUtils.default_supported_file_filesystems();
case "volume":
return ["btrfs", "ext3", "ext4", "ext4dev", "xfs"];
return GeneralUtils.default_supported_block_filesystems();
}
}
@ -620,6 +620,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
let snapshotParentDatasetName = this.getDetachedSnapshotParentDatasetName();
let zvolBlocksize = this.options.zfs.zvolBlocksize || "16K";
let name = call.request.name;
let volume_id = await driver.getVolumeIdFromName(name);
let volume_content_source = call.request.volume_content_source;
if (!datasetParentName) {
@ -710,7 +711,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
* NOTE: avoid the urge to templatize this given the name length limits for zvols
* ie: namespace-name may quite easily exceed 58 chars
*/
const datasetName = datasetParentName + "/" + name;
const datasetName = datasetParentName + "/" + volume_id;
// ensure volumes with the same name being requested a 2nd time but with a different size fails
try {
@ -862,7 +863,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
volume_content_source_snapshot_id +
"@" +
VOLUME_SOURCE_CLONE_SNAPSHOT_PREFIX +
name;
volume_id;
}
driver.ctx.logger.debug("full snapshot name: %s", fullSnapshotName);
@ -909,6 +910,12 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
});
} else {
try {
// remove readonly/undesired props
let cloneProperties = volumeProperties;
delete cloneProperties["aclmode"];
delete cloneProperties["aclinherit"];
delete cloneProperties["acltype"];
delete cloneProperties["casesensitivity"];
response = await zb.zfs.clone(fullSnapshotName, datasetName, {
properties: volumeProperties,
});
@ -971,7 +978,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
volume_content_source_volume_id +
"@" +
VOLUME_SOURCE_CLONE_SNAPSHOT_PREFIX +
name;
volume_id;
driver.ctx.logger.debug("full snapshot name: %s", fullSnapshotName);
@ -1024,9 +1031,15 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
} else {
// create clone
// zfs origin property contains parent info, ie: pool0/k8s/test/PVC-111@clone-test
// remove readonly/undesired props
let cloneProperties = volumeProperties;
delete cloneProperties["aclmode"];
delete cloneProperties["aclinherit"];
delete cloneProperties["acltype"];
delete cloneProperties["casesensitivity"];
try {
response = await zb.zfs.clone(fullSnapshotName, datasetName, {
properties: volumeProperties,
properties: cloneProperties,
});
} catch (err) {
if (err.toString().includes("dataset does not exist")) {
@ -1128,8 +1141,13 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
// TODO: this is unsfafe approach, make it better
// probably could see if ^-.*\s and split and then shell escape
if (this.options.zfs.datasetPermissionsAcls) {
let aclBinary = _.get(
driver.options,
"zfs.datasetPermissionsAclsBinary",
"setfacl"
);
for (const acl of this.options.zfs.datasetPermissionsAcls) {
command = execClient.buildCommand("setfacl", [
command = execClient.buildCommand(aclBinary, [
acl,
properties.mountpoint.value,
]);
@ -1147,7 +1165,6 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
}
}
}
break;
case "volume":
// set properties
@ -1191,7 +1208,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
const res = {
volume: {
volume_id: name,
volume_id,
//capacity_bytes: capacity_bytes, // kubernetes currently pukes if capacity is returned as 0
capacity_bytes:
this.options.zfs.datasetEnableQuotas ||
@ -1315,7 +1332,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
if (current_try > max_tries) {
throw err;
} else {
await sleep(sleep_time);
await GeneralUtils.sleep(sleep_time);
}
} else {
throw err;
@ -2190,7 +2207,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
});
// let things settle down
//await sleep(3000);
//await GneralUtils.sleep(3000);
} else {
try {
await zb.zfs.snapshot(fullSnapshotName, {
@ -2198,7 +2215,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
});
// let things settle down
//await sleep(3000);
//await GeneralUtils.sleep(3000);
} catch (err) {
if (err.toString().includes("dataset does not exist")) {
throw new GrpcError(

View File

@ -4,9 +4,8 @@ const { CsiBaseDriver } = require("../index");
const HttpClient = require("./http").Client;
const TrueNASApiClient = require("./http/api").Api;
const { Zetabyte } = require("../../utils/zfs");
const getLargestNumber = require("../../utils/general").getLargestNumber;
const registry = require("../../utils/registry");
const sleep = require("../../utils/general").sleep;
const GeneralUtils = require("../../utils/general");
const Handlebars = require("handlebars");
const uuidv4 = require("uuid").v4;
@ -1565,7 +1564,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
targetId,
retries
);
await sleep(retryWait);
await GeneralUtils.sleep(retryWait);
response = await httpClient.delete(endpoint);
}
@ -1958,7 +1957,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
if (capability.access_type == "mount") {
if (
capability.mount.fs_type &&
!["btrfs", "ext3", "ext4", "ext4dev", "xfs"].includes(
!GeneralUtils.default_supported_block_filesystems().includes(
capability.mount.fs_type
)
) {
@ -2066,6 +2065,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
let snapshotParentDatasetName = this.getDetachedSnapshotParentDatasetName();
let zvolBlocksize = this.options.zfs.zvolBlocksize || "16K";
let name = call.request.name;
let volume_id = await driver.getVolumeIdFromName(name);
let volume_content_source = call.request.volume_content_source;
let minimum_volume_size = await driver.getMinimumVolumeSize();
let default_required_bytes = 1073741824;
@ -2171,7 +2171,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
* NOTE: avoid the urge to templatize this given the name length limits for zvols
* ie: namespace-name may quite easily exceed 58 chars
*/
const datasetName = datasetParentName + "/" + name;
const datasetName = datasetParentName + "/" + volume_id;
// ensure volumes with the same name being requested a 2nd time but with a different size fails
try {
@ -2326,7 +2326,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
volume_content_source_snapshot_id +
"@" +
VOLUME_SOURCE_CLONE_SNAPSHOT_PREFIX +
name;
volume_id;
}
driver.ctx.logger.debug("full snapshot name: %s", fullSnapshotName);
@ -2378,7 +2378,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
) {
job = await httpApiClient.CoreGetJobs({ id: job_id });
job = job[0];
await sleep(3000);
await GeneralUtils.sleep(3000);
}
job.error = job.error || "";
@ -2488,7 +2488,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
volume_content_source_volume_id +
"@" +
VOLUME_SOURCE_CLONE_SNAPSHOT_PREFIX +
name;
volume_id;
driver.ctx.logger.debug("full snapshot name: %s", fullSnapshotName);
@ -2538,7 +2538,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
) {
job = await httpApiClient.CoreGetJobs({ id: job_id });
job = job[0];
await sleep(3000);
await GeneralUtils.sleep(3000);
}
job.error = job.error || "";
@ -2626,6 +2626,9 @@ class FreeNASApiDriver extends CsiBaseDriver {
volsize: driverZfsResourceType == "volume" ? capacity_bytes : undefined,
sparse: driverZfsResourceType == "volume" ? sparse : undefined,
create_ancestors: true,
share_type: driver.getDriverShareType().includes("smb")
? "SMB"
: "GENERIC",
user_properties: httpApiClient.getPropertiesKeyValueArray(
httpApiClient.getUserProperties(volumeProperties)
),
@ -2721,7 +2724,18 @@ class FreeNASApiDriver extends CsiBaseDriver {
}
if (setPerms) {
await httpApiClient.FilesystemSetperm(perms);
response = await httpApiClient.FilesystemSetperm(perms);
await httpApiClient.CoreWaitForJob(response, 30);
// SetPerm does not alter ownership with extended ACLs
// run this in addition just for good measure
if (perms.uid || perms.gid) {
response = await httpApiClient.FilesystemChown({
path: perms.path,
uid: perms.uid,
gid: perms.gid,
});
await httpApiClient.CoreWaitForJob(response, 30);
}
}
// set acls
@ -2777,7 +2791,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
const res = {
volume: {
volume_id: name,
volume_id,
//capacity_bytes: capacity_bytes, // kubernetes currently pukes if capacity is returned as 0
capacity_bytes:
this.options.zfs.datasetEnableQuotas ||
@ -3649,7 +3663,10 @@ class FreeNASApiDriver extends CsiBaseDriver {
// so we must be cognizant and use the highest possible value here
// note that whatever value is returned here can/will essentially impact the refquota
// value of a derived volume
size_bytes = getLargestNumber(row.referenced, row.logicalreferenced);
size_bytes = GeneralUtils.getLargestNumber(
row.referenced,
row.logicalreferenced
);
} else {
// get the size of the parent volume
size_bytes = row.volsize;
@ -3930,7 +3947,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
while (!job || !["SUCCESS", "ABORTED", "FAILED"].includes(job.state)) {
job = await httpApiClient.CoreGetJobs({ id: job_id });
job = job[0];
await sleep(3000);
await GeneralUtils.sleep(3000);
}
job.error = job.error || "";
@ -4045,7 +4062,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
// so we must be cognizant and use the highest possible value here
// note that whatever value is returned here can/will essentially impact the refquota
// value of a derived volume
size_bytes = getLargestNumber(
size_bytes = GeneralUtils.getLargestNumber(
properties.referenced.rawvalue,
properties.logicalreferenced.rawvalue
// TODO: perhaps include minimum volume size here?

View File

@ -681,7 +681,13 @@ class Api {
throw new Error(JSON.stringify(response.body));
}
async CoreWaitForJob(job_id, timeout = 0) {
/**
*
* @param {*} job_id
* @param {*} timeout in seconds
* @returns
*/
async CoreWaitForJob(job_id, timeout = 0, check_interval = 3000) {
if (!job_id) {
throw new Error("invalid job_id");
}
@ -692,16 +698,17 @@ class Api {
let job;
// wait for job to finish
while (!job || !["SUCCESS", "ABORTED", "FAILED"].includes(job.state)) {
do {
if (job) {
await sleep(check_interval);
}
job = await this.CoreGetJobs({ id: job_id });
job = job[0];
await sleep(3000);
currentTime = Date.now() / 1000;
if (timeout > 0 && currentTime > startTime + timeout) {
throw new Error("timeout waiting for job to complete");
}
}
} while (!["SUCCESS", "ABORTED", "FAILED"].includes(job.state));
return job;
}
@ -754,7 +761,38 @@ class Api {
response = await httpClient.post(endpoint, data);
if (response.statusCode == 200) {
return;
return response.body;
}
throw new Error(JSON.stringify(response.body));
}
/**
*
* @param {*} data
*/
async FilesystemChown(data) {
/*
{
"path": "string",
"uid": 0,
"gid": 0,
"options": {
"recursive": false,
"traverse": false
}
}
*/
const httpClient = await this.getHttpClient(false);
let response;
let endpoint;
endpoint = `/filesystem/chown`;
response = await httpClient.post(endpoint, data);
if (response.statusCode == 200) {
return response.body;
}
throw new Error(JSON.stringify(response.body));

View File

@ -122,6 +122,9 @@ class Client {
_.set(options, prop, "redacted");
}
delete options.httpAgent;
delete options.httpsAgent;
this.logger.debug("FREENAS HTTP REQUEST: " + stringify(options));
this.logger.debug("FREENAS HTTP ERROR: " + error);
this.logger.debug("FREENAS HTTP STATUS: " + response.statusCode);

View File

@ -4,6 +4,7 @@ const { GrpcError, grpc } = require("../../utils/grpc");
const registry = require("../../utils/registry");
const SshClient = require("../../utils/ssh").SshClient;
const HttpClient = require("./http").Client;
const TrueNASApiClient = require("./http/api").Api;
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
const { sleep, stringify } = require("../../utils/general");
@ -112,6 +113,13 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
);
}
async getTrueNASHttpApiClient() {
return registry.getAsync(`${__REGISTRY_NS__}:api_client`, async () => {
const httpClient = await this.getHttpClient();
return new TrueNASApiClient(httpClient, this.ctx.cache);
});
}
getDriverShareType() {
switch (this.options.driver) {
case "freenas-nfs":
@ -1716,6 +1724,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
async setFilesystemMode(path, mode) {
const httpClient = await this.getHttpClient();
const apiVersion = httpClient.getApiVersion();
const httpApiClient = await this.getTrueNASHttpApiClient();
switch (apiVersion) {
case 1:
@ -1747,6 +1756,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
response = await httpClient.post(endpoint, perms);
if (response.statusCode == 200) {
await httpApiClient.CoreWaitForJob(response.body, 30);
return;
}
@ -1764,6 +1774,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
async setFilesystemOwnership(path, user = false, group = false) {
const httpClient = await this.getHttpClient();
const apiVersion = httpClient.getApiVersion();
const httpApiClient = await this.getTrueNASHttpApiClient();
if (user === false || typeof user == "undefined" || user === null) {
user = "";
@ -1832,6 +1843,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
response = await httpClient.post(endpoint, perms);
if (response.statusCode == 200) {
await httpApiClient.CoreWaitForJob(response.body, 30);
return;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,257 @@
const _ = require("lodash");
const grpc = require("./grpc").grpc;
const protoLoader = require("@grpc/proto-loader");
const PROTO_BASE_PATH = __dirname + "/../../csi_proxy_proto";
/**
* leave connection null as by default the named pipe is derrived
*/
const DEFAULT_SERVICES = {
filesystem: { version: "v1", connection: null },
disk: { version: "v1", connection: null },
volume: { version: "v1", connection: null },
smb: { version: "v1", connection: null },
system: { version: "v1alpha1", connection: null },
iscsi: { version: "v1alpha2", connection: null },
};
function capitalize(s) {
return s && s[0].toUpperCase() + s.slice(1);
}
class CsiProxyClient {
constructor(options = {}) {
this.clients = {};
// initialize all clients
const services = Object.assign(
{},
DEFAULT_SERVICES,
options.services || {}
);
const pipePrefix = options.pipe_prefix || "csi-proxy";
for (const serviceName in services) {
const service = services[serviceName];
const serviceVersion =
service.version || DEFAULT_SERVICES[serviceName].version;
const serviceConnection =
service.connection ||
`\\\\.\\\\pipe\\\\${pipePrefix}-${serviceName}-${serviceVersion}`;
const PROTO_PATH = `/${PROTO_BASE_PATH}/${serviceName}/${serviceVersion}/api.proto`;
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: [__dirname + "/../csi_proxy_proto"],
});
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const serviceInstance = new protoDescriptor[serviceVersion][
capitalize(serviceName)
](serviceConnection, grpc.credentials.createInsecure());
this.clients[serviceName] = serviceInstance;
}
}
async executeRPC(serviceName, methodName, options = {}) {
function rescursivePathFixer(obj) {
for (const k in obj) {
if (typeof obj[k] == "object" && obj[k] !== null) {
rescursivePathFixer(obj[k]);
} else {
if (k.includes("path")) {
obj[k] = obj[k].replaceAll("/", "\\");
}
}
}
}
rescursivePathFixer(options);
const cleansedOptions = JSON.parse(JSON.stringify(options));
// This function handles arrays and objects
function recursiveCleanse(obj) {
for (const k in obj) {
if (typeof obj[k] == "object" && obj[k] !== null) {
recursiveCleanse(obj[k]);
} else {
if (
k.includes("secret") ||
k.includes("username") ||
k.includes("password")
) {
obj[k] = "redacted";
}
}
}
}
recursiveCleanse(cleansedOptions);
console.log(
"csi-proxy request %s/%s - data: %j",
capitalize(serviceName),
methodName,
cleansedOptions
);
return new Promise((resolve, reject) => {
const functionRef = this.clients[serviceName.toLowerCase()][methodName];
if (!functionRef) {
reject(
new Error(
`missing method ${methodName} on service ${capitalize(serviceName)}`
)
);
return;
}
this.clients[serviceName.toLowerCase()][methodName](
options,
(error, data) => {
console.log(
"csi-proxy response %s/%s - error: %j, data: %j",
capitalize(serviceName),
methodName,
error,
data
);
if (error) {
reject(error);
}
resolve(data);
}
);
});
}
/**
* Returns a disk_number if the target has 0 or 1 disks
*
* @param {*} target_portal
* @param {*} iqn
* @returns
*/
async getDiskNumberFromIscsiTarget(target_portal, iqn) {
let result;
if (typeof target_portal != "object") {
target_portal = {
target_address: target_portal.split(":")[0],
target_port: target_portal.split(":")[1] || 3260,
};
}
// get device
try {
result = await this.executeRPC("iscsi", "GetTargetDisks", {
target_portal,
iqn,
});
} catch (e) {
let details = _.get(e, "details", "");
if (!details.includes("ObjectNotFound")) {
throw e;
}
}
let diskIds = _.get(result, "diskIDs", []);
if (diskIds.length > 1) {
throw new Error(
`${diskIds.length} disks on the target, no way to know which is the relevant disk`
);
}
return diskIds[0];
}
/**
* Returns a volume_id if the disk has 0 or 1 volumes
*
* @param {*} disk_number
* @returns
*/
async getVolumeIdFromDiskNumber(disk_number) {
let result;
if (disk_number == 0 || disk_number > 0) {
result = await this.executeRPC("volume", "ListVolumesOnDisk", {
disk_number,
});
let volume_ids = _.get(result, "volume_ids", []);
/**
* the 1st partition is a sort of system partion and is ""
* usually around 15MB in size
*/
volume_ids = volume_ids.filter((item) => {
return Boolean(item);
});
if (volume_ids.length > 1) {
throw new Error(
`${volume_ids.length} volumes on the disk, no way to know which is the relevant volume`
);
}
// ok of null/undefined
return volume_ids[0];
}
}
/**
* Return a volume_id if the target and disk both have 0 or 1 entries
*
* @param {*} target_portal
* @param {*} iqn
* @returns
*/
async getVolumeIdFromIscsiTarget(target_portal, iqn) {
const disk_number = await this.getDiskNumberFromIscsiTarget(...arguments);
return await this.getVolumeIdFromDiskNumber(disk_number);
}
async FilesystemPathExists(path) {
let result;
try {
result = await this.executeRPC("filesystem", "PathExists", {
path,
});
return result.exists;
} catch (e) {
let details = _.get(e, "details", "");
if (details.includes("not an absolute Windows path")) {
return false;
} else {
throw e;
}
}
}
async FilesystemIsSymlink(path) {
let result;
try {
result = await this.executeRPC("filesystem", "IsSymlink", {
path,
});
return result.is_symlink;
} catch (e) {
let details = _.get(e, "details", "");
if (details.includes("not an absolute Windows path")) {
return false;
} else {
throw e;
}
}
}
}
module.exports.CsiProxyClient = CsiProxyClient;

View File

@ -1,5 +1,6 @@
const cp = require("child_process");
const fs = require("fs");
const path = require("path");
const DEFAULT_TIMEOUT = process.env.FILESYSTEM_DEFAULT_TIMEOUT || 30000;
@ -25,6 +26,10 @@ class Filesystem {
}
}
covertUnixSeparatorToWindowsSeparator(p) {
return p.replaceAll(path.posix.sep, path.win32.sep);
}
/**
* Attempt to discover if device is a block device
*
@ -615,12 +620,12 @@ class Filesystem {
command = filesystem.options.paths.sudo;
}
console.log("executing filesystem command: %s %s", command, args.join(" "));
return new Promise((resolve, reject) => {
const child = filesystem.options.executor.spawn(command, args, options);
let stdout = "";
let stderr = "";
child.stdout.on("data", function (data) {
stdout = stdout + data;
});

View File

@ -1,4 +1,5 @@
const axios = require("axios");
const crypto = require("crypto");
function sleep(ms) {
return new Promise((resolve) => {
@ -6,6 +7,64 @@ function sleep(ms) {
});
}
function md5(val) {
return crypto.createHash("md5").update(val).digest("hex");
}
function crc32(val) {
for (var a, o = [], c = 0; c < 256; c++) {
a = c;
for (var f = 0; f < 8; f++) a = 1 & a ? 3988292384 ^ (a >>> 1) : a >>> 1;
o[c] = a;
}
for (var n = -1, t = 0; t < val.length; t++)
n = (n >>> 8) ^ o[255 & (n ^ val.charCodeAt(t))];
return (-1 ^ n) >>> 0;
}
const crctab16 = new Uint16Array([
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48,
0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108,
0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb,
0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399,
0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e,
0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e,
0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd,
0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285,
0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44,
0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014,
0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5,
0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3,
0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862,
0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e,
0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1,
0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483,
0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50,
0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710,
0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7,
0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1,
0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72,
0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e,
0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf,
0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d,
0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c,
0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
]);
// calculate the 16-bit CRC of data with predetermined length.
function crc16(data) {
var res = 0x0ffff;
for (let b of data) {
res = ((res >> 8) & 0x0ff) ^ crctab16[(res ^ b) & 0xff];
}
return ~res & 0x0ffff;
}
function lockKeysFromRequest(call, serviceMethodName) {
switch (serviceMethodName) {
// controller
@ -55,9 +114,9 @@ function getLargestNumber() {
/**
* transition function to replicate `request` style requests using axios
*
* @param {*} options
* @param {*} callback
*
* @param {*} options
* @param {*} callback
*/
function axios_request(options, callback = function () {}) {
function prep_response(res) {
@ -110,8 +169,23 @@ function stringify(value) {
return JSON.stringify(value, getCircularReplacer());
}
function default_supported_block_filesystems() {
return ["btrfs", "ext3", "ext4", "ext4dev", "xfs", "ntfs"];
}
function default_supported_file_filesystems() {
return ["nfs", "cifs"];
}
module.exports.sleep = sleep;
module.exports.md5 = md5;
module.exports.crc32 = crc32;
module.exports.crc16 = crc16;
module.exports.lockKeysFromRequest = lockKeysFromRequest;
module.exports.getLargestNumber = getLargestNumber;
module.exports.stringify = stringify;
module.exports.axios_request = axios_request;
module.exports.default_supported_block_filesystems =
default_supported_block_filesystems;
module.exports.default_supported_file_filesystems =
default_supported_file_filesystems;