@@ -987,6 +987,303 @@ var _ = Describe("Workspaces Handler", func() {
987987 Expect (createdWorkspace .Spec .PodTemplate .Volumes .Secrets ).To (Equal (expected ))
988988 })
989989
990+ It ("should update a Workspace successfully" , func () {
991+
992+ By ("creating a Workspace via the API" )
993+ workspaceCreate := & models.WorkspaceCreate {
994+ Name : workspaceName ,
995+ Kind : workspaceKindName ,
996+ Paused : false ,
997+ PodTemplate : models.PodTemplateMutate {
998+ PodMetadata : models.PodMetadataMutate {
999+ Labels : map [string ]string {
1000+ "app" : "testing" ,
1001+ },
1002+ Annotations : map [string ]string {
1003+ "example.com/testing" : "true" ,
1004+ },
1005+ },
1006+ Volumes : models.PodVolumesMutate {
1007+ Home : ptr .To ("my-home-pvc" ),
1008+ Data : []models.PodVolumeMount {
1009+ {
1010+ PVCName : "my-data-pvc" ,
1011+ MountPath : "/data/1" ,
1012+ ReadOnly : false ,
1013+ },
1014+ },
1015+ },
1016+ Options : models.PodTemplateOptionsMutate {
1017+ ImageConfig : "jupyterlab_scipy_180" ,
1018+ PodConfig : "tiny_cpu" ,
1019+ },
1020+ },
1021+ }
1022+ createEnvelope := WorkspaceCreateEnvelope {Data : workspaceCreate }
1023+ createJSON , err := json .Marshal (createEnvelope )
1024+ Expect (err ).NotTo (HaveOccurred ())
1025+
1026+ path := strings .Replace (WorkspacesByNamespacePath , ":" + NamespacePathParam , namespaceNameCrud , 1 )
1027+ req , err := http .NewRequest (http .MethodPost , path , strings .NewReader (string (createJSON )))
1028+ Expect (err ).NotTo (HaveOccurred ())
1029+ req .Header .Set ("Content-Type" , MediaTypeJson )
1030+ req .Header .Set (userIdHeader , adminUser )
1031+
1032+ rr := httptest .NewRecorder ()
1033+ ps := httprouter.Params {
1034+ httprouter.Param {Key : NamespacePathParam , Value : namespaceNameCrud },
1035+ }
1036+ a .CreateWorkspaceHandler (rr , req , ps )
1037+ rs := rr .Result ()
1038+ defer rs .Body .Close ()
1039+ Expect (rs .StatusCode ).To (Equal (http .StatusCreated ), descUnexpectedHTTPStatus , rr .Body .String ())
1040+
1041+ By ("getting the Workspace from the Kubernetes API to obtain its current revision" )
1042+ createdWorkspace := & kubefloworgv1beta1.Workspace {}
1043+ Expect (k8sClient .Get (ctx , workspaceKey , createdWorkspace )).To (Succeed ())
1044+ originalRevision := models .CalculateWorkspaceRevision (createdWorkspace )
1045+
1046+ By ("building a WorkspaceUpdate model with changed fields" )
1047+ workspaceUpdate := & models.WorkspaceUpdate {
1048+ Revision : originalRevision ,
1049+ Paused : true ,
1050+ PodTemplate : models.PodTemplateMutate {
1051+ PodMetadata : models.PodMetadataMutate {
1052+ Labels : map [string ]string {
1053+ "app" : "testing" ,
1054+ "version" : "v2" ,
1055+ },
1056+ Annotations : map [string ]string {
1057+ "example.com/testing" : "false" ,
1058+ },
1059+ },
1060+ Volumes : models.PodVolumesMutate {
1061+ Home : ptr .To ("my-home-pvc" ),
1062+ Data : []models.PodVolumeMount {
1063+ {
1064+ PVCName : "my-data-pvc" ,
1065+ MountPath : "/data/updated" ,
1066+ ReadOnly : true ,
1067+ },
1068+ },
1069+ },
1070+ Options : models.PodTemplateOptionsMutate {
1071+ ImageConfig : "jupyterlab_scipy_180" ,
1072+ PodConfig : "small_cpu" ,
1073+ },
1074+ },
1075+ }
1076+ updateEnvelope := WorkspaceEnvelope {Data : workspaceUpdate }
1077+ updateJSON , err := json .Marshal (updateEnvelope )
1078+ Expect (err ).NotTo (HaveOccurred ())
1079+
1080+ By ("executing UpdateWorkspaceHandler" )
1081+ path = strings .Replace (WorkspacesByNamePath , ":" + NamespacePathParam , namespaceNameCrud , 1 )
1082+ path = strings .Replace (path , ":" + ResourceNamePathParam , workspaceName , 1 )
1083+ req , err = http .NewRequest (http .MethodPut , path , strings .NewReader (string (updateJSON )))
1084+ Expect (err ).NotTo (HaveOccurred ())
1085+ req .Header .Set ("Content-Type" , MediaTypeJson )
1086+ req .Header .Set (userIdHeader , adminUser )
1087+
1088+ rr = httptest .NewRecorder ()
1089+ ps = httprouter.Params {
1090+ httprouter.Param {Key : NamespacePathParam , Value : namespaceNameCrud },
1091+ httprouter.Param {Key : ResourceNamePathParam , Value : workspaceName },
1092+ }
1093+ a .UpdateWorkspaceHandler (rr , req , ps )
1094+ rs = rr .Result ()
1095+ defer rs .Body .Close ()
1096+
1097+ By ("verifying the HTTP response status code" )
1098+ Expect (rs .StatusCode ).To (Equal (http .StatusOK ), descUnexpectedHTTPStatus , rr .Body .String ())
1099+
1100+ By ("reading the HTTP response body" )
1101+ body , err := io .ReadAll (rs .Body )
1102+ Expect (err ).NotTo (HaveOccurred ())
1103+
1104+ By ("unmarshalling the response JSON to WorkspaceEnvelope" )
1105+ var response WorkspaceEnvelope
1106+ err = json .Unmarshal (body , & response )
1107+ Expect (err ).NotTo (HaveOccurred ())
1108+
1109+ By ("verifying the response contains a new revision" )
1110+ Expect (response .Data .Revision ).NotTo (Equal (originalRevision ))
1111+
1112+ By ("getting the updated Workspace from the Kubernetes API" )
1113+ updatedWorkspace := & kubefloworgv1beta1.Workspace {}
1114+ Expect (k8sClient .Get (ctx , workspaceKey , updatedWorkspace )).To (Succeed ())
1115+
1116+ By ("verifying all fields were applied" )
1117+ Expect (ptr .Deref (updatedWorkspace .Spec .Paused , false )).To (BeTrue ())
1118+ Expect (updatedWorkspace .Spec .PodTemplate .PodMetadata .Labels ).To (Equal (workspaceUpdate .PodTemplate .PodMetadata .Labels ))
1119+ Expect (updatedWorkspace .Spec .PodTemplate .PodMetadata .Annotations ).To (Equal (workspaceUpdate .PodTemplate .PodMetadata .Annotations ))
1120+ Expect (updatedWorkspace .Spec .PodTemplate .Options .PodConfig ).To (Equal ("small_cpu" ))
1121+ Expect (updatedWorkspace .Spec .PodTemplate .Volumes .Data ).To (Equal ([]kubefloworgv1beta1.PodVolumeMount {
1122+ {
1123+ PVCName : "my-data-pvc" ,
1124+ MountPath : "/data/updated" ,
1125+ ReadOnly : ptr .To (true ),
1126+ },
1127+ }))
1128+
1129+ By ("cleaning up the Workspace" )
1130+ Expect (k8sClient .Delete (ctx , updatedWorkspace )).To (Succeed ())
1131+ })
1132+
1133+ It ("should return 409 for a stale revision" , func () {
1134+
1135+ By ("creating a Workspace via the API" )
1136+ workspaceCreate := & models.WorkspaceCreate {
1137+ Name : workspaceName ,
1138+ Kind : workspaceKindName ,
1139+ Paused : false ,
1140+ PodTemplate : models.PodTemplateMutate {
1141+ PodMetadata : models.PodMetadataMutate {
1142+ Labels : map [string ]string {},
1143+ Annotations : map [string ]string {},
1144+ },
1145+ Volumes : models.PodVolumesMutate {
1146+ Home : ptr .To ("my-home-pvc" ),
1147+ Data : []models.PodVolumeMount {
1148+ {
1149+ PVCName : "my-data-pvc" ,
1150+ MountPath : "/data/1" ,
1151+ ReadOnly : false ,
1152+ },
1153+ },
1154+ },
1155+ Options : models.PodTemplateOptionsMutate {
1156+ ImageConfig : "jupyterlab_scipy_180" ,
1157+ PodConfig : "tiny_cpu" ,
1158+ },
1159+ },
1160+ }
1161+ createEnvelope := WorkspaceCreateEnvelope {Data : workspaceCreate }
1162+ createJSON , err := json .Marshal (createEnvelope )
1163+ Expect (err ).NotTo (HaveOccurred ())
1164+
1165+ path := strings .Replace (WorkspacesByNamespacePath , ":" + NamespacePathParam , namespaceNameCrud , 1 )
1166+ req , err := http .NewRequest (http .MethodPost , path , strings .NewReader (string (createJSON )))
1167+ Expect (err ).NotTo (HaveOccurred ())
1168+ req .Header .Set ("Content-Type" , MediaTypeJson )
1169+ req .Header .Set (userIdHeader , adminUser )
1170+
1171+ rr := httptest .NewRecorder ()
1172+ ps := httprouter.Params {
1173+ httprouter.Param {Key : NamespacePathParam , Value : namespaceNameCrud },
1174+ }
1175+ a .CreateWorkspaceHandler (rr , req , ps )
1176+ rs := rr .Result ()
1177+ defer rs .Body .Close ()
1178+ Expect (rs .StatusCode ).To (Equal (http .StatusCreated ), descUnexpectedHTTPStatus , rr .Body .String ())
1179+
1180+ By ("getting the Workspace to obtain the current revision" )
1181+ workspace := & kubefloworgv1beta1.Workspace {}
1182+ Expect (k8sClient .Get (ctx , workspaceKey , workspace )).To (Succeed ())
1183+ staleRevision := models .CalculateWorkspaceRevision (workspace )
1184+
1185+ By ("modifying the Workspace directly to change its revision" )
1186+ workspace .Spec .PodTemplate .Options .PodConfig = "small_cpu"
1187+ Expect (k8sClient .Update (ctx , workspace )).To (Succeed ())
1188+
1189+ By ("attempting to update via the API with the stale revision" )
1190+ workspaceUpdate := & models.WorkspaceUpdate {
1191+ Revision : staleRevision ,
1192+ Paused : false ,
1193+ PodTemplate : models.PodTemplateMutate {
1194+ PodMetadata : models.PodMetadataMutate {
1195+ Labels : map [string ]string {},
1196+ Annotations : map [string ]string {},
1197+ },
1198+ Volumes : models.PodVolumesMutate {
1199+ Home : ptr .To ("my-home-pvc" ),
1200+ Data : []models.PodVolumeMount {
1201+ {
1202+ PVCName : "my-data-pvc" ,
1203+ MountPath : "/data/1" ,
1204+ ReadOnly : false ,
1205+ },
1206+ },
1207+ },
1208+ Options : models.PodTemplateOptionsMutate {
1209+ ImageConfig : "jupyterlab_scipy_180" ,
1210+ PodConfig : "tiny_cpu" ,
1211+ },
1212+ },
1213+ }
1214+ updateEnvelope := WorkspaceEnvelope {Data : workspaceUpdate }
1215+ updateJSON , err := json .Marshal (updateEnvelope )
1216+ Expect (err ).NotTo (HaveOccurred ())
1217+
1218+ path = strings .Replace (WorkspacesByNamePath , ":" + NamespacePathParam , namespaceNameCrud , 1 )
1219+ path = strings .Replace (path , ":" + ResourceNamePathParam , workspaceName , 1 )
1220+ req , err = http .NewRequest (http .MethodPut , path , strings .NewReader (string (updateJSON )))
1221+ Expect (err ).NotTo (HaveOccurred ())
1222+ req .Header .Set ("Content-Type" , MediaTypeJson )
1223+ req .Header .Set (userIdHeader , adminUser )
1224+
1225+ rr = httptest .NewRecorder ()
1226+ ps = httprouter.Params {
1227+ httprouter.Param {Key : NamespacePathParam , Value : namespaceNameCrud },
1228+ httprouter.Param {Key : ResourceNamePathParam , Value : workspaceName },
1229+ }
1230+ a .UpdateWorkspaceHandler (rr , req , ps )
1231+ rs = rr .Result ()
1232+ defer rs .Body .Close ()
1233+
1234+ By ("verifying the HTTP response status code is 409" )
1235+ Expect (rs .StatusCode ).To (Equal (http .StatusConflict ), descUnexpectedHTTPStatus , rr .Body .String ())
1236+
1237+ By ("cleaning up the Workspace" )
1238+ Expect (k8sClient .Delete (ctx , workspace )).To (Succeed ())
1239+ })
1240+
1241+ It ("should return 404 for a non-existent Workspace update" , func () {
1242+ missingWorkspaceName := "non-existent-workspace"
1243+
1244+ By ("building an update request for a workspace that doesn't exist" )
1245+ workspaceUpdate := & models.WorkspaceUpdate {
1246+ Revision : "fake-revision" ,
1247+ Paused : false ,
1248+ PodTemplate : models.PodTemplateMutate {
1249+ PodMetadata : models.PodMetadataMutate {
1250+ Labels : map [string ]string {},
1251+ Annotations : map [string ]string {},
1252+ },
1253+ Volumes : models.PodVolumesMutate {
1254+ Data : []models.PodVolumeMount {},
1255+ },
1256+ Options : models.PodTemplateOptionsMutate {
1257+ ImageConfig : "jupyterlab_scipy_180" ,
1258+ PodConfig : "tiny_cpu" ,
1259+ },
1260+ },
1261+ }
1262+ updateEnvelope := WorkspaceEnvelope {Data : workspaceUpdate }
1263+ updateJSON , err := json .Marshal (updateEnvelope )
1264+ Expect (err ).NotTo (HaveOccurred ())
1265+
1266+ By ("executing UpdateWorkspaceHandler" )
1267+ path := strings .Replace (WorkspacesByNamePath , ":" + NamespacePathParam , namespaceNameCrud , 1 )
1268+ path = strings .Replace (path , ":" + ResourceNamePathParam , missingWorkspaceName , 1 )
1269+ req , err := http .NewRequest (http .MethodPut , path , strings .NewReader (string (updateJSON )))
1270+ Expect (err ).NotTo (HaveOccurred ())
1271+ req .Header .Set ("Content-Type" , MediaTypeJson )
1272+ req .Header .Set (userIdHeader , adminUser )
1273+
1274+ rr := httptest .NewRecorder ()
1275+ ps := httprouter.Params {
1276+ httprouter.Param {Key : NamespacePathParam , Value : namespaceNameCrud },
1277+ httprouter.Param {Key : ResourceNamePathParam , Value : missingWorkspaceName },
1278+ }
1279+ a .UpdateWorkspaceHandler (rr , req , ps )
1280+ rs := rr .Result ()
1281+ defer rs .Body .Close ()
1282+
1283+ By ("verifying the HTTP response status code is 404" )
1284+ Expect (rs .StatusCode ).To (Equal (http .StatusNotFound ), descUnexpectedHTTPStatus , rr .Body .String ())
1285+ })
1286+
9901287 // TODO: test when fail to create a Workspace when:
9911288 // - body payload invalid (missing name/kind, and/or non RCF 1123 name)
9921289 // - invalid namespace HTTP path parameter (also test for other API handlers)
0 commit comments