Skip to content

Commit

Permalink
Add code for creating volumes
Browse files Browse the repository at this point in the history
Adds code for creating CSI volumes. This includes:

* The basic Plugin object, which manages the connection to the CSI
plugin
* The basic VolumeManager object, which manages plugins and responds to
store events

This also includes lots of tests and tests rigging, including fake CSI
clients.

Signed-off-by: Drew Erny <derny@mirantis.com>
  • Loading branch information
dperny committed Jul 21, 2020
1 parent 2c05427 commit 80c41f0
Show file tree
Hide file tree
Showing 17 changed files with 2,661 additions and 927 deletions.
85 changes: 82 additions & 3 deletions api/api.pb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4505,6 +4505,76 @@ file {
json_name: "publishedNodes"
}
}
message_type {
name: "VolumeInfo"
field {
name: "capacity_bytes"
number: 1
label: LABEL_OPTIONAL
type: TYPE_INT64
json_name: "capacityBytes"
}
field {
name: "volume_context"
number: 2
label: LABEL_REPEATED
type: TYPE_MESSAGE
type_name: ".docker.swarmkit.v1.VolumeInfo.VolumeContextEntry"
json_name: "volumeContext"
}
field {
name: "volume_id"
number: 3
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "volumeId"
}
field {
name: "accessible_topology"
number: 4
label: LABEL_REPEATED
type: TYPE_MESSAGE
type_name: ".docker.swarmkit.v1.Topology"
json_name: "accessibleTopology"
}
nested_type {
name: "VolumeContextEntry"
field {
name: "key"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "key"
}
field {
name: "value"
number: 2
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "value"
}
options {
map_entry: true
}
}
}
message_type {
name: "CapacityRange"
field {
name: "required_bytes"
number: 1
label: LABEL_OPTIONAL
type: TYPE_INT64
json_name: "requiredBytes"
}
field {
name: "limit_bytes"
number: 2
label: LABEL_OPTIONAL
type: TYPE_INT64
json_name: "limitBytes"
}
}
message_type {
name: "VolumeAssignment"
field {
Expand Down Expand Up @@ -5953,6 +6023,14 @@ file {
type_name: ".docker.swarmkit.v1.TopologyRequirement"
json_name: "AccessibilityRequirements"
}
field {
name: "capacity_range"
number: 7
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: ".docker.swarmkit.v1.CapacityRange"
json_name: "capacityRange"
}
}
syntax: "proto3"
}
Expand Down Expand Up @@ -7266,11 +7344,12 @@ file {
json_name: "status"
}
field {
name: "volume_id"
name: "volume_info"
number: 5
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "volumeId"
type: TYPE_MESSAGE
type_name: ".docker.swarmkit.v1.VolumeInfo"
json_name: "volumeInfo"
}
options {
70001 {
Expand Down
272 changes: 141 additions & 131 deletions api/objects.pb.go

Large diffs are not rendered by default.

9 changes: 3 additions & 6 deletions api/objects.proto
Original file line number Diff line number Diff line change
Expand Up @@ -537,10 +537,7 @@ message Volume {
// Status contains information about how the volume is currently being used.
VolumeStatus status = 4 [(gogoproto.nullable) = false];

// VolumeID is the ID of the volume as reported by the CSI plugin.
// Information about the volume is not cached in swarmkit's object store;
// instead, it is retrieved on-demand as needed. If the VolumeID field is an
// empty string, and the plugin advertises CREATE_DELETE_VOLUME capability,
// then Swarmkit has not yet called CreateVolume.
string volume_id = 5;
// VolumeInfo contains information about the volume originating from the CSI
// plugin
VolumeInfo volume_info = 5;
}
359 changes: 210 additions & 149 deletions api/specs.pb.go

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions api/specs.proto
Original file line number Diff line number Diff line change
Expand Up @@ -537,4 +537,8 @@ message VolumeSpec {
// VOLUME_ACCESSIBILITY_CONSTRAINTS, then Swarmkit will assume the entire
// cluster is a valid target for the volume.
TopologyRequirement AccessibilityRequirements = 6;

// CapacityRange is the capacity this volume should be created with. If nil,
// the plugin will decide the capacity.
CapacityRange capacity_range = 7;
}
1,478 changes: 1,083 additions & 395 deletions api/types.pb.go

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions api/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,40 @@ message VolumeStatus {
repeated string published_nodes = 1;
}

// VolumeInfo contains information about the volume originating from the CSI
// plugin.
message VolumeInfo {
// CapacityBytes is the capacity of this volume in bytes. A value of 0
// indicates that the capcity is unknown.
int64 capacity_bytes = 1;

// VolumeContext includes fields that are opaque to Swarmkit.
map<string, string> volume_context = 2;

// VolumeID is the ID of the volume as reported by the CSI plugin.
// Information about the volume is not cached in swarmkit's object store;
// instead, it is retrieved on-demand as needed. If the VolumeID field is an
// empty string, and the plugin advertises CREATE_DELETE_VOLUME capability,
// then Swarmkit has not yet called CreateVolume.
string volume_id = 3;

// AccessibleTopology is the topology this volume is actually accessible
// from.
repeated Topology accessible_topology = 4;
}

// CapacityRange describes the minimum and maximum capacity a volume should be
// created with.
message CapacityRange {
// RequiredBytes specifies that a volume must be at least this big. The value
// of 0 indicates an unspecified minimum. Must not be negative.
int64 required_bytes = 1;

// LimitBytes specifies that a volume must not be bigger than this. The value
// of 0 indicates an unspecified maximum. Must not be negative.
int64 limit_bytes = 2;
}

// VolumeAssignment contains the information needed by a Node to use a CSI
// volume. This includes the information need to Stage and Publish the volume
// on the node, but never the full Volume object.
Expand Down
114 changes: 114 additions & 0 deletions manager/volumes/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package volumes

import (
"github.com/container-storage-interface/spec/lib/go/csi"
"github.com/docker/swarmkit/api"
)

// convert.go contains functions for converting swarm objects into CSI requests
// and back again.

// makeTopology converts a swarmkit topology into a CSI topology.
func makeTopologyRequirement(t *api.TopologyRequirement) *csi.TopologyRequirement {
return &csi.TopologyRequirement{
Requisite: makeTopologies(t.Requisite),
Preferred: makeTopologies(t.Preferred),
}
}

// makeTopologies converts a slice of swarmkit topologies into a slice of CSI
// topologies.
func makeTopologies(ts []*api.Topology) []*csi.Topology {
if ts == nil {
return nil
}
csiTops := make([]*csi.Topology, len(ts))
for i, t := range ts {
csiTops[i] = makeTopology(t)
}

return csiTops
}

// makeTopology converts a swarmkit topology into a CSI topology. These types
// are essentially homologous, with the swarm type being copied verbatim from
// the CSI type (for build reasons).
func makeTopology(t *api.Topology) *csi.Topology {
return &csi.Topology{
Segments: t.Segments,
}
}

func makeAccessMode(am *api.VolumeAccessMode) *csi.VolumeCapability {
var mode csi.VolumeCapability_AccessMode_Mode
switch am.Scope {
case api.VolumeScopeSingleNode:
switch am.Sharing {
case api.VolumeSharingNone, api.VolumeSharingOneWriter, api.VolumeSharingAll:
mode = csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER
case api.VolumeSharingReadOnly:
mode = csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY
}
case api.VolumeScopeMultiNode:
switch am.Sharing {
case api.VolumeSharingReadOnly:
mode = csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY
case api.VolumeSharingOneWriter:
mode = csi.VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER
case api.VolumeSharingAll:
mode = csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER
}
}

return &csi.VolumeCapability{
AccessMode: &csi.VolumeCapability_AccessMode{
Mode: mode,
},
}
}

// makeCapcityRange converts the swarmkit CapacityRange object to the
// equivalent CSI object
func makeCapacityRange(cr *api.CapacityRange) *csi.CapacityRange {
if cr == nil {
return nil
}

return &csi.CapacityRange{
RequiredBytes: cr.RequiredBytes,
LimitBytes: cr.LimitBytes,
}
}

// unmakeTopologies transforms a CSI-type topology into the equivalent swarm
// type. it is called "unmakeTopologies" because it performs the inverse of
// "makeTopologies".
func unmakeTopologies(topologies []*csi.Topology) []*api.Topology {
if topologies == nil {
return nil
}
swarmTopologies := make([]*api.Topology, len(topologies))
for i, t := range topologies {
swarmTopologies[i] = unmakeTopology(t)
}
return swarmTopologies
}

// unmakeTopology transforms a CSI-type topology into the equivalent swarm
// type.
func unmakeTopology(topology *csi.Topology) *api.Topology {
return &api.Topology{
Segments: topology.Segments,
}
}

// makeVolumeInfo converts a csi.Volume object into a swarmkit VolumeInfo
// object.
func makeVolumeInfo(csiVolume *csi.Volume) *api.VolumeInfo {
return &api.VolumeInfo{
CapacityBytes: csiVolume.CapacityBytes,
VolumeContext: csiVolume.VolumeContext,
VolumeID: csiVolume.VolumeId,
AccessibleTopology: unmakeTopologies(csiVolume.AccessibleTopology),
}
}

0 comments on commit 80c41f0

Please sign in to comment.