Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CSI NodeStage #3918

Merged
merged 1 commit into from Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -22,6 +22,7 @@ _bin/
/bin
*.iml
cover.out
cover.out.tmp
coverprofile*.out
/amazon-ecs-init*
/BUILDROOT/
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -307,7 +307,7 @@ GOFILES:=$(shell go list -f '{{$$p := .}}{{range $$f := .GoFiles}}{{$$p.Dir}}/{{
.PHONY: gocyclo
gocyclo:
# Run gocyclo over all .go files
gocyclo -over 17 ${GOFILES}
gocyclo -over 40 ${GOFILES}

# same as gofiles above, but without the `-f`
.PHONY: govet
Expand Down
8 changes: 8 additions & 0 deletions ecs-agent/daemonimages/csidriver/Dockerfile
@@ -0,0 +1,8 @@
# Build csi-driver from the minimal eks base to add required mount utils
# TODO update Dockerfile with arch to build arm and windows

FROM public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base-csi-ebs:latest.2 AS linux-amazon
COPY csi-driver /bin/aws-ebs-csi-driver
ENTRYPOINT ["/bin/aws-ebs-csi-driver"]


100 changes: 100 additions & 0 deletions ecs-agent/daemonimages/csidriver/driver/constants.go
@@ -0,0 +1,100 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package driver

// constants of keys in VolumeContext
const (
// VolumeAttributePartition represents key for partition config in VolumeContext
// this represents the partition number on a device used to mount
VolumeAttributePartition = "partition"
)

// constants for fstypes
const (
// FSTypeExt2 represents the ext2 filesystem type
FSTypeExt2 = "ext2"
// FSTypeExt3 represents the ext3 filesystem type
FSTypeExt3 = "ext3"
// FSTypeExt4 represents the ext4 filesystem type
FSTypeExt4 = "ext4"
// FSTypeXfs represents the xfs filesystem type
FSTypeXfs = "xfs"
// FSTypeNtfs represents the ntfs filesystem type
FSTypeNtfs = "ntfs"
)

// constants of disk partition suffix
const (
diskPartitionSuffix = ""
nvmeDiskPartitionSuffix = "p"
)

// constants of keys in volume parameters
const (
// BlockSizeKey configures the block size when formatting a volume
BlockSizeKey = "blocksize"

// INodeSizeKey configures the inode size when formatting a volume
INodeSizeKey = "inodesize"

// BytesPerINodeKey configures the `bytes-per-inode` when formatting a volume
BytesPerINodeKey = "bytesperinode"

// NumberOfINodesKey configures the `number-of-inodes` when formatting a volume
NumberOfINodesKey = "numberofinodes"
)

type fileSystemConfig struct {
NotSupportedParams map[string]struct{}
}

// constants of keys in PublishContext
const (
// devicePathKey represents key for device path in PublishContext
// devicePath is the device path where the volume is attached to
DevicePathKey = "devicePath"
)

var (
FileSystemConfigs = map[string]fileSystemConfig{
FSTypeExt2: {
NotSupportedParams: map[string]struct{}{},
},
FSTypeExt3: {
NotSupportedParams: map[string]struct{}{},
},
FSTypeExt4: {
NotSupportedParams: map[string]struct{}{},
},
FSTypeXfs: {
NotSupportedParams: map[string]struct{}{
BytesPerINodeKey: {},
NumberOfINodesKey: {},
},
},
FSTypeNtfs: {
NotSupportedParams: map[string]struct{}{
BlockSizeKey: {},
INodeSizeKey: {},
BytesPerINodeKey: {},
NumberOfINodesKey: {},
},
},
}
)

func (fsConfig fileSystemConfig) isParameterSupported(paramName string) bool {
_, notSupported := fsConfig.NotSupportedParams[paramName]
return !notSupported
}
68 changes: 68 additions & 0 deletions ecs-agent/daemonimages/csidriver/driver/controller.go
@@ -0,0 +1,68 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package driver

import (
"strconv"

csi "github.com/container-storage-interface/spec/lib/go/csi"
"k8s.io/klog/v2"
)

var (
// volumeCaps represents how the volume could be accessed.
// It is SINGLE_NODE_WRITER since EBS volume could only be
// attached to a single node at any given time.
volumeCaps = []csi.VolumeCapability_AccessMode{
{
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
},
}
)

func isValidVolumeCapabilities(volCaps []*csi.VolumeCapability) bool {
hasSupport := func(cap *csi.VolumeCapability) bool {
for _, c := range volumeCaps {
if c.GetMode() == cap.AccessMode.GetMode() {
return true
}
}
return false
}

foundAll := true
for _, c := range volCaps {
if !hasSupport(c) {
foundAll = false
}
}
return foundAll
}

func isValidVolumeContext(volContext map[string]string) bool {
//There could be multiple volume attributes in the volumeContext map
//Validate here case by case
if partition, ok := volContext[VolumeAttributePartition]; ok {
partitionInt, err := strconv.ParseInt(partition, 10, 64)
if err != nil {
klog.ErrorS(err, "failed to parse partition as int", "partition", partition)
return false
}
if partitionInt < 0 {
klog.ErrorS(err, "invalid partition config", "partition", partition)
return false
}
}
return true
}
72 changes: 72 additions & 0 deletions ecs-agent/daemonimages/csidriver/driver/internal/inflight.go
@@ -0,0 +1,72 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package internal

import (
"sync"

"k8s.io/klog/v2"
)

// Idempotent is the interface required to manage in flight requests.
type Idempotent interface {
// The CSI data types are generated using a protobuf.
// The generated structures are guaranteed to implement the Stringer interface.
// Example: https://github.com/container-storage-interface/spec/blob/master/lib/go/csi/csi.pb.go#L3508
// We can use the generated string as the key of our internal inflight database of requests.
String() string
}

const (
VolumeOperationAlreadyExistsErrorMsg = "An operation with the given Volume %s already exists"
)

// InFlight is a struct used to manage in flight requests per volumeId.
type InFlight struct {
mux *sync.Mutex
inFlight map[string]bool
}

// NewInFlight instanciates a InFlight structures.
func NewInFlight() *InFlight {
return &InFlight{
mux: &sync.Mutex{},
inFlight: make(map[string]bool),
}
}

// Insert inserts the entry to the current list of inflight request key is volumeId for node and req hash for controller .
// Returns false when the key already exists.
func (db *InFlight) Insert(key string) bool {
db.mux.Lock()
defer db.mux.Unlock()

_, ok := db.inFlight[key]
if ok {
return false
}

db.inFlight[key] = true
return true
}

// Delete removes the entry from the inFlight entries map.
// It doesn't return anything, and will do nothing if the specified key doesn't exist.
func (db *InFlight) Delete(key string) {
db.mux.Lock()
defer db.mux.Unlock()

delete(db.inFlight, key)
klog.V(4).InfoS("Node Service: volume operation finished", "key", key)
}