Skip to content

Commit

Permalink
Merge 1190833 into c1e3f9f
Browse files Browse the repository at this point in the history
  • Loading branch information
kbasv committed Jun 3, 2021
2 parents c1e3f9f + 1190833 commit dcf1f5b
Show file tree
Hide file tree
Showing 14 changed files with 558 additions and 12 deletions.
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,25 @@ ENV EFS_CLIENT_SOURCE=$client_source

RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} make aws-efs-csi-driver

FROM amazonlinux:2.0.20210219.0
FROM amazonlinux:2.0.20210421.0
# Install efs-utils from github by default. It can be overriden to `yum` with --build-arg when building the Docker image.
# If value of `EFSUTILSSOURCE` build arg is overriden with `yum`, docker will install efs-utils from Amazon Linux 2's yum repo.
ARG EFSUTILSSOURCE=github
RUN if [ "$EFSUTILSSOURCE" = "yum" ]; \
then echo "Installing efs-utils from Amazon Linux 2 yum repo" && \
yum -y install amazon-efs-utils-1.30.1-1.amzn2.noarch; \
yum -y install amazon-efs-utils-1.31.1-1.amzn2.noarch; \
else echo "Installing efs-utils from github" && \
yum -y install git rpm-build make && \
git clone https://github.com/aws/efs-utils && \
cd efs-utils && make rpm && yum -y install build/amazon-efs-utils*rpm; \
fi

# Install botocore required by efs-utils for cross account mount
RUN yum -y install wget
RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py
RUN python3 /tmp/get-pip.py
RUN pip3 install botocore || /usr/local/bin/pip3 install botocore

# At image build time, static files installed by efs-utils in the config directory, i.e. CAs file, need
# to be saved in another place so that the other stateful files created at runtime, i.e. private key for
# client certificate, in the same config directory can be persisted to host with a host path volume.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "watch", "list", "delete", "update", "create"]
- apiGroups: [ "" ]
resources: [ "secrets" ]
verbs: [ "get", "watch", "list" ]

---

Expand Down
3 changes: 3 additions & 0 deletions deploy/kubernetes/base/controller-serviceaccount.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "watch", "list", "delete", "update", "create"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
---
# Source: aws-efs-csi-driver/templates/controller-serviceaccount.yaml
kind: ClusterRoleBinding
Expand Down
100 changes: 97 additions & 3 deletions pkg/cloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import (
"context"
"errors"
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/efs"
"k8s.io/klog"
"math/rand"
"time"
)

const (
Expand Down Expand Up @@ -65,12 +67,20 @@ type AccessPointOptions struct {
Tags map[string]string
}

type MountTarget struct {
AZName string
AZId string
MountTargetId string
IPAddress string
}

// Efs abstracts efs client(https://docs.aws.amazon.com/sdk-for-go/api/service/efs/)
type Efs interface {
CreateAccessPointWithContext(aws.Context, *efs.CreateAccessPointInput, ...request.Option) (*efs.CreateAccessPointOutput, error)
DeleteAccessPointWithContext(aws.Context, *efs.DeleteAccessPointInput, ...request.Option) (*efs.DeleteAccessPointOutput, error)
DescribeAccessPointsWithContext(aws.Context, *efs.DescribeAccessPointsInput, ...request.Option) (*efs.DescribeAccessPointsOutput, error)
DescribeFileSystemsWithContext(aws.Context, *efs.DescribeFileSystemsInput, ...request.Option) (*efs.DescribeFileSystemsOutput, error)
DescribeMountTargetsWithContext(aws.Context, *efs.DescribeMountTargetsInput, ...request.Option) (*efs.DescribeMountTargetsOutput, error)
}

type Cloud interface {
Expand All @@ -79,6 +89,7 @@ type Cloud interface {
DeleteAccessPoint(ctx context.Context, accessPointId string) (err error)
DescribeAccessPoint(ctx context.Context, accessPointId string) (accessPoint *AccessPoint, err error)
DescribeFileSystem(ctx context.Context, fileSystemId string) (fs *FileSystem, err error)
DescribeMountTargets(ctx context.Context, fileSystemId, az string) (fs *MountTarget, err error)
}

type cloud struct {
Expand All @@ -102,6 +113,23 @@ func NewCloud() (Cloud, error) {
}, nil
}

// NewCloudWithRole returns a new instance of AWS cloud after assuming an aws role
// It panics if session is invalid
func NewCloudWithRole(awsRoleArn string) (Cloud, error) {
sess := session.Must(session.NewSession(&aws.Config{}))
metadata, err := NewMetadataService(sess)
if err != nil {
return nil, fmt.Errorf("could not get metadata from AWS: %v", err)
}

creds := stscreds.NewCredentials(sess, awsRoleArn)
efsClient := efs.New(session.Must(session.NewSession(aws.NewConfig().WithCredentials(creds).WithRegion(metadata.GetRegion()))))
return &cloud{
metadata: metadata,
efs: efsClient,
}, nil
}

func (c *cloud) GetMetadata() MetadataService {
return c.metadata
}
Expand Down Expand Up @@ -187,7 +215,7 @@ func (c *cloud) DescribeAccessPoint(ctx context.Context, accessPointId string) (

func (c *cloud) DescribeFileSystem(ctx context.Context, fileSystemId string) (fs *FileSystem, err error) {
describeFsInput := &efs.DescribeFileSystemsInput{FileSystemId: &fileSystemId}
klog.V(5).Infof("Calling DescribeFS with input: %+v", *describeFsInput)
klog.V(5).Infof("Calling DescribeFileSystems with input: %+v", *describeFsInput)
res, err := c.efs.DescribeFileSystemsWithContext(ctx, describeFsInput)
if err != nil {
if isAccessDenied(err) {
Expand All @@ -196,7 +224,7 @@ func (c *cloud) DescribeFileSystem(ctx context.Context, fileSystemId string) (fs
if isFileSystemNotFound(err) {
return nil, ErrNotFound
}
return nil, fmt.Errorf("describe File System failed: %v", err)
return nil, fmt.Errorf("Describe File System failed: %v", err)
}

fileSystems := res.FileSystems
Expand All @@ -208,6 +236,51 @@ func (c *cloud) DescribeFileSystem(ctx context.Context, fileSystemId string) (fs
}, nil
}

func (c *cloud) DescribeMountTargets(ctx context.Context, fileSystemId, azName string) (fs *MountTarget, err error) {
describeMtInput := &efs.DescribeMountTargetsInput{FileSystemId: &fileSystemId}
klog.V(5).Infof("Calling DescribeMountTargets with input: %+v", *describeMtInput)
res, err := c.efs.DescribeMountTargetsWithContext(ctx, describeMtInput)
if err != nil {
if isAccessDenied(err) {
return nil, ErrAccessDenied
}
if isFileSystemNotFound(err) {
return nil, ErrNotFound
}
return nil, fmt.Errorf("Describe Mount Targets failed: %v", err)
}

mountTargets := res.MountTargets
if len(mountTargets) == 0 {
return nil, fmt.Errorf("Cannot find mount targets for file system %v. Please create mount targets for file system.", fileSystemId)
}

availableMountTargets := getAvailableMountTargets(mountTargets)

if len(availableMountTargets) == 0 {
return nil, fmt.Errorf("No mount target for file system %v is in available state. Please retry in 5 minutes.", fileSystemId)
}

var mountTarget *efs.MountTargetDescription
if azName != "" {
mountTarget = getMountTargetForAz(availableMountTargets, azName)
}

// Pick random Mount target from available mount target if azName is not provided.
// Or if there is no mount target matching azName
if mountTarget == nil {
rand.Seed(time.Now().Unix())
mountTarget = availableMountTargets[rand.Intn(len(availableMountTargets))]
}

return &MountTarget{
AZName: *mountTarget.AvailabilityZoneName,
AZId: *mountTarget.AvailabilityZoneId,
MountTargetId: *mountTarget.MountTargetId,
IPAddress: *mountTarget.IpAddress,
}, nil
}

func isFileSystemNotFound(err error) bool {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == efs.ErrCodeFileSystemNotFound {
Expand Down Expand Up @@ -247,3 +320,24 @@ func parseEfsTags(tagMap map[string]string) []*efs.Tag {
}
return efsTags
}

func getAvailableMountTargets(mountTargets []*efs.MountTargetDescription) []*efs.MountTargetDescription {
availableMountTargets := []*efs.MountTargetDescription{}
for _, mt := range mountTargets {
if *mt.LifeCycleState == "available" {
availableMountTargets = append(availableMountTargets, mt)
}
}

return availableMountTargets
}

func getMountTargetForAz(mountTargets []*efs.MountTargetDescription, azName string) *efs.MountTargetDescription {
for _, mt := range mountTargets {
if *mt.AvailabilityZoneName == azName {
return mt
}
}
klog.Infof("There is no mount target match %v. Will pick a random mount target.", azName)
return nil
}

0 comments on commit dcf1f5b

Please sign in to comment.