Skip to content

Add option to deploy GKE managed PD CSI driver for integration tests #515

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 1 commit into from
Jun 12, 2020
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
51 changes: 47 additions & 4 deletions test/k8s-integration/cluster.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"encoding/json"
"errors"
"fmt"
"os"
Expand All @@ -9,6 +10,7 @@ import (
"strconv"
"strings"

apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/klog"
)

Expand Down Expand Up @@ -131,7 +133,7 @@ func setImageTypeEnvs(imageType string) error {
return nil
}

func clusterUpGKE(gceZone, gceRegion string, numNodes int, imageType string) error {
func clusterUpGKE(gceZone, gceRegion string, numNodes int, imageType string, useManagedDriver bool) error {
locationArg, locationVal, err := gkeLocationArgs(gceZone, gceRegion)
if err != nil {
return err
Expand All @@ -150,9 +152,23 @@ func clusterUpGKE(gceZone, gceRegion string, numNodes int, imageType string) err
return err
}
}
cmd := exec.Command("gcloud", "container", "clusters", "create", gkeTestClusterName,
locationArg, locationVal, "--cluster-version", *gkeClusterVer, "--num-nodes", strconv.Itoa(numNodes),
"--quiet", "--machine-type", "n1-standard-2", "--image-type", imageType)

var cmd *exec.Cmd
cmdParams := []string{"container", "clusters", "create", gkeTestClusterName,
locationArg, locationVal, "--num-nodes", strconv.Itoa(numNodes)}
if isVariableSet(gkeClusterVer) {
cmdParams = append(cmdParams, "--cluster-version", *gkeClusterVer)
} else {
cmdParams = append(cmdParams, "--release-channel", *gkeReleaseChannel)
}

if useManagedDriver {
// PD CSI Driver add on is enabled only in gcloud beta.
cmdParams = append([]string{"beta"}, cmdParams...)
cmdParams = append(cmdParams, "--addons", "GcePersistentDiskCsiDriver")
}

cmd = exec.Command("gcloud", cmdParams...)
err = runCommand("Staring E2E Cluster on GKE", cmd)
if err != nil {
return fmt.Errorf("failed to bring up kubernetes e2e cluster on gke: %v", err)
Expand Down Expand Up @@ -290,3 +306,30 @@ func getNormalizedVersion(kubeVersion, gkeVersion string) (string, error) {
return strings.Join(toks[:2], "."), nil

}

func getKubeClusterVersion() (string, error) {
out, err := exec.Command("kubectl", "version", "-o=json").CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to obtain cluster version, error: %v", err)
}
type version struct {
ClientVersion *apimachineryversion.Info `json:"clientVersion,omitempty" yaml:"clientVersion,omitempty"`
ServerVersion *apimachineryversion.Info `json:"serverVersion,omitempty" yaml:"serverVersion,omitempty"`
}

var v version
err = json.Unmarshal(out, &v)
if err != nil {
return "", fmt.Errorf("Failed to parse kubectl version output, error: %v", err)
}

return v.ServerVersion.GitVersion, nil
}

func mustGetKubeClusterVersion() string {
ver, err := getKubeClusterVersion()
if err != nil {
klog.Fatalf("Error: %v", err)
}
return ver
}
155 changes: 95 additions & 60 deletions test/k8s-integration/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,28 @@ import (
"path/filepath"
"syscall"

testutils "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/test/e2e/utils"

"k8s.io/apimachinery/pkg/util/uuid"
apimachineryversion "k8s.io/apimachinery/pkg/util/version"
"k8s.io/klog"
testutils "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/test/e2e/utils"
)

var (
// Kubernetes cluster flags
teardownCluster = flag.Bool("teardown-cluster", true, "teardown the cluster after the e2e test")
teardownDriver = flag.Bool("teardown-driver", true, "teardown the driver after the e2e test")
bringupCluster = flag.Bool("bringup-cluster", true, "build kubernetes and bringup a cluster")
gceZone = flag.String("gce-zone", "", "zone that the gce k8s cluster is created/found in")
gceRegion = flag.String("gce-region", "", "region that gke regional cluster should be created in")
kubeVersion = flag.String("kube-version", "", "version of Kubernetes to download and use for the cluster")
testVersion = flag.String("test-version", "", "version of Kubernetes to download and use for tests")
kubeFeatureGates = flag.String("kube-feature-gates", "", "feature gates to set on new kubernetes cluster")
localK8sDir = flag.String("local-k8s-dir", "", "local prebuilt kubernetes/kubernetes directory to use for cluster and test binaries")
deploymentStrat = flag.String("deployment-strategy", "gce", "choose between deploying on gce or gke")
gkeClusterVer = flag.String("gke-cluster-version", "", "version of Kubernetes master and node for gke")
numNodes = flag.Int("num-nodes", -1, "the number of nodes in the test cluster")
imageType = flag.String("image-type", "cos", "the image type to use for the cluster")
teardownCluster = flag.Bool("teardown-cluster", true, "teardown the cluster after the e2e test")
teardownDriver = flag.Bool("teardown-driver", true, "teardown the driver after the e2e test")
bringupCluster = flag.Bool("bringup-cluster", true, "build kubernetes and bringup a cluster")
gceZone = flag.String("gce-zone", "", "zone that the gce k8s cluster is created/found in")
gceRegion = flag.String("gce-region", "", "region that gke regional cluster should be created in")
kubeVersion = flag.String("kube-version", "", "version of Kubernetes to download and use for the cluster")
testVersion = flag.String("test-version", "", "version of Kubernetes to download and use for tests")
kubeFeatureGates = flag.String("kube-feature-gates", "", "feature gates to set on new kubernetes cluster")
localK8sDir = flag.String("local-k8s-dir", "", "local prebuilt kubernetes/kubernetes directory to use for cluster and test binaries")
deploymentStrat = flag.String("deployment-strategy", "gce", "choose between deploying on gce or gke")
gkeClusterVer = flag.String("gke-cluster-version", "", "version of Kubernetes master and node for gke")
numNodes = flag.Int("num-nodes", -1, "the number of nodes in the test cluster")
imageType = flag.String("image-type", "cos", "the image type to use for the cluster")
gkeReleaseChannel = flag.String("gke-release-channel", "", "GKE release channel to be used for cluster deploy. One of 'rapid', 'stable' or 'regular'")

// Test infrastructure flags
boskosResourceType = flag.String("boskos-resource-type", "gce-project", "name of the boskos resource type to reserve")
Expand All @@ -51,10 +52,11 @@ var (
inProw = flag.Bool("run-in-prow", false, "is the test running in PROW")

// Driver flags
stagingImage = flag.String("staging-image", "", "name of image to stage to")
saFile = flag.String("service-account-file", "", "path of service account file")
deployOverlayName = flag.String("deploy-overlay-name", "", "which kustomize overlay to deploy the driver with")
doDriverBuild = flag.Bool("do-driver-build", true, "building the driver from source")
stagingImage = flag.String("staging-image", "", "name of image to stage to")
saFile = flag.String("service-account-file", "", "path of service account file")
deployOverlayName = flag.String("deploy-overlay-name", "", "which kustomize overlay to deploy the driver with")
doDriverBuild = flag.Bool("do-driver-build", true, "building the driver from source")
useGKEManagedDriver = flag.Bool("use-gke-managed-driver", false, "use GKE managed PD CSI driver for the tests")

// Test flags
migrationTest = flag.Bool("migration-test", false, "sets the flag on the e2e binary signalling migration")
Expand All @@ -75,12 +77,23 @@ func init() {
func main() {
flag.Parse()

if !*inProw {
if !*inProw && !*useGKEManagedDriver {
ensureVariable(stagingImage, true, "staging-image is a required flag, please specify the name of image to stage to")
}

if *useGKEManagedDriver {
ensureVariableVal(deploymentStrat, "gke", "deployment strategy must be GKE for using managed driver")
ensureFlag(doDriverBuild, false, "'do-driver-build' must be false when using GKE managed driver")
ensureFlag(teardownDriver, false, "'teardown-driver' must be false when using GKE managed driver")
ensureVariable(stagingImage, false, "'staging-image' must not be set when using GKE managed driver")
ensureVariable(deployOverlayName, false, "'deploy-overlay-name' must not be set when using GKE managed driver")
}

ensureVariable(saFile, true, "service-account-file is a required flag")
ensureVariable(deployOverlayName, true, "deploy-overlay-name is a required flag")
if !*useGKEManagedDriver {
ensureVariable(deployOverlayName, true, "deploy-overlay-name is a required flag")
}

ensureVariable(testFocus, true, "test-focus is a required flag")
ensureVariable(imageType, true, "image type is a required flag. Available options include 'cos' and 'ubuntu'")

Expand All @@ -103,7 +116,8 @@ func main() {
if *deploymentStrat == "gke" {
ensureFlag(migrationTest, false, "Cannot set deployment strategy to 'gke' for migration tests.")
ensureVariable(kubeVersion, false, "Cannot set kube-version when using deployment strategy 'gke'. Use gke-cluster-version.")
ensureVariable(gkeClusterVer, true, "Must set gke-cluster-version when using deployment strategy 'gke'.")
ensureExactlyOneVariableSet([]*string{gkeClusterVer, gkeReleaseChannel},
"For GKE cluster deployment, exactly one of 'gke-cluster-version' or 'gke-release-channel' must be set")
ensureVariable(kubeFeatureGates, false, "Cannot set feature gates when using deployment strategy 'gke'.")
if len(*localK8sDir) == 0 {
ensureVariable(testVersion, true, "Must set either test-version or local k8s dir when using deployment strategy 'gke'.")
Expand Down Expand Up @@ -243,7 +257,7 @@ func handle() error {
case "gce":
err = clusterUpGCE(k8sDir, *gceZone, *numNodes, *imageType)
case "gke":
err = clusterUpGKE(*gceZone, *gceRegion, *numNodes, *imageType)
err = clusterUpGKE(*gceZone, *gceRegion, *numNodes, *imageType, *useGKEManagedDriver)
default:
err = fmt.Errorf("deployment-strategy must be set to 'gce' or 'gke', but is: %s", *deploymentStrat)
}
Expand Down Expand Up @@ -272,21 +286,24 @@ func handle() error {
}()
}

// Install the driver and defer its teardown
err := installDriver(goPath, pkgDir, *stagingImage, stagingVersion, *deployOverlayName, *doDriverBuild)
if *teardownDriver {
defer func() {
// TODO (#140): collect driver logs
if teardownErr := deleteDriver(goPath, pkgDir, *deployOverlayName); teardownErr != nil {
klog.Errorf("failed to delete driver: %v", teardownErr)
}
}()
}
if err != nil {
return fmt.Errorf("failed to install CSI Driver: %v", err)
if !*useGKEManagedDriver {
// Install the driver and defer its teardown
err := installDriver(goPath, pkgDir, *stagingImage, stagingVersion, *deployOverlayName, *doDriverBuild)
if *teardownDriver {
defer func() {
// TODO (#140): collect driver logs
if teardownErr := deleteDriver(goPath, pkgDir, *deployOverlayName); teardownErr != nil {
klog.Errorf("failed to delete driver: %v", teardownErr)
}
}()
}
if err != nil {
return fmt.Errorf("failed to install CSI Driver: %v", err)
}
}

var cloudProviderArgs []string
var err error
switch *deploymentStrat {
case "gke":
cloudProviderArgs, err = getGKEKubeTestArgs(*gceZone, *gceRegion, *imageType)
Expand All @@ -295,12 +312,20 @@ func handle() error {
}
}

normalizedVersion, err := getNormalizedVersion(*kubeVersion, *gkeClusterVer)
if err != nil {
return fmt.Errorf("failed to get cluster minor version: %v", err)
// Kubernetes version of GKE deployments are expected to be of the pattern x.y.z-gke.k,
// hence we use the main.Version utils to parse and compare GKE managed cluster versions.
// For clusters deployed on GCE, use the apimachinery version utils (which supports non-gke based semantic versioning).
clusterVersion := mustGetKubeClusterVersion()
var testSkip string
switch *deploymentStrat {
case "gce":
testSkip = generateGCETestSkip(clusterVersion)
case "gke":
testSkip = generateGKETestSkip(clusterVersion, *useGKEManagedDriver)
default:
return fmt.Errorf("Unknown deployment strategy %s", *deploymentStrat)
}

testSkip := generateTestSkip(normalizedVersion)
// Run the tests using the testDir kubernetes
if len(*storageClassFile) != 0 {
err = runCSITests(pkgDir, testDir, *testFocus, testSkip, *storageClassFile, *snapshotClassFile, cloudProviderArgs, *deploymentStrat)
Expand All @@ -317,30 +342,40 @@ func handle() error {
return nil
}

func generateTestSkip(normalizedVersion string) string {
func generateGCETestSkip(clusterVersion string) string {
skipString := "\\[Disruptive\\]|\\[Serial\\]"
switch normalizedVersion {
// Fall-through versioning since all test cases we want to skip in 1.15
// should also be skipped in 1.14
case "1.13":
fallthrough
case "1.14":
fallthrough
case "1.15":
fallthrough
case "1.16":
// "volumeMode should not mount / map unused volumes in a pod" tests a
// bug-fix introduced in 1.17
// (https://github.com/kubernetes/kubernetes/pull/81163)
v := apimachineryversion.MustParseSemantic(clusterVersion)

// "volumeMode should not mount / map unused volumes in a pod" tests a
// (https://github.com/kubernetes/kubernetes/pull/81163)
// bug-fix introduced in 1.16
if v.LessThan(apimachineryversion.MustParseSemantic("1.16.0")) {
skipString = skipString + "|volumeMode\\sshould\\snot\\smount\\s/\\smap\\sunused\\svolumes\\sin\\sa\\spod"
// Skip Snapshot tests pre 1.17
skipString = skipString + "|snapshot"
fallthrough
case "1.17":
case "latest":
case "master":
default:
}

if v.LessThan(apimachineryversion.MustParseSemantic("1.17.0")) {
skipString = skipString + "|VolumeSnapshotDataSource"
}
return skipString
}

func generateGKETestSkip(clusterVersion string, use_gke_managed_driver bool) string {
skipString := "\\[Disruptive\\]|\\[Serial\\]"
curVer := mustParseVersion(clusterVersion)

// "volumeMode should not mount / map unused volumes in a pod" tests a
// (https://github.com/kubernetes/kubernetes/pull/81163)
// bug-fix introduced in 1.16
if curVer.lessThan(mustParseVersion("1.16.0")) {
skipString = skipString + "|volumeMode\\sshould\\snot\\smount\\s/\\smap\\sunused\\svolumes\\sin\\sa\\spod"
}

// For GKE deployed PD CSI snapshot is enabled in 1.17.6-gke.4(and higher), 1.18.3-gke.0(and higher).
if (use_gke_managed_driver && curVer.lessThan(mustParseVersion("1.17.6-gke.4"))) ||
(!use_gke_managed_driver && (*curVer).lessThan(mustParseVersion("1.17.0"))) {
skipString = skipString + "|VolumeSnapshotDataSource"
}

return skipString
}

Expand Down
23 changes: 23 additions & 0 deletions test/k8s-integration/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ func ensureFlag(v *bool, setTo bool, msgOnError string) {
}
}

func ensureExactlyOneVariableSet(vars []*string, msgOnError string) {
var count int
for _, v := range vars {
if len(*v) != 0 {
count++
}
}

if count != 1 {
klog.Fatal(msgOnError)
}
}

func shredFile(filePath string) {
if _, err := os.Stat(filePath); os.IsNotExist(err) {
klog.V(4).Infof("File %v was not found, skipping shredding", filePath)
Expand All @@ -79,3 +92,13 @@ func shredFile(filePath string) {
klog.V(4).Infof("Failed to remove service account file %s: %v", filePath, err)
}
}

func ensureVariableVal(v *string, val string, msgOnError string) {
if *v != val {
klog.Fatal(msgOnError)
}
}

func isVariableSet(v *string) bool {
return len(*v) != 0
}
Loading