Skip to content

Commit dee91ba

Browse files
authored
Fix: e2e test failes due to webhook not ready (kubeflow#2149)
Signed-off-by: Yi Chen <github@chenyicn.net>
1 parent 592b649 commit dee91ba

File tree

2 files changed

+113
-45
lines changed

2 files changed

+113
-45
lines changed

test/e2e/sparkapplication_test.go

+4-43
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,19 @@ import (
2121
"os"
2222
"path/filepath"
2323
"strings"
24-
"time"
2524

2625
. "github.com/onsi/ginkgo/v2"
2726
. "github.com/onsi/gomega"
2827

2928
corev1 "k8s.io/api/core/v1"
3029
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3130
"k8s.io/apimachinery/pkg/types"
32-
"k8s.io/apimachinery/pkg/util/wait"
3331
"k8s.io/apimachinery/pkg/util/yaml"
3432

3533
"github.com/kubeflow/spark-operator/api/v1beta2"
3634
"github.com/kubeflow/spark-operator/pkg/util"
3735
)
3836

39-
const (
40-
PollInterval = 1 * time.Second
41-
WaitTimeout = 300 * time.Second
42-
)
43-
4437
var _ = Describe("Example SparkApplication", func() {
4538
Context("spark-pi", func() {
4639
ctx := context.Background()
@@ -72,15 +65,7 @@ var _ = Describe("Example SparkApplication", func() {
7265
It("should complete successfully", func() {
7366
By("Waiting for SparkApplication to complete")
7467
key := types.NamespacedName{Namespace: app.Namespace, Name: app.Name}
75-
cancelCtx, cancelFunc := context.WithTimeout(ctx, WaitTimeout)
76-
defer cancelFunc()
77-
Expect(wait.PollUntilContextCancel(cancelCtx, PollInterval, true, func(ctx context.Context) (done bool, err error) {
78-
err = k8sClient.Get(ctx, key, app)
79-
if app.Status.AppState.State == v1beta2.ApplicationStateCompleted {
80-
return true, nil
81-
}
82-
return false, err
83-
})).NotTo(HaveOccurred())
68+
Expect(waitForSparkApplicationCompleted(ctx, key)).NotTo(HaveOccurred())
8469

8570
By("Checking out driver logs")
8671
driverPodName := util.GetDriverPodName(app)
@@ -148,15 +133,7 @@ var _ = Describe("Example SparkApplication", func() {
148133
It("Should complete successfully", func() {
149134
By("Waiting for SparkApplication to complete")
150135
key := types.NamespacedName{Namespace: app.Namespace, Name: app.Name}
151-
cancelCtx, cancelFunc := context.WithTimeout(ctx, WaitTimeout)
152-
defer cancelFunc()
153-
Expect(wait.PollUntilContextCancel(cancelCtx, PollInterval, true, func(ctx context.Context) (done bool, err error) {
154-
err = k8sClient.Get(ctx, key, app)
155-
if app.Status.AppState.State == v1beta2.ApplicationStateCompleted {
156-
return true, nil
157-
}
158-
return false, err
159-
})).NotTo(HaveOccurred())
136+
Expect(waitForSparkApplicationCompleted(ctx, key)).NotTo(HaveOccurred())
160137

161138
By("Checking out driver logs")
162139
driverPodName := util.GetDriverPodName(app)
@@ -197,15 +174,7 @@ var _ = Describe("Example SparkApplication", func() {
197174
It("Should complete successfully", func() {
198175
By("Waiting for SparkApplication to complete")
199176
key := types.NamespacedName{Namespace: app.Namespace, Name: app.Name}
200-
cancelCtx, cancelFunc := context.WithTimeout(ctx, WaitTimeout)
201-
defer cancelFunc()
202-
Expect(wait.PollUntilContextCancel(cancelCtx, PollInterval, true, func(ctx context.Context) (done bool, err error) {
203-
err = k8sClient.Get(ctx, key, app)
204-
if app.Status.AppState.State == v1beta2.ApplicationStateCompleted {
205-
return true, nil
206-
}
207-
return false, err
208-
})).NotTo(HaveOccurred())
177+
Expect(waitForSparkApplicationCompleted(ctx, key)).NotTo(HaveOccurred())
209178

210179
By("Checking out driver logs")
211180
driverPodName := util.GetDriverPodName(app)
@@ -246,15 +215,7 @@ var _ = Describe("Example SparkApplication", func() {
246215
It("Should complete successfully", func() {
247216
By("Waiting for SparkApplication to complete")
248217
key := types.NamespacedName{Namespace: app.Namespace, Name: app.Name}
249-
cancelCtx, cancelFunc := context.WithTimeout(ctx, WaitTimeout)
250-
defer cancelFunc()
251-
Expect(wait.PollUntilContextCancel(cancelCtx, PollInterval, true, func(ctx context.Context) (done bool, err error) {
252-
err = k8sClient.Get(ctx, key, app)
253-
if app.Status.AppState.State == v1beta2.ApplicationStateCompleted {
254-
return true, nil
255-
}
256-
return false, err
257-
})).NotTo(HaveOccurred())
218+
Expect(waitForSparkApplicationCompleted(ctx, key)).NotTo(HaveOccurred())
258219

259220
By("Checking out driver logs")
260221
driverPodName := util.GetDriverPodName(app)

test/e2e/suit_test.go

+109-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ import (
3131
"helm.sh/helm/v3/pkg/chart/loader"
3232
"helm.sh/helm/v3/pkg/chartutil"
3333
"helm.sh/helm/v3/pkg/cli"
34+
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
3435
corev1 "k8s.io/api/core/v1"
3536
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37+
"k8s.io/apimachinery/pkg/types"
38+
"k8s.io/apimachinery/pkg/util/wait"
3639
"k8s.io/client-go/kubernetes"
3740
"k8s.io/client-go/kubernetes/scheme"
3841
"k8s.io/client-go/rest"
@@ -53,6 +56,12 @@ import (
5356
const (
5457
ReleaseName = "spark-operator"
5558
ReleaseNamespace = "spark-operator"
59+
60+
MutatingWebhookName = "spark-operator-webhook"
61+
ValidatingWebhookName = "spark-operator-webhook"
62+
63+
PollInterval = 1 * time.Second
64+
WaitTimeout = 5 * time.Minute
5665
)
5766

5867
var (
@@ -123,7 +132,7 @@ var _ = BeforeSuite(func() {
123132
installAction.ReleaseName = ReleaseName
124133
installAction.Namespace = envSettings.Namespace()
125134
installAction.Wait = true
126-
installAction.Timeout = 5 * time.Minute
135+
installAction.Timeout = WaitTimeout
127136
chartPath := filepath.Join("..", "..", "charts", "spark-operator-chart")
128137
chart, err := loader.Load(chartPath)
129138
Expect(err).NotTo(HaveOccurred())
@@ -134,6 +143,12 @@ var _ = BeforeSuite(func() {
134143
release, err := installAction.Run(chart, values)
135144
Expect(err).NotTo(HaveOccurred())
136145
Expect(release).NotTo(BeNil())
146+
147+
By("Waiting for the webhooks to be ready")
148+
mutatingWebhookKey := types.NamespacedName{Name: MutatingWebhookName}
149+
validatingWebhookKey := types.NamespacedName{Name: ValidatingWebhookName}
150+
Expect(waitForMutatingWebhookReady(context.Background(), mutatingWebhookKey)).NotTo(HaveOccurred())
151+
Expect(waitForValidatingWebhookReady(context.Background(), validatingWebhookKey)).NotTo(HaveOccurred())
137152
})
138153

139154
var _ = AfterSuite(func() {
@@ -147,7 +162,7 @@ var _ = AfterSuite(func() {
147162
uninstallAction := action.NewUninstall(actionConfig)
148163
Expect(uninstallAction).NotTo(BeNil())
149164
uninstallAction.Wait = true
150-
uninstallAction.Timeout = 5 * time.Minute
165+
uninstallAction.Timeout = WaitTimeout
151166
resp, err := uninstallAction.Run(ReleaseName)
152167
Expect(err).To(BeNil())
153168
Expect(resp).NotTo(BeNil())
@@ -160,3 +175,95 @@ var _ = AfterSuite(func() {
160175
err = testEnv.Stop()
161176
Expect(err).ToNot(HaveOccurred())
162177
})
178+
179+
func waitForMutatingWebhookReady(ctx context.Context, key types.NamespacedName) error {
180+
cancelCtx, cancelFunc := context.WithTimeout(ctx, WaitTimeout)
181+
defer cancelFunc()
182+
183+
mutatingWebhook := admissionregistrationv1.MutatingWebhookConfiguration{}
184+
err := wait.PollUntilContextCancel(cancelCtx, PollInterval, true, func(ctx context.Context) (bool, error) {
185+
if err := k8sClient.Get(ctx, key, &mutatingWebhook); err != nil {
186+
return false, err
187+
}
188+
189+
for _, wh := range mutatingWebhook.Webhooks {
190+
// Checkout webhook CA certificate
191+
if wh.ClientConfig.CABundle == nil {
192+
return false, nil
193+
}
194+
195+
// Checkout webhook service endpoints
196+
svcRef := wh.ClientConfig.Service
197+
if svcRef == nil {
198+
return false, fmt.Errorf("webhook service is nil")
199+
}
200+
endpoints := corev1.Endpoints{}
201+
endpointsKey := types.NamespacedName{Namespace: svcRef.Namespace, Name: svcRef.Name}
202+
if err := k8sClient.Get(ctx, endpointsKey, &endpoints); err != nil {
203+
return false, err
204+
}
205+
if len(endpoints.Subsets) == 0 {
206+
return false, nil
207+
}
208+
}
209+
210+
return true, nil
211+
})
212+
return err
213+
}
214+
215+
func waitForValidatingWebhookReady(ctx context.Context, key types.NamespacedName) error {
216+
cancelCtx, cancelFunc := context.WithTimeout(ctx, WaitTimeout)
217+
defer cancelFunc()
218+
219+
validatingWebhook := admissionregistrationv1.ValidatingWebhookConfiguration{}
220+
err := wait.PollUntilContextCancel(cancelCtx, PollInterval, true, func(ctx context.Context) (bool, error) {
221+
if err := k8sClient.Get(ctx, key, &validatingWebhook); err != nil {
222+
return false, err
223+
}
224+
225+
for _, wh := range validatingWebhook.Webhooks {
226+
// Checkout webhook CA certificate
227+
if wh.ClientConfig.CABundle == nil {
228+
return false, nil
229+
}
230+
231+
// Checkout webhook service endpoints
232+
svcRef := wh.ClientConfig.Service
233+
if svcRef == nil {
234+
return false, fmt.Errorf("webhook service is nil")
235+
}
236+
endpoints := corev1.Endpoints{}
237+
endpointsKey := types.NamespacedName{Namespace: svcRef.Namespace, Name: svcRef.Name}
238+
if err := k8sClient.Get(ctx, endpointsKey, &endpoints); err != nil {
239+
return false, err
240+
}
241+
if len(endpoints.Subsets) == 0 {
242+
return false, nil
243+
}
244+
}
245+
246+
return true, nil
247+
})
248+
return err
249+
}
250+
251+
func waitForSparkApplicationCompleted(ctx context.Context, key types.NamespacedName) error {
252+
cancelCtx, cancelFunc := context.WithTimeout(ctx, WaitTimeout)
253+
defer cancelFunc()
254+
255+
app := &v1beta2.SparkApplication{}
256+
err := wait.PollUntilContextCancel(cancelCtx, PollInterval, true, func(ctx context.Context) (bool, error) {
257+
if err := k8sClient.Get(ctx, key, app); err != nil {
258+
return false, err
259+
}
260+
switch app.Status.AppState.State {
261+
case v1beta2.ApplicationStateFailedSubmission, v1beta2.ApplicationStateFailed:
262+
return false, fmt.Errorf(app.Status.AppState.ErrorMessage)
263+
case v1beta2.ApplicationStateCompleted:
264+
return true, nil
265+
}
266+
return false, nil
267+
})
268+
return err
269+
}

0 commit comments

Comments
 (0)