diff --git a/Gopkg.lock b/Gopkg.lock index 7ad7388ed..3fb3409ea 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -17,7 +17,7 @@ branch = "master" name = "github.com/container-storage-interface/spec" packages = ["lib/go/csi"] - revision = "51e48d38ac8a5b4bdcf41826b77dc016ad83451e" + revision = "9e88e4bfabeca1b8e4810555815f112159292ada" [[projects]] name = "github.com/davecgh/go-spew" diff --git a/vendor/github.com/container-storage-interface/spec/csi.proto b/vendor/github.com/container-storage-interface/spec/csi.proto index 36f9533d6..87d98eec1 100644 --- a/vendor/github.com/container-storage-interface/spec/csi.proto +++ b/vendor/github.com/container-storage-interface/spec/csi.proto @@ -82,7 +82,13 @@ message GetPluginInfoRequest { } message GetPluginInfoResponse { - // This field is REQUIRED. + // The name MUST follow reverse domain name notation format + // (https://en.wikipedia.org/wiki/Reverse_domain_name_notation). + // It SHOULD include the plugin's host company name and the plugin + // name, to minimize the possibility of collisions. It MUST be 63 + // characters or less, beginning and ending with an alphanumeric + // character ([a-z0-9A-Z]) with dashes (-), underscores (_), + // dots (.), and alphanumerics between. This field is REQUIRED. string name = 1; // This field is REQUIRED. Value of this field is opaque to the CO. @@ -145,7 +151,6 @@ message CreateVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 6; } @@ -182,10 +187,12 @@ message VolumeCapability { enum Mode { UNKNOWN = 0; - // Can be published as read/write at one node at a time. + // Can only be published once as read/write on a single node, at + // any given time. SINGLE_NODE_WRITER = 1; - // Can be published as readonly at one node at a time. + // Can only be published once as readonly on a single node, at + // any given time. SINGLE_NODE_READER_ONLY = 2; // Can be published as readonly at multiple nodes simultaneously. @@ -271,7 +278,6 @@ message DeleteVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 3; } @@ -286,10 +292,8 @@ message ControllerPublishVolumeRequest { // This field is REQUIRED. string volume_id = 2; - // The ID of the node. This field is OPTIONAL. The CO SHALL set (or - // clear) this field to match the node ID returned by `GetNodeID`. - // `GetNodeID` is allowed to omit node ID from a successful response; - // in such cases the CO SHALL NOT specify this field. + // The ID of the node. This field is REQUIRED. The CO SHALL set this + // field to match the node ID returned by `GetNodeID`. string node_id = 3; // The capability of the volume the CO expects the volume to have. @@ -311,7 +315,6 @@ message ControllerPublishVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 6; // Attributes of the volume to be used on a node. This field is @@ -335,15 +338,11 @@ message ControllerUnpublishVolumeRequest { // The ID of the volume. This field is REQUIRED. string volume_id = 2; - // The ID of the node. This field is OPTIONAL. The CO SHALL set (or - // clear) this field to match the node ID returned by `GetNodeID`. - // `GetNodeID` is allowed to omit node ID from a successful response; - // in such cases the CO SHALL NOT specify this field. - // - // If `GetNodeID` does not omit node ID from a successful response, - // the CO MAY omit this field as well, indicating that it does not - // know which node the volume was previously used. The Plugin SHOULD - // return an Error if this is not supported. + // The ID of the node. This field is OPTIONAL. The CO SHOULD set this + // field to match the node ID returned by `GetNodeID` or leave it + // unset. If the value is set, the SP MUST unpublish the volume from + // the specified node. If the value is unset, the SP MUST unpublish + // the volume from all nodes it is published to. string node_id = 3; // End user credentials used to authenticate/authorize controller @@ -357,7 +356,6 @@ message ControllerUnpublishVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 4; } @@ -515,6 +513,8 @@ message NodePublishVolumeRequest { // The path to which the volume will be published. It MUST be an // absolute path in the root filesystem of the process serving this // request. The CO SHALL ensure uniqueness of target_path per volume. + // The CO SHALL ensure that the path exists, and that the process + // serving the request has `read` and `write` permissions to the path. // This is a REQUIRED field. string target_path = 4; @@ -537,7 +537,6 @@ message NodePublishVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 7; // Attributes of the volume to publish. This field is OPTIONAL and @@ -572,7 +571,6 @@ message NodeUnpublishVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 4; } @@ -585,10 +583,9 @@ message GetNodeIDRequest { } message GetNodeIDResponse { - // The ID of the node which SHALL be used by CO in - // `ControllerPublishVolume`. This is an OPTIONAL field. If unset, - // the CO SHALL leave the `node_id` field unset in - // `ControllerPublishVolume`. + // The ID of the node as understood by the SP which SHALL be used by + // CO in subsequent `ControllerPublishVolume`. + // This is a REQUIRED field. string node_id = 1; } //////// diff --git a/vendor/github.com/container-storage-interface/spec/lib/go/csi/csi.pb.go b/vendor/github.com/container-storage-interface/spec/lib/go/csi/csi.pb.go index 2ad37234b..7f53a1cb4 100644 --- a/vendor/github.com/container-storage-interface/spec/lib/go/csi/csi.pb.go +++ b/vendor/github.com/container-storage-interface/spec/lib/go/csi/csi.pb.go @@ -73,9 +73,11 @@ type VolumeCapability_AccessMode_Mode int32 const ( VolumeCapability_AccessMode_UNKNOWN VolumeCapability_AccessMode_Mode = 0 - // Can be published as read/write at one node at a time. + // Can only be published once as read/write on a single node, at + // any given time. VolumeCapability_AccessMode_SINGLE_NODE_WRITER VolumeCapability_AccessMode_Mode = 1 - // Can be published as readonly at one node at a time. + // Can only be published once as readonly on a single node, at + // any given time. VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY VolumeCapability_AccessMode_Mode = 2 // Can be published as readonly at multiple nodes simultaneously. VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY VolumeCapability_AccessMode_Mode = 3 @@ -245,7 +247,13 @@ func (m *GetPluginInfoRequest) GetVersion() *Version { } type GetPluginInfoResponse struct { - // This field is REQUIRED. + // The name MUST follow reverse domain name notation format + // (https://en.wikipedia.org/wiki/Reverse_domain_name_notation). + // It SHOULD include the plugin's host company name and the plugin + // name, to minimize the possibility of collisions. It MUST be 63 + // characters or less, beginning and ending with an alphanumeric + // character ([a-z0-9A-Z]) with dashes (-), underscores (_), + // dots (.), and alphanumerics between. This field is REQUIRED. Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` // This field is REQUIRED. Value of this field is opaque to the CO. VendorVersion string `protobuf:"bytes,2,opt,name=vendor_version,json=vendorVersion" json:"vendor_version,omitempty"` @@ -328,7 +336,6 @@ type CreateVolumeRequest struct { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. UserCredentials map[string]string `protobuf:"bytes,6,rep,name=user_credentials,json=userCredentials" json:"user_credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` } @@ -688,7 +695,6 @@ type DeleteVolumeRequest struct { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. UserCredentials map[string]string `protobuf:"bytes,3,rep,name=user_credentials,json=userCredentials" json:"user_credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` } @@ -734,10 +740,8 @@ type ControllerPublishVolumeRequest struct { // The ID of the volume to be used on a node. // This field is REQUIRED. VolumeId string `protobuf:"bytes,2,opt,name=volume_id,json=volumeId" json:"volume_id,omitempty"` - // The ID of the node. This field is OPTIONAL. The CO SHALL set (or - // clear) this field to match the node ID returned by `GetNodeID`. - // `GetNodeID` is allowed to omit node ID from a successful response; - // in such cases the CO SHALL NOT specify this field. + // The ID of the node. This field is REQUIRED. The CO SHALL set this + // field to match the node ID returned by `GetNodeID`. NodeId string `protobuf:"bytes,3,opt,name=node_id,json=nodeId" json:"node_id,omitempty"` // The capability of the volume the CO expects the volume to have. // This is a REQUIRED field. @@ -756,7 +760,6 @@ type ControllerPublishVolumeRequest struct { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. UserCredentials map[string]string `protobuf:"bytes,6,rep,name=user_credentials,json=userCredentials" json:"user_credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // Attributes of the volume to be used on a node. This field is // OPTIONAL and MUST match the attributes of the VolumeInfo identified @@ -846,15 +849,11 @@ type ControllerUnpublishVolumeRequest struct { Version *Version `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"` // The ID of the volume. This field is REQUIRED. VolumeId string `protobuf:"bytes,2,opt,name=volume_id,json=volumeId" json:"volume_id,omitempty"` - // The ID of the node. This field is OPTIONAL. The CO SHALL set (or - // clear) this field to match the node ID returned by `GetNodeID`. - // `GetNodeID` is allowed to omit node ID from a successful response; - // in such cases the CO SHALL NOT specify this field. - // - // If `GetNodeID` does not omit node ID from a successful response, - // the CO MAY omit this field as well, indicating that it does not - // know which node the volume was previously used. The Plugin SHOULD - // return an Error if this is not supported. + // The ID of the node. This field is OPTIONAL. The CO SHOULD set this + // field to match the node ID returned by `GetNodeID` or leave it + // unset. If the value is set, the SP MUST unpublish the volume from + // the specified node. If the value is unset, the SP MUST unpublish + // the volume from all nodes it is published to. NodeId string `protobuf:"bytes,3,opt,name=node_id,json=nodeId" json:"node_id,omitempty"` // End user credentials used to authenticate/authorize controller // unpublish request. @@ -867,7 +866,6 @@ type ControllerUnpublishVolumeRequest struct { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. UserCredentials map[string]string `protobuf:"bytes,4,rep,name=user_credentials,json=userCredentials" json:"user_credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` } @@ -1348,6 +1346,8 @@ type NodePublishVolumeRequest struct { // The path to which the volume will be published. It MUST be an // absolute path in the root filesystem of the process serving this // request. The CO SHALL ensure uniqueness of target_path per volume. + // The CO SHALL ensure that the path exists, and that the process + // serving the request has `read` and `write` permissions to the path. // This is a REQUIRED field. TargetPath string `protobuf:"bytes,4,opt,name=target_path,json=targetPath" json:"target_path,omitempty"` // The capability of the volume the CO expects the volume to have. @@ -1367,7 +1367,6 @@ type NodePublishVolumeRequest struct { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. UserCredentials map[string]string `protobuf:"bytes,7,rep,name=user_credentials,json=userCredentials" json:"user_credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // Attributes of the volume to publish. This field is OPTIONAL and // MUST match the attributes of the VolumeInfo identified by @@ -1466,7 +1465,6 @@ type NodeUnpublishVolumeRequest struct { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. UserCredentials map[string]string `protobuf:"bytes,4,rep,name=user_credentials,json=userCredentials" json:"user_credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` } @@ -1531,10 +1529,9 @@ func (m *GetNodeIDRequest) GetVersion() *Version { } type GetNodeIDResponse struct { - // The ID of the node which SHALL be used by CO in - // `ControllerPublishVolume`. This is an OPTIONAL field. If unset, - // the CO SHALL leave the `node_id` field unset in - // `ControllerPublishVolume`. + // The ID of the node as understood by the SP which SHALL be used by + // CO in subsequent `ControllerPublishVolume`. + // This is a REQUIRED field. NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId" json:"node_id,omitempty"` } diff --git a/vendor/github.com/container-storage-interface/spec/spec.md b/vendor/github.com/container-storage-interface/spec/spec.md index dec10e40c..8fb6e5b8c 100644 --- a/vendor/github.com/container-storage-interface/spec/spec.md +++ b/vendor/github.com/container-storage-interface/spec/spec.md @@ -307,6 +307,11 @@ These limits apply for messages generated by both COs and plugins. | 128 bytes | string | | 4 KiB | map | +#### `REQUIRED` Fields + +* A field listed as `REQUIRED` MUST be specified, subject to any per-RPC caveats; caveats SHOULD be rare. +* A `repeated` or `map` field listed as `REQUIRED` MUST contain at least 1 element. + ### Error Scheme All CSI API calls defined in this spec MUST return a [standard gRPC status](https://github.com/grpc/grpc/blob/master/src/proto/grpc/status/status.proto). @@ -314,11 +319,11 @@ Most gRPC libraries provide helper methods to set and read the status fields. The status `code` MUST contain a [canonical error code](https://github.com/grpc/grpc-go/blob/master/codes/codes.go). COs must handle all valid error codes. Each RPC defines a set of gRPC error codes that MUST be returned by the plugin when specified conditions are encountered. In addition to those, if the conditions defined below are encountered, the plugin MUST return the associated gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Unsupported request version | 3 INVALID_ARGUMENT | Indicates that the version specified in the request is not supported by the Plugin. | Caller MUST NOT retry; caller SHOULD call `GetSupportedVersions` to discover which CSI versions the Plugin supports. | | Missing required field | 3 INVALID_ARGUMENT | Indicates that a required field is missing from the request. More human-readable information MAY be provided in the `error_description` field. | Caller MUST fix the request by adding the missing required field before retrying. | -| Invalid or unsupported field in the request | 3 INVALID_ARGUMENT | Indicates that the one ore more fields in this field is either not allowed by the Plugin or has an invalid value. More human-readable information MAY be provided in the gRPC `status.message` field. | Caller MUST fix the the field before retrying. | +| Invalid or unsupported field in the request | 3 INVALID_ARGUMENT | Indicates that the one ore more fields in this field is either not allowed by the Plugin or has an invalid value. More human-readable information MAY be provided in the gRPC `status.message` field. | Caller MUST fix the field before retrying. | The status `message` MUST contain a human readable description of error, if the status `code` is not `OK`. This string MAY be surfaced by CO to end users. @@ -352,7 +357,7 @@ The general flow of the success case is as follows (protos illustrated in YAML f minor: 1 patch: 0 response: - name: org.foo.whizbang/super-plugin + name: org.foo.whizbang.super-plugin vendor_version: blue-green manifest: baz: qaz @@ -400,7 +405,13 @@ message GetPluginInfoRequest { } message GetPluginInfoResponse { - // This field is REQUIRED. + // The name MUST follow reverse domain name notation format + // (https://en.wikipedia.org/wiki/Reverse_domain_name_notation). + // It SHOULD include the plugin's host company name and the plugin + // name, to minimize the possibility of collisions. It MUST be 63 + // characters or less, beginning and ending with an alphanumeric + // character ([a-z0-9A-Z]) with dashes (-), underscores (_), + // dots (.), and alphanumerics between. This field is REQUIRED. string name = 1; // This field is REQUIRED. Value of this field is opaque to the CO. @@ -421,7 +432,9 @@ If the plugin is unable to complete the GetPluginInfo call successfully, it MUST A Controller Plugin MUST implement this RPC call if it has `CREATE_DELETE_VOLUME` controller capability. This RPC will be called by the CO to provision a new volume on behalf of a user (to be consumed as either a block device or a mounted filesystem). + This operation MUST be idempotent. +If a volume corresponding to the specified volume `name` already exists and is compatible with the specified `capacity_range`, `volume_capabilities` and `parameters` in the `CreateVolumeRequest`, the Plugin MUST reply `0 OK` with the corresponding `CreateVolumeResponse`. ```protobuf message CreateVolumeRequest { @@ -476,7 +489,6 @@ message CreateVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 6; } @@ -513,10 +525,12 @@ message VolumeCapability { enum Mode { UNKNOWN = 0; - // Can be published as read/write at one node at a time. + // Can only be published once as read/write on a single node, at + // any given time. SINGLE_NODE_WRITER = 1; - // Can be published as readonly at one node at a time. + // Can only be published once as readonly on a single node, at + // any given time. SINGLE_NODE_READER_ONLY = 2; // Can be published as readonly at multiple nodes simultaneously. @@ -589,12 +603,12 @@ If the plugin is unable to complete the CreateVolume call successfully, it MUST If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | -| Volume already exists | 6 ALREADY_EXISTS | Indicates that a volume corresponding to the specified volume `name` already exists. Plugin MUST also return a valid `CreateVolumeResponse`. | Caller MUST assume the `CreateVolume` call succeeded. | -| Operation pending for volume | 9 FAILED_PRECONDITION | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| +| Volume already exists but is incompatible | 6 ALREADY_EXISTS | Indicates that a volume corresponding to the specified volume `name` already exists but is incompatible with the specified `capacity_range`, `volume_capabilities` or `parameters`. | Caller MUST fix the arguments or use a different `name` before retrying. | +| Operation pending for volume | 10 ABORTED | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | | Unsupported `capacity_range` | 11 OUT_OF_RANGE | Indicates that the capacity range is not allowed by the Plugin. More human-readable information MAY be provided in the gRPC `status.message` field. | Caller MUST fix the capacity range before retrying. | -| Call not implemented | 12 UNIMPLEMENTED | CreateVolume call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST not retry caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | +| Call not implemented | 12 UNIMPLEMENTED | CreateVolume call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST NOT retry. Caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | #### `DeleteVolume` @@ -604,7 +618,7 @@ This RPC will be called by the CO to deprovision a volume. If successful, the storage space associated with the volume MUST be released and all the data in the volume SHALL NOT be accessible anymore. This operation MUST be idempotent. -This operation SHOULD be best effort in the sense that if the Plugin is certain that the volume as well as the artifacts associated with the volume do not exist anymore, it SHOULD return a success. +If a volume corresponding to the specified `volume_id` does not exist or the artifacts associated with the volume do not exist anymore, the Plugin MUST reply `0 OK`. ```protobuf message DeleteVolumeRequest { @@ -626,7 +640,6 @@ message DeleteVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 3; } @@ -639,12 +652,11 @@ If the plugin is unable to complete the DeleteVolume call successfully, it MUST If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | -| Volume does not exists | 5 NOT_FOUND | Indicates that a volume corresponding to the specified `volume_id` does not exist. | Caller MUST assume the `DeleteVolume` call succeeded. | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Volume in use | 9 FAILED_PRECONDITION | Indicates that the volume corresponding to the specified `volume_id` could not be deleted because it is in use by another resource. | Caller SHOULD ensure that there are no other resources using the volume, and then retry with exponential back off. | -| Operation pending for volume | 9 FAILED_PRECONDITION | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | -| Call not implemented | 12 UNIMPLEMENTED | DeleteVolume call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST not retry caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | +| Operation pending for volume | 10 ABORTED | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | +| Call not implemented | 12 UNIMPLEMENTED | DeleteVolume call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST NOT retry. Caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | #### `ControllerPublishVolume` @@ -655,6 +667,8 @@ The Plugin SHOULD perform the work that is necessary for making the volume avail The Plugin MUST NOT assume that this RPC will be executed on the node where the volume will be used. This operation MUST be idempotent. +If the volume corresponding to the `volume_id` has already been published at the node corresponding to the `node_id`, and is compatible with the specified `volume_capability` and `readonly` flag, the Plugin MUST reply `0 OK`. + If the operation failed or the CO does not know if the operation has failed or not, it MAY choose to call `ControllerPublishVolume` again or choose to call `ControllerUnpublishVolume`. The CO MAY call this RPC for publishing a volume to multiple nodes if the volume has `MULTI_NODE` capability (i.e., `MULTI_NODE_READER_ONLY`, `MULTI_NODE_SINGLE_WRITER` or `MULTI_NODE_MULTI_WRITER`). @@ -668,10 +682,8 @@ message ControllerPublishVolumeRequest { // This field is REQUIRED. string volume_id = 2; - // The ID of the node. This field is OPTIONAL. The CO SHALL set (or - // clear) this field to match the node ID returned by `GetNodeID`. - // `GetNodeID` is allowed to omit node ID from a successful response; - // in such cases the CO SHALL NOT specify this field. + // The ID of the node. This field is REQUIRED. The CO SHALL set this + // field to match the node ID returned by `GetNodeID`. string node_id = 3; // The capability of the volume the CO expects the volume to have. @@ -693,7 +705,6 @@ message ControllerPublishVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 6; // Attributes of the volume to be used on a node. This field is @@ -716,14 +727,15 @@ If the plugin is unable to complete the ControllerPublishVolume call successfull If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Volume does not exists | 5 NOT_FOUND | Indicates that a volume corresponding to the specified `volume_id` does not exist. | Caller MUST verify that the `volume_id` is correct and that the volume is accessible and has not been deleted before retrying with exponential back off. | | Node does not exists | 5 NOT_FOUND | Indicates that a node corresponding to the specified `node_id` does not exist. | Caller MUST verify that the `node_id` is correct and that the node is available and has not been terminated or deleted before retrying with exponential backoff. | -| Volume published to another node | 6 ALREADY_EXISTS | Indicates that a volume corresponding to the specified `volume_id` is already attached to another node and does not support multi-node attach. If this error code is returned, the Plugin SHOULD specify the `node_id` of the node the volume is already attached to as part of the gRPC `status.message`. | Caller SHOULD ensure the specified volume is not attached to any other node before retrying with exponential back off. | +| Volume published but is incompatible | 6 ALREADY_EXISTS | Indicates that a volume corresponding to the specified `volume_id` has already been published at the node corresponding to the specified `volume_id` but is incompatible with the specified `volume_capability` or `readonly` flag . | Caller MUST fix the arguments before retying. | +| Volume published to another node | 9 FAILED_PRECONDITION | Indicates that a volume corresponding to the specified `volume_id` has already been published at another node and does not have MULTI_NODE volume capability. If this error code is returned, the Plugin SHOULD specify the `node_id` of the node at which the volume is published as part of the gRPC `status.message`. | Caller SHOULD ensure the specified volume is not published at any other node before retrying with exponential back off. | | Max volumes attached | 8 RESOURCE_EXHAUSTED | Indicates that the maximum supported number of volumes that can be attached to the specified node are already attached. Therefore, this operation will fail until at least one of the existing attached volumes is detached from the node. | Caller MUST ensure that the number of volumes already attached to the node is less then the maximum supported number of volumes before retrying with exponential backoff. | -| Operation pending for volume | 9 FAILED_PRECONDITION | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | -| Call not implemented | 12 UNIMPLEMENTED | ControllerPublishVolume call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST not retry caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | +| Operation pending for volume | 10 ABORTED | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | +| Call not implemented | 12 UNIMPLEMENTED | ControllerPublishVolume call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST NOT retry. Caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | #### `ControllerUnpublishVolume` @@ -736,6 +748,7 @@ The Plugin MUST NOT assume that this RPC will be executed on the node where the This RPC is typically called by the CO when the workload using the volume is being moved to a different node, or all the workload using the volume on a node has finished. This operation MUST be idempotent. +If the volume corresponding to the `volume_id` is not attached to the node corresponding to the `node_id`, the Plugin MUST reply `0 OK`. If this operation failed, or the CO does not know if the operation failed or not, it can choose to call `ControllerUnpublishVolume` again. ```protobuf @@ -746,15 +759,11 @@ message ControllerUnpublishVolumeRequest { // The ID of the volume. This field is REQUIRED. string volume_id = 2; - // The ID of the node. This field is OPTIONAL. The CO SHALL set (or - // clear) this field to match the node ID returned by `GetNodeID`. - // `GetNodeID` is allowed to omit node ID from a successful response; - // in such cases the CO SHALL NOT specify this field. - // - // If `GetNodeID` does not omit node ID from a successful response, - // the CO MAY omit this field as well, indicating that it does not - // know which node the volume was previously used. The Plugin SHOULD - // return an Error if this is not supported. + // The ID of the node. This field is OPTIONAL. The CO SHOULD set this + // field to match the node ID returned by `GetNodeID` or leave it + // unset. If the value is set, the SP MUST unpublish the volume from + // the specified node. If the value is unset, the SP MUST unpublish + // the volume from all nodes it is published to. string node_id = 3; // End user credentials used to authenticate/authorize controller @@ -768,7 +777,6 @@ message ControllerUnpublishVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 4; } @@ -781,13 +789,12 @@ If the plugin is unable to complete the ControllerUnpublishVolume call successfu If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | -| Volume not attached to specified node | 0 OK | Indicates that the volume corresponding to `volume_id` is not attached to the node corresponding to `node_id`. | Caller MUST assume the `ControllerUnpublishVolume` call succeeded. | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Volume does not exists | 5 NOT_FOUND | Indicates that a volume corresponding to the specified `volume_id` does not exist. | Caller MUST verify that the `volume_id` is correct and that the volume is accessible and has not been deleted before retrying with exponential back off. | | Node does not exists | 5 NOT_FOUND | Indicates that a node corresponding to the specified `node_id` does not exist. | Caller MUST verify that the `node_id` is correct and that the node is available and has not been terminated or deleted before retrying with exponential backoff. | -| Operation pending for volume | 9 FAILED_PRECONDITION | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | -| Call not implemented | 12 UNIMPLEMENTED | ControllerUnpublishVolume call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST not retry caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | +| Operation pending for volume | 10 ABORTED | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | +| Call not implemented | 12 UNIMPLEMENTED | ControllerUnpublishVolume call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST NOT retry. Caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | #### `ValidateVolumeCapabilities` @@ -833,10 +840,11 @@ If the plugin is unable to complete the ValidateVolumeCapabilities call successf If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Volume does not exists | 5 NOT_FOUND | Indicates that a volume corresponding to the specified `volume_id` does not exist. | Caller MUST verify that the `volume_id` is correct and that the volume is accessible and has not been deleted before retrying with exponential back off. | + #### `ListVolumes` A Controller Plugin MUST implement this RPC call if it has `LIST_VOLUMES` capability. @@ -886,10 +894,11 @@ If the plugin is unable to complete the ListVolumes call successfully, it MUST r If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Invalid `starting_token` | 10 ABORTED | Indicates that `starting_token` is not valid. | Caller SHOULD start the `ListVolumes` operation again with an empty `starting_token`. | + #### `GetCapacity` A Controller Plugin MUST implement this RPC call if it has `GET_CAPACITY` controller capability. @@ -950,11 +959,12 @@ If the plugin is unable to complete the ControllerProbe call successfully, it MU If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Bad plugin config | 9 FAILED_PRECONDITION | Indicates that the plugin is misconfigured. | Caller MUST assume the plugin is not healthy. | | Missing required dependency | 9 FAILED_PRECONDITION | Indicates that the plugin is missing one or more required dependency. | Caller MUST assume the plugin is not healthy. | + #### `ControllerGetCapabilities` A Controller Plugin MUST implement this RPC call. This RPC allows the CO to check the supported capabilities of controller service provided by the Plugin. @@ -996,18 +1006,43 @@ message ControllerServiceCapability { If the plugin is unable to complete the ControllerGetCapabilities call successfully, it MUST return a non-ok gRPC code in the gRPC status. +#### RPC Interactions + +##### `CreateVolume`, `DeleteVolume`, `ListVolumes` + +It is worth noting that the plugin-generated `volume_id` is a REQUIRED field for the `DeleteVolume` RPC, as opposed to the CO-generated volume `name` that is REQUIRED for the `CreateVolume` RPC: these fields MAY NOT contain the same value. +If a `CreateVolume` operation times out, leaving the CO without an ID with which to reference a volume, and the CO *also* decides that it no longer needs/wants the volume in question then the CO MAY choose one of the following paths: + +1. Replay the `CreateVolume` RPC that timed out; upon success execute `DeleteVolume` using the known volume ID (from the response to `CreateVolume`). +2. Execute the `ListVolumes` RPC to possibly obtain a volume ID that may be used to execute a `DeleteVolume` RPC; upon success execute `DeleteVolume`. +3. The CO takes no further action regarding the timed out RPC, a volume is possibly leaked and the operator/user is expected to clean up. + +It is NOT REQUIRED for a controller plugin to implement the `LIST_VOLUMES` capability if it supports the `CREATE_DELETE_VOLUME` capability: the onus is upon the CO to take into consideration the full range of plugin capabilities before deciding how to proceed in the above scenario. + ### Node Service RPC #### `NodePublishVolume` This RPC is called by the CO when a workload that wants to use the specified volume is placed (scheduled) on a node. The Plugin SHALL assume that this RPC will be executed on the node where the volume will be used. -This RPC MAY be called by the CO multiple times on the same node for the same volume with possibly different `target_path` and/or auth credentials. + If the corresponding Controller Plugin has `PUBLISH_UNPUBLISH_VOLUME` controller capability, the CO MUST guarantee that this RPC is called after `ControllerPublishVolume` is called for the given volume on the given node and returns a success. This operation MUST be idempotent. +If the volume corresponding to the `volume_id` has already been published at the specified `target_path`, and is compatible with the specified `volume_capability` and `readonly` flag, the Plugin MUST reply `0 OK`. + If this RPC failed, or the CO does not know if it failed or not, it MAY choose to call `NodePublishVolume` again, or choose to call `NodeUnpublishVolume`. +This RPC MAY be called by the CO multiple times on the same node for the same volume with possibly different `target_path` and/or other arguments if the volume has MULTI_NODE capability (i.e., `access_mode` is either `MULTI_NODE_READER_ONLY`, `MULTI_NODE_SINGLE_WRITER` or `MULTI_NODE_MULTI_WRITER`). +The following table shows what the Plugin SHOULD return when receiving a second `NodePublishVolume` on the same volume on the same node: + +| | T1=T2, P1=P2 | T1=T2, P1!=P2 | T1!=T2, P1=P2 | T1!=T2, P1!=P2 | +|----------------|-----------------|----------------|---------------------|--------------------| +| MULTI_NODE | OK (idempotent) | ALREADY_EXISTS | OK | OK | +| Non MULTI_NODE | OK (idempotent) | ALREADY_EXISTS | FAILED_PRECONDITION | FAILED_PRECONDITION| + +(`Tn`: target path of the n-th `NodePublishVolume`, `Pn`: other arguments of the n-th `NodePublishVolume` except `user_credentials`) + ```protobuf message NodePublishVolumeRequest { // The API version assumed by the CO. This is a REQUIRED field. @@ -1026,6 +1061,8 @@ message NodePublishVolumeRequest { // The path to which the volume will be published. It MUST be an // absolute path in the root filesystem of the process serving this // request. The CO SHALL ensure uniqueness of target_path per volume. + // The CO SHALL ensure that the path exists, and that the process + // serving the request has `read` and `write` permissions to the path. // This is a REQUIRED field. string target_path = 4; @@ -1048,7 +1085,6 @@ message NodePublishVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 7; // Attributes of the volume to publish. This field is OPTIONAL and @@ -1066,10 +1102,13 @@ If the plugin is unable to complete the NodePublishVolume call successfully, it If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Volume does not exists | 5 NOT_FOUND | Indicates that a volume corresponding to the specified `volume_id` does not exist. | Caller MUST verify that the `volume_id` is correct and that the volume is accessible and has not been deleted before retrying with exponential back off. | -| Operation pending for volume | 9 FAILED_PRECONDITION | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | +| Volume published but is incompatible | 6 ALREADY_EXISTS | Indicates that a volume corresponding to the specified `volume_id` has already been published at the specified `target_path` but is incompatible with the specified `volume_capability` or `readonly` flag. | Caller MUST fix the arguments before retying. | +| Operation pending for volume | 10 ABORTED | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | +| Exceeds capabilities | 10 FAILED_PRECONDITION | Indicates that the CO has exceeded the volume's capabilities because the volume does not have MULTI_NODE capability. | Caller MAY choose to call `ValidateVolumeCapabilities` to validate the volume capabilities, or wait for the volume to be unpublished on the node. | + #### `NodeUnpublishVolume` @@ -1109,7 +1148,6 @@ message NodeUnpublishVolumeRequest { // users to pass through the required credentials. This information is // sensitive and MUST be treated as such (not logged, etc.) by the CO. // This field is OPTIONAL. - // The total bytes this field must be less than 1 Mebibyte. map user_credentials = 4; } @@ -1122,14 +1160,15 @@ If the plugin is unable to complete the NodeUnpublishVolume call successfully, i If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Volume does not exists | 5 NOT_FOUND | Indicates that a volume corresponding to the specified `volume_id` does not exist. | Caller MUST verify that the `volume_id` is correct and that the volume is accessible and has not been deleted before retrying with exponential back off. | -| Operation pending for volume | 9 FAILED_PRECONDITION | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | +| Operation pending for volume | 10 ABORTED | Indicates that there is a already an operation pending for the specified volume. In general the Cluster Orchestrator (CO) is responsible for ensuring that there is no more than one call "in-flight" per volume at a given time. However, in some circumstances, the CO MAY lose state (for example when the CO crashes and restarts), and MAY issue multiple calls simultaneously for the same volume. The Plugin, SHOULD handle this as gracefully as possible, and MAY return this error code to reject secondary calls. | Caller SHOULD ensure that there are no other calls pending for the specified volume, and then retry with exponential back off. | + #### `GetNodeID` -A Node Plugin MUST implement this RPC call. +A Node Plugin MUST implement this RPC call if the plugin has `PUBLISH_UNPUBLISH_VOLUME` controller capability. The Plugin SHALL assume that this RPC will be executed on the node where the volume will be used. The CO SHOULD call this RPC for the node at which it wants to place the workload. The result of this call will be used by CO in `ControllerPublishVolume`. @@ -1141,10 +1180,9 @@ message GetNodeIDRequest { } message GetNodeIDResponse { - // The ID of the node which SHALL be used by CO in - // `ControllerPublishVolume`. This is an OPTIONAL field. If unset, - // the CO SHALL leave the `node_id` field unset in - // `ControllerPublishVolume`. + // The ID of the node as understood by the SP which SHALL be used by + // CO in subsequent `ControllerPublishVolume`. + // This is a REQUIRED field. string node_id = 1; } ``` @@ -1152,6 +1190,12 @@ message GetNodeIDResponse { ##### GetNodeID Errors If the plugin is unable to complete the GetNodeID call successfully, it MUST return a non-ok gRPC code in the gRPC status. +If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. +The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. + +Condition | gRPC Code | Description | Recovery Behavior +| --- | --- | --- | --- | +| Call not implemented | 12 UNIMPLEMENTED | GetNodeID call is not implemented by the plugin or disabled in the Plugin's current mode of operation. | Caller MUST NOT retry. Caller MAY call `ControllerGetCapabilities` or `NodeGetCapabilities` to discover Plugin capabilities. | #### `NodeProbe` @@ -1177,11 +1221,12 @@ If the plugin is unable to complete the NodeProbe call successfully, it MUST ret If the conditions defined below are encountered, the plugin MUST return the specified gRPC error code. The CO MUST implement the specified error recovery behavior when it encounters the gRPC error code. -Condition | gRPC Code | Description | Recovery Behavior -| --- | --- | --- | --- | +| Condition | gRPC Code | Description | Recovery Behavior | +|-----------|-----------|-------------|-------------------| | Bad plugin config | 9 FAILED_PRECONDITION | Indicates that the plugin is misconfigured. | Caller MUST assume the plugin is not healthy. | | Missing required dependency | 9 FAILED_PRECONDITION | Indicates that the plugin is missing one or more required dependency. | Caller MUST assume the plugin is not healthy. | + #### `NodeGetCapabilities` A Node Plugin MUST implement this RPC call.