|
4 | 4 | "encoding/json" |
5 | 5 | "fmt" |
6 | 6 | "net/http" |
| 7 | + "sync" |
7 | 8 | "testing" |
8 | 9 |
|
9 | 10 | . "github.com/onsi/gomega" |
@@ -1081,3 +1082,165 @@ func TestNodePoolForceDelete(t *testing.T) { |
1081 | 1082 | }) |
1082 | 1083 | } |
1083 | 1084 | } |
| 1085 | + |
| 1086 | +func TestNodePoolPatchSoftDeleted(t *testing.T) { |
| 1087 | + h, client := test.RegisterIntegration(t) |
| 1088 | + |
| 1089 | + account := h.NewRandAccount() |
| 1090 | + ctx := h.NewAuthenticatedContext(account) |
| 1091 | + |
| 1092 | + cluster, err := h.Factories.NewClusters(h.NewID()) |
| 1093 | + Expect(err).NotTo(HaveOccurred()) |
| 1094 | + |
| 1095 | + npResp, err := client.CreateNodePoolWithResponse( |
| 1096 | + ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(newNodePoolInput("patch-del-np")), test.WithAuthToken(ctx), |
| 1097 | + ) |
| 1098 | + Expect(err).NotTo(HaveOccurred()) |
| 1099 | + Expect(npResp.StatusCode()).To(Equal(http.StatusCreated)) |
| 1100 | + nodePoolID := *npResp.JSON201.Id |
| 1101 | + |
| 1102 | + delResp, err := client.DeleteNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx)) |
| 1103 | + Expect(err).NotTo(HaveOccurred()) |
| 1104 | + Expect(delResp.StatusCode()).To(Equal(http.StatusAccepted)) |
| 1105 | + |
| 1106 | + patchBody := openapi.PatchNodePoolByIdJSONRequestBody{ |
| 1107 | + Spec: &openapi.NodePoolSpec{"should": "fail"}, |
| 1108 | + } |
| 1109 | + patchResp, err := client.PatchNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, patchBody, test.WithAuthToken(ctx)) |
| 1110 | + Expect(err).NotTo(HaveOccurred()) |
| 1111 | + Expect(patchResp.StatusCode()).To(Equal(http.StatusConflict), |
| 1112 | + "PATCH on soft-deleted nodepool should return 409") |
| 1113 | +} |
| 1114 | + |
| 1115 | +func TestNodePoolConcurrentDelete(t *testing.T) { |
| 1116 | + h, client := test.RegisterIntegration(t) |
| 1117 | + |
| 1118 | + account := h.NewRandAccount() |
| 1119 | + ctx := h.NewAuthenticatedContext(account) |
| 1120 | + |
| 1121 | + cluster, err := h.Factories.NewClusters(h.NewID()) |
| 1122 | + Expect(err).NotTo(HaveOccurred()) |
| 1123 | + |
| 1124 | + input := newNodePoolInput("concurr-del-np") |
| 1125 | + npResp, err := client.CreateNodePoolWithResponse( |
| 1126 | + ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(input), test.WithAuthToken(ctx), |
| 1127 | + ) |
| 1128 | + Expect(err).NotTo(HaveOccurred()) |
| 1129 | + Expect(npResp.StatusCode()).To(Equal(http.StatusCreated)) |
| 1130 | + nodePoolID := *npResp.JSON201.Id |
| 1131 | + initialGeneration := npResp.JSON201.Generation |
| 1132 | + |
| 1133 | + const concurrency = 5 |
| 1134 | + type deleteResult struct { |
| 1135 | + resp *openapi.DeleteNodePoolByIdResponse |
| 1136 | + err error |
| 1137 | + } |
| 1138 | + results := make([]deleteResult, concurrency) |
| 1139 | + |
| 1140 | + var wg sync.WaitGroup |
| 1141 | + wg.Add(concurrency) |
| 1142 | + for i := range concurrency { |
| 1143 | + go func(idx int) { |
| 1144 | + defer wg.Done() |
| 1145 | + r, e := client.DeleteNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx)) |
| 1146 | + results[idx] = deleteResult{resp: r, err: e} |
| 1147 | + }(i) |
| 1148 | + } |
| 1149 | + wg.Wait() |
| 1150 | + |
| 1151 | + for i, r := range results { |
| 1152 | + Expect(r.err).NotTo(HaveOccurred(), "DELETE request %d should not return error", i) |
| 1153 | + Expect(r.resp.StatusCode()).To(Equal(http.StatusAccepted), "DELETE request %d should return 202", i) |
| 1154 | + Expect(r.resp.JSON202.DeletedTime).NotTo(BeNil(), "DELETE request %d should have deleted_time", i) |
| 1155 | + } |
| 1156 | + |
| 1157 | + referenceTime := *results[0].resp.JSON202.DeletedTime |
| 1158 | + referenceGen := results[0].resp.JSON202.Generation |
| 1159 | + for i := 1; i < concurrency; i++ { |
| 1160 | + Expect(*results[i].resp.JSON202.DeletedTime).To(Equal(referenceTime), |
| 1161 | + "all DELETE responses should carry identical deleted_time") |
| 1162 | + Expect(results[i].resp.JSON202.Generation).To(Equal(referenceGen), |
| 1163 | + "all DELETE responses should carry identical generation") |
| 1164 | + } |
| 1165 | + |
| 1166 | + Expect(referenceGen).To(Equal(initialGeneration+1), |
| 1167 | + "generation should increment by exactly 1, not by the number of concurrent requests") |
| 1168 | +} |
| 1169 | + |
| 1170 | +func TestNodePoolGetSoftDeleted(t *testing.T) { |
| 1171 | + h, client := test.RegisterIntegration(t) |
| 1172 | + |
| 1173 | + account := h.NewRandAccount() |
| 1174 | + ctx := h.NewAuthenticatedContext(account) |
| 1175 | + |
| 1176 | + cluster, err := h.Factories.NewClusters(h.NewID()) |
| 1177 | + Expect(err).NotTo(HaveOccurred()) |
| 1178 | + |
| 1179 | + npResp, err := client.CreateNodePoolWithResponse( |
| 1180 | + ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(newNodePoolInput("get-deleted-np")), test.WithAuthToken(ctx), |
| 1181 | + ) |
| 1182 | + Expect(err).NotTo(HaveOccurred()) |
| 1183 | + Expect(npResp.StatusCode()).To(Equal(http.StatusCreated)) |
| 1184 | + nodePoolID := *npResp.JSON201.Id |
| 1185 | + |
| 1186 | + delResp, err := client.DeleteNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx)) |
| 1187 | + Expect(err).NotTo(HaveOccurred()) |
| 1188 | + Expect(delResp.StatusCode()).To(Equal(http.StatusAccepted)) |
| 1189 | + |
| 1190 | + getResp, err := client.GetNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx)) |
| 1191 | + Expect(err).NotTo(HaveOccurred()) |
| 1192 | + Expect(getResp.StatusCode()).To(Equal(http.StatusOK), |
| 1193 | + "GET on soft-deleted nodepool should return 200, not 404") |
| 1194 | + Expect(getResp.JSON200.DeletedTime).NotTo(BeNil(), |
| 1195 | + "soft-deleted nodepool should have deleted_time in GET response") |
| 1196 | + Expect(*getResp.JSON200.Id).To(Equal(nodePoolID)) |
| 1197 | +} |
| 1198 | + |
| 1199 | +func TestNodePoolListIncludesSoftDeleted(t *testing.T) { |
| 1200 | + h, client := test.RegisterIntegration(t) |
| 1201 | + |
| 1202 | + account := h.NewRandAccount() |
| 1203 | + ctx := h.NewAuthenticatedContext(account) |
| 1204 | + |
| 1205 | + cluster, err := h.Factories.NewClusters(h.NewID()) |
| 1206 | + Expect(err).NotTo(HaveOccurred()) |
| 1207 | + |
| 1208 | + activeResp, err := client.CreateNodePoolWithResponse( |
| 1209 | + ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(newNodePoolInput("active-np")), test.WithAuthToken(ctx), |
| 1210 | + ) |
| 1211 | + Expect(err).NotTo(HaveOccurred()) |
| 1212 | + Expect(activeResp.StatusCode()).To(Equal(http.StatusCreated)) |
| 1213 | + activeID := *activeResp.JSON201.Id |
| 1214 | + |
| 1215 | + deletedResp, err := client.CreateNodePoolWithResponse( |
| 1216 | + ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(newNodePoolInput("deleted-np")), test.WithAuthToken(ctx), |
| 1217 | + ) |
| 1218 | + Expect(err).NotTo(HaveOccurred()) |
| 1219 | + Expect(deletedResp.StatusCode()).To(Equal(http.StatusCreated)) |
| 1220 | + deletedID := *deletedResp.JSON201.Id |
| 1221 | + |
| 1222 | + delResp, err := client.DeleteNodePoolByIdWithResponse(ctx, cluster.ID, deletedID, test.WithAuthToken(ctx)) |
| 1223 | + Expect(err).NotTo(HaveOccurred()) |
| 1224 | + Expect(delResp.StatusCode()).To(Equal(http.StatusAccepted)) |
| 1225 | + |
| 1226 | + listResp, err := client.GetNodePoolsByClusterIdWithResponse(ctx, cluster.ID, nil, test.WithAuthToken(ctx)) |
| 1227 | + Expect(err).NotTo(HaveOccurred()) |
| 1228 | + Expect(listResp.StatusCode()).To(Equal(http.StatusOK)) |
| 1229 | + |
| 1230 | + var foundActive, foundDeleted bool |
| 1231 | + for _, item := range listResp.JSON200.Items { |
| 1232 | + if item.Id == nil { |
| 1233 | + continue |
| 1234 | + } |
| 1235 | + if *item.Id == activeID { |
| 1236 | + Expect(item.DeletedTime).To(BeNil(), "active nodepool should not have deleted_time") |
| 1237 | + foundActive = true |
| 1238 | + } |
| 1239 | + if *item.Id == deletedID { |
| 1240 | + Expect(item.DeletedTime).NotTo(BeNil(), "soft-deleted nodepool should have deleted_time") |
| 1241 | + foundDeleted = true |
| 1242 | + } |
| 1243 | + } |
| 1244 | + Expect(foundActive).To(BeTrue(), "active nodepool should appear in LIST") |
| 1245 | + Expect(foundDeleted).To(BeTrue(), "soft-deleted nodepool should appear in LIST") |
| 1246 | +} |
0 commit comments