Skip to content

Commit e95e75e

Browse files
committed
e2e for windows
1 parent 4f3f98b commit e95e75e

File tree

7 files changed

+154
-6
lines changed

7 files changed

+154
-6
lines changed

Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ EXP_DIR := exp
4848
GO_INSTALL = ./scripts/go_install.sh
4949
E2E_DATA_DIR ?= $(ROOT_DIR)/test/e2e/data
5050
KUBETEST_CONF_PATH ?= $(abspath $(E2E_DATA_DIR)/kubetest/conformance.yaml)
51+
KUBETEST_WINDOWS_CONF_PATH ?= $(abspath $(E2E_DATA_DIR)/kubetest/upstream-windows.yaml)
52+
KUBETEST_REPO_LIST_PATH ?= $(abspath $(E2E_DATA_DIR)/kubetest/repo-list.yaml)
5153

5254
# set --output-base used for conversion-gen which needs to be different for in GOPATH and outside GOPATH dev
5355
ifneq ($(abspath $(ROOT_DIR)),$(GOPATH)/src/sigs.k8s.io/cluster-api-provider-azure)
@@ -196,6 +198,11 @@ test-conformance: ## Run conformance test on workload cluster.
196198
test-conformance-fast: ## Run conformance test on workload cluster using a subset of the conformance suite in parallel.
197199
$(MAKE) test-conformance CONFORMANCE_E2E_ARGS="-kubetest.config-file=$(KUBETEST_FAST_CONF_PATH) -kubetest.ginkgo-nodes=5 $(E2E_ARGS)"
198200

201+
.PHONY: test-windows-upstream
202+
test-windows-upstream: ## Run windows upstream tests on workload cluster.
203+
curl --retry $(CURL_RETRIES) https://raw.githubusercontent.com/kubernetes-sigs/windows-testing/master/images/image-repo-list-master -o $(KUBETEST_REPO_LIST_PATH)
204+
$(MAKE) test-conformance CONFORMANCE_E2E_ARGS="-kubetest.config-file=$(KUBETEST_WINDOWS_CONF_PATH) -kubetest.repo-list-file=$(KUBETEST_REPO_LIST_PATH) $(E2E_ARGS)"
205+
199206
$(KUBE_APISERVER) $(ETCD): ## install test asset kubectl, kube-apiserver, etcd
200207
source ./scripts/fetch_ext_bins.sh && fetch_tools
201208

scripts/ci-conformance.sh

+5-1
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,8 @@ cleanup() {
9999

100100
trap cleanup EXIT
101101

102-
make test-conformance
102+
if [[ "${WINDOWS}" == "true" ]]; then
103+
make test-windows-upstream
104+
else
105+
make test-conformance
106+
fi

test/e2e/conformance_test.go

+39-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ import (
2424
"os"
2525
"path/filepath"
2626
"strconv"
27+
"strings"
28+
29+
"sigs.k8s.io/cluster-api-provider-azure/test/e2e/kubernetes/node"
30+
31+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2732

2833
. "github.com/onsi/ginkgo"
2934
. "github.com/onsi/gomega"
@@ -70,12 +75,19 @@ var _ = Describe("Conformance Tests", func() {
7075

7176
kubernetesVersion := e2eConfig.GetVariable(capi_e2e.KubernetesVersion)
7277
flavor := clusterctl.DefaultFlavor
78+
if isWindows(kubetestConfigFilePath) {
79+
flavor = "windows"
80+
}
7381
if useCIArtifacts {
7482
flavor = "conformance-ci-artifacts"
83+
if isWindows(kubetestConfigFilePath) {
84+
flavor = "conformance-ci-artifacts-windows"
85+
}
7586
kubernetesVersion, err = kubernetesversions.LatestCIRelease()
7687
Expect(err).NotTo(HaveOccurred())
7788
Expect(os.Setenv("CI_VERSION", kubernetesVersion)).To(Succeed())
7889
}
90+
7991
workerMachineCount, err := strconv.ParseInt(e2eConfig.GetVariable("CONFORMANCE_WORKER_MACHINE_COUNT"), 10, 64)
8092
Expect(err).NotTo(HaveOccurred())
8193
controlPlaneMachineCount, err := strconv.ParseInt(e2eConfig.GetVariable("CONFORMANCE_CONTROL_PLANE_MACHINE_COUNT"), 10, 64)
@@ -106,14 +118,33 @@ var _ = Describe("Conformance Tests", func() {
106118

107119
b.RecordValue("cluster creation", runtime.Seconds())
108120
workloadProxy := bootstrapClusterProxy.GetWorkloadCluster(ctx, namespace.Name, clusterName)
121+
122+
// Windows requires a taint on control nodes nodes since not all conformance tests have ability to run
123+
if isWindows(kubetestConfigFilePath) {
124+
options := v1.ListOptions{
125+
LabelSelector: "kubernetes.io/os=linux",
126+
}
127+
128+
noScheduleTaint := &corev1.Taint{
129+
Key: "node-role.kubernetes.io/master",
130+
Value: "",
131+
Effect: "NoSchedule",
132+
}
133+
134+
err := node.TaintNode(workloadProxy.GetClientSet(), options, noScheduleTaint)
135+
Expect(err).NotTo(HaveOccurred())
136+
}
137+
109138
runtime = b.Time("conformance suite", func() {
110-
kubetest.Run(context.Background(),
139+
err := kubetest.Run(context.Background(),
111140
kubetest.RunInput{
112-
ClusterProxy: workloadProxy,
113-
NumberOfNodes: int(workerMachineCount),
114-
ConfigFilePath: kubetestConfigFilePath,
141+
ClusterProxy: workloadProxy,
142+
NumberOfNodes: int(workerMachineCount),
143+
ConfigFilePath: kubetestConfigFilePath,
144+
KubeTestRepoListPath: kubetestRepoListPath,
115145
},
116146
)
147+
Expect(err).NotTo(HaveOccurred())
117148
})
118149
b.RecordValue("conformance suite run time", runtime.Seconds())
119150
}, 1)
@@ -127,3 +158,7 @@ var _ = Describe("Conformance Tests", func() {
127158
})
128159

129160
})
161+
162+
func isWindows(kubetestConfigFilePath string) bool {
163+
return strings.Contains(kubetestConfigFilePath, "windows")
164+
}

test/e2e/data/kubetest/repo-list.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
gcAuthenticatedRegistry: e2eprivate
2+
gcEtcdRegistry: k8sprow.azurecr.io/kubernetes-e2e-test-images
3+
privateRegistry: e2eteam
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
ginkgo.focus: \[Conformance\]|\[NodeConformance\]|\[sig-windows\]|\[sig-apps\].CronJob|\[sig-api-machinery\].ResourceQuota|\[sig-scheduling\].SchedulerPreemption
2+
ginkgo.skip: \[LinuxOnly\]|\[Serial\]|Guestbook.application.should.create.and.stop.a.working.application|device.plugin.for.Windows|Container.Lifecycle.Hook.when.create.a.pod.with.lifecycle.hook.should.execute(.*)http.hook.properly
3+
disable-log-dump: true
4+
ginkgo.progress: true
5+
ginkgo.slowSpecThreshold: 120.0
6+
ginkgo.flakeAttempts: 0
7+
ginkgo.trace: true
8+
ginkgo.v: true
9+
node-os-distro: windows

test/e2e/e2e_suite_test.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ var (
9292
// kubetestConfigFilePath is the path to the kubetest configuration file
9393
kubetestConfigFilePath string
9494

95+
// kubetestRepoListPath
96+
kubetestRepoListPath string
97+
9598
// useCIArtifacts specifies whether or not to use the latest build from the main branch of the Kubernetes repository
9699
useCIArtifacts bool
97100
)
@@ -238,7 +241,7 @@ func init() {
238241
flag.BoolVar(&skipCleanup, "e2e.skip-resource-cleanup", false, "if true, the resource cleanup after tests will be skipped")
239242
flag.BoolVar(&useExistingCluster, "e2e.use-existing-cluster", false, "if true, the test uses the current cluster instead of creating a new one (default discovery rules apply)")
240243
flag.StringVar(&kubetestConfigFilePath, "kubetest.config-file", "", "path to the kubetest configuration file")
241-
244+
flag.StringVar(&kubetestRepoListPath, "kubetest.repo-list-file", "", "path to the kubetest repo-list file")
242245
}
243246

244247
func TestE2E(t *testing.T) {

test/e2e/kubernetes/node/node.go

+87
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,17 @@ package node
2020

2121
import (
2222
"context"
23+
24+
"encoding/json"
2325
"fmt"
2426
"strings"
2527

28+
"k8s.io/apimachinery/pkg/api/equality"
29+
30+
"k8s.io/apimachinery/pkg/types"
31+
"k8s.io/apimachinery/pkg/util/strategicpatch"
32+
33+
corev1 "k8s.io/api/core/v1"
2634
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2735
"k8s.io/client-go/kubernetes"
2836
"sigs.k8s.io/cluster-api-provider-azure/test/e2e/kubernetes/windows"
@@ -54,3 +62,82 @@ func GetWindowsVersion(ctx context.Context, clientset *kubernetes.Clientset) (wi
5462
return windows.LTSC2019, nil
5563
}
5664
}
65+
66+
func TaintNode(clientset *kubernetes.Clientset, options v1.ListOptions, taint *corev1.Taint) error {
67+
result, err := clientset.CoreV1().Nodes().List(context.Background(), options)
68+
if err != nil {
69+
return err
70+
}
71+
72+
if len(result.Items) == 0 {
73+
return fmt.Errorf("No Nodes found.")
74+
}
75+
76+
for _, n := range result.Items {
77+
newNode, needsUpdate := addOrUpdateTaint(&n, taint)
78+
if !needsUpdate {
79+
continue
80+
}
81+
82+
err = PatchNodeTaints(clientset, newNode.Name, &n, newNode)
83+
if err != nil {
84+
return err
85+
}
86+
}
87+
88+
return nil
89+
}
90+
91+
// From https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/cloud-provider/node/helpers/taints.go#L116
92+
func PatchNodeTaints(clientset *kubernetes.Clientset, nodeName string, oldNode *corev1.Node, newNode *corev1.Node) error {
93+
oldData, err := json.Marshal(oldNode)
94+
if err != nil {
95+
return fmt.Errorf("failed to marshal old node %#v for node %q: %v", oldNode, nodeName, err)
96+
}
97+
98+
newTaints := newNode.Spec.Taints
99+
newNodeClone := oldNode.DeepCopy()
100+
newNodeClone.Spec.Taints = newTaints
101+
newData, err := json.Marshal(newNodeClone)
102+
if err != nil {
103+
return fmt.Errorf("failed to marshal new node %#v for node %q: %v", newNodeClone, nodeName, err)
104+
}
105+
106+
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, corev1.Node{})
107+
if err != nil {
108+
return fmt.Errorf("failed to create patch for node %q: %v", nodeName, err)
109+
}
110+
111+
_, err = clientset.CoreV1().Nodes().Patch(context.Background(), nodeName, types.StrategicMergePatchType, patchBytes, v1.PatchOptions{})
112+
return err
113+
}
114+
115+
// From https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/cloud-provider/node/helpers/taints.go#L116
116+
// addOrUpdateTaint tries to add a taint to annotations list. Returns a new copy of updated Node and true if something was updated
117+
// false otherwise.
118+
func addOrUpdateTaint(node *corev1.Node, taint *corev1.Taint) (*corev1.Node, bool) {
119+
newNode := node.DeepCopy()
120+
nodeTaints := newNode.Spec.Taints
121+
122+
var newTaints []corev1.Taint
123+
updated := false
124+
for i := range nodeTaints {
125+
if taint.MatchTaint(&nodeTaints[i]) {
126+
if equality.Semantic.DeepEqual(*taint, nodeTaints[i]) {
127+
return newNode, false
128+
}
129+
newTaints = append(newTaints, *taint)
130+
updated = true
131+
continue
132+
}
133+
134+
newTaints = append(newTaints, nodeTaints[i])
135+
}
136+
137+
if !updated {
138+
newTaints = append(newTaints, *taint)
139+
}
140+
141+
newNode.Spec.Taints = newTaints
142+
return newNode, true
143+
}

0 commit comments

Comments
 (0)