@@ -33,7 +33,6 @@ import (
33
33
corev1 "k8s.io/api/core/v1"
34
34
netv1 "k8s.io/api/networking/v1"
35
35
rbacv1 "k8s.io/api/rbac/v1"
36
- apierr "k8s.io/apimachinery/pkg/api/errors"
37
36
"k8s.io/apimachinery/pkg/api/resource"
38
37
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
39
38
"k8s.io/apimachinery/pkg/types"
@@ -1020,114 +1019,112 @@ var _ = Describe("The Openshift Notebook controller", func() {
1020
1019
)
1021
1020
1022
1021
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
1027
1025
)
1028
1026
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 }
1054
1030
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 )
1061
1033
1062
- It ("Should add a finalizer when a Notebook has been created " , func () {
1034
+ It ("Should create a Notebook CR successfully " , func () {
1063
1035
By ("Creating a new Notebook" )
1064
1036
Expect (cli .Create (ctx , notebook )).Should (Succeed ())
1065
1037
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
+ })
1078
1042
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 )
1082
1054
})
1083
1055
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
+ })
1088
1077
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 ))
1091
1085
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 ())
1094
1090
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 ())
1097
1095
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" )
1100
1103
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
+ })
1114
1112
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" )))
1129
1120
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" )
1131
1128
})
1132
1129
})
1133
1130
@@ -1218,6 +1215,75 @@ func createNotebook(name, namespace string) *nbv1.Notebook {
1218
1215
}
1219
1216
}
1220
1217
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
+
1221
1287
func getRouteFromList (route * routev1.Route , notebook * nbv1.Notebook , name , namespace string ) (* routev1.Route , error ) {
1222
1288
routeList := & routev1.RouteList {}
1223
1289
opts := []client.ListOption {
0 commit comments