Skip to content

Add CMEK E2E Test #218

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

Merged
merged 2 commits into from
Mar 1, 2019
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 227 additions & 32 deletions Gopkg.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

[[constraint]]
name = "cloud.google.com/go"
version = "0.19.0"
version = "0.34.0"

[[constraint]]
name = "github.com/container-storage-interface/spec"
Expand Down
63 changes: 46 additions & 17 deletions test/e2e/tests/multi_zone_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.
package tests

import (
"fmt"
"path/filepath"
"strings"

Expand Down Expand Up @@ -143,68 +144,96 @@ var _ = Describe("GCE PD CSI Driver Multi-Zone", func() {

})

func testAttachWriteReadDetach(volID string, volName string, instance *remote.InstanceInfo, client *remote.CsiClient, readOnly bool) {
func testAttachWriteReadDetach(volID string, volName string, instance *remote.InstanceInfo, client *remote.CsiClient, readOnly bool) error {
var err error

glog.Infof("Starting testAttachWriteReadDetach with volume %v node %v with readonly %v\n", volID, instance.GetNodeID(), readOnly)
// Attach Disk
err = client.ControllerPublishVolume(volID, instance.GetNodeID())
Expect(err).To(BeNil(), "ControllerPublishVolume failed with error for disk %v on node %v", volID, instance.GetNodeID())
if err != nil {
return fmt.Errorf("ControllerPublishVolume failed with error for disk %v on node %v: %v", volID, instance.GetNodeID(), err)
}

defer func() {

// Detach Disk
err = client.ControllerUnpublishVolume(volID, instance.GetNodeID())
Expect(err).To(BeNil(), "ControllerUnpublishVolume failed with error")
if err != nil {
glog.Errorf("Failed to detach disk: %v", err)
}

}()

// Stage Disk
stageDir := filepath.Join("/tmp/", volName, "stage")
client.NodeStageVolume(volID, stageDir)
Expect(err).To(BeNil(), "NodeStageVolume failed with error")
err = client.NodeStageVolume(volID, stageDir)
if err != nil {
return fmt.Errorf("NodeStageVolume failed with error: %v", err)
}

defer func() {
// Unstage Disk
err = client.NodeUnstageVolume(volID, stageDir)
Expect(err).To(BeNil(), "NodeUnstageVolume failed with error")
err = testutils.RmAll(instance, filepath.Join("/tmp/", volName))
Expect(err).To(BeNil(), "Failed to remove temp directory")
if err != nil {
glog.Errorf("Failed to unstage volume: %v", err)
}
fp := filepath.Join("/tmp/", volName)
err = testutils.RmAll(instance, fp)
if err != nil {
glog.Errorf("Failed to rm file path %s: %v", fp, err)
}
}()

// Mount Disk
publishDir := filepath.Join("/tmp/", volName, "mount")
err = client.NodePublishVolume(volID, stageDir, publishDir)
Expect(err).To(BeNil(), "NodePublishVolume failed with error")
if err != nil {
return fmt.Errorf("NodePublishVolume failed with error: %v", err)
}
err = testutils.ForceChmod(instance, filepath.Join("/tmp/", volName), "777")
Expect(err).To(BeNil(), "Chmod failed with error")
if err != nil {
return fmt.Errorf("Chmod failed with error: %v", err)
}
testFileContents := "test"
if !readOnly {
// Write a file
testFile := filepath.Join(publishDir, "testfile")
err = testutils.WriteFile(instance, testFile, testFileContents)
Expect(err).To(BeNil(), "Failed to write file")
if err != nil {
return fmt.Errorf("Failed to write file: %v", err)
}
}

// Unmount Disk
err = client.NodeUnpublishVolume(volID, publishDir)
Expect(err).To(BeNil(), "NodeUnpublishVolume failed with error")
if err != nil {
return fmt.Errorf("NodeUnpublishVolume failed with error: %v", err)
}

// Mount disk somewhere else
secondPublishDir := filepath.Join("/tmp/", volName, "secondmount")
err = client.NodePublishVolume(volID, stageDir, secondPublishDir)
Expect(err).To(BeNil(), "NodePublishVolume failed with error")
if err != nil {
return fmt.Errorf("NodePublishVolume failed with error: %v", err)
}
err = testutils.ForceChmod(instance, filepath.Join("/tmp/", volName), "777")
Expect(err).To(BeNil(), "Chmod failed with error")
if err != nil {
return fmt.Errorf("Chmod failed with error: %v", err)
}

// Read File
secondTestFile := filepath.Join(secondPublishDir, "testfile")
readContents, err := testutils.ReadFile(instance, secondTestFile)
Expect(err).To(BeNil(), "ReadFile failed with error")
if err != nil {
return fmt.Errorf("ReadFile failed with error: %v", err)
}
Expect(strings.TrimSpace(string(readContents))).To(Equal(testFileContents))

// Unmount Disk
err = client.NodeUnpublishVolume(volID, secondPublishDir)
Expect(err).To(BeNil(), "NodeUnpublishVolume failed with error")
if err != nil {
return fmt.Errorf("NodeUnpublishVolume failed with error: %v", err)
}

glog.Infof("Completed testAttachWriteReadDetach with volume %v node %v\n", volID, instance.GetNodeID())
return nil
}
7 changes: 7 additions & 0 deletions test/e2e/tests/setup_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ limitations under the License.
package tests

import (
"context"
"flag"
"fmt"
"math/rand"
"testing"
"time"

cloudkms "cloud.google.com/go/kms/apiv1"
"github.com/golang/glog"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
Expand All @@ -39,6 +41,7 @@ var (
testContexts = []*remote.TestContext{}
computeService *compute.Service
betaComputeService *computebeta.Service
kmsClient *cloudkms.KeyManagementClient
)

func TestE2E(t *testing.T) {
Expand All @@ -62,6 +65,10 @@ var _ = BeforeSuite(func() {
betaComputeService, err = remote.GetBetaComputeClient()
Expect(err).To(BeNil())

// Create the KMS client.
kmsClient, err = cloudkms.NewKeyManagementClient(context.Background())
Expect(err).To(BeNil())

if *runInProw {
*project, *serviceAccount = testutils.SetupProwConfig("gce-project")
}
Expand Down
166 changes: 164 additions & 2 deletions test/e2e/tests/single_zone_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ limitations under the License.
package tests

import (
"context"
"fmt"
"strings"
"time"

Expand All @@ -26,6 +28,10 @@ import (
csi "github.com/container-storage-interface/spec/lib/go/csi"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

"google.golang.org/api/iterator"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
fieldmask "google.golang.org/genproto/protobuf/field_mask"
)

const (
Expand Down Expand Up @@ -78,7 +84,8 @@ var _ = Describe("GCE PD CSI Driver", func() {
}()

// Attach Disk
testAttachWriteReadDetach(volID, volName, instance, client, false /* readOnly */)
err = testAttachWriteReadDetach(volID, volName, instance, client, false /* readOnly */)
Expect(err).To(BeNil(), "Failed to go through volume lifecycle")

})

Expand Down Expand Up @@ -152,7 +159,8 @@ var _ = Describe("GCE PD CSI Driver", func() {
}()

// Attach Disk
testAttachWriteReadDetach(underSpecifiedID, volName, instance, client, false /* readOnly */)
err = testAttachWriteReadDetach(underSpecifiedID, volName, instance, client, false /* readOnly */)
Expect(err).To(BeNil(), "Failed to go through volume lifecycle")

})

Expand Down Expand Up @@ -295,6 +303,160 @@ var _ = Describe("GCE PD CSI Driver", func() {
}()
})

It("Should create CMEK key, go through volume lifecycle, validate behavior on key revoke and restore", func() {
ctx := context.Background()
Expect(testContexts).ToNot(BeEmpty())
testContext := getRandomTestContext()

controllerInstance := testContext.Instance
controllerClient := testContext.Client

p, z, _ := controllerInstance.GetIdentity()
locationID := "global"

// The resource name of the key rings.
parentName := fmt.Sprintf("projects/%s/locations/%s", p, locationID)
keyRingId := "gce-pd-csi-test-ring"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any possible issue of multiple tests sharing the same key ring? Or leaving a key ring around at the end of the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key ring resource names are immutable.. all the tests will share the same key ring and the key ring will exist for the forseeable future. Talked to the CMEK TL and this is intended behavior


// Create KeyRing
ringReq := &kmspb.CreateKeyRingRequest{
Parent: parentName,
KeyRingId: keyRingId,
}
keyRing, err := kmsClient.CreateKeyRing(ctx, ringReq)
if !gce.IsGCEError(err, "alreadyExists") {
getKeyRingReq := &kmspb.GetKeyRingRequest{
Name: fmt.Sprintf("%s/keyRings/%s", parentName, keyRingId),
}
keyRing, err = kmsClient.GetKeyRing(ctx, getKeyRingReq)

}
Expect(err).To(BeNil(), "Failed to create or get key ring %v", keyRingId)

// Create CryptoKey in KeyRing
keyId := "test-key-" + string(uuid.NewUUID())
keyReq := &kmspb.CreateCryptoKeyRequest{
Parent: keyRing.Name,
CryptoKeyId: keyId,
CryptoKey: &kmspb.CryptoKey{
Purpose: kmspb.CryptoKey_ENCRYPT_DECRYPT,
VersionTemplate: &kmspb.CryptoKeyVersionTemplate{
Algorithm: kmspb.CryptoKeyVersion_GOOGLE_SYMMETRIC_ENCRYPTION,
},
},
}
key, err := kmsClient.CreateCryptoKey(ctx, keyReq)
Expect(err).To(BeNil(), "Failed to create crypto key %v in key ring %v", keyId, keyRing.Name)

keyVersions := []string{}
keyVersionReq := &kmspb.ListCryptoKeyVersionsRequest{
Parent: key.Name,
}

it := kmsClient.ListCryptoKeyVersions(ctx, keyVersionReq)

for {
keyVersion, err := it.Next()
if err == iterator.Done {
break
}
Expect(err).To(BeNil(), "Failed to list crypto key versions")

keyVersions = append(keyVersions, keyVersion.Name)
}

// Defer deletion of all key versions
// https://cloud.google.com/kms/docs/destroy-restore
defer func() {

for _, keyVersion := range keyVersions {
destroyKeyReq := &kmspb.DestroyCryptoKeyVersionRequest{
Name: keyVersion,
}
_, err = kmsClient.DestroyCryptoKeyVersion(ctx, destroyKeyReq)
Expect(err).To(BeNil(), "Failed to destroy crypto key version: %v", keyVersion)
}

}()

// Go through volume lifecycle using CMEK-ed PD
// Create Disk
volName := testNamePrefix + string(uuid.NewUUID())
volID, err := controllerClient.CreateVolume(volName, map[string]string{
common.ParameterKeyDiskEncryptionKmsKey: key.Name,
}, defaultSizeGb,
&csi.TopologyRequirement{
Requisite: []*csi.Topology{
{
Segments: map[string]string{common.TopologyKeyZone: z},
},
},
})
Expect(err).To(BeNil(), "CreateVolume failed with error: %v", err)

// Validate Disk Created
cloudDisk, err := computeService.Disks.Get(p, z, volName).Do()
Expect(err).To(BeNil(), "Could not get disk from cloud directly")
Expect(cloudDisk.Type).To(ContainSubstring(standardDiskType))
Expect(cloudDisk.Status).To(Equal(readyState))
Expect(cloudDisk.SizeGb).To(Equal(defaultSizeGb))
Expect(cloudDisk.Name).To(Equal(volName))

defer func() {
// Delete Disk
err = controllerClient.DeleteVolume(volID)
Expect(err).To(BeNil(), "DeleteVolume failed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is err coming from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previous line but it got lost somehow


// Validate Disk Deleted
_, err = computeService.Disks.Get(p, z, volName).Do()
Expect(gce.IsGCEError(err, "notFound")).To(BeTrue(), "Expected disk to not be found")
}()

// Test disk works
err = testAttachWriteReadDetach(volID, volName, controllerInstance, controllerClient, false /* readOnly */)
Expect(err).To(BeNil(), "Failed to go through volume lifecycle before revoking CMEK key")

// Revoke CMEK key
// https://cloud.google.com/kms/docs/enable-disable

for _, keyVersion := range keyVersions {
disableReq := &kmspb.UpdateCryptoKeyVersionRequest{
CryptoKeyVersion: &kmspb.CryptoKeyVersion{
Name: keyVersion,
State: kmspb.CryptoKeyVersion_DISABLED,
},
UpdateMask: &fieldmask.FieldMask{
Paths: []string{"state"},
},
}
_, err = kmsClient.UpdateCryptoKeyVersion(ctx, disableReq)
Expect(err).To(BeNil(), "Failed to disable crypto key")
}

// Make sure attach of PD fails
err = testAttachWriteReadDetach(volID, volName, controllerInstance, controllerClient, false /* readOnly */)
Expect(err).ToNot(BeNil(), "Volume lifecycle should have failed, but succeeded")

// Restore CMEK key
for _, keyVersion := range keyVersions {
enableReq := &kmspb.UpdateCryptoKeyVersionRequest{
CryptoKeyVersion: &kmspb.CryptoKeyVersion{
Name: keyVersion,
State: kmspb.CryptoKeyVersion_ENABLED,
},
UpdateMask: &fieldmask.FieldMask{
Paths: []string{"state"},
},
}
_, err = kmsClient.UpdateCryptoKeyVersion(ctx, enableReq)
Expect(err).To(BeNil(), "Failed to enable crypto key")
}

// Make sure attach of PD succeeds
err = testAttachWriteReadDetach(volID, volName, controllerInstance, controllerClient, false /* readOnly */)
Expect(err).To(BeNil(), "Failed to go through volume lifecycle after restoring CMEK key")
})

It("Should create and delete snapshot for RePD in two zones ", func() {
Expect(testContexts).ToNot(BeEmpty())
testContext := getRandomTestContext()
Expand Down
1 change: 1 addition & 0 deletions vendor/cloud.google.com/go/CONTRIBUTORS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading