Skip to content

Commit 70b92d0

Browse files
committed
Implement changes in the structure of the OAuth Cleanup tests
1 parent 105b60a commit 70b92d0

File tree

1 file changed

+158
-92
lines changed

1 file changed

+158
-92
lines changed

components/odh-notebook-controller/controllers/notebook_controller_test.go

Lines changed: 158 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import (
3333
corev1 "k8s.io/api/core/v1"
3434
netv1 "k8s.io/api/networking/v1"
3535
rbacv1 "k8s.io/api/rbac/v1"
36-
apierr "k8s.io/apimachinery/pkg/api/errors"
3736
"k8s.io/apimachinery/pkg/api/resource"
3837
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3938
"k8s.io/apimachinery/pkg/types"
@@ -1020,114 +1019,112 @@ var _ = Describe("The Openshift Notebook controller", func() {
10201019
)
10211020

10221021
var (
1023-
notebook *nbv1.Notebook
1024-
oauthClient *oauthv1.OAuthClient
1025-
oauthClientName string
1026-
ctx context.Context
1022+
notebook *nbv1.Notebook
1023+
ctx context.Context
1024+
notebookKey types.NamespacedName
10271025
)
10281026

1029-
// Setup for all tests in this context
1030-
BeforeEach(func() {
1031-
ctx = context.Background()
1032-
notebook = createNotebook(Name, Namespace)
1033-
oauthClientName = fmt.Sprintf("%s-%s-%s", Name, Namespace, "oauth-client")
1034-
1035-
oauthClient = &oauthv1.OAuthClient{
1036-
ObjectMeta: metav1.ObjectMeta{
1037-
Name: oauthClientName,
1038-
},
1039-
RedirectURIs: []string{"https://www.kubeflow.org/"},
1040-
GrantMethod: oauthv1.GrantHandlerAuto,
1041-
}
1042-
})
1043-
1044-
// Clean up resources after each test
1045-
AfterEach(func() {
1046-
nbToCleanup := &nbv1.Notebook{}
1047-
err := cli.Get(ctx, types.NamespacedName{Name: Name, Namespace: Namespace}, nbToCleanup)
1048-
if err == nil {
1049-
// Remove finalizers to allow deletion
1050-
nbToCleanup.Finalizers = nil
1051-
_ = cli.Update(ctx, nbToCleanup)
1052-
_ = cli.Delete(ctx, nbToCleanup)
1053-
}
1027+
// Initialize context and keys once
1028+
ctx = context.Background()
1029+
notebookKey = types.NamespacedName{Name: Name, Namespace: Namespace}
10541030

1055-
oauthClientToCleanup := &oauthv1.OAuthClient{}
1056-
err = cli.Get(ctx, types.NamespacedName{Name: oauthClientName}, oauthClientToCleanup)
1057-
if err == nil {
1058-
_ = cli.Delete(ctx, oauthClientToCleanup)
1059-
}
1060-
})
1031+
// Create notebook once for all tests - WITH OAUTH ENABLED
1032+
notebook = createNotebookWithOAuth(Name, Namespace)
10611033

1062-
It("Should add a finalizer when a Notebook has been created", func() {
1034+
It("Should create a Notebook CR successfully", func() {
10631035
By("Creating a new Notebook")
10641036
Expect(cli.Create(ctx, notebook)).Should(Succeed())
10651037

1066-
By("Creating an OAuthClient")
1067-
err := cli.Create(ctx, oauthClient)
1068-
if err != nil {
1069-
GinkgoT().Logf("OAuthClient creation result: %v", err)
1070-
}
1071-
1072-
By("Adding the finalizer to the Notebook")
1073-
key := types.NamespacedName{Name: Name, Namespace: Namespace}
1074-
Expect(cli.Get(ctx, key, notebook)).Should(Succeed())
1075-
1076-
notebook.Finalizers = append(notebook.Finalizers, Finalizer)
1077-
Expect(cli.Update(ctx, notebook)).Should(Succeed())
1038+
// Wait for the Reconciler to create the route, or else the OAuth reconciler
1039+
// cannot create the OAuthClient object
1040+
time.Sleep(3 * time.Second)
1041+
})
10781042

1079-
By("Verifying the finalizer is added to the Notebook")
1080-
Expect(cli.Get(ctx, key, notebook)).Should(Succeed())
1081-
Expect(notebook.Finalizers).To(ContainElement(Finalizer))
1043+
It("Should have finalizer set by controller or webhook", func() {
1044+
By("Waiting for controller to add the finalizer")
1045+
Eventually(func() []string {
1046+
var updated nbv1.Notebook
1047+
err := cli.Get(ctx, notebookKey, &updated)
1048+
if err != nil {
1049+
return nil
1050+
}
1051+
return updated.Finalizers
1052+
}, duration, interval).Should(ContainElement(Finalizer),
1053+
"Expected finalizer '%s' to be added by controller", Finalizer)
10821054
})
10831055

1084-
It("Should delete the OAuthClient when the Notebook is deleted", func() {
1085-
By("Creating a Notebook with finalizer")
1086-
notebook.Finalizers = []string{Finalizer}
1087-
Expect(cli.Create(ctx, notebook)).Should(Succeed())
1056+
It("Should create OAuthClient for the Notebook CR", func() {
1057+
By("Ensuring finalizer exists first")
1058+
Eventually(func() []string {
1059+
var updated nbv1.Notebook
1060+
cli.Get(ctx, notebookKey, &updated)
1061+
return updated.Finalizers
1062+
}, duration, interval).Should(ContainElement(Finalizer))
1063+
1064+
By("Waiting for controller to create OAuthClient")
1065+
oauthClientKey := types.NamespacedName{Name: Name + "-" + Namespace + "-oauth-client"}
1066+
createdOAuthClient := &oauthv1.OAuthClient{}
1067+
Eventually(func() error {
1068+
return cli.Get(ctx, oauthClientKey, createdOAuthClient)
1069+
}, time.Second*90, time.Second*3).Should(Succeed(),
1070+
"Expected OAuthClient to be created by odh-notebook-controller")
1071+
1072+
By("Verifying OAuthClient has proper labels")
1073+
Expect(createdOAuthClient.Labels).Should(
1074+
HaveKeyWithValue("notebook-owner", Name),
1075+
"OAuthClient should have labels linking it to the notebook")
1076+
})
10881077

1089-
By("Creating an OAuthClient")
1090-
Expect(cli.Create(ctx, oauthClient)).Should(Succeed())
1078+
It("Should delete OAuthClient when Notebook is deleted", func() {
1079+
By("Ensuring prerequisite resources exist")
1080+
Eventually(func() []string {
1081+
var updated nbv1.Notebook
1082+
cli.Get(ctx, notebookKey, &updated)
1083+
return updated.Finalizers
1084+
}, duration, interval).Should(ContainElement(Finalizer))
10911085

1092-
By("Requesting deletion of the Notebook")
1093-
Expect(cli.Delete(ctx, notebook)).Should(Succeed())
1086+
oauthClientKey := types.NamespacedName{Name: Name + "-" + Namespace + "-oauth-client"}
1087+
Eventually(func() error {
1088+
return cli.Get(ctx, oauthClientKey, &oauthv1.OAuthClient{})
1089+
}, duration, interval).Should(Succeed())
10941090

1095-
By("Checking if the controller deletes the OAuthClient")
1096-
oauthClientKey := types.NamespacedName{Name: oauthClientName}
1091+
By("Deleting the notebook")
1092+
var notebookToDelete nbv1.Notebook
1093+
Expect(cli.Get(ctx, notebookKey, &notebookToDelete)).Should(Succeed())
1094+
Expect(cli.Delete(ctx, &notebookToDelete)).Should(Succeed())
10971095

1098-
// First verify that the OAuthClient exists initially
1099-
Expect(cli.Get(ctx, oauthClientKey, oauthClient)).Should(Succeed())
1096+
By("Verifying notebook is marked for deletion but persists due to finalizer")
1097+
Eventually(func() bool {
1098+
var updated nbv1.Notebook
1099+
err := cli.Get(ctx, notebookKey, &updated)
1100+
return err == nil && updated.DeletionTimestamp != nil
1101+
}, duration, interval).Should(BeTrue(),
1102+
"Notebook should be marked for deletion but persist due to finalizer")
11001103

1101-
// Log the notebook state for debugging
1102-
key := types.NamespacedName{Name: notebook.Name, Namespace: notebook.Namespace}
1103-
nb := &nbv1.Notebook{}
1104-
if err := cli.Get(ctx, key, nb); err == nil {
1105-
hasTimestamp := "no"
1106-
if nb.DeletionTimestamp != nil {
1107-
hasTimestamp = "yes"
1108-
}
1109-
GinkgoT().Logf("Notebook state: exists=true, has deletion timestamp=%s, finalizers=%v",
1110-
hasTimestamp, nb.Finalizers)
1111-
} else {
1112-
GinkgoT().Logf("Notebook state: exists=false, error=%v", err)
1113-
}
1104+
By("Verifying controller deletes the OAuthClient")
1105+
Eventually(func() error {
1106+
var oauth oauthv1.OAuthClient
1107+
return cli.Get(ctx, oauthClientKey, &oauth)
1108+
}, time.Second*90, time.Second*3).Should(
1109+
MatchError(ContainSubstring("not found")),
1110+
"Expected OAuthClient to be deleted by odh-notebook-controller during finalizer processing")
1111+
})
11141112

1115-
// Wait for the controller to potentially delete the OAuthClient
1116-
// without any manual intervention
1117-
time.Sleep(5 * time.Second)
1118-
1119-
// Check and log the final state
1120-
client := &oauthv1.OAuthClient{}
1121-
err := cli.Get(ctx, oauthClientKey, client)
1122-
if apierr.IsNotFound(err) {
1123-
GinkgoT().Logf("OAuthClient was successfully deleted by the controller")
1124-
} else if err != nil {
1125-
GinkgoT().Logf("Error checking OAuthClient: %v", err)
1126-
} else {
1127-
GinkgoT().Logf("OAuthClient still exists, which suggests the controller isn't handling deletion in the test environment")
1128-
}
1113+
It("Should remove finalizer and delete Notebook after OAuthClient cleanup", func() {
1114+
By("Verifying OAuthClient was deleted in previous test")
1115+
oauthClientKey := types.NamespacedName{Name: Name + "-" + Namespace + "-oauth-client"}
1116+
Eventually(func() error {
1117+
var oauth oauthv1.OAuthClient
1118+
return cli.Get(ctx, oauthClientKey, &oauth)
1119+
}, time.Second*30).Should(MatchError(ContainSubstring("not found")))
11291120

1130-
// No assertion here - we're just observing behavior
1121+
By("Verifying finalizer is removed and notebook is deleted")
1122+
Eventually(func() error {
1123+
var updated nbv1.Notebook
1124+
return cli.Get(ctx, notebookKey, &updated)
1125+
}, time.Second*60, time.Second*2).Should(
1126+
MatchError(ContainSubstring("not found")),
1127+
"Expected Notebook to be fully deleted after finalizer removal")
11311128
})
11321129
})
11331130

@@ -1218,6 +1215,75 @@ func createNotebook(name, namespace string) *nbv1.Notebook {
12181215
}
12191216
}
12201217

1218+
func createNotebookWithOAuth(name, namespace string) *nbv1.Notebook {
1219+
return &nbv1.Notebook{
1220+
ObjectMeta: metav1.ObjectMeta{
1221+
Name: name,
1222+
Namespace: namespace,
1223+
Labels: map[string]string{
1224+
"app.kubernetes.io/instance": name,
1225+
},
1226+
Annotations: map[string]string{
1227+
"notebooks.opendatahub.io/inject-oauth": "true",
1228+
"notebooks.opendatahub.io/foo": "bar",
1229+
"notebooks.opendatahub.io/oauth-logout-url": "https://example.notebook-url/notebook/" + namespace + "/" + name,
1230+
"kubeflow-resource-stopped": "odh-notebook-controller-lock",
1231+
},
1232+
},
1233+
Spec: nbv1.NotebookSpec{
1234+
Template: nbv1.NotebookTemplateSpec{
1235+
Spec: corev1.PodSpec{
1236+
ServiceAccountName: name,
1237+
Containers: []corev1.Container{
1238+
{
1239+
Name: name,
1240+
Image: "registry.redhat.io/ubi8/ubi:latest",
1241+
},
1242+
createOAuthContainer(name, namespace),
1243+
},
1244+
Volumes: []corev1.Volume{
1245+
{
1246+
Name: "notebook-data",
1247+
VolumeSource: corev1.VolumeSource{
1248+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1249+
ClaimName: name + "-data",
1250+
},
1251+
},
1252+
},
1253+
{
1254+
Name: "oauth-config",
1255+
VolumeSource: corev1.VolumeSource{
1256+
Secret: &corev1.SecretVolumeSource{
1257+
SecretName: name + "-oauth-config",
1258+
DefaultMode: ptr.To[int32](420),
1259+
},
1260+
},
1261+
},
1262+
{
1263+
Name: "oauth-client",
1264+
VolumeSource: corev1.VolumeSource{
1265+
Secret: &corev1.SecretVolumeSource{
1266+
SecretName: name + "-oauth-client",
1267+
DefaultMode: ptr.To[int32](420),
1268+
},
1269+
},
1270+
},
1271+
{
1272+
Name: "tls-certificates",
1273+
VolumeSource: corev1.VolumeSource{
1274+
Secret: &corev1.SecretVolumeSource{
1275+
SecretName: name + "-tls",
1276+
DefaultMode: ptr.To[int32](420),
1277+
},
1278+
},
1279+
},
1280+
},
1281+
},
1282+
},
1283+
},
1284+
}
1285+
}
1286+
12211287
func getRouteFromList(route *routev1.Route, notebook *nbv1.Notebook, name, namespace string) (*routev1.Route, error) {
12221288
routeList := &routev1.RouteList{}
12231289
opts := []client.ListOption{

0 commit comments

Comments
 (0)