Skip to content

Commit

Permalink
Add support for CSI driver
Browse files Browse the repository at this point in the history
  • Loading branch information
helayoty committed Apr 24, 2022
1 parent 14114f3 commit a0036e9
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.6.0
github.com/stretchr/testify v1.5.1
github.com/thoas/go-funk v0.9.1
github.com/virtual-kubelet/node-cli v0.7.0
github.com/virtual-kubelet/virtual-kubelet v1.6.0
Expand Down
48 changes: 48 additions & 0 deletions provider/aci.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ const (
virtualKubeletDNSNameLabel = "virtualkubelet.io/dnsnamelabel"

subnetDelegationService = "Microsoft.ContainerInstance/containerGroups"
// Parameter names defined in azure file CSI driver, refer to
// https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/docs/driver-parameters.md
azureFileShareName = "shareName"
)

// DNS configuration settings
Expand Down Expand Up @@ -1632,9 +1635,54 @@ func getProbe(probe *v1.Probe, ports []v1.ContainerPort) (*aci.ContainerProbe, e
}, nil
}

func (p *ACIProvider) getAzureFileCSI(volume v1.Volume, namespace string) (*aci.Volume, error) {
azureSource := &aci.AzureFileVolume{
ReadOnly: *volume.CSI.ReadOnly,
}
if volume.CSI.NodePublishSecretRef != nil && volume.CSI.NodePublishSecretRef.Name != "" {

// Set the storage account name and key
secret, err := p.resourceManager.GetSecret(volume.CSI.NodePublishSecretRef.Name, namespace)
if err != nil {
return nil, fmt.Errorf("The secret %s for AzureFile CSI driver %s is not found, err: %s", volume.CSI.NodePublishSecretRef.Name, volume.Name, err)
}

if secret == nil {
return nil, fmt.Errorf("Getting secret for AzureFile CSI driver %s returned an empty secret", volume.Name)
}

azureSource.StorageAccountName = string(secret.Data["azurestorageaccountname"])
azureSource.StorageAccountKey = string(secret.Data["azurestorageaccountkey"])

// Set shareName
if volume.CSI.VolumeAttributes != nil {
if shareName, ok := volume.CSI.VolumeAttributes[azureFileShareName]; ok {
azureSource.ShareName = shareName
}
}
volume := aci.Volume{
Name: volume.Name,
AzureFile: azureSource,
}
return &volume, nil
} else {
return nil, fmt.Errorf("NodePublishSecretRef for AzureFile CSI driver %s cannot be empty or nil", volume.Name)
}
}

func (p *ACIProvider) getVolumes(pod *v1.Pod) ([]aci.Volume, error) {
volumes := make([]aci.Volume, 0, len(pod.Spec.Volumes))
for _, v := range pod.Spec.Volumes {
// Handle the case for Azure File CSI driver
if v.CSI != nil {
csiVolume, err := p.getAzureFileCSI(v, pod.Namespace)
if err != nil {
return volumes, err
}
volumes = append(volumes, *csiVolume)
continue
}

// Handle the case for the AzureFile volume.
if v.AzureFile != nil {
secret, err := p.resourceManager.GetSecret(v.AzureFile.SecretName, pod.Namespace)
Expand Down
174 changes: 174 additions & 0 deletions provider/aci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

testifyassert "github.com/stretchr/testify/assert"

"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
)
Expand Down Expand Up @@ -1191,3 +1193,175 @@ func TestCreatePodWithProjectedVolume(t *testing.T) {
t.Fatal("Failed to create pod", err)
}
}

func TestCreatePodWithCSIVolume(t *testing.T) {
podName := "pod-name"
podNamespace := "ns-name"
pvName := "pv-name"
fakeVolumeSecret := "fake-volume-secret"

aadServerMocker := NewAADMock()
aciServerMocker := NewACIMock()
mockCtrl := gomock.NewController(GinkgoT())
mockPodLister := NewMockPodLister(mockCtrl)
mockSecretLister := NewMockSecretLister(mockCtrl)
mockSecretNamespaceLister := NewMockSecretNamespaceLister(mockCtrl)
mockConfigMapLister := NewMockConfigMapLister(mockCtrl)
mockServiceLister := NewMockServiceLister(mockCtrl)
mockPvcLister := NewMockPersistentVolumeClaimLister(mockCtrl)
mockPvLister := NewMockPersistentVolumeLister(mockCtrl)

aciServerMocker.OnGetRPManifest = func() (int, interface{}) {
manifest := &aci.ResourceProviderManifest{
Metadata: &aci.ResourceProviderMetadata{
GPURegionalSKUs: []*aci.GPURegionalSKU{
{
Location: fakeRegion,
SKUs: []aci.GPUSKU{aci.K80, aci.P100, aci.V100},
},
},
},
}

return http.StatusOK, manifest
}
resourceManager, err := manager.NewResourceManager(mockPodLister, mockSecretLister, mockConfigMapLister, mockServiceLister, mockPvcLister, mockPvLister)
if err != nil {
t.Fatal("Unable to prepare the mocks for resourceManager", err)
}

provider, err := createTestProvider(aadServerMocker, aciServerMocker, resourceManager)
if err != nil {
t.Fatal("Unable to create test provider", err)
}

aciServerMocker.OnCreate = func(subscription, resourceGroup, containerGroup string, cg *aci.ContainerGroup) (int, interface{}) {
assert.Check(t, is.Equal(fakeSubscription, subscription), "Subscription doesn't match")
assert.Check(t, is.Equal(fakeResourceGroup, resourceGroup), "Resource group doesn't match")
assert.Check(t, cg != nil, "Container group is nil")
assert.Check(t, is.Equal(podNamespace+"-"+podName, containerGroup), "Container group name is not expected")
assert.Check(t, cg.ContainerGroupProperties.Containers != nil, "Containers should not be nil")
assert.Check(t, is.Equal(1, len(cg.ContainerGroupProperties.Containers)), "1 Container is expected")
assert.Check(t, is.Equal("nginx", cg.ContainerGroupProperties.Containers[0].Name), "Container nginx is expected")
assert.Check(t, is.Equal(1, len(cg.Volumes)), "volume count not match")

return http.StatusOK, cg
}

configMapNamespaceLister := NewMockConfigMapNamespaceLister(mockCtrl)
mockConfigMapLister.EXPECT().ConfigMaps(podNamespace).Return(configMapNamespaceLister)
configMapNamespaceLister.EXPECT().Get("kube-root-ca.crt").Return(&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "kube-root-ca.crt",
},
Data: map[string]string{
"ca.crt": "fake-ca-data",
"foo": "bar",
},
}, nil)

mockSecretLister.EXPECT().Secrets(podNamespace).Return(mockSecretNamespaceLister)

mockSecretNamespaceLister.EXPECT().Get(fakeVolumeSecret).Return(&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: fakeVolumeSecret,
Namespace: podNamespace,
},
Data: map[string][]byte{
"azurestorageaccountname": []byte("azure storage account name"),
"azurestorageaccountkey": []byte("azure storage account key")},
}, nil)

mockPvLister.EXPECT().Get(pvName).Return(&v1.PersistentVolume{
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{
Driver: "file.csi.azure.com",
NodeStageSecretRef: &v1.SecretReference{
Name: fakeVolumeSecret,
Namespace: podNamespace,
},
},
},
},
}, nil)

readOnly := false

cases := []struct {
description string
volume v1.Volume
expectedError error
}{
{
description: "Volume has NodePublishSecretRef with valid value",
volume: v1.Volume{
Name: pvName,
VolumeSource: v1.VolumeSource{
CSI: &v1.CSIVolumeSource{
Driver: "file.csi.azure.com",
NodePublishSecretRef: &v1.LocalObjectReference{
Name: fakeVolumeSecret,
},
ReadOnly: &readOnly,
},
}},
expectedError: nil,
},
{
description: "Volume has no NodePublishSecretRef",
volume: v1.Volume{
Name: pvName,
VolumeSource: v1.VolumeSource{
CSI: &v1.CSIVolumeSource{
Driver: "file.csi.azure.com",
ReadOnly: &readOnly,
},
}},
expectedError: fmt.Errorf("NodePublishSecretRef for AzureFile CSI driver %s cannot be empty or nil", pvName),
},
}
for _, tc := range cases {
t.Run(tc.description, func(t *testing.T) {

pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Namespace: podNamespace,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "nginx",
ReadinessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Port: intstr.FromInt(8080),
Path: "/",
},
},
InitialDelaySeconds: 10,
PeriodSeconds: 5,
TimeoutSeconds: 60,
SuccessThreshold: 3,
FailureThreshold: 5,
},
},
},
},
}

volumeMount := v1.VolumeMount{
Name: pvName,
MountPath: "/temp",
}
pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, volumeMount)

pod.Spec.Volumes = append(pod.Spec.Volumes, tc.volume)

err = provider.CreatePod(context.Background(), pod)

testifyassert.Equalf(t, tc.expectedError, err, "\nTest case: %s", tc.description)
})
}
}

0 comments on commit a0036e9

Please sign in to comment.