diff --git a/backend/console/src/main/java/com/alibaba/higress/console/config/SdkConfig.java b/backend/console/src/main/java/com/alibaba/higress/console/config/SdkConfig.java index 59fc0319..060953ed 100644 --- a/backend/console/src/main/java/com/alibaba/higress/console/config/SdkConfig.java +++ b/backend/console/src/main/java/com/alibaba/higress/console/config/SdkConfig.java @@ -16,6 +16,7 @@ import javax.annotation.PostConstruct; +import com.alibaba.higress.sdk.service.OpenAPIService; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -36,6 +37,7 @@ import com.alibaba.higress.sdk.service.consumer.ConsumerService; import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; +import com.alibaba.higress.sdk.service.HigressConfigService; @Configuration public class SdkConfig { @@ -129,6 +131,15 @@ public WasmPluginInstanceService wasmPluginInstanceService() { return serviceProvider.wasmPluginInstanceService(); } + @Bean + public OpenAPIService openApiService() { + return serviceProvider.openApiService(); + } + + @Bean + public HigressConfigService higressConfigService() { + return serviceProvider.higressConfigService(); + } @Bean public ConsumerService consumerService() { return serviceProvider.consumerService(); diff --git a/backend/console/src/main/java/com/alibaba/higress/console/controller/DomainsController.java b/backend/console/src/main/java/com/alibaba/higress/console/controller/DomainsController.java index 173745dc..a6d45f65 100644 --- a/backend/console/src/main/java/com/alibaba/higress/console/controller/DomainsController.java +++ b/backend/console/src/main/java/com/alibaba/higress/console/controller/DomainsController.java @@ -39,7 +39,7 @@ import com.alibaba.higress.sdk.service.RouteService; @RestController("DomainsController") -@RequestMapping("/v1/domains") +@RequestMapping("/v2/domains") @Validated public class DomainsController { diff --git a/backend/console/src/main/java/com/alibaba/higress/console/controller/HigressConfigController.java b/backend/console/src/main/java/com/alibaba/higress/console/controller/HigressConfigController.java new file mode 100644 index 00000000..8b8576df --- /dev/null +++ b/backend/console/src/main/java/com/alibaba/higress/console/controller/HigressConfigController.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.console.controller; + +import com.alibaba.higress.sdk.service.HigressConfigService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@RestController("HigressController") +@RequestMapping("/v1/workmode") +@Validated +public class HigressConfigController { + @Resource + HigressConfigService higressConfigService; + + @GetMapping + public Boolean getWorkMode() { + return higressConfigService.getWorkMode(); + } + + @PutMapping + public Boolean updateWorkMode(@RequestParam Boolean mode) { + return higressConfigService.putWorkMode(mode); + } + +} diff --git a/backend/console/src/main/java/com/alibaba/higress/console/controller/OpenAPIController.java b/backend/console/src/main/java/com/alibaba/higress/console/controller/OpenAPIController.java new file mode 100644 index 00000000..5cee10bf --- /dev/null +++ b/backend/console/src/main/java/com/alibaba/higress/console/controller/OpenAPIController.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.console.controller; + +import com.alibaba.higress.console.controller.dto.Response; +import com.alibaba.higress.console.controller.util.ControllerUtil; +import com.alibaba.higress.sdk.model.OpenAPISpecification; +import com.alibaba.higress.sdk.service.OpenAPIService; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@RestController("OpenAPIController") +@RequestMapping("/v1/openapi") +@Validated +public class OpenAPIController { + @Resource + private OpenAPIService openApiService; + + @PostMapping + public ResponseEntity> add(@RequestBody OpenAPISpecification oas) { + return ControllerUtil.buildResponseEntity(openApiService.add(oas)); + } +} diff --git a/backend/console/src/main/java/com/alibaba/higress/console/service/SystemServiceImpl.java b/backend/console/src/main/java/com/alibaba/higress/console/service/SystemServiceImpl.java index 345c09ca..a9c096f6 100644 --- a/backend/console/src/main/java/com/alibaba/higress/console/service/SystemServiceImpl.java +++ b/backend/console/src/main/java/com/alibaba/higress/console/service/SystemServiceImpl.java @@ -210,7 +210,9 @@ private void initDefaultRoutes() { Domain domain = new Domain(); domain.setName(HigressConstants.DEFAULT_DOMAIN); domain.setEnableHttps(Domain.EnableHttps.ON); - domain.setCertIdentifier(DEFAULT_TLS_CERTIFICATE_NAME); + Map portAndCert = new HashMap<>(); + portAndCert.put(443, DEFAULT_TLS_CERTIFICATE_NAME); + domain.setPortAndCertMap(portAndCert); domainService.add(domain); } catch (ResourceConflictException e) { // Ignore it. diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/HigressConstants.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/HigressConstants.java index f160c110..c54fa934 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/HigressConstants.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/HigressConstants.java @@ -24,4 +24,6 @@ public class HigressConstants { public static final String INTERNAL_RESOURCE_NAME_SUFFIX = ".internal"; public static final String FALLBACK_ROUTE_NAME_SUFFIX = ".fallback"; public static final String FALLBACK_FROM_HEADER = "x-higress-fallback-from"; + public static final String DEFAULT_CONFIG = "higress-config"; + public static final String PORT_CONFIG = "higress-ports"; } diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/KubernetesConstants.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/KubernetesConstants.java index 724b4051..ae965d0c 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/KubernetesConstants.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/KubernetesConstants.java @@ -26,6 +26,14 @@ public class KubernetesConstants { public static final String SECRET_TLS_KEY_FIELD = "tls.key"; public static final String YAML_SEPARATOR = "---\n"; + public static class WorkMode { + public static final String KEY = "workMode"; + public static final String GATEWAY = "gateway"; + public static final String INGRESS = "ingress"; + } + + + public static class Annotation { public static final String KEY_PREFIX = "higress.io/"; public static final String NGINX_INGRESS_KEY_PREFIX = "nginx.ingress.kubernetes.io/"; diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/Separators.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/Separators.java index 71f50d86..a2b893b0 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/Separators.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/Separators.java @@ -33,4 +33,6 @@ public class Separators { public static final String UNDERSCORE = "_"; public static final String COLON = ":"; + + public static final String SLASH = "/"; } diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/Domain.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/Domain.java index 7acc137e..d7b30c04 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/Domain.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/Domain.java @@ -18,6 +18,8 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.util.Map; + @Data @Builder @NoArgsConstructor @@ -30,6 +32,7 @@ public static class EnableHttps { public static final String ON = "on"; public static final String FORCE = "force"; } + private Boolean isIngressMode; private String name; @@ -37,5 +40,5 @@ public static class EnableHttps { private String enableHttps; - private String certIdentifier; + private Map portAndCertMap; } diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/OpenAPISpecification.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/OpenAPISpecification.java new file mode 100644 index 00000000..51987c62 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/OpenAPISpecification.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.model; + +import com.alibaba.higress.sdk.model.openapi.OpenAPIComponents; +import com.alibaba.higress.sdk.model.openapi.OpenAPIInfo; +import com.alibaba.higress.sdk.model.openapi.OpenAPIPathDescription; +import com.alibaba.higress.sdk.model.openapi.OpenAPIServer; +import com.alibaba.higress.sdk.model.openapi.OpenAPITag; +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ApiModel("OpenAPI") +public class OpenAPISpecification { + private String openapi; + private OpenAPIInfo info; + private List servers; + private Map paths; + private List components; + private List tags; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/Route.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/Route.java index 2acc468e..507dbe49 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/Route.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/Route.java @@ -41,6 +41,8 @@ public class Route implements VersionedDto { private String name; + private Boolean isIngressMode; + private String version; private List domains; diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIComponents.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIComponents.java new file mode 100644 index 00000000..fc091d6a --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIComponents.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIComponents { + private Map schemas; + private Map responses; + private Map parameters; + private Map requestBodies; + private Map extensions; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIContactDetails.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIContactDetails.java new file mode 100644 index 00000000..a92dbdb9 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIContactDetails.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIContactDetails { + private String name; + private String url; + private String email; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIEncoding.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIEncoding.java new file mode 100644 index 00000000..2b32270e --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIEncoding.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIEncoding { + private String contentType; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIExternalDocs.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIExternalDocs.java new file mode 100644 index 00000000..7c48ad43 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIExternalDocs.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIExternalDocs { + private String description; + private String url; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIInfo.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIInfo.java new file mode 100644 index 00000000..18619ea3 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIInfo.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIInfo { + private String title; + private String description; + private OpenAPIContactDetails contact; + private OpenAPILicense license; + private String version; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPILicense.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPILicense.java new file mode 100644 index 00000000..4d105742 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPILicense.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPILicense { + private String name; + private String url; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIMediaType.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIMediaType.java new file mode 100644 index 00000000..9246d48b --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIMediaType.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIMediaType { + private OpenAPISchema schema; + private Map encoding; + private Map extensions; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIOperation.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIOperation.java new file mode 100644 index 00000000..507d4664 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIOperation.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIOperation { + private List tags; + private String summary; + private String description; + private OpenAPIExternalDocs externalDocs; + private String operationId; + private List parameters; + private OpenAPIRequestBody requestBody; + private Map responses; + private boolean deprecated = false; + private Map> security; + private List servers; + private Map extensions; + +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIParameter.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIParameter.java new file mode 100644 index 00000000..71749b05 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIParameter.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIParameter { + private String name; + private String description; + private String in; + private boolean required = true; + private boolean deprecated = false; + private boolean allowEmptyValue = false; + @JsonProperty("$ref") + private String ref; + private OpenAPISchema schema; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIPathDescription.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIPathDescription.java new file mode 100644 index 00000000..a5914a38 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIPathDescription.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIPathDescription { + private String summary; + private String description; + private OpenAPIOperation get; + private OpenAPIOperation put; + private OpenAPIOperation post; + private OpenAPIOperation delete; + private OpenAPIOperation options; + private OpenAPIOperation head; + private OpenAPIOperation patch; + private OpenAPIOperation trace; + private List servers; + private List parameters; + @JsonProperty("$ref") + private String ref; + private Map extensions; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIRequestBody.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIRequestBody.java new file mode 100644 index 00000000..9610ef15 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIRequestBody.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIRequestBody { + private String description; + private Map content; + private boolean required = true; + private Map extensions; + @JsonProperty("$ref") + private String ref; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIResponse.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIResponse.java new file mode 100644 index 00000000..41e1300d --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIResponse.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIResponse { + private String description; + private Map content; + private Map extensions; + @JsonProperty("$ref") + private String ref; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPISchema.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPISchema.java new file mode 100644 index 00000000..93d5490b --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPISchema.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPISchema { + private String name; + private String title; + private List required; + private String type; + private String description; + private String format; + private boolean nullable = false; + private boolean readOnly = false; + private boolean writeOnly = false; + private boolean deprecated = false; + private OpenAPIExternalDocs externalDocs; + private Map extensions; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIServer.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIServer.java new file mode 100644 index 00000000..bbe844e6 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIServer.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIServer { + private String description; + private String url; + private Map variables; + private Map extensions; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIServerVariable.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIServerVariable.java new file mode 100644 index 00000000..cd08ba1a --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPIServerVariable.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPIServerVariable { + @JsonProperty("enum") + private List enums; + + @JsonProperty("default") + private String defaultValue; + + private String description; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPITag.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPITag.java new file mode 100644 index 00000000..5b283b08 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/model/openapi/OpenAPITag.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.model.openapi; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenAPITag { + private String name; + private String description; + private OpenAPIExternalDocs externalDocs; +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/DomainService.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/DomainService.java index 62fad453..eb42f00c 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/DomainService.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/DomainService.java @@ -12,10 +12,10 @@ */ package com.alibaba.higress.sdk.service; +import com.alibaba.higress.sdk.exception.BusinessException; import com.alibaba.higress.sdk.model.CommonPageQuery; import com.alibaba.higress.sdk.model.Domain; import com.alibaba.higress.sdk.model.PaginatedResult; - public interface DomainService { Domain add(Domain domain); @@ -26,5 +26,7 @@ public interface DomainService { void delete(String domainName); - Domain put(Domain domain); + Domain put(Domain domain) throws BusinessException; + + Domain addOrUpdate(Domain domain) throws BusinessException; } \ No newline at end of file diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/DomainServiceImpl.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/DomainServiceImpl.java index d92747bc..862abbcb 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/DomainServiceImpl.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/DomainServiceImpl.java @@ -16,14 +16,16 @@ import java.util.Optional; import java.util.stream.Collectors; +import com.alibaba.higress.sdk.service.strategy.domain.DomainContext; +import com.alibaba.higress.sdk.service.strategy.domain.DomainStrategy; +import com.alibaba.higress.sdk.service.strategy.domain.GatewayDomainStrategy; +import com.alibaba.higress.sdk.service.strategy.domain.IngressDomainStrategy; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import com.alibaba.higress.sdk.constant.CommonKey; import com.alibaba.higress.sdk.constant.HigressConstants; import com.alibaba.higress.sdk.exception.BusinessException; -import com.alibaba.higress.sdk.exception.ResourceConflictException; -import com.alibaba.higress.sdk.http.HttpStatus; import com.alibaba.higress.sdk.model.CommonPageQuery; import com.alibaba.higress.sdk.model.Domain; import com.alibaba.higress.sdk.model.PaginatedResult; @@ -57,17 +59,8 @@ public DomainServiceImpl(KubernetesClientService kubernetesClientService, @Override public Domain add(Domain domain) { - V1ConfigMap domainConfigMap = kubernetesModelConverter.domain2ConfigMap(domain); - V1ConfigMap newDomainConfigMap; - try { - newDomainConfigMap = kubernetesClientService.createConfigMap(domainConfigMap); - } catch (ApiException e) { - if (e.getCode() == HttpStatus.CONFLICT) { - throw new ResourceConflictException(); - } - throw new BusinessException("Error occurs when adding a new domain.", e); - } - return kubernetesModelConverter.configMap2Domain(newDomainConfigMap); + DomainStrategy strategy = getStrategy(domain); + return new DomainContext(strategy).add(domain); } @Override @@ -98,48 +91,36 @@ public Domain query(String domainName) { @Override public void delete(String domainName) { + Domain domain = query(domainName); RoutePageQuery query = new RoutePageQuery(); query.setDomainName(domainName); PaginatedResult routes = routeService.list(query); if (CollectionUtils.isNotEmpty(routes.getData())) { throw new IllegalArgumentException("The domain has routes. Please delete them first."); } - - String configMapName = kubernetesModelConverter.domainName2ConfigMapName(domainName); - try { - kubernetesClientService.deleteConfigMap(configMapName); - } catch (ApiException e) { - throw new BusinessException("Error occurs when deleting the ConfigMap with name: " + configMapName, e); - } - + DomainStrategy strategy = getStrategy(domain); + new DomainContext(strategy).delete(domainName); wasmPluginInstanceService.deleteAll(WasmPluginInstanceScope.DOMAIN, domainName); } @Override public Domain put(Domain domain) { - V1ConfigMap domainConfigMap = kubernetesModelConverter.domain2ConfigMap(domain); - V1ConfigMap updatedConfigMap; - try { - updatedConfigMap = kubernetesClientService.replaceConfigMap(domainConfigMap); - } catch (ApiException e) { - if (e.getCode() == HttpStatus.CONFLICT) { - throw new ResourceConflictException(); - } - throw new BusinessException( - "Error occurs when replacing the ConfigMap generated by domain: " + domain.getName(), e); - } - List routes; if (HigressConstants.DEFAULT_DOMAIN.equals(domain.getName())) { PaginatedResult routeQueryResult = routeService.list(null); routes = routeQueryResult.getData().stream().filter(r -> CollectionUtils.isEmpty(r.getDomains())) - .collect(Collectors.toList()); + .collect(Collectors.toList()); } else { RoutePageQuery query = new RoutePageQuery(); query.setDomainName(domain.getName()); PaginatedResult routeQueryResult = routeService.list(query); routes = routeQueryResult.getData(); } + checkUpdatedDomainValid(domain, routes); + + DomainStrategy strategy = getStrategy(domain); + Domain newDomain = new DomainContext(strategy).put(domain); + // TODO: Switch to the new logic after 2025/03/31 // String domainName = domain.getName(); @@ -155,6 +136,43 @@ public Domain put(Domain domain) { routes.forEach(routeService::update); } - return kubernetesModelConverter.configMap2Domain(updatedConfigMap); + return newDomain; + } + + public void checkUpdatedDomainValid(Domain domain, List routes) { + if(domain.getPortAndCertMap() == null) { + throw new BusinessException("Port and certificate map is missing for the domain."); + } + boolean port80EnabledForHttp = StringUtils.EMPTY.equals(domain.getPortAndCertMap().get(80)); + if (domain.getIsIngressMode() || port80EnabledForHttp) { + return; + } + for (Route route: routes) { + if (route.getIsIngressMode()) { + throw new BusinessException("The domain has ingress bindings and must keep port 80 open for HTTP."); + } + } + } + + @Override + public Domain addOrUpdate(Domain domain){ + if (domain == null) { + throw new BusinessException("Domain cannot be null"); + } + Domain existingDomain = query(domain.getName()); + if (existingDomain == null) { + return add(domain); + } else { + domain.setVersion(existingDomain.getVersion()); + return put(domain); + } + } + + private DomainStrategy getStrategy(Domain domain) { + if (domain.getIsIngressMode()) { + return new IngressDomainStrategy(kubernetesClientService, kubernetesModelConverter); + } else { + return new GatewayDomainStrategy(kubernetesClientService, kubernetesModelConverter); + } } } \ No newline at end of file diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressConfigService.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressConfigService.java new file mode 100644 index 00000000..ed7ba38c --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressConfigService.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service; + +public interface HigressConfigService { + Boolean getWorkMode(); + Boolean putWorkMode(Boolean isIngressMode); +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressConfigServiceImpl.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressConfigServiceImpl.java new file mode 100644 index 00000000..04e3c15d --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressConfigServiceImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service; + +import com.alibaba.higress.sdk.exception.BusinessException; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; +import io.kubernetes.client.openapi.ApiException; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class HigressConfigServiceImpl implements HigressConfigService{ + private final KubernetesClientService kubernetesClientService; + + public HigressConfigServiceImpl(KubernetesClientService kubernetesClientService) { + this.kubernetesClientService = kubernetesClientService; + } + + @Override + public Boolean getWorkMode() { + return kubernetesClientService.isIngressWorkMode(); + } + + @Override + public Boolean putWorkMode(Boolean isIngressMode) { + Boolean updatedIsIngressMode = isIngressMode; + try { + updatedIsIngressMode = kubernetesClientService.setIngressMode(isIngressMode); + } catch (ApiException e) { + throw new BusinessException("Error occurs when updating the Higress workmode: ", e); + } + return updatedIsIngressMode; + } +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressServiceProvider.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressServiceProvider.java index 20eb561f..26d38146 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressServiceProvider.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressServiceProvider.java @@ -14,10 +14,10 @@ import java.io.IOException; -import com.alibaba.higress.sdk.config.HigressServiceConfig; import com.alibaba.higress.sdk.service.ai.AiRouteService; import com.alibaba.higress.sdk.service.ai.LlmProviderService; import com.alibaba.higress.sdk.service.consumer.ConsumerService; +import com.alibaba.higress.sdk.config.HigressServiceConfig; import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; @@ -48,6 +48,10 @@ static HigressServiceProvider create(HigressServiceConfig config) throws IOExcep WasmPluginInstanceService wasmPluginInstanceService(); + OpenAPIService openApiService(); + + HigressConfigService higressConfigService(); + ConsumerService consumerService(); AiRouteService aiRouteService(); diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressServiceProviderImpl.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressServiceProviderImpl.java index e5be42b8..80aba3c0 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressServiceProviderImpl.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/HigressServiceProviderImpl.java @@ -12,17 +12,17 @@ */ package com.alibaba.higress.sdk.service; -import java.io.IOException; - import com.alibaba.higress.sdk.config.HigressServiceConfig; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; import com.alibaba.higress.sdk.service.ai.AiRouteService; import com.alibaba.higress.sdk.service.ai.AiRouteServiceImpl; import com.alibaba.higress.sdk.service.ai.LlmProviderService; import com.alibaba.higress.sdk.service.ai.LlmProviderServiceImpl; import com.alibaba.higress.sdk.service.consumer.ConsumerService; import com.alibaba.higress.sdk.service.consumer.ConsumerServiceImpl; -import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; -import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; + +import java.io.IOException; /** * @author CH3CHO @@ -38,10 +38,13 @@ class HigressServiceProviderImpl implements HigressServiceProvider { private final TlsCertificateService tlsCertificateService; private final WasmPluginService wasmPluginService; private final WasmPluginInstanceService wasmPluginInstanceService; + private final OpenAPIService openApiService; private final ConsumerService consumerService; private final AiRouteService aiRouteService; private final LlmProviderService llmProviderService; + private final HigressConfigService higressConfigService; + HigressServiceProviderImpl(HigressServiceConfig config) throws IOException { kubernetesClientService = new KubernetesClientService(config); kubernetesModelConverter = new KubernetesModelConverter(kubernetesClientService); @@ -55,6 +58,8 @@ class HigressServiceProviderImpl implements HigressServiceProvider { new RouteServiceImpl(kubernetesClientService, kubernetesModelConverter, wasmPluginInstanceService); domainService = new DomainServiceImpl(kubernetesClientService, kubernetesModelConverter, routeService, wasmPluginInstanceService); + openApiService = new OpenAPIServiceImpl(kubernetesClientService, domainService, routeService, wasmPluginInstanceService); + higressConfigService = new HigressConfigServiceImpl(kubernetesClientService); consumerService = new ConsumerServiceImpl(wasmPluginInstanceService); llmProviderService = new LlmProviderServiceImpl(serviceSourceService, wasmPluginInstanceService); aiRouteService = new AiRouteServiceImpl(kubernetesModelConverter, kubernetesClientService, routeService, @@ -106,6 +111,15 @@ public WasmPluginInstanceService wasmPluginInstanceService() { return wasmPluginInstanceService; } + @Override + public OpenAPIService openApiService() { + return openApiService; + } + + @Override + public HigressConfigService higressConfigService() { + return higressConfigService; + } @Override public ConsumerService consumerService() { return consumerService; diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/OpenAPIService.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/OpenAPIService.java new file mode 100644 index 00000000..b34aa95e --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/OpenAPIService.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service; + +import com.alibaba.higress.sdk.model.OpenAPISpecification; + + +public interface OpenAPIService { + OpenAPISpecification add(OpenAPISpecification oas); +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/OpenAPIServiceImpl.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/OpenAPIServiceImpl.java new file mode 100644 index 00000000..1e9fc2d9 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/OpenAPIServiceImpl.java @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service; + +import com.alibaba.higress.sdk.constant.HigressConstants; +import com.alibaba.higress.sdk.exception.BusinessException; +import com.alibaba.higress.sdk.model.Domain; +import com.alibaba.higress.sdk.model.OpenAPISpecification; +import com.alibaba.higress.sdk.model.Route; +import com.alibaba.higress.sdk.model.WasmPluginInstance; +import com.alibaba.higress.sdk.model.WasmPluginInstanceScope; +import com.alibaba.higress.sdk.model.openapi.OpenAPIParameter; +import com.alibaba.higress.sdk.model.openapi.OpenAPIPathDescription; +import com.alibaba.higress.sdk.model.openapi.OpenAPIServer; +import com.alibaba.higress.sdk.model.openapi.OpenAPIServerVariable; +import com.alibaba.higress.sdk.model.route.RoutePredicate; +import com.alibaba.higress.sdk.model.route.RoutePredicateTypeEnum; +import com.alibaba.higress.sdk.model.route.UpstreamService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesUtil; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecMatches; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +@Slf4j +public class OpenAPIServiceImpl implements OpenAPIService{ + private final KubernetesClientService kubernetesClientService; + private final DomainService domainService; + private final RouteService routeService; + + private final WasmPluginInstanceService wasmPluginInstanceService; + + public OpenAPIServiceImpl(KubernetesClientService kubernetesClientService, DomainService domainService, + RouteService routeService, WasmPluginInstanceService wasmPluginInstanceService) { + this.kubernetesClientService = kubernetesClientService; + this.domainService = domainService; + this.routeService = routeService; + this.wasmPluginInstanceService = wasmPluginInstanceService; + } + + + @Override + public OpenAPISpecification add(OpenAPISpecification openApi) { + if (kubernetesClientService.isIngressWorkMode()) { + throw new BusinessException("Ingress mode is not supported for OpenAPI Specification yet"); + } + + List domainInfos = processDomains(openApi); + addOrUpdateDomains(domainInfos); + + List routeInfos = processRoutes(openApi); + addRoutes(routeInfos, openApi.getPaths(), domainInfos); + + return null; + } + private static class PathAndType { + String pathPrefix; + Boolean isExact; + + PathAndType(String pathPrefix, Boolean isExact) { + this.pathPrefix = pathPrefix; + this.isExact = isExact; + } + } + private static class DomainInfo { + Domain domain; + PathAndType pathAndType; + + List instances; + + DomainInfo(Domain domain, PathAndType pathAndType, List instances) { + this.domain = domain; + this.pathAndType = pathAndType; + this.instances = instances; + } + } + + private static class RouteInfo { + Route route; + List instances; + + RouteInfo(Route route, List instances) { + this.route = route; + this.instances = instances; + } + } + + private List processDomains(OpenAPISpecification openApi) { + List domainInfos = new ArrayList<>(); + List servers = openApi.getServers(); + + if (servers == null || servers.isEmpty()) { + domainInfos.add(createDefaultDomainInfo()); + } else { + for (OpenAPIServer server : servers) { + domainInfos.addAll(createDomainInfoFromServer(server)); + } + } + + return domainInfos; + } + + private DomainInfo createDefaultDomainInfo() { + Domain domain = new Domain(); + domain.setName("*"); + return new DomainInfo(domain, new PathAndType("/", Boolean.TRUE), null); + } + + private List createDomainInfoFromServer(OpenAPIServer server) { + String url = server.getUrl(); + Map variables = server.getVariables(); + List domainInfos = new ArrayList<>(); + + PathAndType pathAndType = extractPathPrefix(url, variables); + List schemes = extractSchemes(url, variables); + List hosts = extractHosts(url, variables); + + for (String host : hosts) { + Domain domain = new Domain(); + domain.setIsIngressMode(kubernetesClientService.isIngressWorkMode()); + domain.setName(host); + // For absolute URLs, create a DomainInfo for each scheme + domain.setEnableHttps(Domain.EnableHttps.OFF); + Map portAndCertMap = new HashMap<>(); + for (String scheme : schemes) { + if ("http".equalsIgnoreCase(scheme)) { + portAndCertMap.put(80, ""); + } else if ("https".equalsIgnoreCase(scheme)) { + domain.setEnableHttps(Domain.EnableHttps.ON); + // TODO: change the default tls name + String tls; + if (server.getExtensions()!=null && server.getExtensions().containsKey("tls_name")) { + tls= (String) server.getExtensions().get("tls_name"); + } else { + throw new BusinessException("error openapi specification: protocol is https but without cert"); + } + portAndCertMap.put(443, tls); + } + } + domain.setPortAndCertMap(portAndCertMap); + List instances = new ArrayList<>(); + if (server.getExtensions()!=null && server.getExtensions().containsKey("policy")) { + @SuppressWarnings("unchecked") + Map> wasm = (Map>) server.getExtensions().get("policy"); + if (wasm.get("pluginName")!=null && wasm.get("enabled")!=null && wasm.get("rawConfigurations")!=null) { + List pluginNames = wasm.get("pluginName"); + List enabled = wasm.get("enabled"); + List rawConfigs = wasm.get("rawConfigurations"); + for (int i = 0; i < pluginNames.size(); i++) { + WasmPluginInstance instance = new WasmPluginInstance(); + instance.setScope(WasmPluginInstanceScope.DOMAIN); + instance.setPluginName(pluginNames.get(i)); + if (i < enabled.size()) { + instance.setEnabled(Boolean.parseBoolean(enabled.get(i))); + } + if (i < rawConfigs.size()) { + instance.setRawConfigurations(rawConfigs.get(i)); + } + instance.setTarget(domain.getName()); + instances.add(instance); + } + } + } + domainInfos.add(new DomainInfo(domain, pathAndType, instances)); + } + + return domainInfos; + } + + private PathAndType extractPathPrefix(String url, Map variables) { + String path; + String processedUrl = renderDefaultSchemes(url, variables); + try { + path = new URL(processedUrl).getPath(); + } catch (MalformedURLException e) { + // If URL is relative, it's the path prefix itself + path = url; + } + return processPathVariables(path, variables); + } + + private List extractSchemes(String url, Map variables) { + List schemes = new ArrayList<>(); + + if (url.startsWith("{")) { + int endIndex = url.indexOf("}"); + if (endIndex != -1) { + String schemePlaceholder = url.substring(1, endIndex); + OpenAPIServerVariable schemeVar = variables.get(schemePlaceholder); + if (schemeVar != null && schemeVar.getEnums() != null) { + schemes.addAll(schemeVar.getEnums()); + } else { + schemes.add("http"); + } + } + } else { + try { + URL parsedUrl = new URL(url); + schemes.add(parsedUrl.getProtocol()); + } catch (MalformedURLException e) { + // If URL is relative, only http + schemes.add("http"); + } + } + + return schemes; + } + + private List extractHosts(String url, Map variables) { + List hosts = new ArrayList<>(); + String processedUrl = renderDefaultSchemes(url, variables); + + try { + URL parsedUrl = new URL(processedUrl); + String host = parsedUrl.getHost(); + + if (host.contains("{")) { + String[] parts = host.split("\\."); + List hostVariations = new ArrayList<>(); + hostVariations.add(host); + + for (String part : parts) { + if (part.startsWith("{") && part.endsWith("}")) { + String varName = part.substring(1, part.length() - 1); + OpenAPIServerVariable var = variables.get(varName); + if (var != null) { + List values = var.getEnums() != null ? var.getEnums() : + (var.getDefaultValue() != null ? Arrays.asList(var.getDefaultValue()) : + Arrays.asList("*")); + List newVariations = new ArrayList<>(); + for (String variation : hostVariations) { + for (String value : values) { + newVariations.add(variation.replace("{" + varName + "}", value)); + } + } + hostVariations = newVariations; + } + } + } + hosts.addAll(hostVariations); + } else { + hosts.add(host); + } + } catch (MalformedURLException e) { + // If URL is relative, return wildcard + hosts.add(HigressConstants.DEFAULT_DOMAIN); + } + + return hosts; + } + + private String renderDefaultSchemes(String url, Map variables) { + if (variables == null) { + return url; + } + if (url.startsWith("{")) { + int endIndex = url.indexOf("}"); + if (endIndex != -1) { + String schemePlaceholder = url.substring(1, endIndex); + OpenAPIServerVariable schemeVar = variables.get(schemePlaceholder); + if (schemeVar == null) { + return url; + } + String value = schemeVar.getDefaultValue() != null ? schemeVar.getDefaultValue() : + (schemeVar.getEnums() != null && !schemeVar.getEnums().isEmpty() ? schemeVar.getEnums().get(0) : ""); + url = url.replace("{" + schemePlaceholder + "}", value); + } + } + return url; + } + + private PathAndType processPathVariables(String path, Map variables) { + // "^/(v2|v3)$" + if (variables == null) { + return new PathAndType(path, Boolean.TRUE); + } + + StringBuilder regex = new StringBuilder("^"); + String[] segments = path.split("/"); + + Boolean isExact = Boolean.TRUE; + + for (String segment : segments) { + if (segment.startsWith("{") && segment.endsWith("}")) { + isExact = Boolean.FALSE; + String varName = segment.substring(1, segment.length() - 1); + OpenAPIServerVariable var = variables.get(varName); + if (var != null && var.getEnums() != null && !var.getEnums().isEmpty()) { + regex.append("/(" + String.join("|", var.getEnums()) + ")"); + } else { + regex.append("/[^/]+"); + } + } else { + regex.append("/" + Pattern.quote(segment)); + } + } + if(!isExact) { + regex.append("$"); + } + return new PathAndType(regex.toString(), isExact); + } + + + private List processRoutes(OpenAPISpecification openApi) { + List routeInfos = new ArrayList<>(); + Map paths = openApi.getPaths(); + + for (Map.Entry entry : paths.entrySet()) { + String path = entry.getKey(); + OpenAPIPathDescription pathItem = entry.getValue(); + + Route route = new Route(); + route.setIsIngressMode(kubernetesClientService.isIngressWorkMode()); + route.setName(path); + RoutePredicate routePath = new RoutePredicate(RoutePredicateTypeEnum.EQUAL.toString(), path, false); + route.setPath(routePath); + + boolean hasPathParameter = path.contains("{") && path.contains("}"); + + if (hasPathParameter) { + String regexPath = convertPathToRegex(path, getFirstOperationParameters(pathItem)); + routePath.setMatchValue(regexPath); + routePath.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + } + List instances = new ArrayList<>(); + setRouteProperties(route, pathItem, instances); + routeInfos.add(new RouteInfo(route, instances)); + } + + return routeInfos; + } + + private List getFirstOperationParameters(OpenAPIPathDescription pathItem) { + if (pathItem.getGet() != null && pathItem.getGet().getParameters() != null) { + return pathItem.getGet().getParameters(); + } else if (pathItem.getPost() != null && pathItem.getPost().getParameters() != null) { + return pathItem.getPost().getParameters(); + } else if (pathItem.getPut() != null && pathItem.getPut().getParameters() != null) { + return pathItem.getPut().getParameters(); + } else if (pathItem.getDelete() != null && pathItem.getDelete().getParameters() != null) { + return pathItem.getDelete().getParameters(); + } else if (pathItem.getPatch() != null && pathItem.getPatch().getParameters() != null) { + return pathItem.getPatch().getParameters(); + } else if (pathItem.getOptions() != null && pathItem.getOptions().getParameters() != null) { + return pathItem.getOptions().getParameters(); + } else if (pathItem.getHead() != null && pathItem.getHead().getParameters() != null) { + return pathItem.getHead().getParameters(); + } else if (pathItem.getTrace() != null && pathItem.getTrace().getParameters() != null) { + return pathItem.getTrace().getParameters(); + } + return null; + } + + private String convertPathToRegex(String path, List parameters) { + if (parameters == null) { + return path; + } + StringBuilder regex = new StringBuilder("^"); + String[] segments = path.split("/"); + + for (String segment : segments) { + if (segment.startsWith("{") && segment.endsWith("}")) { + String paramName = segment.substring(1, segment.length() - 1); + OpenAPIParameter param = parameters.stream() + .filter(p -> p.getName().equals(paramName) && "path".equals(p.getIn())) + .findFirst() + .orElse(null); + + if (param != null && param.getSchema() != null) { + String format = param.getSchema().getFormat(); + String type = param.getSchema().getType(); + if ("integer".equals(type) || "int32".equals(format) || "int64".equals(format)) { + regex.append("/([0-9]+)"); + } else if ("float".equals(type) || "double".equals(type)) { + regex.append("/([0-9]*\\.?[0-9]+)"); + } else { + regex.append("/([^/]+)"); + } + } else { + regex.append("/([^/]+)"); + } + } else { + regex.append("/" + Pattern.quote(segment)); + } + } + regex.append("$"); + + return regex.toString(); + } + + private void setRouteProperties(Route route, OpenAPIPathDescription pathItem, List instances) { + List methods = new ArrayList<>(); + if (pathItem.getGet() != null) { + methods.add(V1HTTPRouteSpecMatches.MethodEnum.GET.toString()); + } + if (pathItem.getPut() != null) { + methods.add(V1HTTPRouteSpecMatches.MethodEnum.PUT.toString()); + } + if (pathItem.getPost() != null) { + methods.add(V1HTTPRouteSpecMatches.MethodEnum.POST.toString()); + } + if (pathItem.getDelete() != null) { + methods.add(V1HTTPRouteSpecMatches.MethodEnum.DELETE.toString()); + } + if (pathItem.getOptions() != null) { + methods.add(V1HTTPRouteSpecMatches.MethodEnum.OPTIONS.toString()); + } + if (pathItem.getHead() != null) { + methods.add(V1HTTPRouteSpecMatches.MethodEnum.HEAD.toString()); + } + if (pathItem.getPatch() != null) { + methods.add(V1HTTPRouteSpecMatches.MethodEnum.PATCH.toString()); + } + if (pathItem.getTrace() != null) { + methods.add(V1HTTPRouteSpecMatches.MethodEnum.TRACE.toString()); + } + + route.setMethods(methods); + // TODO: backend -> weight + if (pathItem.getExtensions()!=null && pathItem.getExtensions().containsKey("x-backends")) { + @SuppressWarnings("unchecked") + Map> backends = (Map>) pathItem.getExtensions().get("x-backends"); + List services = new ArrayList<>(); + if (backends.get("name")!=null && backends.get("weight")!=null) { + List names = backends.get("name"); + List weights = backends.get("weight"); + for (int i = 0; i < names.size(); i++) { + UpstreamService service = new UpstreamService(); + service.setName(names.get(i)); + if (i < weights.size()) { + service.setWeight(Integer.parseInt(weights.get(i))); + } + services.add(service); + } + } + route.setServices(services); + } + + if (pathItem.getExtensions()!=null && pathItem.getExtensions().containsKey("policy")) { + @SuppressWarnings("unchecked") + Map> wasm = (Map>) pathItem.getExtensions().get("policy"); + if (wasm.get("pluginName")!=null && wasm.get("enabled")!=null && wasm.get("rawConfigurations")!=null) { + List pluginNames = wasm.get("pluginName"); + List enabled = wasm.get("enabled"); + List rawConfigs = wasm.get("rawConfigurations"); + for (int i = 0; i < pluginNames.size(); i++) { + WasmPluginInstance instance = new WasmPluginInstance(); + instance.setScope(WasmPluginInstanceScope.ROUTE); + instance.setPluginName(pluginNames.get(i)); + if (i < enabled.size()) { + instance.setEnabled(Boolean.parseBoolean(enabled.get(i))); + } + if (i < rawConfigs.size()) { + instance.setRawConfigurations(rawConfigs.get(i)); + } + instances.add(instance); + } + } + } + + } + + private void addOrUpdateDomains(List domainInfos) { + for (DomainInfo domainInfo : domainInfos) { + domainService.addOrUpdate(domainInfo.domain); + List instances = domainInfo.instances; + if (CollectionUtils.isNotEmpty(instances)) { + for (WasmPluginInstance instance: instances) { + wasmPluginInstanceService.addOrUpdate(instance); + } + } + } + } + + private void addRoutes(List routeInfos, Map paths, List domainInfos) { + for (RouteInfo routeInfo : routeInfos){ + Route route = routeInfo.route; + List instances = routeInfo.instances; + String path = route.getName(); + String matchType = route.getPath().getMatchType(); + String matchValue = route.getPath().getMatchValue(); + List servers = paths.get(path).getServers(); + if (CollectionUtils.isNotEmpty(servers)) { + for (OpenAPIServer server : servers) { + String url = server.getUrl(); + Map variables = server.getVariables(); + PathAndType pathAndType = extractPathPrefix(url, variables); + List hosts = extractHosts(url, variables); + route.getPath().setMatchValue(pathAndType.pathPrefix+matchValue); + if (pathAndType.isExact) { + route.getPath().setMatchType(matchType); + } else { + route.getPath().setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + } + for (String host: hosts) { + route.setName(KubernetesUtil.normalizeRouteName(host+path)); + route.setDomains(Collections.singletonList(host)); + routeService.add(route); + if (CollectionUtils.isNotEmpty(instances)) { + for (WasmPluginInstance instance: instances) { + instance.setTarget(KubernetesUtil.normalizeRouteName(host+path)); + wasmPluginInstanceService.addOrUpdate(instance); + } + } + } + } + } else { + // DomainInfos + for (DomainInfo domainInfo : domainInfos) { + route.setName(KubernetesUtil.normalizeRouteName(domainInfo.domain.getName()+path)); + route.setDomains(Collections.singletonList(domainInfo.domain.getName())); + route.getPath().setMatchValue(domainInfo.pathAndType.pathPrefix+matchValue); + if (domainInfo.pathAndType.isExact) { + route.getPath().setMatchType(matchType); + } else { + route.getPath().setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + } + routeService.add(route); + if (CollectionUtils.isNotEmpty(instances)) { + for (WasmPluginInstance instance: instances) { + instance.setTarget(KubernetesUtil.normalizeRouteName(domainInfo.domain.getName()+path)); + wasmPluginInstanceService.addOrUpdate(instance); + } + } + } + } + } + } +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/RouteServiceImpl.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/RouteServiceImpl.java index a21f95d9..be8887af 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/RouteServiceImpl.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/RouteServiceImpl.java @@ -12,20 +12,22 @@ */ package com.alibaba.higress.sdk.service; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRoute; +import com.alibaba.higress.sdk.service.strategy.route.HttpRouteStrategy; +import com.alibaba.higress.sdk.service.strategy.route.IngressRouteStrategy; +import com.alibaba.higress.sdk.service.strategy.route.RouteContext; +import com.alibaba.higress.sdk.service.strategy.route.RouteStrategy; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import com.alibaba.higress.sdk.exception.BusinessException; -import com.alibaba.higress.sdk.exception.ResourceConflictException; import com.alibaba.higress.sdk.exception.ValidationException; -import com.alibaba.higress.sdk.http.HttpStatus; import com.alibaba.higress.sdk.model.PaginatedResult; import com.alibaba.higress.sdk.model.Route; import com.alibaba.higress.sdk.model.RoutePageQuery; -import com.alibaba.higress.sdk.model.WasmPluginInstanceScope; import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; @@ -51,12 +53,29 @@ public RouteServiceImpl(KubernetesClientService kubernetesClientService, @Override public PaginatedResult list(RoutePageQuery query) { - List ingresses = listIngresses(query); + if (query == null) { + query = DEFAULT_QUERY; + } - if (CollectionUtils.isEmpty(ingresses)) { - return PaginatedResult.createFromFullList(Collections.emptyList(), query); + List ingresses = listIngresses(query); + List ingressListRoutes = ingresses.stream() + .map(kubernetesModelConverter::ingress2Route) + .toList(); + + List httpRoutes = listHttpRoutes(query); + List httpListRoutes = httpRoutes.stream() + .map(kubernetesModelConverter::httpRoute2Route) + .toList(); + + List combinedRoutes = new ArrayList<>(); + if (!CollectionUtils.isEmpty(ingressListRoutes)) { + combinedRoutes.addAll(ingressListRoutes); + } + if (!CollectionUtils.isEmpty(httpRoutes)) { + combinedRoutes.addAll(httpListRoutes); } - return PaginatedResult.createFromFullList(ingresses, query, kubernetesModelConverter::ingress2Route); + + return PaginatedResult.createFromFullList(combinedRoutes, query); } private List listIngresses(RoutePageQuery query) { @@ -83,6 +102,21 @@ private List listIngresses(RoutePageQuery query) { } } + private List listHttpRoutes(RoutePageQuery query) { + if (query == null) { + query = DEFAULT_QUERY; + } + + if (StringUtils.isNotEmpty(query.getDomainName())) { + if (Boolean.TRUE.equals(query.getAll())) { + throw new ValidationException( + "The query parameter 'all' is not supported when querying by domain."); + } + return kubernetesClientService.listHttpRouteByDomain(query.getDomainName()); + } else { + return kubernetesClientService.listHttpRoute(); + } + } @Override public Route query(String routeName) { V1Ingress ingress; @@ -91,50 +125,57 @@ public Route query(String routeName) { } catch (ApiException e) { throw new BusinessException("Error occurs when reading the Ingress with name: " + routeName, e); } - return ingress != null ? kubernetesModelConverter.ingress2Route(ingress) : null; + if (ingress != null) { + return kubernetesModelConverter.ingress2Route(ingress); + } + V1HTTPRoute httpRoute; + try { + httpRoute = kubernetesClientService.readHttpRoute(routeName); + } catch (ApiException e) { + throw new BusinessException("Error occurs when reading the HttpRoute with name: " + routeName, e); + } + if (httpRoute != null) { + return kubernetesModelConverter.httpRoute2Route(httpRoute); + } + return null; } @Override public Route add(Route route) { - V1Ingress ingress = kubernetesModelConverter.route2Ingress(route); - V1Ingress newIngress; - try { - newIngress = kubernetesClientService.createIngress(ingress); - } catch (ApiException e) { - if (e.getCode() == HttpStatus.CONFLICT) { - throw new ResourceConflictException(); - } - throw new BusinessException( - "Error occurs when updating the ingress generated by route with name: " + route.getName(), e); - } - return kubernetesModelConverter.ingress2Route(newIngress); + RouteStrategy strategy = getStrategy(route); + return new RouteContext(strategy).add(route); } @Override public Route update(Route route) { - V1Ingress ingress = kubernetesModelConverter.route2Ingress(route); - - V1Ingress updatedIngress; - try { - updatedIngress = kubernetesClientService.replaceIngress(ingress); - } catch (ApiException e) { - if (e.getCode() == HttpStatus.CONFLICT) { - throw new ResourceConflictException(); - } - throw new BusinessException( - "Error occurs when updating the ingress generated by route with name: " + route.getName(), e); + String name = route.getName(); + if (StringUtils.isEmpty(name)) { + throw new IllegalArgumentException("Route name must not be null"); } - return kubernetesModelConverter.ingress2Route(updatedIngress); + RouteStrategy strategy = getStrategy(route); + return new RouteContext(strategy).update(route); } @Override public void delete(String name) { + Boolean isIngress = Boolean.TRUE; try { - kubernetesClientService.deleteIngress(name); + V1Ingress ingress = kubernetesClientService.readIngress(name); + if (ingress == null) { + isIngress = Boolean.FALSE; + } } catch (ApiException e) { - throw new BusinessException("Error occurs when deleting ingress with name: " + name, e); + throw new BusinessException("Error occurs when reading ingress"+" with name: " + name, e); } - wasmPluginInstanceService.deleteAll(WasmPluginInstanceScope.ROUTE, name); + RouteStrategy strategy = isIngress ? + new IngressRouteStrategy(kubernetesClientService, kubernetesModelConverter, wasmPluginInstanceService) : + new HttpRouteStrategy(kubernetesClientService, kubernetesModelConverter, wasmPluginInstanceService); + new RouteContext(strategy).delete(name); + } + private RouteStrategy getStrategy(Route route) { + return route.getIsIngressMode() ? + new IngressRouteStrategy(kubernetesClientService, kubernetesModelConverter, wasmPluginInstanceService) : + new HttpRouteStrategy(kubernetesClientService, kubernetesModelConverter, wasmPluginInstanceService); } } diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/WasmPluginInstanceServiceImpl.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/WasmPluginInstanceServiceImpl.java index c544fa15..d1cf069e 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/WasmPluginInstanceServiceImpl.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/WasmPluginInstanceServiceImpl.java @@ -12,6 +12,8 @@ */ package com.alibaba.higress.sdk.service; +import com.alibaba.higress.sdk.constant.HigressConstants; +import com.alibaba.higress.sdk.constant.Separators; import java.io.IOException; import java.io.StringReader; import java.util.Collection; @@ -34,11 +36,13 @@ import com.alibaba.higress.sdk.model.WasmPluginInstanceScope; import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRoute; import com.alibaba.higress.sdk.service.kubernetes.KubernetesUtil; import com.alibaba.higress.sdk.service.kubernetes.crd.wasm.V1alpha1WasmPlugin; import com.alibaba.higress.sdk.util.MapUtil; import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1Ingress; import io.swagger.v3.core.util.Yaml; import lombok.extern.slf4j.Slf4j; @@ -50,7 +54,7 @@ class WasmPluginInstanceServiceImpl implements WasmPluginInstanceService { private final KubernetesModelConverter kubernetesModelConverter; public WasmPluginInstanceServiceImpl(WasmPluginService wasmPluginService, - KubernetesClientService kubernetesClientService, KubernetesModelConverter kubernetesModelConverter) { + KubernetesClientService kubernetesClientService, KubernetesModelConverter kubernetesModelConverter) { this.wasmPluginService = wasmPluginService; this.kubernetesClientService = kubernetesClientService; this.kubernetesModelConverter = kubernetesModelConverter; @@ -95,7 +99,7 @@ public List list(WasmPluginInstanceScope scope, String targe return Collections.emptyList(); } return plugins.stream().map(p -> kubernetesModelConverter.getWasmPluginInstanceFromCr(p, scope, target)) - .filter(Objects::nonNull).toList(); + .filter(Objects::nonNull).toList(); } @Override @@ -155,6 +159,11 @@ public WasmPluginInstance addOrUpdate(WasmPluginInstance instance) { throw new IllegalArgumentException( "instance.target must not be null or empty when scope is not GLOBAL."); } + if (!isTargetIngressWorkMode(entry.getValue())) { + if (!HigressConstants.NS_DEFAULT.equals(kubernetesClientService.httpRouteNameSpace)) { + entry.setValue(kubernetesClientService.httpRouteNameSpace + Separators.SLASH + entry.getValue()); + } + } } } @@ -185,7 +194,7 @@ public WasmPluginInstance addOrUpdate(WasmPluginInstance instance) { instance.setConfigurations(configurations); } catch (IOException e) { throw new ValidationException( - "Error occurs when parsing raw configurations: " + instance.getRawConfigurations(), e); + "Error occurs when parsing raw configurations: " + instance.getRawConfigurations(), e); } } @@ -215,11 +224,33 @@ public WasmPluginInstance addOrUpdate(WasmPluginInstance instance) { throw new ResourceConflictException(); } throw new BusinessException( - "Error occurs when adding or updating the WasmPlugin CR with name: " + plugin.getName(), e); + "Error occurs when adding or updating the WasmPlugin CR with name: " + plugin.getName(), e); } return kubernetesModelConverter.getWasmPluginInstanceFromCr(result, targets); } + public Boolean isTargetIngressWorkMode(String target) { + V1Ingress ingress; + try { + ingress = kubernetesClientService.readIngress(target); + } catch (ApiException e) { + throw new BusinessException("Error occurs when reading the Ingress with name: " + target, e); + } + if (ingress != null) { + return Boolean.TRUE; + } + V1HTTPRoute httpRoute; + try { + httpRoute = kubernetesClientService.readHttpRoute(target); + } catch (ApiException e) { + throw new BusinessException("Error occurs when reading the HttpRoute with name: " + target, e); + } + if (httpRoute != null) { + return Boolean.FALSE; + } + return Boolean.TRUE; + } + @Override public void delete(WasmPluginInstanceScope scope, String target, String pluginName) { delete(MapUtil.of(scope, target), pluginName); @@ -269,7 +300,7 @@ private void deletePluginInstances(List crs, Map data = higressConfig.getData(); + workMode = "ingress"; + if (data!=null && data.containsKey("workMode")) { + workMode = data.get("workMode"); + } + } + public boolean isIngressV1Supported() { + return ingressV1Supported; + } + public boolean isIngressWorkMode() { + return workMode.equals("ingress"); + } + + public Boolean setIngressMode(Boolean isIngressMode) throws ApiException { + if (isIngressMode == null) { + isIngressMode = true; // default to ingress mode + } + + V1ConfigMap higressConfig = readConfigMap(HigressConstants.DEFAULT_CONFIG); + if (higressConfig == null) { + throw new BusinessException("ConfigMap not found: " + HigressConstants.DEFAULT_CONFIG); + } + + Map data = higressConfig.getData(); + if (data == null) { + data = new HashMap<>(); + higressConfig.setData(data); + } + + data.put("workMode", isIngressMode ? "ingress" : "gateway"); + + replaceConfigMap(higressConfig); + getIngressOrGatewayMode(); + return isIngressWorkMode(); + } + public boolean isNamespaceProtected(String namespace) { return KubernetesConstants.KUBE_SYSTEM_NS.equals(namespace) || controllerNamespace.equals(namespace); } + private void createSecretReferenceGrantForGateway() throws ApiException { + if (isIngressWorkMode() || controllerNamespace.equals(gatewayNameSpace)) { + return; + } + String referenceGrantName = KubernetesUtil.getReferenceGrantName(controllerNamespace, "gateway2secret"); + V1beta1ReferenceGrant v1beta1ReferenceGrant = readReferenceGrant(referenceGrantName, controllerNamespace); + if (v1beta1ReferenceGrant == null) { + v1beta1ReferenceGrant = new V1beta1ReferenceGrant(); + V1ObjectMeta metadata = new V1ObjectMeta(); + metadata.setName(referenceGrantName); + metadata.setNamespace(controllerNamespace); + v1beta1ReferenceGrant.setMetadata(metadata); + V1beta1ReferenceGrantSpec spec = new V1beta1ReferenceGrantSpec(); + V1beta1ReferenceGrantSpecFrom from = new V1beta1ReferenceGrantSpecFrom(); + from.setNamespace(gatewayNameSpace); + from.setKind(V1Gateway.KIND); + from.setGroup(V1Gateway.API_GROUP); + spec.getFrom().add(from); + V1beta1ReferenceGrantSpecTo to = new V1beta1ReferenceGrantSpecTo(); + to.setKind("Secret"); + to.setGroup(""); + spec.getTo().add(to); + v1beta1ReferenceGrant.setSpec(spec); + createReferenceGrant(v1beta1ReferenceGrant); + } + } public boolean isDefinedByConsole(KubernetesObject metadata) { return isDefinedByConsole(metadata.getMetadata()); } @@ -347,6 +478,21 @@ public V1ConfigMap readConfigMap(String name) throws ApiException { } } + public V1ConfigMap readOrCreateConfigMap(String name) throws ApiException { + CoreV1Api coreV1Api = new CoreV1Api(client); + try { + return coreV1Api.readNamespacedConfigMap(name, controllerNamespace, null); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.NOT_FOUND) { + // ConfigMap not found, create a new one + V1ConfigMap newConfigMap = new V1ConfigMap(); + newConfigMap.setMetadata(new V1ObjectMeta().name(name)); + return createConfigMap(newConfigMap); + } + throw e; + } + } + public void deleteConfigMap(String name) throws ApiException { CoreV1Api coreV1Api = new CoreV1Api(client); V1Status status; @@ -482,6 +628,283 @@ public V1McpBridge readMcpBridge(String name) throws ApiException { } } + public List listGateway() { + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + try { + Object response = customObjectsApi.listNamespacedCustomObject(V1Gateway.API_GROUP, V1Gateway.VERSION, + gatewayNameSpace, V1Gateway.PLURAL, null, null, null, null, null, null, null, null, null, null); + io.kubernetes.client.openapi.JSON json = new io.kubernetes.client.openapi.JSON(); + V1GatewayList list = json.deserialize(json.serialize(response), V1GatewayList.class); + return sortKubernetesObjects(list.getItems()); + } catch (ApiException e) { + log.error("listGateway Status code: " + e.getCode() + "Reason: " + e.getResponseBody() + + "Response headers: " + e.getResponseHeaders(), e); + return null; + } + } + + public V1Gateway createGateway(V1Gateway gateway) throws ApiException { + renderDefaultLabels(gateway); + modifyLoadBalancerPorts(null, gateway); + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + Object response = customObjectsApi.createNamespacedCustomObject(V1Gateway.API_GROUP, V1Gateway.VERSION, + gatewayNameSpace, V1Gateway.PLURAL, gateway, null, null, null); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1Gateway.class); + } + + public V1Gateway replaceGateway(V1Gateway gateway) throws ApiException { + V1ObjectMeta metadata = gateway.getMetadata(); + if (metadata == null) { + throw new IllegalArgumentException("gateway doesn't have a valid metadata."); + } + renderDefaultLabels(gateway); + V1Gateway gatewayOri = readGateway(metadata.getName()); + modifyLoadBalancerPorts(gatewayOri, gateway); + gateway.getMetadata().setResourceVersion(gatewayOri.getMetadata().getResourceVersion()); + metadata.setNamespace(gatewayNameSpace); + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + Object response = customObjectsApi.replaceNamespacedCustomObject(V1Gateway.API_GROUP, V1Gateway.VERSION, + gatewayNameSpace, V1Gateway.PLURAL, metadata.getName(), gateway, null, null); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1Gateway.class); + } + + public void deleteGateway(String name) throws ApiException { + V1Gateway gateway = readGateway(name); + modifyLoadBalancerPorts(gateway, null); + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + customObjectsApi.deleteNamespacedCustomObject(V1Gateway.API_GROUP, V1Gateway.VERSION, gatewayNameSpace, + V1Gateway.PLURAL, name, null, null, null, null, null); + } + + public V1Gateway readGateway(String name) throws ApiException { + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + try { + Object response = customObjectsApi.getNamespacedCustomObject(V1Gateway.API_GROUP, V1Gateway.VERSION, + gatewayNameSpace, V1Gateway.PLURAL, name); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1Gateway.class); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.NOT_FOUND) { + return null; + } + throw e; + } + } + + public void modifyLoadBalancerPorts(V1Gateway gatewayOri, V1Gateway gatewayReplaced) { + CoreV1Api coreV1Api = new CoreV1Api(client); + try { + // get port counter from config + V1ConfigMap portConfig = readOrCreateConfigMap(HigressConstants.PORT_CONFIG); + Map portCount = portConfig.getData(); + if (portCount == null) { + portCount = new HashMap<>(); + } + V1Service service = coreV1Api.readNamespacedService(V1GatewayClass.DEFAULT_NAME, gatewayNameSpace, null); + List ports = Objects.requireNonNull(service.getSpec()).getPorts(); + assert ports != null; + + Map portFrequency = new HashMap<>(); + if (gatewayOri != null) { + List listenersOri = gatewayOri.getSpec().getListeners(); + for (V1GatewaySpecListeners listener : listenersOri) { + portFrequency.put(listener.getPort(), portFrequency.getOrDefault(listener.getPort(), 0) - 1); + } + } + if (gatewayReplaced != null) { + List listenersReplaced = gatewayReplaced.getSpec().getListeners(); + for (V1GatewaySpecListeners listener : listenersReplaced) { + portFrequency.put(listener.getPort(), portFrequency.getOrDefault(listener.getPort(), 0) + 1); + } + } + + for (Map.Entry entry : portFrequency.entrySet()) { + Integer port = entry.getKey(); + Integer freq = entry.getValue(); + if (port == 80 || port == 443 || freq == 0) { + continue; + } + int count = Integer.parseInt(portCount.getOrDefault(port.toString(), "0")) + (freq>0?1:-1); + + if (count <= 0) { + portCount.remove(port.toString()); + ports.removeIf(p -> p.getPort().equals(port)); + } else { + portCount.put(port.toString(), Integer.toString(count)); + if (count==1) { + V1ServicePort servicePort = new V1ServicePort().port(port); + servicePort.setProtocol("TCP"); + servicePort.setName("port"+Separators.DASH+ port); + ports.add(servicePort); + } + } + } + + portConfig.setData(portCount); + replaceConfigMap(portConfig); + service.getSpec().setPorts(ports); + coreV1Api.replaceNamespacedService(V1GatewayClass.DEFAULT_NAME, gatewayNameSpace, service, null, null, null, null); + } catch (ApiException e) { + log.error("Error when modifying LoadBalancer ports ", e); + } + } + + public List listHttpRouteByDomain(String domainName) { + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + String labelSelectors = joinLabelSelectors(DEFAULT_LABEL_SELECTORS, buildDomainLabelSelector(domainName)); + try { + Object response = customObjectsApi.listNamespacedCustomObject(V1HTTPRoute.API_GROUP, V1HTTPRoute.VERSION, + httpRouteNameSpace, V1HTTPRoute.PLURAL, null, null, null, null, labelSelectors, null, null, null, null, null); + io.kubernetes.client.openapi.JSON json = new io.kubernetes.client.openapi.JSON(); + V1HTTPRouteList list = json.deserialize(json.serialize(response), V1HTTPRouteList.class); + if (list == null) { + return Collections.emptyList(); + } + return sortKubernetesObjects(list.getItems()); + } catch (ApiException e) { + log.error("listHttpRouteByDomain Status code: " + e.getCode() + "Reason: " + e.getResponseBody() + + "Response headers: " + e.getResponseHeaders(), e); + return null; + } + } + + public List listHttpRoute() { + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + try { + Object response = customObjectsApi.listNamespacedCustomObject(V1HTTPRoute.API_GROUP, V1HTTPRoute.VERSION, + httpRouteNameSpace, V1HTTPRoute.PLURAL, null, null, null, null, null, null, null, null, null, null); + io.kubernetes.client.openapi.JSON json = new io.kubernetes.client.openapi.JSON(); + V1HTTPRouteList list = json.deserialize(json.serialize(response), V1HTTPRouteList.class); + if (list == null) { + return Collections.emptyList(); + } + return sortKubernetesObjects(list.getItems()); + } catch (ApiException e) { + log.error("listHttpRouteByDomain Status code: " + e.getCode() + "Reason: " + e.getResponseBody() + + "Response headers: " + e.getResponseHeaders(), e); + return null; + } + } + + public V1HTTPRoute readHttpRoute(String name) throws ApiException { + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + try { + Object response = customObjectsApi.getNamespacedCustomObject(V1HTTPRoute.API_GROUP, V1HTTPRoute.VERSION, + httpRouteNameSpace, V1HTTPRoute.PLURAL, name); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1HTTPRoute.class); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.NOT_FOUND) { + return null; + } + throw e; + } + } + + public V1HTTPRoute createHttpRoute(V1HTTPRoute httpRoute) throws ApiException { + renderDefaultLabels(httpRoute); + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + Object response = customObjectsApi.createNamespacedCustomObject(V1HTTPRoute.API_GROUP, V1HTTPRoute.VERSION, + httpRouteNameSpace, V1HTTPRoute.PLURAL, httpRoute, null, null, null); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1HTTPRoute.class); + } + + public void deleteHttpRoute(String name) throws ApiException { + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + customObjectsApi.deleteNamespacedCustomObject(V1HTTPRoute.API_GROUP, V1HTTPRoute.VERSION, httpRouteNameSpace, + V1HTTPRoute.PLURAL, name, null, null, null, null, null); + } + + public V1HTTPRoute replaceHttpRoute(V1HTTPRoute httpRoute) throws ApiException { + V1ObjectMeta metadata = httpRoute.getMetadata(); + if (metadata == null) { + throw new IllegalArgumentException("httpRoute doesn't have a valid metadata."); + } + renderDefaultLabels(httpRoute); + metadata.setNamespace(httpRouteNameSpace); + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + Object response = customObjectsApi.replaceNamespacedCustomObject(V1HTTPRoute.API_GROUP, V1HTTPRoute.VERSION, + httpRouteNameSpace, V1HTTPRoute.PLURAL, metadata.getName(), httpRoute, null, null); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1HTTPRoute.class); + } + + public V1beta1ReferenceGrant addReferenceGrantForHttpRoute2Service(String fromNamespace, String toNamespace) throws ApiException { + if (fromNamespace.equals(toNamespace)) { + return null; + } + String referenceGrantName = KubernetesUtil.getReferenceGrantName(toNamespace, "httproute2service"); + V1beta1ReferenceGrant v1beta1ReferenceGrant = readReferenceGrant(referenceGrantName, toNamespace); + if (v1beta1ReferenceGrant == null) { + v1beta1ReferenceGrant = new V1beta1ReferenceGrant(); + V1ObjectMeta metadata = new V1ObjectMeta(); + metadata.setName(referenceGrantName); + metadata.setNamespace(toNamespace); + v1beta1ReferenceGrant.setMetadata(metadata); + V1beta1ReferenceGrantSpec spec = new V1beta1ReferenceGrantSpec(); + V1beta1ReferenceGrantSpecFrom from = new V1beta1ReferenceGrantSpecFrom(); + from.setNamespace(fromNamespace); + from.setKind(V1HTTPRoute.KIND); + from.setGroup(V1HTTPRoute.API_GROUP); + spec.getFrom().add(from); + V1beta1ReferenceGrantSpecTo to = new V1beta1ReferenceGrantSpecTo(); + to.setKind("Service"); + to.setGroup(""); + spec.getTo().add(to); + v1beta1ReferenceGrant.setSpec(spec); + return createReferenceGrant(v1beta1ReferenceGrant); + } else { + List froms = v1beta1ReferenceGrant.getSpec().getFrom(); + for (V1beta1ReferenceGrantSpecFrom from : froms) { + if (fromNamespace.equals(from.getNamespace())) { + return v1beta1ReferenceGrant; + } + } + V1beta1ReferenceGrantSpecFrom from = new V1beta1ReferenceGrantSpecFrom(); + from.setNamespace(fromNamespace); + from.setKind(V1HTTPRoute.KIND); + from.setGroup(V1HTTPRoute.API_GROUP); + froms.add(from); + return replaceReferenceGrant(v1beta1ReferenceGrant); + } + } + public V1beta1ReferenceGrant readReferenceGrant(String name, String nameSpace) throws ApiException { + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + try { + Object response = customObjectsApi.getNamespacedCustomObject(V1beta1ReferenceGrant.API_GROUP, V1beta1ReferenceGrant.VERSION, + nameSpace, V1beta1ReferenceGrant.PLURAL, name); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1beta1ReferenceGrant.class); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.NOT_FOUND) { + return null; + } + throw e; + } + } + public V1beta1ReferenceGrant createReferenceGrant(V1beta1ReferenceGrant referenceGrant) throws ApiException { + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + String namespace = controllerNamespace; + if (referenceGrant.getMetadata()!=null && referenceGrant.getMetadata().getNamespace() != null) { + namespace = referenceGrant.getMetadata().getNamespace(); + } + Object response = customObjectsApi.createNamespacedCustomObject(V1beta1ReferenceGrant.API_GROUP, V1beta1ReferenceGrant.VERSION, + namespace, V1beta1ReferenceGrant.PLURAL, referenceGrant, null, null, null); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1beta1ReferenceGrant.class); + } + + public V1beta1ReferenceGrant replaceReferenceGrant(V1beta1ReferenceGrant referenceGrant) throws ApiException{ + V1ObjectMeta metadata = referenceGrant.getMetadata(); + if (metadata == null) { + throw new IllegalArgumentException("gateway doesn't have a valid metadata."); + } + String namespace = controllerNamespace; + if (metadata.getNamespace() != null) { + namespace = referenceGrant.getMetadata().getNamespace(); + } + CustomObjectsApi customObjectsApi = new CustomObjectsApi(client); + Object response = customObjectsApi.replaceNamespacedCustomObject(V1beta1ReferenceGrant.API_GROUP, V1beta1ReferenceGrant.VERSION, + namespace, V1beta1ReferenceGrant.PLURAL, metadata.getName(), referenceGrant, null, null); + return client.getJSON().deserialize(client.getJSON().serialize(response), V1beta1ReferenceGrant.class); + } + + public List listWasmPlugin() throws ApiException { return listWasmPlugin(null, null, null); } diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesModelConverter.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesModelConverter.java index 9679100d..e3bc6c59 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesModelConverter.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesModelConverter.java @@ -38,6 +38,29 @@ import javax.naming.ldap.LdapName; import javax.security.auth.x500.X500Principal; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gatewayclass.V1GatewayClass; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1Gateway; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1GatewaySpec; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1GatewaySpecListeners; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1GatewaySpecTls; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRoute; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpec; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecBackendRefs; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecFilters; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecHeaders; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecMatches; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecParentRefs; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecPath; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecQueryParams; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecRequestHeaderModifier; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecRequestHeaderModifierAdd; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecResponseHeaderModifier; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecRules; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecUrlRewrite; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecUrlRewritePath; +import io.kubernetes.client.common.KubernetesObject; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.ObjectUtils; @@ -185,8 +208,76 @@ public boolean isIngressSupported(V1Ingress ingress) { return true; } + public boolean isGatewaySupported(V1Gateway gateway) { + if (gateway.getMetadata() == null) { + return false; + } + V1GatewaySpec spec = gateway.getSpec(); + if (spec == null) { + return false; + } + if (StringUtils.isEmpty(spec.getGatewayClassName())) { + return false; + } + if (CollectionUtils.isEmpty(spec.getListeners())) { + return false; + } + // Check each listener + for (V1GatewaySpecListeners listener : spec.getListeners()) { + if (StringUtils.isEmpty(listener.getName())) { + return false; + } + if (listener.getProtocol() == null || + !listener.getProtocol().equals("HTTP") && !listener.getProtocol().equals("HTTPS")) { + return false; + } + if (listener.getProtocol().equals("HTTPS")) { + V1GatewaySpecTls tls = listener.getTls(); + if (tls == null || CollectionUtils.isEmpty(tls.getCertificateRefs())) { + return false; + } + } + if (listener.getPort() == null || listener.getPort() <= 0) { + return false; + } + } + return true; + } + + public boolean isHttpRouteSupported(V1HTTPRoute httpRoute) { + if (httpRoute.getMetadata() == null) { + return false; + } + V1HTTPRouteSpec spec = httpRoute.getSpec(); + if (spec == null) { + return false; + } + if (CollectionUtils.isEmpty(spec.getParentRefs())) { + return false; + } + if (CollectionUtils.isEmpty(spec.getRules())) { + return false; + } + if (!CollectionUtils.isEmpty(spec.getHostnames()) && spec.getHostnames().size() > 1) { + return false; + } + for (V1HTTPRouteSpecRules rule : spec.getRules()) { + if (CollectionUtils.isEmpty(rule.getBackendRefs())) { + return false; + } + for (V1HTTPRouteSpecBackendRefs backendRef : rule.getBackendRefs()) { + if (backendRef.getKind()!=null && !"Service".equals(backendRef.getKind())) { + return false; + } + } + } + return true; + } + + public Route ingress2Route(V1Ingress ingress) { Route route = new Route(); + route.setIsIngressMode(Boolean.TRUE); fillRouteMetadata(route, ingress.getMetadata()); fillRouteInfo(route, ingress.getMetadata(), ingress.getSpec()); fillCustomConfigs(route, ingress.getMetadata()); @@ -205,6 +296,63 @@ public V1Ingress route2Ingress(Route route) { return ingress; } + public Route httpRoute2Route(V1HTTPRoute httpRoute){ + Route route = new Route(); + route.setIsIngressMode(Boolean.FALSE); + fillRouteMetadata(route, httpRoute.getMetadata()); + fillRouteInfoFromHttpRoute(route, httpRoute.getSpec(), httpRoute.getMetadata()); + fillCustomConfigs(route, httpRoute.getMetadata()); + route.setReadonly(!kubernetesClientService.isDefinedByConsole(httpRoute) || !isHttpRouteSupported(httpRoute)); + return route; + } + + public V1HTTPRoute route2HttpRoute(Route route){ + V1HTTPRoute httpRoute = new V1HTTPRoute(); + httpRoute.setMetadata(new V1ObjectMeta()); + httpRoute.setSpec(new V1HTTPRouteSpec()); + fillHttpRouteMetadata(httpRoute, route); + fillHttpRouteSpec(httpRoute, route); + fillIngressCors(httpRoute, route); + fillIngressAnnotations(httpRoute, route); + return httpRoute; + } + + public V1Gateway domain2Gateway(Domain domain){ + V1Gateway gateway = new V1Gateway(); + V1ObjectMeta metadata = new V1ObjectMeta(); + metadata.setName(domainName2GatewayName(domain.getName())); + metadata.setResourceVersion(domain.getVersion()); + metadata.setNamespace(kubernetesClientService.gatewayNameSpace); + gateway.setMetadata(metadata); + // set the gateway address to higress-gateway + V1GatewaySpec spec = new V1GatewaySpec().addDefaultAddress(); + spec.setGatewayClassName(V1GatewayClass.DEFAULT_NAME); + List listeners = new ArrayList<>(); + if (domain.getPortAndCertMap().isEmpty()){ + log.error("Domain must have at least one port!"); + return null; + } + domain.getPortAndCertMap().forEach((port, cert) -> { + V1GatewaySpecListeners listener = new V1GatewaySpecListeners(); + listener.setName(KubernetesUtil.normalizeDomainName(domain.getName())+Separators.DASH+port); + listener.setPort(port); + if (!Separators.ASTERISK.equals(domain.getName()) && !HigressConstants.DEFAULT_DOMAIN.equals(domain.getName())) { + listener.setHostname(domain.getName()); + } + listener.setProtocol("HTTP"); + if (!"".equals(cert)){ + listener.setProtocol("HTTPS"); + V1GatewaySpecTls tls = V1GatewaySpecListeners.getDefaultTls(cert); + listener.setTls(tls); + } + listener.setAllowedRoutes(V1GatewaySpecListeners.getDefaultAllowedRoutes()); + listeners.add(listener); + }); + spec.setListeners(listeners); + gateway.setSpec(spec); + return gateway; + } + private static void fillRouteCors(Route route, V1ObjectMeta metadata) { if (metadata == null || metadata.getAnnotations() == null) { return; @@ -253,8 +401,10 @@ public V1ConfigMap domain2ConfigMap(Domain domain) { Map configMap = new HashMap<>(); configMap.put(CommonKey.DOMAIN, domain.getName()); - configMap.put(KubernetesConstants.K8S_CERT, domain.getCertIdentifier()); + configMap.put(KubernetesConstants.K8S_CERT, JSON.toJSONString(domain.getPortAndCertMap())); configMap.put(KubernetesConstants.K8S_ENABLE_HTTPS, domain.getEnableHttps()); + configMap.put(KubernetesConstants.WorkMode.KEY, + domain.getIsIngressMode()==null || domain.getIsIngressMode() ? KubernetesConstants.WorkMode.INGRESS : KubernetesConstants.WorkMode.GATEWAY); domainConfigMap.data(configMap); return domainConfigMap; @@ -273,7 +423,22 @@ public Domain configMap2Domain(V1ConfigMap configMap) { throw new IllegalArgumentException("No data is found in the ConfigMap."); } domain.setName(configMapData.get(CommonKey.DOMAIN)); - domain.setCertIdentifier(configMapData.get(KubernetesConstants.K8S_CERT)); + String certData = configMapData.get(KubernetesConstants.K8S_CERT); + domain.setIsIngressMode(Boolean.TRUE); + try { + domain.setPortAndCertMap(JSON.parseObject(certData, Map.class)); + String workMode = configMapData.get(KubernetesConstants.WorkMode.KEY); + // if workMode is null -> set to true(ingress) + domain.setIsIngressMode(!KubernetesConstants.WorkMode.GATEWAY.equals(workMode)); + }catch(JSONException e){ + // ingress-style cert (Only String) -> turn to map[443:certName] + Map portCertMap = new HashMap<>(); + if (StringUtils.isNotEmpty(certData)) { + portCertMap.put(443, certData); + } + domain.setPortAndCertMap(portCertMap); + } + domain.setEnableHttps(configMapData.get(KubernetesConstants.K8S_ENABLE_HTTPS)); return domain; } @@ -282,6 +447,13 @@ public String domainName2ConfigMapName(String domainName) { return CommonKey.DOMAIN_PREFIX + KubernetesUtil.normalizeDomainName(domainName); } + public String domainName2GatewayName(String domainName) { + return KubernetesUtil.normalizeDomainName(domainName); + } + + public String gatewayName2DomainName(String gatewayName) { + return gatewayName.replace('-', '.'); + } public V1ConfigMap aiRoute2ConfigMap(AiRoute route) { V1ConfigMap domainConfigMap = new V1ConfigMap(); @@ -975,13 +1147,12 @@ private static void fillPathRoute(Route route, V1ObjectMeta metadata, V1HTTPIngr fillRouteDestinations(route, metadata, path.getBackend()); } - private void fillIngressCors(V1Ingress ingress, Route route) { + private void fillIngressCors(KubernetesObject object, Route route) { CorsConfig cors = route.getCors(); if (Objects.isNull(cors)) { return; } - - V1ObjectMeta metadata = Objects.requireNonNull(ingress.getMetadata()); + V1ObjectMeta metadata = Objects.requireNonNull(object.getMetadata()); if (!Objects.isNull(cors.getEnabled())) { KubernetesUtil.setAnnotation(metadata, KubernetesConstants.Annotation.CORS_ENABLED_KEY, cors.getEnabled().toString()); @@ -1012,7 +1183,7 @@ private void fillIngressCors(V1Ingress ingress, Route route) { } } - private void fillIngressAnnotations(V1Ingress ingress, Route route) { + private void fillIngressAnnotations(KubernetesObject object, Route route) { if (MapUtils.isEmpty(route.getCustomConfigs())) { return; } @@ -1030,7 +1201,7 @@ private void fillIngressAnnotations(V1Ingress ingress, Route route) { + "Please configure it in the corresponding section instead of using custom annotations."); } } - KubernetesUtil.setAnnotation(ingress, config.getKey(), config.getValue()); + KubernetesUtil.setAnnotation(object, config.getKey(), config.getValue()); } } @@ -1460,14 +1631,17 @@ private void fillIngressTls(V1ObjectMeta metadata, V1IngressSpec spec, Route rou if (configMap == null) { continue; } - Domain domain = configMap2Domain(configMap); + Map portAndCertMap = domain.getPortAndCertMap(); + if (portAndCertMap.get(80)==null || StringUtils.isNotEmpty(portAndCertMap.get(80))) { + throw new BusinessException("The domain does not have port 80 for HTTP enabled."); + } if (Domain.EnableHttps.OFF.equals(domain.getEnableHttps())) { continue; } - if (StringUtils.isEmpty(domain.getCertIdentifier())) { + if (StringUtils.isEmpty(portAndCertMap.get(443))) { continue; } @@ -1475,7 +1649,7 @@ private void fillIngressTls(V1ObjectMeta metadata, V1IngressSpec spec, Route rou if (!HigressConstants.DEFAULT_DOMAIN.equals(domainName)) { tls.setHosts(Collections.singletonList(domainName)); } - tls.setSecretName(domain.getCertIdentifier()); + tls.setSecretName(portAndCertMap.get(443)); if (tlses == null) { tlses = new ArrayList<>(); spec.setTls(tlses); @@ -1571,6 +1745,568 @@ private static void fillIngressDestination(V1ObjectMeta metadata, Route route) { } } + private void fillHttpRouteMetadata(V1HTTPRoute httpRoute, Route route) { + V1ObjectMeta metadata = Objects.requireNonNull(httpRoute.getMetadata()); + metadata.setName(route.getName()); + metadata.setResourceVersion(route.getVersion()); + metadata.setNamespace(kubernetesClientService.httpRouteNameSpace); + metadata.setLabels(new HashMap<>()); + List routeDomains = route.getDomains(); + if (CollectionUtils.isNotEmpty(routeDomains)) { + for (String domainName : routeDomains) { + setDomainLabel(metadata, domainName); + if (Strings.isNullOrEmpty(domainName)) { + continue; + } + V1ConfigMap configMap; + try { + configMap = kubernetesClientService.readConfigMap(domainName2ConfigMapName(domainName)); + } catch (ApiException e) { + throw new BusinessException("Error occurs when reading config map associated with domain " + domainName, + e); + } + if (configMap == null) { + continue; + } + Domain domain = configMap2Domain(configMap); + if (Domain.EnableHttps.FORCE.equals(domain.getEnableHttps())) { + KubernetesUtil.setAnnotation(metadata, KubernetesConstants.Annotation.SSL_REDIRECT_KEY, + KubernetesConstants.Annotation.TRUE_VALUE); + } + } + } + fillIngressProxyNextUpstreamConfig(metadata, route.getProxyNextUpstream()); + } + private void fillHttpRouteSpec(V1HTTPRoute httpRoute, Route route) { + V1HTTPRouteSpec spec = Objects.requireNonNull(httpRoute.getSpec()); + List domains = route.getDomains(); + if(domains.size() > 1) { + throw new IllegalArgumentException("Only one domain is allowed, domain size: " + domains.size()); + } + V1HTTPRouteSpecParentRefs parentRef = new V1HTTPRouteSpecParentRefs(); + if (!domains.isEmpty()) { + String domainName = domains.get(0); + if (!Separators.ASTERISK.equals(domainName)) { + spec.addHostnamesItem(domainName); + } + parentRef.setName(domainName2GatewayName(domainName)); + parentRef.setNamespace(kubernetesClientService.gatewayNameSpace); + } + spec.addParentRefsItem(parentRef); + V1HTTPRouteSpecRules specRule = new V1HTTPRouteSpecRules(); + fillHttpRouteMatches(httpRoute.getMetadata(), specRule, route); + fillHttpRouteDestination(specRule, route); + fillHttpRouteFilters(specRule, route); + spec.addRulesItem(specRule); + } + + private void fillHttpRouteMatches(V1ObjectMeta metadata, V1HTTPRouteSpecRules specRule, Route route){ + V1HTTPRouteSpecMatches matchesItem = new V1HTTPRouteSpecMatches(); + // path + V1HTTPRouteSpecPath path = new V1HTTPRouteSpecPath(); + RoutePredicate pathPredicate = route.getPath(); + if (pathPredicate != null) { + String matchType = pathPredicate.getMatchType(); + if (StringUtils.isNotEmpty(matchType)) { + if (RoutePredicateTypeEnum.EQUAL.toString().equals(matchType)) { + path.setType(V1HTTPRouteSpecPath.TypeEnum.EXACT); + } else if (RoutePredicateTypeEnum.PRE.toString().equals(matchType)) { + path.setType(V1HTTPRouteSpecPath.TypeEnum.PATHPREFIX); + } else if (RoutePredicateTypeEnum.REGULAR.toString().equals(matchType)) { + path.setType(V1HTTPRouteSpecPath.TypeEnum.REGULAREXPRESSION); + } else { + throw new IllegalArgumentException("Unsupported path match type: " + matchType); + } + path.setValue(pathPredicate.getMatchValue()); + } + if (null != pathPredicate.getCaseSensitive()) { + KubernetesUtil.setAnnotation(metadata, KubernetesConstants.Annotation.IGNORE_PATH_CASE_KEY, + String.valueOf(!pathPredicate.getCaseSensitive())); + } + } + matchesItem.setPath(path); + // header + List specHeaders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(route.getHeaders())) { + for (KeyedRoutePredicate header : route.getHeaders()) { + V1HTTPRouteSpecHeaders specHeader = new V1HTTPRouteSpecHeaders(); + String matchType = header.getMatchType(); + specHeader.setName(header.getKey()); + specHeader.setValue(header.getMatchValue()); + if (RoutePredicateTypeEnum.EQUAL.toString().equals(matchType)) { + specHeader.setType(V1HTTPRouteSpecHeaders.TypeEnum.EXACT); + specHeaders.add(specHeader); + } else if (RoutePredicateTypeEnum.PRE.toString().equals(matchType)) { + specHeader.setType(V1HTTPRouteSpecHeaders.TypeEnum.REGULAREXPRESSION); + specHeader.setValue("^"+Pattern.quote(header.getMatchValue())+".*"); + specHeaders.add(specHeader); + } else if (RoutePredicateTypeEnum.REGULAR.toString().equals(matchType)) { + specHeader.setType(V1HTTPRouteSpecHeaders.TypeEnum.REGULAREXPRESSION); + specHeaders.add(specHeader); + } else { + throw new IllegalArgumentException("Unsupported header match type: " + matchType); + } + } + } + if (CollectionUtils.isNotEmpty(specHeaders)) { + matchesItem.setHeaders(specHeaders); + } + // param + List queryParams = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(route.getUrlParams())) { + for (KeyedRoutePredicate param : route.getUrlParams()) { + V1HTTPRouteSpecQueryParams queryParam = new V1HTTPRouteSpecQueryParams(); + String matchType = param.getMatchType(); + queryParam.setName(param.getKey()); + queryParam.setValue(param.getMatchValue()); + if (RoutePredicateTypeEnum.EQUAL.toString().equals(matchType)) { + queryParam.setType(V1HTTPRouteSpecQueryParams.TypeEnum.EXACT); + queryParams.add(queryParam); + } else if (RoutePredicateTypeEnum.PRE.toString().equals(matchType)) { + queryParam.setType(V1HTTPRouteSpecQueryParams.TypeEnum.REGULAREXPRESSION); + queryParam.setValue("^"+Pattern.quote(param.getMatchValue())+".*"); + queryParams.add(queryParam); + } else if (RoutePredicateTypeEnum.REGULAR.toString().equals(matchType)) { + queryParam.setType(V1HTTPRouteSpecQueryParams.TypeEnum.REGULAREXPRESSION); + queryParams.add(queryParam); + } else { + throw new IllegalArgumentException("Unsupported header match type: " + matchType); + } + } + } + if (CollectionUtils.isNotEmpty(queryParams)) { + matchesItem.setQueryParams(queryParams); + } + // method + // one matchesItem only have one method, need to copy + if (CollectionUtils.isNotEmpty(route.getMethods())) { + for (String method : route.getMethods()) { + V1HTTPRouteSpecMatches matchesItemCopy; + try{ + matchesItemCopy= (V1HTTPRouteSpecMatches) matchesItem.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("Error when clone matches", e); + } + if (V1HTTPRouteSpecMatches.MethodEnum.GET.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.GET); + } else if (V1HTTPRouteSpecMatches.MethodEnum.POST.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.POST); + } else if (V1HTTPRouteSpecMatches.MethodEnum.PUT.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.PUT); + } else if (V1HTTPRouteSpecMatches.MethodEnum.DELETE.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.DELETE); + } else if (V1HTTPRouteSpecMatches.MethodEnum.PATCH.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.PATCH); + } else if (V1HTTPRouteSpecMatches.MethodEnum.HEAD.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.HEAD); + } else if (V1HTTPRouteSpecMatches.MethodEnum.OPTIONS.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.OPTIONS); + } else if (V1HTTPRouteSpecMatches.MethodEnum.CONNECT.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.CONNECT); + } else if (V1HTTPRouteSpecMatches.MethodEnum.TRACE.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.TRACE); + } else { + throw new IllegalArgumentException("Unsupported method type: " + method); + } + specRule.addMatchesItem(matchesItemCopy); + } + } else { + specRule.addMatchesItem(matchesItem); + } + } + + private void fillHttpRouteDestination(V1HTTPRouteSpecRules specRule, Route route){ + List services = route.getServices(); + if (CollectionUtils.isEmpty(services)) { + return; + } + List backendRefs = new ArrayList<>(); + for(UpstreamService service : services){ + String name = service.getName(); + V1HTTPRouteSpecBackendRefs backend = new V1HTTPRouteSpecBackendRefs(); + if (name.contains("cluster.local")){ + String[] split = name.split("\\."); + if (split.length!=5) { + throw new IllegalArgumentException("Illegal svc name: " + name); + } + + backend.setName(split[0]); + + if (split[4].split(":").length>1) { + backend.setPort(Integer.parseInt(split[4].split(":")[1])); + } + String httpRouteNs = HigressConstants.NS_DEFAULT; + if (StringUtils.isNotEmpty(kubernetesClientService.httpRouteNameSpace)) { + httpRouteNs = kubernetesClientService.httpRouteNameSpace; + } + if(!httpRouteNs.equals(split[1])){ + // namespaces of httpRoute and service are different, create referenceGrant + String fromNs = kubernetesClientService.httpRouteNameSpace; + String toNs = split[1]; + try { + kubernetesClientService.addReferenceGrantForHttpRoute2Service(fromNs, toNs); + } catch (Exception ex) { + log.error("Failed to create ReferenceGrant:\nfrom:" + fromNs + " to:" + toNs, ex); + } + backend.setNamespace(split[1]); + } + } else { + // mcp + backend.setName(name); + backend.setGroup(V1McpBridge.API_GROUP); + } + if(service.getWeight()!= null){ + backend.setWeight(service.getWeight()); + } + backendRefs.add(backend); + } + if (CollectionUtils.isNotEmpty(backendRefs)) { + specRule.setBackendRefs(backendRefs); + } else { + specRule.setBackendRefs(Collections.singletonList(new V1HTTPRouteSpecBackendRefs())); + } + } + + private void fillHttpRouteFilters(V1HTTPRouteSpecRules specRule, Route route){ + List filters = new ArrayList<>(); + // url rewrite + RewriteConfig rewrite = route.getRewrite(); + if (rewrite!=null && rewrite.getEnabled()) { + V1HTTPRouteSpecFilters filter = new V1HTTPRouteSpecFilters(); + V1HTTPRouteSpecUrlRewrite urlRewrite = new V1HTTPRouteSpecUrlRewrite(); + if (StringUtils.isNotEmpty(rewrite.getHost())) { + urlRewrite.setHostname(rewrite.getHost()); + } else { + urlRewrite.setHostname(route.getDomains().get(0)); + } + V1HTTPRouteSpecUrlRewritePath path = new V1HTTPRouteSpecUrlRewritePath(); + String matchType = route.getPath().getMatchType(); + if (RoutePredicateTypeEnum.PRE.toString().equals(matchType)) { + // pre: REPLACEPREFIXMATCH + path.setReplacePrefixMatch(rewrite.getPath()); + path.setType(V1HTTPRouteSpecUrlRewritePath.TypeEnum.REPLACEPREFIXMATCH); + } else if (RoutePredicateTypeEnum.REGULAR.toString().equals(matchType) || RoutePredicateTypeEnum.EQUAL.toString().equals(matchType)) { + // regular or exact: REPLACEFULLPATH + path.setReplaceFullPath(rewrite.getPath()); + path.setType(V1HTTPRouteSpecUrlRewritePath.TypeEnum.REPLACEFULLPATH); + } + urlRewrite.setPath(path); + filter.setUrlRewrite(urlRewrite); + filter.setType(V1HTTPRouteSpecFilters.TypeEnum.URLREWRITE); + filters.add(filter); + } + // headerModifier + HeaderControlConfig headerControl = route.getHeaderControl(); + if (headerControl != null && headerControl.getEnabled()) { + if (headerControl.getRequest() != null) { + V1HTTPRouteSpecFilters filter = new V1HTTPRouteSpecFilters(); + HeaderControlStageConfig config = headerControl.getRequest(); + V1HTTPRouteSpecRequestHeaderModifier requestHeaderModifier = new V1HTTPRouteSpecRequestHeaderModifier(); + fillHeaderModifier(config, requestHeaderModifier, null, true); + filter.setRequestHeaderModifier(requestHeaderModifier); + filter.setType(V1HTTPRouteSpecFilters.TypeEnum.REQUESTHEADERMODIFIER); + filters.add(filter); + } + if (headerControl.getResponse() != null) { + V1HTTPRouteSpecFilters filter = new V1HTTPRouteSpecFilters(); + V1HTTPRouteSpecResponseHeaderModifier responseHeaderModifier = new V1HTTPRouteSpecResponseHeaderModifier(); + HeaderControlStageConfig config = headerControl.getResponse(); + fillHeaderModifier(config, null, responseHeaderModifier, false); + filter.setResponseHeaderModifier(responseHeaderModifier); + filter.setType(V1HTTPRouteSpecFilters.TypeEnum.RESPONSEHEADERMODIFIER); + filters.add(filter); + } + } + if (CollectionUtils.isNotEmpty(filters)) { + specRule.setFilters(filters); + } + } + + private void fillHeaderModifier(HeaderControlStageConfig config, V1HTTPRouteSpecRequestHeaderModifier requestHeader, V1HTTPRouteSpecResponseHeaderModifier responseHeader, Boolean isRequest){ + if (config == null) { + return; + } + if (CollectionUtils.isNotEmpty(config.getAdd())) { + for (Header header : config.getAdd()) { + V1HTTPRouteSpecRequestHeaderModifierAdd add = new V1HTTPRouteSpecRequestHeaderModifierAdd(); + add.setName(header.getKey()); + add.setValue(header.getValue()); + if (isRequest) { + requestHeader.addAddItem(add); + } else { + responseHeader.addAddItem(add); + } + } + } + if (CollectionUtils.isNotEmpty(config.getSet())) { + for (Header header : config.getSet()) { + V1HTTPRouteSpecRequestHeaderModifierAdd set = new V1HTTPRouteSpecRequestHeaderModifierAdd(); + set.setName(header.getKey()); + set.setValue(header.getValue()); + if (isRequest) { + requestHeader.addSetItem(set); + } else { + responseHeader.addSetItem(set); + } + } + } + if (CollectionUtils.isNotEmpty(config.getRemove())) { + for (String key : config.getRemove()) { + if (isRequest) { + requestHeader.addRemoveItem(key); + } else { + responseHeader.addRemoveItem(key); + } + } + } + } + + private void fillRouteInfoFromHttpRoute(Route route, V1HTTPRouteSpec spec, V1ObjectMeta metadata) { + if (spec == null) { + return; + } + // domains + List parentRefs = spec.getParentRefs(); + if (CollectionUtils.isNotEmpty(parentRefs)) { + String gatewayName = parentRefs.get(0).getName(); + if (StringUtils.isNotEmpty(gatewayName)) { + String host = gatewayName2DomainName(gatewayName); + route.setDomains(Collections.singletonList(host)); + } else { + route.setDomains(Collections.emptyList()); + } + } else { + route.setDomains(Collections.emptyList()); + } + // paths + List rules = spec.getRules(); + if (CollectionUtils.isNotEmpty(rules)) { + fillPathRouteFromHttpRoute(route, metadata, rules.get(0)); + List filters = rules.get(0).getFilters(); + if (CollectionUtils.isNotEmpty(filters)) { + for (V1HTTPRouteSpecFilters filter : filters) { + if (V1HTTPRouteSpecFilters.TypeEnum.URLREWRITE == filter.getType()) { + fillRewriteConfigFromHttpRoute(route, filter); + } else if (V1HTTPRouteSpecFilters.TypeEnum.REQUESTHEADERMODIFIER == filter.getType() || V1HTTPRouteSpecFilters.TypeEnum.RESPONSEHEADERMODIFIER == filter.getType()) { + fillHeaderConfigFromHttpRoute(route, filter); + } + } + } + fillHeaderAndQueryConfigFromHttpRoute(route, rules.get(0)); + fillMethodConfigFromHttpRoute(route, rules.get(0)); + } + + // retry & cors + Map annotations = metadata.getAnnotations(); + if (MapUtils.isNotEmpty(annotations)) { + fillProxyNextUpstreamConfig(annotations, route); + fillHeaderConfigConfig(annotations, route); + } + fillRouteCors(route, metadata); + } + + private void fillPathRouteFromHttpRoute(Route route, V1ObjectMeta metadata, V1HTTPRouteSpecRules rule) { + // route.path && services + List matches = rule.getMatches(); + RoutePredicate routePredicate = new RoutePredicate(); + if (CollectionUtils.isNotEmpty(matches)) { + V1HTTPRouteSpecPath path = matches.get(0).getPath(); + if (path != null) { + routePredicate.setMatchValue(path.getValue()); + V1HTTPRouteSpecPath.TypeEnum type = path.getType(); + if (type == V1HTTPRouteSpecPath.TypeEnum.EXACT) { + routePredicate.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + } else if (type == V1HTTPRouteSpecPath.TypeEnum.REGULAREXPRESSION) { + routePredicate.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + } else if (type == V1HTTPRouteSpecPath.TypeEnum.PATHPREFIX) { + routePredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + } + } + } + if (metadata != null && metadata.getAnnotations() != null) { + String ignorePathCase = metadata.getAnnotations().get(KubernetesConstants.Annotation.IGNORE_PATH_CASE_KEY); + if (StringUtils.isNotEmpty(ignorePathCase)) { + routePredicate.setCaseSensitive(!Boolean.parseBoolean(ignorePathCase)); + } + } + route.setPath(routePredicate); + List backendRefs = rule.getBackendRefs(); + if (CollectionUtils.isNotEmpty(backendRefs)) { + List services = new ArrayList<>(); + for (V1HTTPRouteSpecBackendRefs backendRef : backendRefs) { + UpstreamService service = new UpstreamService(); + String name = backendRef.getName(); + if (!V1McpBridge.API_GROUP.equals(backendRef.getGroup())) { + if(StringUtils.isNotEmpty(name)) { + if (backendRef.getNamespace()!=null && !backendRef.getNamespace().isEmpty()) { + name = name + "." + backendRef.getNamespace() + ".svc.cluster.local"; + } else { + name = name + ".default.svc.cluster.local"; + } + if (backendRef.getPort() != null) { + name = name + ":" + backendRef.getPort(); + } + } + } + service.setName(name); + service.setWeight(backendRef.getWeight()); + services.add(service); + } + route.setServices(services); + } + } + + private void fillRewriteConfigFromHttpRoute(Route route, V1HTTPRouteSpecFilters filter) { + V1HTTPRouteSpecUrlRewrite urlRewrite = filter.getUrlRewrite(); + if (urlRewrite == null) { + return; + } + RewriteConfig rewrite = new RewriteConfig(); + rewrite.setEnabled(Boolean.TRUE); + rewrite.setHost(urlRewrite.getHostname()); + String matchType = route.getPath().getMatchType(); + if (RoutePredicateTypeEnum.PRE.toString().equals(matchType)) { + rewrite.setPath(urlRewrite.getPath().getReplacePrefixMatch()); + } else if (RoutePredicateTypeEnum.REGULAR.toString().equals(matchType) || RoutePredicateTypeEnum.EQUAL.toString().equals(matchType)) { + rewrite.setPath(urlRewrite.getPath().getReplaceFullPath()); + } + route.setRewrite(rewrite); + } + + private void fillHeaderConfigFromHttpRoute(Route route, V1HTTPRouteSpecFilters filter) { + if (V1HTTPRouteSpecFilters.TypeEnum.REQUESTHEADERMODIFIER == filter.getType()) { + HeaderControlConfig headerControl = new HeaderControlConfig(); + headerControl.setEnabled(Boolean.TRUE); + HeaderControlStageConfig config = new HeaderControlStageConfig(); + headerControl.setRequest(config); + V1HTTPRouteSpecRequestHeaderModifier requestHeaderModifier = filter.getRequestHeaderModifier(); + if (requestHeaderModifier != null) { + List adds = requestHeaderModifier.getAdd(); + if (adds != null) { + for (V1HTTPRouteSpecRequestHeaderModifierAdd add : adds) { + List
configAdd = new ArrayList<>(); + config.setAdd(configAdd); + configAdd.add(new Header(add.getName(), add.getValue())); + } + } + List sets = requestHeaderModifier.getSet(); + if (sets != null) { + for (V1HTTPRouteSpecRequestHeaderModifierAdd set : sets) { + List
configSet = new ArrayList<>(); + config.setSet(configSet); + configSet.add(new Header(set.getName(), set.getValue())); + } + } + List removes = requestHeaderModifier.getRemove(); + if (removes != null) { + config.setRemove(new ArrayList<>(removes)); + } + } + route.setHeaderControl(headerControl); + } else if (V1HTTPRouteSpecFilters.TypeEnum.RESPONSEHEADERMODIFIER == filter.getType()) { + HeaderControlConfig headerControl = new HeaderControlConfig(); + headerControl.setEnabled(Boolean.TRUE); + HeaderControlStageConfig config = new HeaderControlStageConfig(); + headerControl.setResponse(config); + V1HTTPRouteSpecResponseHeaderModifier responseHeaderModifier = filter.getResponseHeaderModifier(); + if (responseHeaderModifier != null) { + List adds = responseHeaderModifier.getAdd(); + if (adds != null) { + for (V1HTTPRouteSpecRequestHeaderModifierAdd add : adds) { + List
configAdd = new ArrayList<>(); + config.setAdd(configAdd); + configAdd.add(new Header(add.getName(), add.getValue())); + } + } + List sets = responseHeaderModifier.getSet(); + if (sets != null) { + for (V1HTTPRouteSpecRequestHeaderModifierAdd set : sets) { + List
configSet = new ArrayList<>(); + config.setSet(configSet); + configSet.add(new Header(set.getName(), set.getValue())); + } + } + List removes = responseHeaderModifier.getRemove(); + if (removes != null) { + config.setRemove(new ArrayList<>(removes)); + } + } + route.setHeaderControl(headerControl); + } + } + + private void fillHeaderAndQueryConfigFromHttpRoute(Route route, V1HTTPRouteSpecRules rule) { + List headers = new ArrayList<>(); + List urlParams = new ArrayList<>(); + List matches = rule.getMatches(); + if (CollectionUtils.isEmpty(matches)) { + return; + } + V1HTTPRouteSpecMatches match = matches.get(0); + List httpRouteHeaders = match.getHeaders(); + if (CollectionUtils.isNotEmpty(httpRouteHeaders)){ + for (V1HTTPRouteSpecHeaders httpRouteHeader: httpRouteHeaders) { + KeyedRoutePredicate header = new KeyedRoutePredicate(); + header.setKey(httpRouteHeader.getName()); + header.setMatchValue(httpRouteHeader.getValue()); + if (V1HTTPRouteSpecHeaders.TypeEnum.EXACT == httpRouteHeader.getType()){ + header.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + } else if (V1HTTPRouteSpecHeaders.TypeEnum.REGULAREXPRESSION == httpRouteHeader.getType()) { + String regex = httpRouteHeader.getValue(); + if (regex.startsWith("^\\Q") && regex.endsWith("\\E.*")) { + header.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + header.setMatchValue(regex.substring(3, regex.length() - 4)); + } else { + header.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + } + } + headers.add(header); + } + } + List queryParams = match.getQueryParams(); + if (CollectionUtils.isNotEmpty(queryParams)) { + for (V1HTTPRouteSpecQueryParams queryParam : queryParams) { + KeyedRoutePredicate param = new KeyedRoutePredicate(); + param.setKey(queryParam.getName()); + param.setMatchValue(queryParam.getValue()); + if (V1HTTPRouteSpecQueryParams.TypeEnum.EXACT == queryParam.getType()) { + param.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + } else if (V1HTTPRouteSpecQueryParams.TypeEnum.REGULAREXPRESSION == queryParam.getType()) { + String regex = queryParam.getValue(); + if (regex.startsWith("^\\Q") && regex.endsWith("\\E.*")) { + param.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + param.setMatchValue(regex.substring(3, regex.length() - 4)); + } else { + param.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + } + } + urlParams.add(param); + } + } + if (CollectionUtils.isNotEmpty(headers)) { + route.setHeaders(headers); + } + if (CollectionUtils.isNotEmpty(urlParams)) { + route.setUrlParams(urlParams); + } + } + + private void fillMethodConfigFromHttpRoute(Route route, V1HTTPRouteSpecRules rule) { + List matches = rule.getMatches(); + if (CollectionUtils.isEmpty(matches)) { + return; + } + List methods = new ArrayList<>(); + for (V1HTTPRouteSpecMatches match : matches) { + if(match.getMethod() != null) { + methods.add(match.getMethod().toString()); + } + } + if (CollectionUtils.isNotEmpty(methods)) { + route.setMethods(methods); + } + } + public ServiceSource v1RegistryConfig2ServiceSource(V1RegistryConfig v1RegistryConfig) { ServiceSource serviceSource = new ServiceSource(); fillServiceSourceInfo(serviceSource, v1RegistryConfig); diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesUtil.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesUtil.java index 778e0a38..2155cce9 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesUtil.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesUtil.java @@ -30,14 +30,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; - import io.kubernetes.client.common.KubernetesObject; import io.kubernetes.client.openapi.models.V1ObjectMeta; + public class KubernetesUtil { private static final ObjectMapper YAML = - new ObjectMapper(new YAMLFactory().enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)); + new ObjectMapper(new YAMLFactory().enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)); public static String toYaml(Object obj) { try { @@ -108,12 +108,33 @@ public static void setAnnotation(V1ObjectMeta metadata, String key, String value } public static String normalizeDomainName(String name) { + if (StringUtils.isNotBlank(name) && name.startsWith(Separators.ASTERISK)) { + name = CommonKey.WILDCARD + name.substring(Separators.ASTERISK.length()); + if (CommonKey.WILDCARD.equals(name)) { + name = HigressConstants.DEFAULT_DOMAIN; + } + } + return name; + } + + public static String normalizeRouteName(String name) { + // for openapi + // domain and path: foo.bar.com/user/{userId} + // turns to : foo.bar.com-user-userId if (StringUtils.isNotBlank(name) && name.startsWith(Separators.ASTERISK)) { name = CommonKey.WILDCARD + name.substring(Separators.ASTERISK.length()); } + name = name.replace("/", "-") + .replaceAll("-\\{", "-") + .replaceAll("\\{", "-") + .replaceAll("}", "").toLowerCase(); return name; } + public static String getReferenceGrantName(String name, String type) { + return name+Separators.DASH+type; + } + public static String joinLabelSelectors(String... selectors) { return String.join(Separators.COMMA, Arrays.stream(selectors).filter(StringUtils::isNotBlank).toArray(String[]::new)); @@ -121,7 +142,7 @@ public static String joinLabelSelectors(String... selectors) { public static String buildDomainLabelSelector(String domainName) { return buildLabelSelector(KubernetesConstants.Label.DOMAIN_KEY_PREFIX + normalizeDomainName(domainName), - KubernetesConstants.Label.DOMAIN_VALUE_DUMMY); + KubernetesConstants.Label.DOMAIN_VALUE_DUMMY); } public static String buildLabelSelector(String name, String value) { diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClass.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClass.java new file mode 100644 index 00000000..4c0f63e1 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClass.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gatewayclass; + +import com.google.gson.annotations.SerializedName; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import lombok.Data; + + +@Data +public class V1GatewayClass implements io.kubernetes.client.common.KubernetesObject { + public static final String API_GROUP = "gateway.networking.k8s.io"; + public static final String VERSION = "v1"; + public static final String KIND = "GatewayClass"; + public static final String PLURAL = "gatewayclasses"; + public static final String DEFAULT_NAME = "higress-gateway"; + + + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_METADATA = "metadata"; + public static final String SERIALIZED_NAME_SPEC = "spec"; + public static final String SERIALIZED_NAME_STATUS = "status"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion = API_GROUP + "/" + VERSION; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind = KIND; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ObjectMeta metadata = null; + @SerializedName(SERIALIZED_NAME_SPEC) + private V1GatewayClassSpec spec; + @SerializedName(SERIALIZED_NAME_STATUS) + private V1GatewayClassStatus status; + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassList.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassList.java new file mode 100644 index 00000000..46218e1e --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassList.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gatewayclass; + +import com.google.gson.annotations.SerializedName; +import io.kubernetes.client.openapi.models.V1ListMeta; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * GatewayClassList is a list of GatewayClass + */ +@Data +public class V1GatewayClassList implements io.kubernetes.client.common.KubernetesListObject { + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + public static final String SERIALIZED_NAME_ITEMS = "items"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_METADATA = "metadata"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion; + @SerializedName(SERIALIZED_NAME_ITEMS) + private List items = new ArrayList<>(); + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ListMeta metadata = null; + + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassSpec.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassSpec.java new file mode 100644 index 00000000..3ae13613 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassSpec.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gatewayclass; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +/** + * Spec defines the desired state of GatewayClass. + */ +@Data +public class V1GatewayClassSpec { + public static final String CONTROLLER_NAME = "higress.io/gateway-controller"; + + public static final String SERIALIZED_NAME_CONTROLLER_NAME = "controllerName"; + public static final String SERIALIZED_NAME_DESCRIPTION = "description"; + public static final String SERIALIZED_NAME_PARAMETERS_REF = "parametersRef"; + @SerializedName(SERIALIZED_NAME_CONTROLLER_NAME) + private String controllerName = CONTROLLER_NAME; + @SerializedName(SERIALIZED_NAME_DESCRIPTION) + private String description; + @SerializedName(SERIALIZED_NAME_PARAMETERS_REF) + private V1GatewayClassSpecParametersRef parametersRef; + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassSpecParametersRef.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassSpecParametersRef.java new file mode 100644 index 00000000..6760fe42 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassSpecParametersRef.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gatewayclass; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + + +/** + * ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the GatewayClass SHOULD be rejected with the \"Accepted\" status condition set to \"False\" and an \"InvalidParameters\" reason. A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. Support: Implementation-specific + */ +@Data +public class V1GatewayClassSpecParametersRef { + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_NAMESPACE = "namespace"; + static final String SERIALIZED_NAME_GROUP = "group"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_NAMESPACE) + private String namespace; + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassStatus.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassStatus.java new file mode 100644 index 00000000..eb0699f3 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassStatus.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gatewayclass; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status defines the current state of GatewayClass. Implementations MUST populate status on all GatewayClass resources which specify their controller name. + */ +@Data +public class V1GatewayClassStatus { + public static final String SERIALIZED_NAME_CONDITIONS = "conditions"; + @SerializedName(SERIALIZED_NAME_CONDITIONS) + private List conditions = null; + + public V1GatewayClassStatus addConditionsItem(V1GatewayClassStatusConditions conditionsItem) { + if (this.conditions == null) { + this.conditions = new ArrayList<>(); + } + this.conditions.add(conditionsItem); + return this; + } +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassStatusConditions.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassStatusConditions.java new file mode 100644 index 00000000..d093653d --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gatewayclass/V1GatewayClassStatusConditions.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gatewayclass; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.time.OffsetDateTime; + +/** + * Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` // other fields } + */ +@Data +public class V1GatewayClassStatusConditions { + + public static final String SERIALIZED_NAME_LAST_TRANSITION_TIME = "lastTransitionTime"; + public static final String SERIALIZED_NAME_MESSAGE = "message"; + public static final String SERIALIZED_NAME_OBSERVED_GENERATION = "observedGeneration"; + public static final String SERIALIZED_NAME_REASON = "reason"; + public static final String SERIALIZED_NAME_STATUS = "status"; + public static final String SERIALIZED_NAME_TYPE = "type"; + @SerializedName(SERIALIZED_NAME_LAST_TRANSITION_TIME) + private OffsetDateTime lastTransitionTime; + @SerializedName(SERIALIZED_NAME_MESSAGE) + private String message; + @SerializedName(SERIALIZED_NAME_OBSERVED_GENERATION) + private Long observedGeneration; + @SerializedName(SERIALIZED_NAME_REASON) + private String reason; + @SerializedName(SERIALIZED_NAME_STATUS) + private StatusEnum status; + @SerializedName(SERIALIZED_NAME_TYPE) + private String type; + /** + * status of the condition, one of True, False, Unknown. + */ + @JsonAdapter(StatusEnum.Adapter.class) + public enum StatusEnum { + /** + * Represents the state where the condition is true. + */ + TRUE("True"), + + /** + * Represents the state where the condition is false. + */ + FALSE("False"), + + /** + * Represents an unknown state. + */ + UNKNOWN("Unknown"); + + private final String value; + + StatusEnum(String value) { + this.value = value; + } + + public static StatusEnum fromValue(String value) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final StatusEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public StatusEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return StatusEnum.fromValue(value); + } + } + } +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1Gateway.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1Gateway.java new file mode 100644 index 00000000..394a39b5 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1Gateway.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import lombok.Data; + +import java.util.Objects; + +/** + * Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. + */ +@Data +public class V1Gateway implements io.kubernetes.client.common.KubernetesObject { + public static final String API_GROUP = "gateway.networking.k8s.io"; + public static final String VERSION = "v1"; + public static final String KIND = "Gateway"; + public static final String PLURAL = "gateways"; + + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_METADATA = "metadata"; + public static final String SERIALIZED_NAME_SPEC = "spec"; + public static final String SERIALIZED_NAME_STATUS = "status"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion = API_GROUP + "/" + VERSION; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind = KIND; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ObjectMeta metadata = null; + @SerializedName(SERIALIZED_NAME_SPEC) + private V1GatewaySpec spec; + @SerializedName(SERIALIZED_NAME_STATUS) + private V1GatewayStatus status; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1Gateway v1Gateway = (V1Gateway) o; + return Objects.equals(this.apiVersion, v1Gateway.apiVersion) && + Objects.equals(this.kind, v1Gateway.kind) && + Objects.equals(this.metadata, v1Gateway.metadata) && + Objects.equals(this.spec, v1Gateway.spec) && + Objects.equals(this.status, v1Gateway.status); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, kind, metadata, spec, status); + } + + + @Override + public String toString() { + String sb = "class V1beta1Gateway {\n" + + " apiVersion: " + toIndentedString(apiVersion) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " metadata: " + toIndentedString(metadata) + "\n" + + " spec: " + toIndentedString(spec) + "\n" + + " status: " + toIndentedString(status) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayList.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayList.java new file mode 100644 index 00000000..298bf7a9 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayList.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import io.kubernetes.client.openapi.models.V1ListMeta; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * GatewayList is a list of Gateway + */ +@Data +public class V1GatewayList implements io.kubernetes.client.common.KubernetesListObject { + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + public static final String SERIALIZED_NAME_ITEMS = "items"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_METADATA = "metadata"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion; + @SerializedName(SERIALIZED_NAME_ITEMS) + private List items = new ArrayList<>(); + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ListMeta metadata = null; + + public V1GatewayList addItemsItem(V1Gateway itemsItem) { + this.items.add(itemsItem); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewayList v1GatewayList = (V1GatewayList) o; + return Objects.equals(this.apiVersion, v1GatewayList.apiVersion) && + Objects.equals(this.items, v1GatewayList.items) && + Objects.equals(this.kind, v1GatewayList.kind) && + Objects.equals(this.metadata, v1GatewayList.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, items, kind, metadata); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewayList {\n" + + " apiVersion: " + toIndentedString(apiVersion) + "\n" + + " items: " + toIndentedString(items) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " metadata: " + toIndentedString(metadata) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpec.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpec.java new file mode 100644 index 00000000..5148ee0b --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpec.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Spec defines the desired state of Gateway. + */ +@Data +public class V1GatewaySpec { + public static final String HIGRESS_GATEWAY_SERVICE = "higress-gateway.higress-system.svc.cluster.local"; + + public static final String SERIALIZED_NAME_ADDRESSES = "addresses"; + public static final String SERIALIZED_NAME_GATEWAY_CLASS_NAME = "gatewayClassName"; + public static final String SERIALIZED_NAME_LISTENERS = "listeners"; + @SerializedName(SERIALIZED_NAME_ADDRESSES) + private List addresses = null; + @SerializedName(SERIALIZED_NAME_GATEWAY_CLASS_NAME) + private String gatewayClassName; + @SerializedName(SERIALIZED_NAME_LISTENERS) + private List listeners = new ArrayList<>(); + + public V1GatewaySpec addAddressesItem(V1GatewaySpecAddresses addressesItem) { + if (this.addresses == null) { + this.addresses = new ArrayList<>(); + } + this.addresses.add(addressesItem); + return this; + } + + public V1GatewaySpec addDefaultAddress() { + V1GatewaySpecAddresses addressesItem = new V1GatewaySpecAddresses(); + addressesItem.setType("Hostname"); + addressesItem.setValue(HIGRESS_GATEWAY_SERVICE); + return addAddressesItem(addressesItem); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpec v1GatewaySpec = (V1GatewaySpec) o; + return Objects.equals(this.addresses, v1GatewaySpec.addresses) && + Objects.equals(this.gatewayClassName, v1GatewaySpec.gatewayClassName) && + Objects.equals(this.listeners, v1GatewaySpec.listeners); + } + + @Override + public int hashCode() { + return Objects.hash(addresses, gatewayClassName, listeners); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpec {\n" + + " addresses: " + toIndentedString(addresses) + "\n" + + " gatewayClassName: " + toIndentedString(gatewayClassName) + "\n" + + " listeners: " + toIndentedString(listeners) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAddresses.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAddresses.java new file mode 100644 index 00000000..d408b927 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAddresses.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * GatewayAddress describes an address that can be bound to a Gateway. + */ +@Data +public class V1GatewaySpecAddresses { + public static final String SERIALIZED_NAME_TYPE = "type"; + public static final String SERIALIZED_NAME_VALUE = "value"; + @SerializedName(SERIALIZED_NAME_TYPE) + private String type; + @SerializedName(SERIALIZED_NAME_VALUE) + private String value; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecAddresses v1GatewaySpecAddresses = (V1GatewaySpecAddresses) o; + return Objects.equals(this.type, v1GatewaySpecAddresses.type) && + Objects.equals(this.value, v1GatewaySpecAddresses.value); + } + + @Override + public int hashCode() { + return Objects.hash(type, value); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecAddresses {\n" + + " type: " + toIndentedString(type) + "\n" + + " value: " + toIndentedString(value) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutes.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutes.java new file mode 100644 index 00000000..d3e85fbe --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutes.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. Support: Core + */ +@Data +public class V1GatewaySpecAllowedRoutes { + public static final String SERIALIZED_NAME_KINDS = "kinds"; + public static final String SERIALIZED_NAME_NAMESPACES = "namespaces"; + @SerializedName(SERIALIZED_NAME_KINDS) + private List kinds = null; + @SerializedName(SERIALIZED_NAME_NAMESPACES) + private V1GatewaySpecAllowedRoutesNamespaces namespaces; + + + public V1GatewaySpecAllowedRoutes addKindsItem(V1GatewaySpecAllowedRoutesKinds kindsItem) { + if (this.kinds == null) { + this.kinds = new ArrayList<>(); + } + this.kinds.add(kindsItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecAllowedRoutes v1GatewaySpecAllowedRoutes = (V1GatewaySpecAllowedRoutes) o; + return Objects.equals(this.kinds, v1GatewaySpecAllowedRoutes.kinds) && + Objects.equals(this.namespaces, v1GatewaySpecAllowedRoutes.namespaces); + } + + @Override + public int hashCode() { + return Objects.hash(kinds, namespaces); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecAllowedRoutes {\n" + + " kinds: " + toIndentedString(kinds) + "\n" + + " namespaces: " + toIndentedString(namespaces) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesKinds.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesKinds.java new file mode 100644 index 00000000..65fbc097 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesKinds.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * RouteGroupKind indicates the group and kind of a Route resource. + */ +@Data +public class V1GatewaySpecAllowedRoutesKinds { + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecAllowedRoutesKinds v1GatewaySpecAllowedRoutesKinds = (V1GatewaySpecAllowedRoutesKinds) o; + return Objects.equals(this.group, v1GatewaySpecAllowedRoutesKinds.group) && + Objects.equals(this.kind, v1GatewaySpecAllowedRoutesKinds.kind); + } + + @Override + public int hashCode() { + return Objects.hash(group, kind); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecAllowedRoutesKinds {\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespaces.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespaces.java new file mode 100644 index 00000000..3148c27b --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespaces.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.Objects; + +/** + * Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. Support: Core + */ +@Data +public class V1GatewaySpecAllowedRoutesNamespaces { + public static final String SERIALIZED_NAME_FROM = "from"; + public static final String SERIALIZED_NAME_SELECTOR = "selector"; + @SerializedName(SERIALIZED_NAME_FROM) + private FromEnum from; + @SerializedName(SERIALIZED_NAME_SELECTOR) + private V1GatewaySpecAllowedRoutesNamespacesSelector selector; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecAllowedRoutesNamespaces v1GatewaySpecAllowedRoutesNamespaces = (V1GatewaySpecAllowedRoutesNamespaces) o; + return Objects.equals(this.from, v1GatewaySpecAllowedRoutesNamespaces.from) && + Objects.equals(this.selector, v1GatewaySpecAllowedRoutesNamespaces.selector); + } + + @Override + public int hashCode() { + return Objects.hash(from, selector); + } + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecAllowedRoutesNamespaces {\n" + + " from: " + toIndentedString(from) + "\n" + + " selector: " + toIndentedString(selector) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. Support: Core + */ + @JsonAdapter(FromEnum.Adapter.class) + public enum FromEnum { + /** + * Routes in all namespaces may be used by this Gateway. + */ + ALL("All"), + /** + * Routes in namespaces selected by the selector may be used by this Gateway. + */ + SELECTOR("Selector"), + /** + * only Routes in the same namespace may be used by this Gateway. + */ + SAME("Same"); + private final String value; + + FromEnum(String value) { + this.value = value; + } + + public static FromEnum fromValue(String value) { + for (FromEnum b : FromEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final FromEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public FromEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return FromEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespacesSelector.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespacesSelector.java new file mode 100644 index 00000000..8ebbd813 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespacesSelector.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". Support: Core + */ +@Data +public class V1GatewaySpecAllowedRoutesNamespacesSelector { + public static final String SERIALIZED_NAME_MATCH_EXPRESSIONS = "matchExpressions"; + public static final String SERIALIZED_NAME_MATCH_LABELS = "matchLabels"; + @SerializedName(SERIALIZED_NAME_MATCH_EXPRESSIONS) + private List matchExpressions = null; + @SerializedName(SERIALIZED_NAME_MATCH_LABELS) + private Map matchLabels = null; + + + public V1GatewaySpecAllowedRoutesNamespacesSelector addMatchExpressionsItem(V1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions matchExpressionsItem) { + if (this.matchExpressions == null) { + this.matchExpressions = new ArrayList<>(); + } + this.matchExpressions.add(matchExpressionsItem); + return this; + } + + /** + * matchExpressions is a list of label selector requirements. The requirements are ANDed. + * + * @return matchExpressions + **/ + + public V1GatewaySpecAllowedRoutesNamespacesSelector putMatchLabelsItem(String key, String matchLabelsItem) { + if (this.matchLabels == null) { + this.matchLabels = new HashMap<>(); + } + this.matchLabels.put(key, matchLabelsItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecAllowedRoutesNamespacesSelector v1GatewaySpecAllowedRoutesNamespacesSelector = (V1GatewaySpecAllowedRoutesNamespacesSelector) o; + return Objects.equals(this.matchExpressions, v1GatewaySpecAllowedRoutesNamespacesSelector.matchExpressions) && + Objects.equals(this.matchLabels, v1GatewaySpecAllowedRoutesNamespacesSelector.matchLabels); + } + + @Override + public int hashCode() { + return Objects.hash(matchExpressions, matchLabels); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecAllowedRoutesNamespacesSelector {\n" + + " matchExpressions: " + toIndentedString(matchExpressions) + "\n" + + " matchLabels: " + toIndentedString(matchLabels) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions.java new file mode 100644 index 00000000..ae8e0f7d --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + */ +@Data +public class V1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions { + public static final String SERIALIZED_NAME_KEY = "key"; + public static final String SERIALIZED_NAME_OPERATOR = "operator"; + public static final String SERIALIZED_NAME_VALUES = "values"; + @SerializedName(SERIALIZED_NAME_KEY) + private String key; + @SerializedName(SERIALIZED_NAME_OPERATOR) + private String operator; + @SerializedName(SERIALIZED_NAME_VALUES) + private List values = null; + + + public V1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions addValuesItem(String valuesItem) { + if (this.values == null) { + this.values = new ArrayList<>(); + } + this.values.add(valuesItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions v1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions = (V1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions) o; + return Objects.equals(this.key, v1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions.key) && + Objects.equals(this.operator, v1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions.operator) && + Objects.equals(this.values, v1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions.values); + } + + @Override + public int hashCode() { + return Objects.hash(key, operator, values); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecAllowedRoutesNamespacesSelectorMatchExpressions {\n" + + " key: " + toIndentedString(key) + "\n" + + " operator: " + toIndentedString(operator) + "\n" + + " values: " + toIndentedString(values) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecListeners.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecListeners.java new file mode 100644 index 00000000..4b438ac0 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecListeners.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. + */ +@Data +public class V1GatewaySpecListeners { + public static final String SERIALIZED_NAME_ALLOWED_ROUTES = "allowedRoutes"; + public static final String SERIALIZED_NAME_HOSTNAME = "hostname"; + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_PORT = "port"; + public static final String SERIALIZED_NAME_PROTOCOL = "protocol"; + public static final String SERIALIZED_NAME_TLS = "tls"; + @SerializedName(SERIALIZED_NAME_ALLOWED_ROUTES) + private V1GatewaySpecAllowedRoutes allowedRoutes; + @SerializedName(SERIALIZED_NAME_HOSTNAME) + private String hostname; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_PORT) + private Integer port; + @SerializedName(SERIALIZED_NAME_PROTOCOL) + private String protocol; + @SerializedName(SERIALIZED_NAME_TLS) + private V1GatewaySpecTls tls; + + @NotNull + public static V1GatewaySpecTls getDefaultTls(String cert) { + V1GatewaySpecTls tls = new V1GatewaySpecTls(); + List certificateRefs = new ArrayList<>(); + V1GatewaySpecTlsCertificateRefs certificateRef = new V1GatewaySpecTlsCertificateRefs(); + certificateRef.setKind("Secret"); + certificateRef.setName(cert); + certificateRefs.add(certificateRef); + tls.setCertificateRefs(certificateRefs); + tls.setMode(V1GatewaySpecTls.ModeEnum.TERMINATE); + return tls; + } + + @NotNull + public static V1GatewaySpecAllowedRoutes getDefaultAllowedRoutes() { + V1GatewaySpecAllowedRoutes allowedRoute = new V1GatewaySpecAllowedRoutes(); + V1GatewaySpecAllowedRoutesNamespaces ns = new V1GatewaySpecAllowedRoutesNamespaces(); + ns.setFrom(V1GatewaySpecAllowedRoutesNamespaces.FromEnum.ALL); + allowedRoute.setNamespaces(ns); + return allowedRoute; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecListeners v1GatewaySpecListeners = (V1GatewaySpecListeners) o; + return Objects.equals(this.allowedRoutes, v1GatewaySpecListeners.allowedRoutes) && + Objects.equals(this.hostname, v1GatewaySpecListeners.hostname) && + Objects.equals(this.name, v1GatewaySpecListeners.name) && + Objects.equals(this.port, v1GatewaySpecListeners.port) && + Objects.equals(this.protocol, v1GatewaySpecListeners.protocol) && + Objects.equals(this.tls, v1GatewaySpecListeners.tls); + } + + @Override + public int hashCode() { + return Objects.hash(allowedRoutes, hostname, name, port, protocol, tls); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecListeners {\n" + + " allowedRoutes: " + toIndentedString(allowedRoutes) + "\n" + + " hostname: " + toIndentedString(hostname) + "\n" + + " name: " + toIndentedString(name) + "\n" + + " port: " + toIndentedString(port) + "\n" + + " protocol: " + toIndentedString(protocol) + "\n" + + " tls: " + toIndentedString(tls) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecTls.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecTls.java new file mode 100644 index 00000000..e26371fd --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecTls.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. Support: Core + */ +@Data +public class V1GatewaySpecTls { + public static final String SERIALIZED_NAME_CERTIFICATE_REFS = "certificateRefs"; + public static final String SERIALIZED_NAME_MODE = "mode"; + public static final String SERIALIZED_NAME_OPTIONS = "options"; + @SerializedName(SERIALIZED_NAME_CERTIFICATE_REFS) + private List certificateRefs = null; + @SerializedName(SERIALIZED_NAME_MODE) + private ModeEnum mode; + @SerializedName(SERIALIZED_NAME_OPTIONS) + private Map options = null; + + public V1GatewaySpecTls addCertificateRefsItem(V1GatewaySpecTlsCertificateRefs certificateRefsItem) { + if (this.certificateRefs == null) { + this.certificateRefs = new ArrayList<>(); + } + this.certificateRefs.add(certificateRefsItem); + return this; + } + + /** + * CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls Support: Implementation-specific (More than one reference or other resource types) + * + * @return certificateRefs + **/ + + public V1GatewaySpecTls putOptionsItem(String key, String optionsItem) { + if (this.options == null) { + this.options = new HashMap<>(); + } + this.options.put(key, optionsItem); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecTls v1GatewaySpecTls = (V1GatewaySpecTls) o; + return Objects.equals(this.certificateRefs, v1GatewaySpecTls.certificateRefs) && + Objects.equals(this.mode, v1GatewaySpecTls.mode) && + Objects.equals(this.options, v1GatewaySpecTls.options); + } + + @Override + public int hashCode() { + return Objects.hash(certificateRefs, mode, options); + } + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecTls {\n" + + " certificateRefs: " + toIndentedString(certificateRefs) + "\n" + + " mode: " + toIndentedString(mode) + "\n" + + " options: " + toIndentedString(options) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificates to be specified in some way, such as populating the certificateRefs field. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. The certificateRefs field is ignored in this mode. Support: Core + */ + @JsonAdapter(ModeEnum.Adapter.class) + public enum ModeEnum { + /** + * The TLS session between the downstream client and the Gateway is terminated at the Gateway. + * This mode requires certificates to be specified in some way, such as populating the certificateRefs field. + */ + TERMINATE("Terminate"), + /** + * The TLS session is NOT terminated by the Gateway. + * This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. + * The certificateRefs field is ignored in this mode. + */ + PASSTHROUGH("Passthrough"); + + private final String value; + + ModeEnum(String value) { + this.value = value; + } + + public static ModeEnum fromValue(String value) { + for (ModeEnum b : ModeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final ModeEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public ModeEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return ModeEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecTlsCertificateRefs.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecTlsCertificateRefs.java new file mode 100644 index 00000000..1630ef12 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewaySpecTlsCertificateRefs.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * SecretObjectReference identifies an API object including its namespace, defaulting to Secret. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. + */ +@Data +public class V1GatewaySpecTlsCertificateRefs { + + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_NAMESPACE = "namespace"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_NAMESPACE) + private String namespace; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewaySpecTlsCertificateRefs v1GatewaySpecTlsCertificateRefs = (V1GatewaySpecTlsCertificateRefs) o; + return Objects.equals(this.group, v1GatewaySpecTlsCertificateRefs.group) && + Objects.equals(this.kind, v1GatewaySpecTlsCertificateRefs.kind) && + Objects.equals(this.name, v1GatewaySpecTlsCertificateRefs.name) && + Objects.equals(this.namespace, v1GatewaySpecTlsCertificateRefs.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(group, kind, name, namespace); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewaySpecTlsCertificateRefs {\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " name: " + toIndentedString(name) + "\n" + + " namespace: " + toIndentedString(namespace) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatus.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatus.java new file mode 100644 index 00000000..67ca85ec --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatus.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Status defines the current state of Gateway. + */ +@Data +public class V1GatewayStatus { + public static final String SERIALIZED_NAME_ADDRESSES = "addresses"; + public static final String SERIALIZED_NAME_CONDITIONS = "conditions"; + public static final String SERIALIZED_NAME_LISTENERS = "listeners"; + @SerializedName(SERIALIZED_NAME_ADDRESSES) + private List addresses = null; + @SerializedName(SERIALIZED_NAME_CONDITIONS) + private List conditions = null; + @SerializedName(SERIALIZED_NAME_LISTENERS) + private List listeners = null; + + public V1GatewayStatus addAddressesItem(V1GatewayStatusAddresses addressesItem) { + if (this.addresses == null) { + this.addresses = new ArrayList<>(); + } + this.addresses.add(addressesItem); + return this; + } + + public V1GatewayStatus addConditionsItem(V1GatewayStatusConditions conditionsItem) { + if (this.conditions == null) { + this.conditions = new ArrayList<>(); + } + this.conditions.add(conditionsItem); + return this; + } + + public V1GatewayStatus addListenersItem(V1GatewayStatusListeners listenersItem) { + if (this.listeners == null) { + this.listeners = new ArrayList<>(); + } + this.listeners.add(listenersItem); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewayStatus v1GatewayStatus = (V1GatewayStatus) o; + return Objects.equals(this.addresses, v1GatewayStatus.addresses) && + Objects.equals(this.conditions, v1GatewayStatus.conditions) && + Objects.equals(this.listeners, v1GatewayStatus.listeners); + } + + @Override + public int hashCode() { + return Objects.hash(addresses, conditions, listeners); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewayStatus {\n" + + " addresses: " + toIndentedString(addresses) + "\n" + + " conditions: " + toIndentedString(conditions) + "\n" + + " listeners: " + toIndentedString(listeners) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusAddresses.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusAddresses.java new file mode 100644 index 00000000..9a1a894f --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusAddresses.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * GatewayStatusAddress describes a network address that is bound to a Gateway. + */ +@Data +public class V1GatewayStatusAddresses { + public static final String SERIALIZED_NAME_TYPE = "type"; + public static final String SERIALIZED_NAME_VALUE = "value"; + @SerializedName(SERIALIZED_NAME_TYPE) + private String type; + @SerializedName(SERIALIZED_NAME_VALUE) + private String value; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewayStatusAddresses v1GatewayStatusAddresses = (V1GatewayStatusAddresses) o; + return Objects.equals(this.type, v1GatewayStatusAddresses.type) && + Objects.equals(this.value, v1GatewayStatusAddresses.value); + } + + @Override + public int hashCode() { + return Objects.hash(type, value); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewayStatusAddresses {\n" + + " type: " + toIndentedString(type) + "\n" + + " value: " + toIndentedString(value) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusConditions.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusConditions.java new file mode 100644 index 00000000..ffcb6f45 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusConditions.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.Objects; + +/** + * Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` // other fields } + */ +@Data +public class V1GatewayStatusConditions { + public static final String SERIALIZED_NAME_LAST_TRANSITION_TIME = "lastTransitionTime"; + public static final String SERIALIZED_NAME_MESSAGE = "message"; + public static final String SERIALIZED_NAME_OBSERVED_GENERATION = "observedGeneration"; + public static final String SERIALIZED_NAME_REASON = "reason"; + public static final String SERIALIZED_NAME_STATUS = "status"; + public static final String SERIALIZED_NAME_TYPE = "type"; + @SerializedName(SERIALIZED_NAME_LAST_TRANSITION_TIME) + private OffsetDateTime lastTransitionTime; + @SerializedName(SERIALIZED_NAME_MESSAGE) + private String message; + @SerializedName(SERIALIZED_NAME_OBSERVED_GENERATION) + private Long observedGeneration; + @SerializedName(SERIALIZED_NAME_REASON) + private String reason; + @SerializedName(SERIALIZED_NAME_STATUS) + private StatusEnum status; + @SerializedName(SERIALIZED_NAME_TYPE) + private String type; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewayStatusConditions v1GatewayStatusConditions = (V1GatewayStatusConditions) o; + return Objects.equals(this.lastTransitionTime, v1GatewayStatusConditions.lastTransitionTime) && + Objects.equals(this.message, v1GatewayStatusConditions.message) && + Objects.equals(this.observedGeneration, v1GatewayStatusConditions.observedGeneration) && + Objects.equals(this.reason, v1GatewayStatusConditions.reason) && + Objects.equals(this.status, v1GatewayStatusConditions.status) && + Objects.equals(this.type, v1GatewayStatusConditions.type); + } + + @Override + public int hashCode() { + return Objects.hash(lastTransitionTime, message, observedGeneration, reason, status, type); + } + + @Override + public String toString() { + String sb = "class V1beta1GatewayStatusConditions {\n" + + " lastTransitionTime: " + toIndentedString(lastTransitionTime) + "\n" + + " message: " + toIndentedString(message) + "\n" + + " observedGeneration: " + toIndentedString(observedGeneration) + "\n" + + " reason: " + toIndentedString(reason) + "\n" + + " status: " + toIndentedString(status) + "\n" + + " type: " + toIndentedString(type) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * status of the condition, one of True, False, Unknown. + */ + @JsonAdapter(StatusEnum.Adapter.class) + public enum StatusEnum { + /** + * Represents the state where the condition is true. + */ + TRUE("True"), + + /** + * Represents the state where the condition is false. + */ + FALSE("False"), + + /** + * Represents an unknown state. + */ + UNKNOWN("Unknown"); + + private final String value; + + StatusEnum(String value) { + this.value = value; + } + + public static StatusEnum fromValue(String value) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final StatusEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public StatusEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return StatusEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusListeners.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusListeners.java new file mode 100644 index 00000000..b69a1aa2 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/gateways/V1GatewayStatusListeners.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * ListenerStatus is the status associated with a Listener. + */ +@Data +public class V1GatewayStatusListeners { + public static final String SERIALIZED_NAME_ATTACHED_ROUTES = "attachedRoutes"; + public static final String SERIALIZED_NAME_CONDITIONS = "conditions"; + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_SUPPORTED_KINDS = "supportedKinds"; + @SerializedName(SERIALIZED_NAME_ATTACHED_ROUTES) + private Integer attachedRoutes; + @SerializedName(SERIALIZED_NAME_CONDITIONS) + private List conditions = new ArrayList<>(); + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_SUPPORTED_KINDS) + private List supportedKinds = new ArrayList<>(); + + public V1GatewayStatusListeners addConditionsItem(V1GatewayStatusConditions conditionsItem) { + this.conditions.add(conditionsItem); + return this; + } + + public V1GatewayStatusListeners addSupportedKindsItem(V1GatewaySpecAllowedRoutesKinds supportedKindsItem) { + this.supportedKinds.add(supportedKindsItem); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1GatewayStatusListeners v1GatewayStatusListeners = (V1GatewayStatusListeners) o; + return Objects.equals(this.attachedRoutes, v1GatewayStatusListeners.attachedRoutes) && + Objects.equals(this.conditions, v1GatewayStatusListeners.conditions) && + Objects.equals(this.name, v1GatewayStatusListeners.name) && + Objects.equals(this.supportedKinds, v1GatewayStatusListeners.supportedKinds); + } + + @Override + public int hashCode() { + return Objects.hash(attachedRoutes, conditions, name, supportedKinds); + } + + + @Override + public String toString() { + String sb = "class V1beta1GatewayStatusListeners {\n" + + " attachedRoutes: " + toIndentedString(attachedRoutes) + "\n" + + " conditions: " + toIndentedString(conditions) + "\n" + + " name: " + toIndentedString(name) + "\n" + + " supportedKinds: " + toIndentedString(supportedKinds) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRoute.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRoute.java new file mode 100644 index 00000000..21a048f9 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRoute.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import lombok.Data; + +import java.util.Objects; + +/** + * HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. + */ +@Data +public class V1HTTPRoute implements io.kubernetes.client.common.KubernetesObject { + public static final String API_GROUP = "gateway.networking.k8s.io"; + public static final String VERSION = "v1"; + public static final String KIND = "HTTPRoute"; + public static final String PLURAL = "httproutes"; + + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_METADATA = "metadata"; + public static final String SERIALIZED_NAME_SPEC = "spec"; + public static final String SERIALIZED_NAME_STATUS = "status"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion = API_GROUP + "/" + VERSION; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind = KIND; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ObjectMeta metadata = null; + @SerializedName(SERIALIZED_NAME_SPEC) + private V1HTTPRouteSpec spec; + @SerializedName(SERIALIZED_NAME_STATUS) + private V1HTTPRouteStatus status; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRoute v1HttpRoute = (V1HTTPRoute) o; + return Objects.equals(this.apiVersion, v1HttpRoute.apiVersion) && + Objects.equals(this.kind, v1HttpRoute.kind) && + Objects.equals(this.metadata, v1HttpRoute.metadata) && + Objects.equals(this.spec, v1HttpRoute.spec) && + Objects.equals(this.status, v1HttpRoute.status); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, kind, metadata, spec, status); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRoute {\n" + + " apiVersion: " + toIndentedString(apiVersion) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " metadata: " + toIndentedString(metadata) + "\n" + + " spec: " + toIndentedString(spec) + "\n" + + " status: " + toIndentedString(status) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteList.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteList.java new file mode 100644 index 00000000..00d3f09d --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteList.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import io.kubernetes.client.openapi.models.V1ListMeta; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * HTTPRouteList is a list of HTTPRoute + */ +@Data +public class V1HTTPRouteList implements io.kubernetes.client.common.KubernetesListObject { + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + public static final String SERIALIZED_NAME_ITEMS = "items"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_METADATA = "metadata"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion; + @SerializedName(SERIALIZED_NAME_ITEMS) + private List items = new ArrayList<>(); + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ListMeta metadata = null; + + public V1HTTPRouteList addItemsItem(V1HTTPRoute itemsItem) { + this.items.add(itemsItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteList v1HttpRouteList = (V1HTTPRouteList) o; + return Objects.equals(this.apiVersion, v1HttpRouteList.apiVersion) && + Objects.equals(this.items, v1HttpRouteList.items) && + Objects.equals(this.kind, v1HttpRouteList.kind) && + Objects.equals(this.metadata, v1HttpRouteList.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, items, kind, metadata); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteList {\n" + + " apiVersion: " + toIndentedString(apiVersion) + "\n" + + " items: " + toIndentedString(items) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " metadata: " + toIndentedString(metadata) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpec.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpec.java new file mode 100644 index 00000000..ed4c9679 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpec.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Spec defines the desired state of HTTPRoute. + */ +@Data +public class V1HTTPRouteSpec { + public static final String SERIALIZED_NAME_HOSTNAMES = "hostnames"; + public static final String SERIALIZED_NAME_PARENT_REFS = "parentRefs"; + public static final String SERIALIZED_NAME_RULES = "rules"; + @SerializedName(SERIALIZED_NAME_HOSTNAMES) + private List hostnames = null; + @SerializedName(SERIALIZED_NAME_PARENT_REFS) + private List parentRefs = null; + @SerializedName(SERIALIZED_NAME_RULES) + private List rules = null; + + public V1HTTPRouteSpec addHostnamesItem(String hostnamesItem) { + if (this.hostnames == null) { + this.hostnames = new ArrayList<>(); + } + this.hostnames.add(hostnamesItem); + return this; + } + + public V1HTTPRouteSpec addParentRefsItem(V1HTTPRouteSpecParentRefs parentRefsItem) { + if (this.parentRefs == null) { + this.parentRefs = new ArrayList<>(); + } + this.parentRefs.add(parentRefsItem); + return this; + } + + public V1HTTPRouteSpec addRulesItem(V1HTTPRouteSpecRules rulesItem) { + if (this.rules == null) { + this.rules = new ArrayList<>(); + } + this.rules.add(rulesItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpec v1HttpRouteSpec = (V1HTTPRouteSpec) o; + return Objects.equals(this.hostnames, v1HttpRouteSpec.hostnames) && + Objects.equals(this.parentRefs, v1HttpRouteSpec.parentRefs) && + Objects.equals(this.rules, v1HttpRouteSpec.rules); + } + + @Override + public int hashCode() { + return Objects.hash(hostnames, parentRefs, rules); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpec {\n" + + " hostnames: " + toIndentedString(hostnames) + "\n" + + " parentRefs: " + toIndentedString(parentRefs) + "\n" + + " rules: " + toIndentedString(rules) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecBackendRefs.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecBackendRefs.java new file mode 100644 index 00000000..3ca72f16 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecBackendRefs.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. <gateway:experimental:description> When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the \"ResolvedRefs\" condition to \"False\" with the \"UnsupportedProtocol\" reason. </gateway:experimental:description> + */ +@Data +public class V1HTTPRouteSpecBackendRefs { + public static final String SERIALIZED_NAME_FILTERS = "filters"; + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_NAMESPACE = "namespace"; + public static final String SERIALIZED_NAME_PORT = "port"; + public static final String SERIALIZED_NAME_WEIGHT = "weight"; + @SerializedName(SERIALIZED_NAME_FILTERS) + private List filters = null; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_NAMESPACE) + private String namespace; + @SerializedName(SERIALIZED_NAME_PORT) + private Integer port; + @SerializedName(SERIALIZED_NAME_WEIGHT) + private Integer weight; + + public V1HTTPRouteSpecBackendRefs addFiltersItem(V1HTTPRouteSpecFilters filtersItem) { + if (this.filters == null) { + this.filters = new ArrayList<>(); + } + this.filters.add(filtersItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecBackendRefs v1HttpRouteSpecBackendRefs = (V1HTTPRouteSpecBackendRefs) o; + return Objects.equals(this.filters, v1HttpRouteSpecBackendRefs.filters) && + Objects.equals(this.group, v1HttpRouteSpecBackendRefs.group) && + Objects.equals(this.kind, v1HttpRouteSpecBackendRefs.kind) && + Objects.equals(this.name, v1HttpRouteSpecBackendRefs.name) && + Objects.equals(this.namespace, v1HttpRouteSpecBackendRefs.namespace) && + Objects.equals(this.port, v1HttpRouteSpecBackendRefs.port) && + Objects.equals(this.weight, v1HttpRouteSpecBackendRefs.weight); + } + + @Override + public int hashCode() { + return Objects.hash(filters, group, kind, name, namespace, port, weight); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecBackendRefs {\n" + + " filters: " + toIndentedString(filters) + "\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " name: " + toIndentedString(name) + "\n" + + " namespace: " + toIndentedString(namespace) + "\n" + + " port: " + toIndentedString(port) + "\n" + + " weight: " + toIndentedString(weight) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecExtensionRef.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecExtensionRef.java new file mode 100644 index 00000000..70ed816f --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecExtensionRef.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific + */ +@Data +public class V1HTTPRouteSpecExtensionRef { + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAME = "name"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecExtensionRef v1HttpRouteSpecExtensionRef = (V1HTTPRouteSpecExtensionRef) o; + return Objects.equals(this.group, v1HttpRouteSpecExtensionRef.group) && + Objects.equals(this.kind, v1HttpRouteSpecExtensionRef.kind) && + Objects.equals(this.name, v1HttpRouteSpecExtensionRef.name); + } + + @Override + public int hashCode() { + return Objects.hash(group, kind, name); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecExtensionRef {\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " name: " + toIndentedString(name) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecFilters.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecFilters.java new file mode 100644 index 00000000..3c096d73 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecFilters.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.Objects; + +/** + * HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + */ +@Data +public class V1HTTPRouteSpecFilters { + public static final String SERIALIZED_NAME_EXTENSION_REF = "extensionRef"; + public static final String SERIALIZED_NAME_REQUEST_HEADER_MODIFIER = "requestHeaderModifier"; + public static final String SERIALIZED_NAME_REQUEST_MIRROR = "requestMirror"; + public static final String SERIALIZED_NAME_REQUEST_REDIRECT = "requestRedirect"; + public static final String SERIALIZED_NAME_RESPONSE_HEADER_MODIFIER = "responseHeaderModifier"; + public static final String SERIALIZED_NAME_TYPE = "type"; + public static final String SERIALIZED_NAME_URL_REWRITE = "urlRewrite"; + @SerializedName(SERIALIZED_NAME_EXTENSION_REF) + private V1HTTPRouteSpecExtensionRef extensionRef; + @SerializedName(SERIALIZED_NAME_REQUEST_HEADER_MODIFIER) + private V1HTTPRouteSpecRequestHeaderModifier requestHeaderModifier; + @SerializedName(SERIALIZED_NAME_REQUEST_MIRROR) + private V1HTTPRouteSpecRequestMirror requestMirror; + @SerializedName(SERIALIZED_NAME_REQUEST_REDIRECT) + private V1HTTPRouteSpecRequestRedirect requestRedirect; + @SerializedName(SERIALIZED_NAME_RESPONSE_HEADER_MODIFIER) + private V1HTTPRouteSpecResponseHeaderModifier responseHeaderModifier; + @SerializedName(SERIALIZED_NAME_TYPE) + private TypeEnum type; + @SerializedName(SERIALIZED_NAME_URL_REWRITE) + private V1HTTPRouteSpecUrlRewrite urlRewrite; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecFilters v1HttpRouteSpecFilters = (V1HTTPRouteSpecFilters) o; + return Objects.equals(this.extensionRef, v1HttpRouteSpecFilters.extensionRef) && + Objects.equals(this.requestHeaderModifier, v1HttpRouteSpecFilters.requestHeaderModifier) && + Objects.equals(this.requestMirror, v1HttpRouteSpecFilters.requestMirror) && + Objects.equals(this.requestRedirect, v1HttpRouteSpecFilters.requestRedirect) && + Objects.equals(this.responseHeaderModifier, v1HttpRouteSpecFilters.responseHeaderModifier) && + Objects.equals(this.type, v1HttpRouteSpecFilters.type) && + Objects.equals(this.urlRewrite, v1HttpRouteSpecFilters.urlRewrite); + } + + @Override + public int hashCode() { + return Objects.hash(extensionRef, requestHeaderModifier, requestMirror, requestRedirect, responseHeaderModifier, type, urlRewrite); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecFilters {\n" + + " extensionRef: " + toIndentedString(extensionRef) + "\n" + + " requestHeaderModifier: " + toIndentedString(requestHeaderModifier) + "\n" + + " requestMirror: " + toIndentedString(requestMirror) + "\n" + + " requestRedirect: " + toIndentedString(requestRedirect) + "\n" + + " responseHeaderModifier: " + toIndentedString(responseHeaderModifier) + "\n" + + " type: " + toIndentedString(type) + "\n" + + " urlRewrite: " + toIndentedString(urlRewrite) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. + */ + @JsonAdapter(TypeEnum.Adapter.class) + public enum TypeEnum { + /** + * RequestHeaderModifier is a core filter type used to modify the headers of incoming HTTP requests. + * All implementations must support this filter type. + */ + REQUESTHEADERMODIFIER("RequestHeaderModifier"), + + /** + * ResponseHeaderModifier is a core filter type used to modify the headers of outgoing HTTP responses. + * All implementations must support this filter type. + */ + RESPONSEHEADERMODIFIER("ResponseHeaderModifier"), + + /** + * RequestMirror is an extended filter type used to mirror HTTP requests to another service or destination. + * Implementers are encouraged to support this filter type. + */ + REQUESTMIRROR("RequestMirror"), + + /** + * RequestRedirect is a core filter type used to redirect HTTP requests to a different URL. + * All implementations must support this filter type. + */ + REQUESTREDIRECT("RequestRedirect"), + + /** + * URLRewrite is a core filter type used to rewrite the URL path or query in an HTTP request. + * All implementations must support this filter type. + */ + URLREWRITE("URLRewrite"), + + /** + * ExtensionRef is an implementation-specific filter type used for custom filters defined by the implementer. + * This type should be set to "ExtensionRef" for custom filters, and the specific configuration is specified using the ExtensionRef field. + * If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped, and the request must receive an HTTP error response. + */ + EXTENSIONREF("ExtensionRef"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final TypeEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public TypeEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return TypeEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecHeaders.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecHeaders.java new file mode 100644 index 00000000..3ca14b0f --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecHeaders.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.Objects; + +/** + * HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. + */ +@Data +public class V1HTTPRouteSpecHeaders { + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_TYPE = "type"; + public static final String SERIALIZED_NAME_VALUE = "value"; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_TYPE) + private TypeEnum type; + @SerializedName(SERIALIZED_NAME_VALUE) + private String value; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecHeaders v1HttpRouteSpecHeaders = (V1HTTPRouteSpecHeaders) o; + return Objects.equals(this.name, v1HttpRouteSpecHeaders.name) && + Objects.equals(this.type, v1HttpRouteSpecHeaders.type) && + Objects.equals(this.value, v1HttpRouteSpecHeaders.value); + } + + @Override + public int hashCode() { + return Objects.hash(name, type, value); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecHeaders {\n" + + " name: " + toIndentedString(name) + "\n" + + " type: " + toIndentedString(type) + "\n" + + " value: " + toIndentedString(value) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Type specifies how to match against the value of the header. Support: Core (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. + */ + @JsonAdapter(TypeEnum.Adapter.class) + public enum TypeEnum { + /** + * EXACT is a core match type that matches the header value exactly. + * All implementations must support this match type. + */ + EXACT("Exact"), + + /** + * REGULAREXPRESSION is an implementation-specific match type that allows matching header values using regular expressions. + * The specific regular expression dialect supported may vary between implementations. + */ + REGULAREXPRESSION("RegularExpression"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final TypeEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public TypeEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return TypeEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecMatches.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecMatches.java new file mode 100644 index 00000000..ba85db52 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecMatches.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: ``` match: path: value: \"/foo\" headers: - name: \"version\" value \"v1\" ``` + */ +@Data +public class V1HTTPRouteSpecMatches implements Cloneable{ + public static final String SERIALIZED_NAME_HEADERS = "headers"; + public static final String SERIALIZED_NAME_METHOD = "method"; + public static final String SERIALIZED_NAME_PATH = "path"; + public static final String SERIALIZED_NAME_QUERY_PARAMS = "queryParams"; + @SerializedName(SERIALIZED_NAME_HEADERS) + private List headers = null; + @SerializedName(SERIALIZED_NAME_METHOD) + private MethodEnum method; + @SerializedName(SERIALIZED_NAME_PATH) + private V1HTTPRouteSpecPath path; + @SerializedName(SERIALIZED_NAME_QUERY_PARAMS) + private List queryParams = null; + + public V1HTTPRouteSpecMatches addHeadersItem(V1HTTPRouteSpecHeaders headersItem) { + if (this.headers == null) { + this.headers = new ArrayList<>(); + } + this.headers.add(headersItem); + return this; + } + + public V1HTTPRouteSpecMatches addQueryParamsItem(V1HTTPRouteSpecQueryParams queryParamsItem) { + if (this.queryParams == null) { + this.queryParams = new ArrayList<>(); + } + this.queryParams.add(queryParamsItem); + return this; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecMatches v1HttpRouteSpecMatches = (V1HTTPRouteSpecMatches) o; + return Objects.equals(this.headers, v1HttpRouteSpecMatches.headers) && + Objects.equals(this.method, v1HttpRouteSpecMatches.method) && + Objects.equals(this.path, v1HttpRouteSpecMatches.path) && + Objects.equals(this.queryParams, v1HttpRouteSpecMatches.queryParams); + } + + @Override + public int hashCode() { + return Objects.hash(headers, method, path, queryParams); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecMatches {\n" + + " headers: " + toIndentedString(headers) + "\n" + + " method: " + toIndentedString(method) + "\n" + + " path: " + toIndentedString(path) + "\n" + + " queryParams: " + toIndentedString(queryParams) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. Support: Extended + */ + @JsonAdapter(MethodEnum.Adapter.class) + public enum MethodEnum { + /** + * GET is an HTTP method used to request data from a specified resource. + * Commonly used to retrieve information. + */ + GET("GET"), + + /** + * HEAD is an HTTP method used to request headers from a specified resource. + * Similar to GET, but without the response body. + */ + HEAD("HEAD"), + + /** + * POST is an HTTP method used to send data to a server to create/update a resource. + * Commonly used for submitting form data or uploading files. + */ + POST("POST"), + + /** + * PUT is an HTTP method used to update or create a resource at the specified resource. + * Typically used for updating existing resources. + */ + PUT("PUT"), + + /** + * DELETE is an HTTP method used to delete a specified resource. + * Used for removing resources from the server. + */ + DELETE("DELETE"), + + /** + * CONNECT is an HTTP method used to establish a tunnel to the server identified by the target resource. + * Commonly used for tunneling HTTP requests over a network. + */ + CONNECT("CONNECT"), + + /** + * OPTIONS is an HTTP method used to describe the communication options for the target resource. + * Used to determine the capabilities of a server, such as allowed HTTP methods. + */ + OPTIONS("OPTIONS"), + + /** + * TRACE is an HTTP method used to perform a message loop-back test along the path to the target resource. + * Typically used for diagnostic purposes. + */ + TRACE("TRACE"), + + /** + * PATCH is an HTTP method used to apply partial modifications to a resource. + * Often used when you need to update a resource with a few changes rather than replacing the entire resource. + */ + PATCH("PATCH"); + + private final String value; + + MethodEnum(String value) { + this.value = value; + } + + public static MethodEnum fromValue(String value) { + for (MethodEnum b : MethodEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final MethodEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public MethodEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return MethodEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecParentRefs.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecParentRefs.java new file mode 100644 index 00000000..7681a92f --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecParentRefs.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with \"Core\" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. + */ +@Data +public class V1HTTPRouteSpecParentRefs { + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_NAMESPACE = "namespace"; + public static final String SERIALIZED_NAME_PORT = "port"; + public static final String SERIALIZED_NAME_SECTION_NAME = "sectionName"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_NAMESPACE) + private String namespace; + @SerializedName(SERIALIZED_NAME_PORT) + private Integer port; + @SerializedName(SERIALIZED_NAME_SECTION_NAME) + private String sectionName; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecParentRefs v1HttpRouteSpecParentRefs = (V1HTTPRouteSpecParentRefs) o; + return Objects.equals(this.group, v1HttpRouteSpecParentRefs.group) && + Objects.equals(this.kind, v1HttpRouteSpecParentRefs.kind) && + Objects.equals(this.name, v1HttpRouteSpecParentRefs.name) && + Objects.equals(this.namespace, v1HttpRouteSpecParentRefs.namespace) && + Objects.equals(this.port, v1HttpRouteSpecParentRefs.port) && + Objects.equals(this.sectionName, v1HttpRouteSpecParentRefs.sectionName); + } + + @Override + public int hashCode() { + return Objects.hash(group, kind, name, namespace, port, sectionName); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecParentRefs {\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " name: " + toIndentedString(name) + "\n" + + " namespace: " + toIndentedString(namespace) + "\n" + + " port: " + toIndentedString(port) + "\n" + + " sectionName: " + toIndentedString(sectionName) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecPath.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecPath.java new file mode 100644 index 00000000..6afb1e38 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecPath.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.Objects; + +/** + * Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the \"/\" path is provided. + */ +@Data +public class V1HTTPRouteSpecPath { + + public static final String SERIALIZED_NAME_TYPE = "type"; + public static final String SERIALIZED_NAME_VALUE = "value"; + @SerializedName(SERIALIZED_NAME_TYPE) + private TypeEnum type; + @SerializedName(SERIALIZED_NAME_VALUE) + private String value; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecPath v1HttpRouteSpecPath = (V1HTTPRouteSpecPath) o; + return Objects.equals(this.type, v1HttpRouteSpecPath.type) && + Objects.equals(this.value, v1HttpRouteSpecPath.value); + } + + @Override + public int hashCode() { + return Objects.hash(type, value); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecPath {\n" + + " type: " + toIndentedString(type) + "\n" + + " value: " + toIndentedString(value) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Type specifies how to match against the path Value. Support: Core (Exact, PathPrefix) Support: Implementation-specific (RegularExpression) + */ + @JsonAdapter(TypeEnum.Adapter.class) + public enum TypeEnum { + /** + * EXACT matches the path exactly as specified. + * This is a core match type and must be supported by all implementations. + */ + EXACT("Exact"), + + /** + * PATHPREFIX matches the beginning of the path against the specified value. + * This is a core match type and must be supported by all implementations. + */ + PATHPREFIX("PathPrefix"), + + /** + * REGULAREXPRESSION allows matching the path using a regular expression. + * This is an implementation-specific match type and may vary depending on the implementation. + */ + REGULAREXPRESSION("RegularExpression"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final TypeEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public TypeEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return TypeEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecQueryParams.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecQueryParams.java new file mode 100644 index 00000000..7f2aaa54 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecQueryParams.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.Objects; + +/** + * HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. + */ +@Data +public class V1HTTPRouteSpecQueryParams { + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_TYPE = "type"; + public static final String SERIALIZED_NAME_VALUE = "value"; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_TYPE) + private TypeEnum type; + @SerializedName(SERIALIZED_NAME_VALUE) + private String value; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecQueryParams v1HttpRouteSpecQueryParams = (V1HTTPRouteSpecQueryParams) o; + return Objects.equals(this.name, v1HttpRouteSpecQueryParams.name) && + Objects.equals(this.type, v1HttpRouteSpecQueryParams.type) && + Objects.equals(this.value, v1HttpRouteSpecQueryParams.value); + } + + @Override + public int hashCode() { + return Objects.hash(name, type, value); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecQueryParams {\n" + + " name: " + toIndentedString(name) + "\n" + + " type: " + toIndentedString(type) + "\n" + + " value: " + toIndentedString(value) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Type specifies how to match against the value of the query parameter. Support: Extended (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. + */ + @JsonAdapter(TypeEnum.Adapter.class) + public enum TypeEnum { + /** + * EXACT matches the query parameter value exactly as specified. + * This is an extended match type that is commonly supported by implementations. + */ + EXACT("Exact"), + + /** + * REGULAREXPRESSION allows matching the query parameter value using a regular expression. + * This is an implementation-specific match type, and the supported regular expression dialect may vary depending on the implementation. + */ + REGULAREXPRESSION("RegularExpression"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final TypeEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public TypeEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return TypeEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestHeaderModifier.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestHeaderModifier.java new file mode 100644 index 00000000..871764b3 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestHeaderModifier.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core + */ +@Data +public class V1HTTPRouteSpecRequestHeaderModifier { + public static final String SERIALIZED_NAME_ADD = "add"; + public static final String SERIALIZED_NAME_REMOVE = "remove"; + public static final String SERIALIZED_NAME_SET = "set"; + @SerializedName(SERIALIZED_NAME_ADD) + private List add = null; + @SerializedName(SERIALIZED_NAME_REMOVE) + private List remove = null; + @SerializedName(SERIALIZED_NAME_SET) + private List set = null; + + + public V1HTTPRouteSpecRequestHeaderModifier addAddItem(V1HTTPRouteSpecRequestHeaderModifierAdd addItem) { + if (this.add == null) { + this.add = new ArrayList<>(); + } + this.add.add(addItem); + return this; + } + + /** + * Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: \"my-header\" value: \"bar,baz\" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz + * + * @return add + **/ + + public V1HTTPRouteSpecRequestHeaderModifier addRemoveItem(String removeItem) { + if (this.remove == null) { + this.remove = new ArrayList<>(); + } + this.remove.add(removeItem); + return this; + } + + public V1HTTPRouteSpecRequestHeaderModifier addSetItem(V1HTTPRouteSpecRequestHeaderModifierAdd setItem) { + if (this.set == null) { + this.set = new ArrayList<>(); + } + this.set.add(setItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecRequestHeaderModifier v1HttpRouteSpecRequestHeaderModifier = (V1HTTPRouteSpecRequestHeaderModifier) o; + return Objects.equals(this.add, v1HttpRouteSpecRequestHeaderModifier.add) && + Objects.equals(this.remove, v1HttpRouteSpecRequestHeaderModifier.remove) && + Objects.equals(this.set, v1HttpRouteSpecRequestHeaderModifier.set); + } + + @Override + public int hashCode() { + return Objects.hash(add, remove, set); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecRequestHeaderModifier {\n" + + " add: " + toIndentedString(add) + "\n" + + " remove: " + toIndentedString(remove) + "\n" + + " set: " + toIndentedString(set) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestHeaderModifierAdd.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestHeaderModifierAdd.java new file mode 100644 index 00000000..e77b7c89 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestHeaderModifierAdd.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + */ +@Data +public class V1HTTPRouteSpecRequestHeaderModifierAdd { + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_VALUE = "value"; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_VALUE) + private String value; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecRequestHeaderModifierAdd v1HttpRouteSpecRequestHeaderModifierAdd = (V1HTTPRouteSpecRequestHeaderModifierAdd) o; + return Objects.equals(this.name, v1HttpRouteSpecRequestHeaderModifierAdd.name) && + Objects.equals(this.value, v1HttpRouteSpecRequestHeaderModifierAdd.value); + } + + @Override + public int hashCode() { + return Objects.hash(name, value); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecRequestHeaderModifierAdd {\n" + + " name: " + toIndentedString(name) + "\n" + + " value: " + toIndentedString(value) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestMirror.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestMirror.java new file mode 100644 index 00000000..a1a74a31 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestMirror.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended + */ +@Data +public class V1HTTPRouteSpecRequestMirror { + public static final String SERIALIZED_NAME_BACKEND_REF = "backendRef"; + @SerializedName(SERIALIZED_NAME_BACKEND_REF) + private V1HTTPRouteSpecRequestMirrorBackendRef backendRef; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecRequestMirror v1HttpRouteSpecRequestMirror = (V1HTTPRouteSpecRequestMirror) o; + return Objects.equals(this.backendRef, v1HttpRouteSpecRequestMirror.backendRef); + } + + @Override + public int hashCode() { + return Objects.hash(backendRef); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecRequestMirror {\n" + + " backendRef: " + toIndentedString(backendRef) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestMirrorBackendRef.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestMirrorBackendRef.java new file mode 100644 index 00000000..edd30a8c --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestMirrorBackendRef.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource + */ +@Data +public class V1HTTPRouteSpecRequestMirrorBackendRef { + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_NAMESPACE = "namespace"; + public static final String SERIALIZED_NAME_PORT = "port"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_NAMESPACE) + private String namespace; + @SerializedName(SERIALIZED_NAME_PORT) + private Integer port; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecRequestMirrorBackendRef v1HttpRouteSpecRequestMirrorBackendRef = (V1HTTPRouteSpecRequestMirrorBackendRef) o; + return Objects.equals(this.group, v1HttpRouteSpecRequestMirrorBackendRef.group) && + Objects.equals(this.kind, v1HttpRouteSpecRequestMirrorBackendRef.kind) && + Objects.equals(this.name, v1HttpRouteSpecRequestMirrorBackendRef.name) && + Objects.equals(this.namespace, v1HttpRouteSpecRequestMirrorBackendRef.namespace) && + Objects.equals(this.port, v1HttpRouteSpecRequestMirrorBackendRef.port); + } + + @Override + public int hashCode() { + return Objects.hash(group, kind, name, namespace, port); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecRequestMirrorBackendRef {\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " name: " + toIndentedString(name) + "\n" + + " namespace: " + toIndentedString(namespace) + "\n" + + " port: " + toIndentedString(port) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestRedirect.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestRedirect.java new file mode 100644 index 00000000..e2ac1b49 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestRedirect.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.Objects; + +/** + * RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core + */ +@Data +public class V1HTTPRouteSpecRequestRedirect { + public static final String SERIALIZED_NAME_HOSTNAME = "hostname"; + public static final String SERIALIZED_NAME_PATH = "path"; + public static final String SERIALIZED_NAME_PORT = "port"; + public static final String SERIALIZED_NAME_SCHEME = "scheme"; + public static final String SERIALIZED_NAME_STATUS_CODE = "statusCode"; + @SerializedName(SERIALIZED_NAME_HOSTNAME) + private String hostname; + @SerializedName(SERIALIZED_NAME_PATH) + private V1HTTPRouteSpecRequestRedirectPath path; + @SerializedName(SERIALIZED_NAME_PORT) + private Integer port; + @SerializedName(SERIALIZED_NAME_SCHEME) + private SchemeEnum scheme; + @SerializedName(SERIALIZED_NAME_STATUS_CODE) + private Integer statusCode; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecRequestRedirect v1HttpRouteSpecRequestRedirect = (V1HTTPRouteSpecRequestRedirect) o; + return Objects.equals(this.hostname, v1HttpRouteSpecRequestRedirect.hostname) && + Objects.equals(this.path, v1HttpRouteSpecRequestRedirect.path) && + Objects.equals(this.port, v1HttpRouteSpecRequestRedirect.port) && + Objects.equals(this.scheme, v1HttpRouteSpecRequestRedirect.scheme) && + Objects.equals(this.statusCode, v1HttpRouteSpecRequestRedirect.statusCode); + } + + @Override + public int hashCode() { + return Objects.hash(hostname, path, port, scheme, statusCode); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecRequestRedirect {\n" + + " hostname: " + toIndentedString(hostname) + "\n" + + " path: " + toIndentedString(path) + "\n" + + " port: " + toIndentedString(port) + "\n" + + " scheme: " + toIndentedString(scheme) + "\n" + + " statusCode: " + toIndentedString(statusCode) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended + */ + @JsonAdapter(SchemeEnum.Adapter.class) + public enum SchemeEnum { + /** + * HTTP is used to specify that the scheme in the `Location` header should be "http". + * This is commonly used for non-secure redirects. + */ + HTTP("http"), + + /** + * HTTPS is used to specify that the scheme in the `Location` header should be "https". + * This is commonly used for secure redirects. + */ + HTTPS("https"); + + private final String value; + + SchemeEnum(String value) { + this.value = value; + } + + public static SchemeEnum fromValue(String value) { + for (SchemeEnum b : SchemeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final SchemeEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public SchemeEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return SchemeEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestRedirectPath.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestRedirectPath.java new file mode 100644 index 00000000..7528e5d3 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRequestRedirectPath.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.Objects; + +/** + * Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended + */ +@Data +public class V1HTTPRouteSpecRequestRedirectPath { + public static final String SERIALIZED_NAME_REPLACE_FULL_PATH = "replaceFullPath"; + public static final String SERIALIZED_NAME_REPLACE_PREFIX_MATCH = "replacePrefixMatch"; + public static final String SERIALIZED_NAME_TYPE = "type"; + @SerializedName(SERIALIZED_NAME_REPLACE_FULL_PATH) + private String replaceFullPath; + @SerializedName(SERIALIZED_NAME_REPLACE_PREFIX_MATCH) + private String replacePrefixMatch; + @SerializedName(SERIALIZED_NAME_TYPE) + private TypeEnum type; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecRequestRedirectPath v1HttpRouteSpecRequestRedirectPath = (V1HTTPRouteSpecRequestRedirectPath) o; + return Objects.equals(this.replaceFullPath, v1HttpRouteSpecRequestRedirectPath.replaceFullPath) && + Objects.equals(this.replacePrefixMatch, v1HttpRouteSpecRequestRedirectPath.replacePrefixMatch) && + Objects.equals(this.type, v1HttpRouteSpecRequestRedirectPath.type); + } + + @Override + public int hashCode() { + return Objects.hash(replaceFullPath, replacePrefixMatch, type); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecRequestRedirectPath {\n" + + " replaceFullPath: " + toIndentedString(replaceFullPath) + "\n" + + " replacePrefixMatch: " + toIndentedString(replacePrefixMatch) + "\n" + + " type: " + toIndentedString(type) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. + */ + @JsonAdapter(TypeEnum.Adapter.class) + public enum TypeEnum { + /** + * REPLACEFULLPATH is used to replace the entire path of the request with a new path. + * This modifier is typically used when you need to completely change the path in a request. + */ + REPLACEFULLPATH("ReplaceFullPath"), + + /** + * REPLACEPREFIXMATCH is used to replace the matching prefix of the path in the request. + * This modifier is commonly used when you want to modify just the beginning of the path while preserving the rest. + */ + REPLACEPREFIXMATCH("ReplacePrefixMatch"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final TypeEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public TypeEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return TypeEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecResponseHeaderModifier.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecResponseHeaderModifier.java new file mode 100644 index 00000000..b48a8940 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecResponseHeaderModifier.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended + */ +@Data +public class V1HTTPRouteSpecResponseHeaderModifier { + public static final String SERIALIZED_NAME_ADD = "add"; + public static final String SERIALIZED_NAME_REMOVE = "remove"; + public static final String SERIALIZED_NAME_SET = "set"; + @SerializedName(SERIALIZED_NAME_ADD) + private List add = null; + @SerializedName(SERIALIZED_NAME_REMOVE) + private List remove = null; + @SerializedName(SERIALIZED_NAME_SET) + private List set = null; + + public V1HTTPRouteSpecResponseHeaderModifier addAddItem(V1HTTPRouteSpecRequestHeaderModifierAdd addItem) { + if (this.add == null) { + this.add = new ArrayList<>(); + } + this.add.add(addItem); + return this; + } + + public V1HTTPRouteSpecResponseHeaderModifier addRemoveItem(String removeItem) { + if (this.remove == null) { + this.remove = new ArrayList<>(); + } + this.remove.add(removeItem); + return this; + } + + public V1HTTPRouteSpecResponseHeaderModifier addSetItem(V1HTTPRouteSpecRequestHeaderModifierAdd setItem) { + if (this.set == null) { + this.set = new ArrayList<>(); + } + this.set.add(setItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecResponseHeaderModifier v1HttpRouteSpecResponseHeaderModifier = (V1HTTPRouteSpecResponseHeaderModifier) o; + return Objects.equals(this.add, v1HttpRouteSpecResponseHeaderModifier.add) && + Objects.equals(this.remove, v1HttpRouteSpecResponseHeaderModifier.remove) && + Objects.equals(this.set, v1HttpRouteSpecResponseHeaderModifier.set); + } + + @Override + public int hashCode() { + return Objects.hash(add, remove, set); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecResponseHeaderModifier {\n" + + " add: " + toIndentedString(add) + "\n" + + " remove: " + toIndentedString(remove) + "\n" + + " set: " + toIndentedString(set) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRules.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRules.java new file mode 100644 index 00000000..ad5825f3 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecRules.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + */ +@Data +public class V1HTTPRouteSpecRules { + public static final String SERIALIZED_NAME_BACKEND_REFS = "backendRefs"; + public static final String SERIALIZED_NAME_FILTERS = "filters"; + public static final String SERIALIZED_NAME_MATCHES = "matches"; + @SerializedName(SERIALIZED_NAME_BACKEND_REFS) + private List backendRefs = null; + @SerializedName(SERIALIZED_NAME_FILTERS) + private List filters = null; + @SerializedName(SERIALIZED_NAME_MATCHES) + private List matches = null; + + public V1HTTPRouteSpecRules addBackendRefsItem(V1HTTPRouteSpecBackendRefs backendRefsItem) { + if (this.backendRefs == null) { + this.backendRefs = new ArrayList<>(); + } + this.backendRefs.add(backendRefsItem); + return this; + } + + public V1HTTPRouteSpecRules addFiltersItem(V1HTTPRouteSpecFilters filtersItem) { + if (this.filters == null) { + this.filters = new ArrayList<>(); + } + this.filters.add(filtersItem); + return this; + } + + + public V1HTTPRouteSpecRules addMatchesItem(V1HTTPRouteSpecMatches matchesItem) { + if (this.matches == null) { + this.matches = new ArrayList<>(); + } + this.matches.add(matchesItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecRules v1HttpRouteSpecRules = (V1HTTPRouteSpecRules) o; + return Objects.equals(this.backendRefs, v1HttpRouteSpecRules.backendRefs) && + Objects.equals(this.filters, v1HttpRouteSpecRules.filters) && + Objects.equals(this.matches, v1HttpRouteSpecRules.matches); + } + + @Override + public int hashCode() { + return Objects.hash(backendRefs, filters, matches); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecRules {\n" + + " backendRefs: " + toIndentedString(backendRefs) + "\n" + + " filters: " + toIndentedString(filters) + "\n" + + " matches: " + toIndentedString(matches) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecUrlRewrite.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecUrlRewrite.java new file mode 100644 index 00000000..4d3a3316 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecUrlRewrite.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended + */ +@Data +public class V1HTTPRouteSpecUrlRewrite { + public static final String SERIALIZED_NAME_HOSTNAME = "hostname"; + public static final String SERIALIZED_NAME_PATH = "path"; + @SerializedName(SERIALIZED_NAME_HOSTNAME) + private String hostname; + @SerializedName(SERIALIZED_NAME_PATH) + private V1HTTPRouteSpecUrlRewritePath path; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecUrlRewrite v1HttpRouteSpecUrlRewrite = (V1HTTPRouteSpecUrlRewrite) o; + return Objects.equals(this.hostname, v1HttpRouteSpecUrlRewrite.hostname) && + Objects.equals(this.path, v1HttpRouteSpecUrlRewrite.path); + } + + @Override + public int hashCode() { + return Objects.hash(hostname, path); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecUrlRewrite {\n" + + " hostname: " + toIndentedString(hostname) + "\n" + + " path: " + toIndentedString(path) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecUrlRewritePath.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecUrlRewritePath.java new file mode 100644 index 00000000..61d77fcf --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteSpecUrlRewritePath.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.util.Objects; + +/** + * Path defines a path rewrite. Support: Extended + */ +@Data +public class V1HTTPRouteSpecUrlRewritePath { + public static final String SERIALIZED_NAME_REPLACE_FULL_PATH = "replaceFullPath"; + public static final String SERIALIZED_NAME_REPLACE_PREFIX_MATCH = "replacePrefixMatch"; + public static final String SERIALIZED_NAME_TYPE = "type"; + @SerializedName(SERIALIZED_NAME_REPLACE_FULL_PATH) + private String replaceFullPath; + @SerializedName(SERIALIZED_NAME_REPLACE_PREFIX_MATCH) + private String replacePrefixMatch; + @SerializedName(SERIALIZED_NAME_TYPE) + private TypeEnum type; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteSpecUrlRewritePath v1HttpRouteSpecUrlRewritePath = (V1HTTPRouteSpecUrlRewritePath) o; + return Objects.equals(this.replaceFullPath, v1HttpRouteSpecUrlRewritePath.replaceFullPath) && + Objects.equals(this.replacePrefixMatch, v1HttpRouteSpecUrlRewritePath.replacePrefixMatch) && + Objects.equals(this.type, v1HttpRouteSpecUrlRewritePath.type); + } + + @Override + public int hashCode() { + return Objects.hash(replaceFullPath, replacePrefixMatch, type); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteSpecUrlRewritePath {\n" + + " replaceFullPath: " + toIndentedString(replaceFullPath) + "\n" + + " replacePrefixMatch: " + toIndentedString(replacePrefixMatch) + "\n" + + " type: " + toIndentedString(type) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. + */ + @JsonAdapter(TypeEnum.Adapter.class) + public enum TypeEnum { + /** + * REPLACEFULLPATH replaces the entire path of the incoming request with a new specified path. + * This is useful when you want to completely override the original request path. + */ + REPLACEFULLPATH("ReplaceFullPath"), + + /** + * REPLACEPREFIXMATCH replaces the matching prefix of the path in the incoming request with a new prefix. + * This is commonly used when you need to change the beginning of the path while preserving the rest of the original path. + */ + REPLACEPREFIXMATCH("ReplacePrefixMatch"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final TypeEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public TypeEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return TypeEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatus.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatus.java new file mode 100644 index 00000000..8a362f51 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatus.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Status defines the current state of HTTPRoute. + */ +@Data +public class V1HTTPRouteStatus { + public static final String SERIALIZED_NAME_PARENTS = "parents"; + @SerializedName(SERIALIZED_NAME_PARENTS) + private List parents = new ArrayList<>(); + + + public V1HTTPRouteStatus addParentsItem(V1HTTPRouteStatusParents parentsItem) { + this.parents.add(parentsItem); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteStatus v1HttpRouteStatus = (V1HTTPRouteStatus) o; + return Objects.equals(this.parents, v1HttpRouteStatus.parents); + } + + @Override + public int hashCode() { + return Objects.hash(parents); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteStatus {\n" + + " parents: " + toIndentedString(parents) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusConditions.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusConditions.java new file mode 100644 index 00000000..16a57217 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusConditions.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Data; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.Objects; + +/** + * Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` // other fields } + */ +@Data +public class V1HTTPRouteStatusConditions { + public static final String SERIALIZED_NAME_LAST_TRANSITION_TIME = "lastTransitionTime"; + public static final String SERIALIZED_NAME_MESSAGE = "message"; + public static final String SERIALIZED_NAME_OBSERVED_GENERATION = "observedGeneration"; + public static final String SERIALIZED_NAME_REASON = "reason"; + public static final String SERIALIZED_NAME_STATUS = "status"; + public static final String SERIALIZED_NAME_TYPE = "type"; + @SerializedName(SERIALIZED_NAME_LAST_TRANSITION_TIME) + private OffsetDateTime lastTransitionTime; + @SerializedName(SERIALIZED_NAME_MESSAGE) + private String message; + @SerializedName(SERIALIZED_NAME_OBSERVED_GENERATION) + private Long observedGeneration; + @SerializedName(SERIALIZED_NAME_REASON) + private String reason; + @SerializedName(SERIALIZED_NAME_STATUS) + private StatusEnum status; + @SerializedName(SERIALIZED_NAME_TYPE) + private String type; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteStatusConditions v1HttpRouteStatusConditions = (V1HTTPRouteStatusConditions) o; + return Objects.equals(this.lastTransitionTime, v1HttpRouteStatusConditions.lastTransitionTime) && + Objects.equals(this.message, v1HttpRouteStatusConditions.message) && + Objects.equals(this.observedGeneration, v1HttpRouteStatusConditions.observedGeneration) && + Objects.equals(this.reason, v1HttpRouteStatusConditions.reason) && + Objects.equals(this.status, v1HttpRouteStatusConditions.status) && + Objects.equals(this.type, v1HttpRouteStatusConditions.type); + } + + @Override + public int hashCode() { + return Objects.hash(lastTransitionTime, message, observedGeneration, reason, status, type); + } + + @Override + public String toString() { + String sb = "class V1HTTPRouteStatusConditions {\n" + + " lastTransitionTime: " + toIndentedString(lastTransitionTime) + "\n" + + " message: " + toIndentedString(message) + "\n" + + " observedGeneration: " + toIndentedString(observedGeneration) + "\n" + + " reason: " + toIndentedString(reason) + "\n" + + " status: " + toIndentedString(status) + "\n" + + " type: " + toIndentedString(type) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * status of the condition, one of True, False, Unknown. + */ + @JsonAdapter(StatusEnum.Adapter.class) + public enum StatusEnum { + /** + * Represents the state where the condition is true. + */ + TRUE("True"), + + /** + * Represents the state where the condition is false. + */ + FALSE("False"), + + /** + * Represents an unknown state. + */ + UNKNOWN("Unknown"); + + private final String value; + + StatusEnum(String value) { + this.value = value; + } + + public static StatusEnum fromValue(String value) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final StatusEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public StatusEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return StatusEnum.fromValue(value); + } + } + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusParentRef.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusParentRef.java new file mode 100644 index 00000000..539d57b5 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusParentRef.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + */ +@Data +public class V1HTTPRouteStatusParentRef { + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAME = "name"; + public static final String SERIALIZED_NAME_NAMESPACE = "namespace"; + public static final String SERIALIZED_NAME_PORT = "port"; + public static final String SERIALIZED_NAME_SECTION_NAME = "sectionName"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + @SerializedName(SERIALIZED_NAME_NAMESPACE) + private String namespace; + @SerializedName(SERIALIZED_NAME_PORT) + private Integer port; + @SerializedName(SERIALIZED_NAME_SECTION_NAME) + private String sectionName; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteStatusParentRef v1HttpRouteStatusParentRef = (V1HTTPRouteStatusParentRef) o; + return Objects.equals(this.group, v1HttpRouteStatusParentRef.group) && + Objects.equals(this.kind, v1HttpRouteStatusParentRef.kind) && + Objects.equals(this.name, v1HttpRouteStatusParentRef.name) && + Objects.equals(this.namespace, v1HttpRouteStatusParentRef.namespace) && + Objects.equals(this.port, v1HttpRouteStatusParentRef.port) && + Objects.equals(this.sectionName, v1HttpRouteStatusParentRef.sectionName); + } + + @Override + public int hashCode() { + return Objects.hash(group, kind, name, namespace, port, sectionName); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteStatusParentRef {\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " name: " + toIndentedString(name) + "\n" + + " namespace: " + toIndentedString(namespace) + "\n" + + " port: " + toIndentedString(port) + "\n" + + " sectionName: " + toIndentedString(sectionName) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusParents.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusParents.java new file mode 100644 index 00000000..ced0d985 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/httproute/V1HTTPRouteStatusParents.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * RouteParentStatus describes the status of a route with respect to an associated Parent. + */ +@Data +public class V1HTTPRouteStatusParents { + public static final String SERIALIZED_NAME_CONDITIONS = "conditions"; + public static final String SERIALIZED_NAME_CONTROLLER_NAME = "controllerName"; + public static final String SERIALIZED_NAME_PARENT_REF = "parentRef"; + @SerializedName(SERIALIZED_NAME_CONDITIONS) + private List conditions = null; + @SerializedName(SERIALIZED_NAME_CONTROLLER_NAME) + private String controllerName; + @SerializedName(SERIALIZED_NAME_PARENT_REF) + private V1HTTPRouteStatusParentRef parentRef; + + public V1HTTPRouteStatusParents addConditionsItem(V1HTTPRouteStatusConditions conditionsItem) { + if (this.conditions == null) { + this.conditions = new ArrayList<>(); + } + this.conditions.add(conditionsItem); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1HTTPRouteStatusParents v1HttpRouteStatusParents = (V1HTTPRouteStatusParents) o; + return Objects.equals(this.conditions, v1HttpRouteStatusParents.conditions) && + Objects.equals(this.controllerName, v1HttpRouteStatusParents.controllerName) && + Objects.equals(this.parentRef, v1HttpRouteStatusParents.parentRef); + } + + @Override + public int hashCode() { + return Objects.hash(conditions, controllerName, parentRef); + } + + + @Override + public String toString() { + String sb = "class V1HTTPRouteStatusParents {\n" + + " conditions: " + toIndentedString(conditions) + "\n" + + " controllerName: " + toIndentedString(controllerName) + "\n" + + " parentRef: " + toIndentedString(parentRef) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrant.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrant.java new file mode 100644 index 00000000..441515c8 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrant.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.referencegrant; + +import com.google.gson.annotations.SerializedName; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import lombok.Data; + +import java.util.Objects; + +/** + * ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. + */ + +@Data +public class V1beta1ReferenceGrant implements io.kubernetes.client.common.KubernetesObject { + public static final String API_GROUP = "gateway.networking.k8s.io"; + public static final String VERSION = "v1beta1"; + public static final String KIND = "ReferenceGrant"; + public static final String PLURAL = "referencegrants"; + + + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_METADATA = "metadata"; + public static final String SERIALIZED_NAME_SPEC = "spec"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion = API_GROUP + "/" + VERSION; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind = KIND; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ObjectMeta metadata = null; + @SerializedName(SERIALIZED_NAME_SPEC) + private V1beta1ReferenceGrantSpec spec; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1beta1ReferenceGrant v1beta1ReferenceGrant = (V1beta1ReferenceGrant) o; + return Objects.equals(this.apiVersion, v1beta1ReferenceGrant.apiVersion) && + Objects.equals(this.kind, v1beta1ReferenceGrant.kind) && + Objects.equals(this.metadata, v1beta1ReferenceGrant.metadata) && + Objects.equals(this.spec, v1beta1ReferenceGrant.spec); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, kind, metadata, spec); + } + + + @Override + public String toString() { + String sb = "class V1beta1ReferenceGrant {\n" + + " apiVersion: " + toIndentedString(apiVersion) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " metadata: " + toIndentedString(metadata) + "\n" + + " spec: " + toIndentedString(spec) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantList.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantList.java new file mode 100644 index 00000000..28dd389f --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantList.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.referencegrant; + +import com.google.gson.annotations.SerializedName; +import io.kubernetes.client.openapi.models.V1ListMeta; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * ReferenceGrantList is a list of ReferenceGrant + */ +@Data +public class V1beta1ReferenceGrantList implements io.kubernetes.client.common.KubernetesListObject { + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + public static final String SERIALIZED_NAME_ITEMS = "items"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_METADATA = "metadata"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion; + @SerializedName(SERIALIZED_NAME_ITEMS) + private List items = new ArrayList<>(); + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ListMeta metadata = null; + + + public V1beta1ReferenceGrantList addItemsItem(V1beta1ReferenceGrant itemsItem) { + this.items.add(itemsItem); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1beta1ReferenceGrantList v1beta1ReferenceGrantList = (V1beta1ReferenceGrantList) o; + return Objects.equals(this.apiVersion, v1beta1ReferenceGrantList.apiVersion) && + Objects.equals(this.items, v1beta1ReferenceGrantList.items) && + Objects.equals(this.kind, v1beta1ReferenceGrantList.kind) && + Objects.equals(this.metadata, v1beta1ReferenceGrantList.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, items, kind, metadata); + } + + + @Override + public String toString() { + String sb = "class V1beta1ReferenceGrantList {\n" + + " apiVersion: " + toIndentedString(apiVersion) + "\n" + + " items: " + toIndentedString(items) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " metadata: " + toIndentedString(metadata) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpec.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpec.java new file mode 100644 index 00000000..f9d42bbc --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpec.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.referencegrant; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Spec defines the desired state of ReferenceGrant. + */ +@Data +public class V1beta1ReferenceGrantSpec { + public static final String SERIALIZED_NAME_FROM = "from"; + public static final String SERIALIZED_NAME_TO = "to"; + @SerializedName(SERIALIZED_NAME_FROM) + private List from = new ArrayList<>(); + @SerializedName(SERIALIZED_NAME_TO) + private List to = new ArrayList<>(); + + + public V1beta1ReferenceGrantSpec addToItem(V1beta1ReferenceGrantSpecTo toItem) { + this.to.add(toItem); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1beta1ReferenceGrantSpec v1beta1ReferenceGrantSpec = (V1beta1ReferenceGrantSpec) o; + return Objects.equals(this.from, v1beta1ReferenceGrantSpec.from) && + Objects.equals(this.to, v1beta1ReferenceGrantSpec.to); + } + + @Override + public int hashCode() { + return Objects.hash(from, to); + } + + + @Override + public String toString() { + String sb = "class V1beta1ReferenceGrantSpec {\n" + + " from: " + toIndentedString(from) + "\n" + + " to: " + toIndentedString(to) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpecFrom.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpecFrom.java new file mode 100644 index 00000000..59bf16f2 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpecFrom.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.referencegrant; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * ReferenceGrantFrom describes trusted namespaces and kinds. + */ +@Data +public class V1beta1ReferenceGrantSpecFrom { + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAMESPACE = "namespace"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAMESPACE) + private String namespace; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1beta1ReferenceGrantSpecFrom v1beta1ReferenceGrantSpecFrom = (V1beta1ReferenceGrantSpecFrom) o; + return Objects.equals(this.group, v1beta1ReferenceGrantSpecFrom.group) && + Objects.equals(this.kind, v1beta1ReferenceGrantSpecFrom.kind) && + Objects.equals(this.namespace, v1beta1ReferenceGrantSpecFrom.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(group, kind, namespace); + } + + + @Override + public String toString() { + String sb = "class V1beta1ReferenceGrantSpecFrom {\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " namespace: " + toIndentedString(namespace) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpecTo.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpecTo.java new file mode 100644 index 00000000..ac0322a0 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes/crd/gatewayapi/referencegrant/V1beta1ReferenceGrantSpecTo.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.referencegrant; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.Objects; + +/** + * ReferenceGrantTo describes what Kinds are allowed as targets of the references. + */ +@Data +public class V1beta1ReferenceGrantSpecTo { + public static final String SERIALIZED_NAME_GROUP = "group"; + public static final String SERIALIZED_NAME_KIND = "kind"; + public static final String SERIALIZED_NAME_NAME = "name"; + @SerializedName(SERIALIZED_NAME_GROUP) + private String group; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + @SerializedName(SERIALIZED_NAME_NAME) + private String name; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1beta1ReferenceGrantSpecTo v1beta1ReferenceGrantSpecTo = (V1beta1ReferenceGrantSpecTo) o; + return Objects.equals(this.group, v1beta1ReferenceGrantSpecTo.group) && + Objects.equals(this.kind, v1beta1ReferenceGrantSpecTo.kind) && + Objects.equals(this.name, v1beta1ReferenceGrantSpecTo.name); + } + + @Override + public int hashCode() { + return Objects.hash(group, kind, name); + } + + + @Override + public String toString() { + String sb = "class V1beta1ReferenceGrantSpecTo {\n" + + " group: " + toIndentedString(group) + "\n" + + " kind: " + toIndentedString(kind) + "\n" + + " name: " + toIndentedString(name) + "\n" + + "}"; + return sb; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/DomainContext.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/DomainContext.java new file mode 100644 index 00000000..46154b8e --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/DomainContext.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service.strategy.domain; + +import com.alibaba.higress.sdk.model.Domain; + +public class DomainContext { + + private final DomainStrategy strategy; + + public DomainContext(DomainStrategy strategy) { + this.strategy = strategy; + } + + public Domain add(Domain domain) { + return strategy.add(domain); + } + + public Domain put(Domain domain) { + return strategy.put(domain); + } + + public void delete(String domainName) { + strategy.delete(domainName); + } +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/DomainStrategy.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/DomainStrategy.java new file mode 100644 index 00000000..84fc8b05 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/DomainStrategy.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service.strategy.domain; + +import com.alibaba.higress.sdk.model.Domain; + +public interface DomainStrategy { + Domain add(Domain domain); + + Domain put(Domain domain); + + void delete(String domainName); +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/GatewayDomainStrategy.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/GatewayDomainStrategy.java new file mode 100644 index 00000000..b6cb7d66 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/GatewayDomainStrategy.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service.strategy.domain; + +import com.alibaba.higress.sdk.exception.BusinessException; +import com.alibaba.higress.sdk.exception.ResourceConflictException; +import com.alibaba.higress.sdk.http.HttpStatus; +import com.alibaba.higress.sdk.model.Domain; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1Gateway; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1ConfigMap; + +public class GatewayDomainStrategy implements DomainStrategy { + + private final KubernetesClientService kubernetesClientService; + private final KubernetesModelConverter kubernetesModelConverter; + + public GatewayDomainStrategy(KubernetesClientService kubernetesClientService, KubernetesModelConverter kubernetesModelConverter) { + this.kubernetesClientService = kubernetesClientService; + this.kubernetesModelConverter = kubernetesModelConverter; + } + + @Override + public Domain add(Domain domain) { + V1ConfigMap domainConfigMap = kubernetesModelConverter.domain2ConfigMap(domain); + V1ConfigMap newDomainConfigMap; + try { + newDomainConfigMap = kubernetesClientService.createConfigMap(domainConfigMap); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException("Error occurs when adding a new gateway domain.", e); + } + V1Gateway gateway = kubernetesModelConverter.domain2Gateway(domain); + try { + kubernetesClientService.createGateway(gateway); + } catch (ApiException e) { + throw new BusinessException("Error occurs when creating a Gateway for the domain.", e); + } + return kubernetesModelConverter.configMap2Domain(newDomainConfigMap); + } + + @Override + public Domain put(Domain domain) { + V1ConfigMap domainConfigMap = kubernetesModelConverter.domain2ConfigMap(domain); + V1ConfigMap updatedConfigMap; + try { + updatedConfigMap = kubernetesClientService.replaceConfigMap(domainConfigMap); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException("Error occurs when updating the gateway domain.", e); + } + V1Gateway gateway = kubernetesModelConverter.domain2Gateway(domain); + try { + kubernetesClientService.replaceGateway(gateway); + } catch (ApiException e) { + throw new BusinessException("Error occurs when updating a Gateway for the domain.", e); + } + return kubernetesModelConverter.configMap2Domain(updatedConfigMap); + } + + @Override + public void delete(String domainName) { + String configMapName = kubernetesModelConverter.domainName2ConfigMapName(domainName); + try { + kubernetesClientService.deleteConfigMap(configMapName); + } catch (ApiException e) { + throw new BusinessException("Error occurs when deleting the gateway ConfigMap with name: " + configMapName, e); + } + String gatewayName = kubernetesModelConverter.domainName2GatewayName(domainName); + try { + kubernetesClientService.deleteGateway(gatewayName); + } catch (ApiException e) { + throw new BusinessException("Error occurs when deleting the Gateway with name: " + gatewayName, e); + } + } +} + diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/IngressDomainStrategy.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/IngressDomainStrategy.java new file mode 100644 index 00000000..c5f42cb8 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/domain/IngressDomainStrategy.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service.strategy.domain; + +import com.alibaba.higress.sdk.exception.BusinessException; +import com.alibaba.higress.sdk.exception.ResourceConflictException; +import com.alibaba.higress.sdk.http.HttpStatus; +import com.alibaba.higress.sdk.model.Domain; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1ConfigMap; + +public class IngressDomainStrategy implements DomainStrategy { + + private final KubernetesClientService kubernetesClientService; + private final KubernetesModelConverter kubernetesModelConverter; + + public IngressDomainStrategy(KubernetesClientService kubernetesClientService, KubernetesModelConverter kubernetesModelConverter) { + this.kubernetesClientService = kubernetesClientService; + this.kubernetesModelConverter = kubernetesModelConverter; + } + + @Override + public Domain add(Domain domain) { + V1ConfigMap domainConfigMap = kubernetesModelConverter.domain2ConfigMap(domain); + V1ConfigMap newDomainConfigMap; + try { + newDomainConfigMap = kubernetesClientService.createConfigMap(domainConfigMap); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException("Error occurs when adding a new ingress domain.", e); + } + return kubernetesModelConverter.configMap2Domain(newDomainConfigMap); + } + + @Override + public Domain put(Domain domain) { + V1ConfigMap domainConfigMap = kubernetesModelConverter.domain2ConfigMap(domain); + V1ConfigMap updatedConfigMap; + try { + updatedConfigMap = kubernetesClientService.replaceConfigMap(domainConfigMap); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException("Error occurs when updating the ingress domain.", e); + } + return kubernetesModelConverter.configMap2Domain(updatedConfigMap); + } + + @Override + public void delete(String domainName) { + String configMapName = kubernetesModelConverter.domainName2ConfigMapName(domainName); + try { + kubernetesClientService.deleteConfigMap(configMapName); + } catch (ApiException e) { + throw new BusinessException("Error occurs when deleting the ingress ConfigMap with name: " + configMapName, e); + } + } +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/HttpRouteStrategy.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/HttpRouteStrategy.java new file mode 100644 index 00000000..ad3c1183 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/HttpRouteStrategy.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service.strategy.route; + +import com.alibaba.higress.sdk.constant.HigressConstants; +import com.alibaba.higress.sdk.constant.Separators; +import com.alibaba.higress.sdk.exception.BusinessException; +import com.alibaba.higress.sdk.exception.ResourceConflictException; +import com.alibaba.higress.sdk.http.HttpStatus; +import com.alibaba.higress.sdk.model.Domain; +import com.alibaba.higress.sdk.model.Route; +import com.alibaba.higress.sdk.model.WasmPluginInstanceScope; +import com.alibaba.higress.sdk.service.WasmPluginInstanceService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1Gateway; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRoute; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1ConfigMap; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +public class HttpRouteStrategy implements RouteStrategy { + + private final KubernetesClientService kubernetesClientService; + private final KubernetesModelConverter kubernetesModelConverter; + private final WasmPluginInstanceService wasmPluginInstanceService; + + + public HttpRouteStrategy(KubernetesClientService kubernetesClientService, KubernetesModelConverter kubernetesModelConverter, WasmPluginInstanceService wasmPluginInstanceService) { + this.kubernetesClientService = kubernetesClientService; + this.kubernetesModelConverter = kubernetesModelConverter; + this.wasmPluginInstanceService = wasmPluginInstanceService; + } + + @Override + public Route add(Route route) { + createIfDomainIsNotGateway(route.getDomains()); + V1HTTPRoute httpRoute = kubernetesModelConverter.route2HttpRoute(route); + V1HTTPRoute newHttpRoute; + try { + newHttpRoute = kubernetesClientService.createHttpRoute(httpRoute); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException( + "Error occurs when adding the HTTPRoute generated by route with name: " + route.getName(), e); + } + return kubernetesModelConverter.httpRoute2Route(newHttpRoute); + } + + @Override + public Route update(Route route) { + createIfDomainIsNotGateway(route.getDomains()); + V1HTTPRoute httpRoute = kubernetesModelConverter.route2HttpRoute(route); + V1HTTPRoute updatedHttpRoute; + try { + updatedHttpRoute = kubernetesClientService.replaceHttpRoute(httpRoute); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException( + "Error occurs when updating the HTTP route generated by route with name: " + route.getName(), e); + } + return kubernetesModelConverter.httpRoute2Route(updatedHttpRoute); + } + + @Override + public void delete(String name) { + try { + kubernetesClientService.deleteHttpRoute(name); + if (!HigressConstants.NS_DEFAULT.equals(kubernetesClientService.httpRouteNameSpace)) { + name = kubernetesClientService.httpRouteNameSpace + Separators.SLASH + name; + } + } catch (ApiException e) { + throw new BusinessException("Error occurs when deleting HTTP route with name: " + name, e); + } + wasmPluginInstanceService.deleteAll(WasmPluginInstanceScope.ROUTE, name); + } + + public void createIfDomainIsNotGateway(List domains) { + if (domains.isEmpty()) { + return; + } + // if domain is ingress-style, create a gateway cr + String domainName = domains.get(0); + V1ConfigMap configMap; + try { + configMap = kubernetesClientService.readConfigMap(kubernetesModelConverter.domainName2ConfigMapName(domainName)); + } catch (ApiException e) { + throw new BusinessException("Error occurs when reading config map associated with domain " + domainName, + e); + } + if (configMap == null) { + return; + } + Domain domain = kubernetesModelConverter.configMap2Domain(configMap); + if (domain.getIsIngressMode()) { + domain.setIsIngressMode(Boolean.FALSE); + V1ConfigMap domainConfigMap = kubernetesModelConverter.domain2ConfigMap(domain); + V1ConfigMap updatedConfigMap; + try { + updatedConfigMap = kubernetesClientService.replaceConfigMap(domainConfigMap); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException( + "Error occurs when replacing the ConfigMap generated by domain: " + domain.getName(), e); + } + domain = kubernetesModelConverter.configMap2Domain(updatedConfigMap); + V1Gateway gateway = kubernetesModelConverter.domain2Gateway(domain); + gateway.getMetadata().setResourceVersion(StringUtils.EMPTY); + try { + kubernetesClientService.createGateway(gateway); + } catch (ApiException e) { + throw new BusinessException("Error occurs when creating a Gateway", e); + } + } + } +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/IngressRouteStrategy.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/IngressRouteStrategy.java new file mode 100644 index 00000000..bf077172 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/IngressRouteStrategy.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service.strategy.route; + +import com.alibaba.higress.sdk.exception.BusinessException; +import com.alibaba.higress.sdk.exception.ResourceConflictException; +import com.alibaba.higress.sdk.http.HttpStatus; +import com.alibaba.higress.sdk.model.Route; +import com.alibaba.higress.sdk.model.WasmPluginInstanceScope; +import com.alibaba.higress.sdk.service.WasmPluginInstanceService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService; +import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1Ingress; + +public class IngressRouteStrategy implements RouteStrategy { + + private final KubernetesClientService kubernetesClientService; + private final KubernetesModelConverter kubernetesModelConverter; + private final WasmPluginInstanceService wasmPluginInstanceService; + + public IngressRouteStrategy(KubernetesClientService kubernetesClientService, KubernetesModelConverter kubernetesModelConverter, WasmPluginInstanceService wasmPluginInstanceService) { + this.kubernetesClientService = kubernetesClientService; + this.kubernetesModelConverter = kubernetesModelConverter; + this.wasmPluginInstanceService = wasmPluginInstanceService; + } + + @Override + public Route add(Route route) { + V1Ingress ingress = kubernetesModelConverter.route2Ingress(route); + V1Ingress newIngress; + try { + newIngress = kubernetesClientService.createIngress(ingress); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException( + "Error occurs when adding the ingress generated by route with name: " + route.getName(), e); + } + return kubernetesModelConverter.ingress2Route(newIngress); + } + + @Override + public Route update(Route route) { + V1Ingress ingress = kubernetesModelConverter.route2Ingress(route); + V1Ingress updatedIngress; + try { + updatedIngress = kubernetesClientService.replaceIngress(ingress); + } catch (ApiException e) { + if (e.getCode() == HttpStatus.CONFLICT) { + throw new ResourceConflictException(); + } + throw new BusinessException( + "Error occurs when updating the ingress generated by route with name: " + route.getName(), e); + } + return kubernetesModelConverter.ingress2Route(updatedIngress); + } + + @Override + public void delete(String name) { + try { + V1Ingress ingress = kubernetesClientService.readIngress(name); + if (ingress != null) { + kubernetesClientService.deleteIngress(name); + } + } catch (ApiException e) { + throw new BusinessException("Error occurs when deleting ingress with name: " + name, e); + } + wasmPluginInstanceService.deleteAll(WasmPluginInstanceScope.ROUTE, name); + } +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/RouteContext.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/RouteContext.java new file mode 100644 index 00000000..e7428e85 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/RouteContext.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service.strategy.route; + +import com.alibaba.higress.sdk.model.Route; + +public class RouteContext { + + private final RouteStrategy strategy; + + public RouteContext(RouteStrategy strategy) { + this.strategy = strategy; + } + + public Route add(Route route) { + return strategy.add(route); + } + + public Route update(Route route) { + return strategy.update(route); + } + + public void delete(String name) { + strategy.delete(name); + } +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/RouteStrategy.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/RouteStrategy.java new file mode 100644 index 00000000..f4144d4a --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/strategy/route/RouteStrategy.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022-2023 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.alibaba.higress.sdk.service.strategy.route; + +import com.alibaba.higress.sdk.model.Route; + +public interface RouteStrategy { + Route add(Route route); + + Route update(Route route); + + void delete(String name); +} diff --git a/backend/sdk/src/test/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesModelConverterTest.java b/backend/sdk/src/test/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesModelConverterTest.java index eb0229a3..0d45c0c5 100644 --- a/backend/sdk/src/test/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesModelConverterTest.java +++ b/backend/sdk/src/test/java/com/alibaba/higress/sdk/service/kubernetes/KubernetesModelConverterTest.java @@ -23,6 +23,37 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; + + +import com.alibaba.higress.sdk.constant.HigressConstants; +import com.alibaba.higress.sdk.constant.Separators; +import com.alibaba.higress.sdk.model.Domain; +import com.alibaba.higress.sdk.model.route.Header; +import com.alibaba.higress.sdk.model.route.HeaderControlConfig; +import com.alibaba.higress.sdk.model.route.HeaderControlStageConfig; +import com.alibaba.higress.sdk.model.route.KeyedRoutePredicate; +import com.alibaba.higress.sdk.model.route.RewriteConfig; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gatewayclass.V1GatewayClass; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1Gateway; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1GatewaySpec; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1GatewaySpecListeners; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.gateways.V1GatewaySpecTls; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRoute; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpec; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecBackendRefs; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecFilters; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecHeaders; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecMatches; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecParentRefs; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecPath; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecQueryParams; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecRequestHeaderModifier; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecRequestHeaderModifierAdd; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecResponseHeaderModifier; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecRules; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecUrlRewrite; +import com.alibaba.higress.sdk.service.kubernetes.crd.gatewayapi.httproute.V1HTTPRouteSpecUrlRewritePath; import java.util.Objects; import java.util.function.Predicate; @@ -35,7 +66,6 @@ import com.alibaba.higress.sdk.constant.CommonKey; import com.alibaba.higress.sdk.constant.KubernetesConstants; import com.alibaba.higress.sdk.exception.ValidationException; -import com.alibaba.higress.sdk.model.Domain; import com.alibaba.higress.sdk.model.Route; import com.alibaba.higress.sdk.model.ServiceSource; import com.alibaba.higress.sdk.model.TlsCertificate; @@ -44,7 +74,6 @@ import com.alibaba.higress.sdk.model.WasmPluginInstanceScope; import com.alibaba.higress.sdk.model.route.CorsConfig; import com.alibaba.higress.sdk.model.route.ProxyNextUpstreamConfig; -import com.alibaba.higress.sdk.model.route.RewriteConfig; import com.alibaba.higress.sdk.model.route.RoutePredicate; import com.alibaba.higress.sdk.model.route.RoutePredicateTypeEnum; import com.alibaba.higress.sdk.model.route.UpstreamService; @@ -213,6 +242,7 @@ public void ingress2RouteTestPrefixPathSingleService() { Route route = converter.ingress2Route(ingress); Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(true); expectedRoute.setName(metadata.getName()); RoutePredicate pathPredicate = expectedRoute.getPath(); pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); @@ -239,6 +269,7 @@ public void ingress2RouteTestPrefixPathSingleServiceWithWeight() { Route route = converter.ingress2Route(ingress); Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(true); expectedRoute.setName(metadata.getName()); RoutePredicate pathPredicate = expectedRoute.getPath(); pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); @@ -265,6 +296,7 @@ public void ingress2RouteTestPrefixPathSingleServiceWithPort() { Route route = converter.ingress2Route(ingress); Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(true); expectedRoute.setName(metadata.getName()); RoutePredicate pathPredicate = expectedRoute.getPath(); pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); @@ -291,6 +323,7 @@ public void ingress2RouteTestPrefixPathSingleServiceWithPortAndVersion() { Route route = converter.ingress2Route(ingress); Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(true); expectedRoute.setName(metadata.getName()); RoutePredicate pathPredicate = expectedRoute.getPath(); pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); @@ -318,6 +351,7 @@ public void ingress2RouteTestPrefixPathMultipleServices() { Route route = converter.ingress2Route(ingress); Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(true); expectedRoute.setName(metadata.getName()); RoutePredicate pathPredicate = expectedRoute.getPath(); pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); @@ -346,6 +380,7 @@ public void ingress2RouteTestExactPathSingleService() { Route route = converter.ingress2Route(ingress); Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(true); expectedRoute.setName(metadata.getName()); RoutePredicate pathPredicate = expectedRoute.getPath(); pathPredicate.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); @@ -374,6 +409,7 @@ public void ingress2RouteTestRegularPathSingleService() { Route route = converter.ingress2Route(ingress); Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(true); expectedRoute.setName(metadata.getName()); RoutePredicate pathPredicate = expectedRoute.getPath(); pathPredicate.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); @@ -733,15 +769,18 @@ void domain2ConfigMapTestNormalizeDomainName() { metadata.setLabels(Map.of(KubernetesConstants.Label.CONFIG_MAP_TYPE_KEY, KubernetesConstants.Label.CONFIG_MAP_TYPE_VALUE_DOMAIN)); Map configMap = new HashMap<>(); + configMap.put("workMode", "ingress"); configMap.put(CommonKey.DOMAIN, "domain-name"); - configMap.put(KubernetesConstants.K8S_CERT, "domain-cert"); + configMap.put(KubernetesConstants.K8S_CERT, "{443:\"domain-cert\"}"); configMap.put(KubernetesConstants.K8S_ENABLE_HTTPS, "domain-https"); domainConfigMap.metadata(metadata); domainConfigMap.data(configMap); Domain domain = new Domain(); domain.setName("domain-name"); domain.setVersion("0.0.1"); - domain.setCertIdentifier("domain-cert"); + Map portAndCertMap = new HashMap<>(); + portAndCertMap.put(443, "domain-cert"); + domain.setPortAndCertMap(portAndCertMap); domain.setEnableHttps("domain-https"); V1ConfigMap target = converter.domain2ConfigMap(domain); Assertions.assertEquals(domainConfigMap, target); @@ -766,7 +805,7 @@ void configMap2DomainTestValidConfigMapShouldReturnDomain() { Assertions.assertNotNull(domain); Assertions.assertEquals("higress.cn", domain.getName()); Assertions.assertEquals("1", domain.getVersion()); - Assertions.assertEquals("cert-identifier", domain.getCertIdentifier()); + Assertions.assertEquals("cert-identifier", domain.getPortAndCertMap().get(443)); Assertions.assertEquals("true", domain.getEnableHttps()); } @@ -783,7 +822,7 @@ void configMap2DomainTestNullMetadataShouldReturnDomainWithNullVersion() { Assertions.assertNotNull(domain); Assertions.assertEquals("higress.cn", domain.getName()); Assertions.assertNull(domain.getVersion()); - Assertions.assertNull(domain.getCertIdentifier()); + Assertions.assertNull(domain.getPortAndCertMap()); Assertions.assertNull(domain.getEnableHttps()); } @@ -810,7 +849,7 @@ void configMap2DomainTestMissingFieldsShouldReturnDomainWithNullFields() { Assertions.assertNotNull(domain); Assertions.assertEquals("higress.cn", domain.getName()); - Assertions.assertNull(domain.getCertIdentifier()); + Assertions.assertNull(domain.getPortAndCertMap()); Assertions.assertNull(domain.getEnableHttps()); } @@ -1932,6 +1971,1313 @@ private static Route buildBasicRoute() { return route; } + @Test + public void isGatewaySupportedTestMissingMetadata() { + V1Gateway gateway = buildBasicSupportedGateway(); + gateway.setMetadata(null); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + } + + @Test + public void isGatewaySupportedTestMissingSpec() { + V1Gateway gateway = buildBasicSupportedGateway(); + gateway.setSpec(null); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + } + + @Test + public void isGatewaySupportedTestMissingGatewayClass() { + V1Gateway gateway = buildBasicSupportedGateway(); + gateway.getSpec().setGatewayClassName(null); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + } + + @Test + public void isGatewaySupportedTestMissingListeners() { + V1Gateway gateway = buildBasicSupportedGateway(); + gateway.getSpec().setListeners(null); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + + gateway.getSpec().setListeners(Collections.emptyList()); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + } + + @Test + public void isGatewaySupportedTestInvalidListener() { + V1Gateway gateway = buildBasicSupportedGateway(); + V1GatewaySpecListeners listener = gateway.getSpec().getListeners().get(0); + + listener.setPort(null); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + + listener.setPort(0); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + + listener.setPort(80); + + listener.setName(null); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + + listener.setName("valid-name"); + listener.setProtocol("INVALID"); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + + listener.setProtocol("HTTPS"); + listener.setTls(null); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + V1GatewaySpecTls v1GatewaySpecTls = new V1GatewaySpecTls(); + v1GatewaySpecTls.setCertificateRefs(Collections.emptyList()); + listener.setTls(v1GatewaySpecTls); + Assertions.assertFalse(converter.isGatewaySupported(gateway)); + + + } + + @Test + public void isGatewaySupportedTestAllGood() { + V1Gateway gateway = buildBasicSupportedGateway(); + Assertions.assertTrue(converter.isGatewaySupported(gateway)); + } + + @Test + public void isHttpRouteSupportedTestMissingMetadata() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + httpRoute.setMetadata(null); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + } + + @Test + public void isHttpRouteSupportedTestMissingSpec() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + httpRoute.setSpec(null); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + } + + @Test + public void isHttpRouteSupportedTestMissingParentRefs() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + httpRoute.getSpec().setParentRefs(null); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + + httpRoute.getSpec().setParentRefs(Collections.emptyList()); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + } + + @Test + public void isHttpRouteSupportedTestMissingRules() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + httpRoute.getSpec().setRules(null); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + + httpRoute.getSpec().setRules(Collections.emptyList()); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + } + + @Test + public void isHttpRouteSupportedTestTooManyHostnames() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + httpRoute.getSpec().setHostnames(Arrays.asList("host1.com", "host2.com")); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + } + + @Test + public void isHttpRouteSupportedTestInvalidBackendRefs() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecRules rule = httpRoute.getSpec().getRules().get(0); + + rule.setBackendRefs(null); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + + rule.setBackendRefs(Collections.emptyList()); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + + V1HTTPRouteSpecBackendRefs backendRef = new V1HTTPRouteSpecBackendRefs(); + backendRef.setKind("InvalidKind"); + rule.setBackendRefs(Collections.singletonList(backendRef)); + Assertions.assertFalse(converter.isHttpRouteSupported(httpRoute)); + } + + @Test + public void isHttpRouteSupportedTestAllGood() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + Assertions.assertTrue(converter.isHttpRouteSupported(httpRoute)); + } + + private V1HTTPRoute buildBasicSupportedHttpRoute() { + V1HTTPRoute httpRoute = new V1HTTPRoute(); + V1ObjectMeta metadata = new V1ObjectMeta(); + metadata.setNamespace(DEFAULT_NAMESPACE); + KubernetesUtil.setLabel(metadata, KubernetesConstants.Label.RESOURCE_DEFINER_KEY, + KubernetesConstants.Label.RESOURCE_DEFINER_VALUE); + httpRoute.setMetadata(metadata); + V1HTTPRouteSpec spec = new V1HTTPRouteSpec(); + spec.setParentRefs(Collections.singletonList(new V1HTTPRouteSpecParentRefs())); + V1HTTPRouteSpecRules rule = new V1HTTPRouteSpecRules(); + V1HTTPRouteSpecMatches match = new V1HTTPRouteSpecMatches(); + V1HTTPRouteSpecPath path = new V1HTTPRouteSpecPath(); + path.setType(V1HTTPRouteSpecPath.TypeEnum.PATHPREFIX); + path.setValue("/"); + match.setPath(path); + rule.setMatches(Collections.singletonList(match)); + V1HTTPRouteSpecBackendRefs backendRef = new V1HTTPRouteSpecBackendRefs(); + backendRef.setKind("Service"); + rule.setBackendRefs(Collections.singletonList(backendRef)); + spec.setRules(Collections.singletonList(rule)); + httpRoute.setSpec(spec); + return httpRoute; + } + + private V1Gateway buildBasicSupportedGateway() { + V1Gateway gateway = new V1Gateway(); + gateway.setMetadata(new V1ObjectMeta()); + V1GatewaySpec spec = new V1GatewaySpec(); + spec.setGatewayClassName(V1GatewayClass.DEFAULT_NAME); + V1GatewaySpecListeners listener = new V1GatewaySpecListeners(); + listener.setName("http"); + listener.setProtocol("HTTP"); + listener.setPort(80); + spec.setListeners(Collections.singletonList(listener)); + gateway.setSpec(spec); + return gateway; + } + + @Test + public void httpRoute2RouteTestBasic() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + httpRoute.getMetadata().setName("test-route"); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + expectedRoute.setName("test-route"); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + + Assertions.assertEquals(expectedRoute, route); + } + @Test + public void httpRoute2RouteTestWithDomain() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecParentRefs parentRefs = new V1HTTPRouteSpecParentRefs(); + parentRefs.setName(converter.domainName2GatewayName("example.com")); + httpRoute.getSpec().setParentRefs(Collections.singletonList(parentRefs)); + httpRoute.getSpec().addHostnamesItem("example.com"); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + expectedRoute.setDomains(Collections.singletonList("example.com")); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithSinglePath() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecPath path = new V1HTTPRouteSpecPath(); + path.setType(V1HTTPRouteSpecPath.TypeEnum.EXACT); + path.setValue("/exact"); + V1HTTPRouteSpecMatches match = new V1HTTPRouteSpecMatches(); + match.setPath(path); + httpRoute.getSpec().getRules().get(0).setMatches(Collections.singletonList(match)); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + RoutePredicate predicate1 = new RoutePredicate(); + predicate1.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + predicate1.setMatchValue("/exact"); + predicate1.setCaseSensitive(null); + expectedRoute.setPath(predicate1); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithHeaders() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecHeaders header = new V1HTTPRouteSpecHeaders(); + header.setName("X-Test"); + header.setType(V1HTTPRouteSpecHeaders.TypeEnum.EXACT); + header.setValue("test-value"); + httpRoute.getSpec().getRules().get(0).getMatches().get(0).addHeadersItem(header); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + + KeyedRoutePredicate headerPredicate = new KeyedRoutePredicate(); + headerPredicate.setKey("X-Test"); + headerPredicate.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + headerPredicate.setMatchValue("test-value"); + expectedRoute.setHeaders(Collections.singletonList(headerPredicate)); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithQueryParams() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecQueryParams param = new V1HTTPRouteSpecQueryParams(); + param.setName("q"); + param.setType(V1HTTPRouteSpecQueryParams.TypeEnum.EXACT); + param.setValue("search"); + httpRoute.getSpec().getRules().get(0).getMatches().get(0).addQueryParamsItem(param); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + + KeyedRoutePredicate queryParamPredicate = new KeyedRoutePredicate(); + queryParamPredicate.setKey("q"); + queryParamPredicate.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + queryParamPredicate.setMatchValue("search"); + expectedRoute.setUrlParams(Collections.singletonList(queryParamPredicate)); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithMethods() { + List methods = Arrays.asList("GET", "POST", "DELETE", "TRACE"); + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecMatches matchesItem = new V1HTTPRouteSpecMatches(); + + V1HTTPRouteSpecPath path = new V1HTTPRouteSpecPath(); + path.setType(V1HTTPRouteSpecPath.TypeEnum.EXACT); + path.setValue("/exact"); + + matchesItem.setPath(path); + List matches = new ArrayList<>(); + for (String method: methods) { + V1HTTPRouteSpecMatches matchesItemCopy; + try{ + matchesItemCopy= (V1HTTPRouteSpecMatches) matchesItem.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("Error when clone matches", e); + } + if (V1HTTPRouteSpecMatches.MethodEnum.GET.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.GET); + } else if (V1HTTPRouteSpecMatches.MethodEnum.POST.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.POST); + } else if (V1HTTPRouteSpecMatches.MethodEnum.PUT.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.PUT); + } else if (V1HTTPRouteSpecMatches.MethodEnum.DELETE.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.DELETE); + } else if (V1HTTPRouteSpecMatches.MethodEnum.PATCH.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.PATCH); + } else if (V1HTTPRouteSpecMatches.MethodEnum.HEAD.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.HEAD); + } else if (V1HTTPRouteSpecMatches.MethodEnum.OPTIONS.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.OPTIONS); + } else if (V1HTTPRouteSpecMatches.MethodEnum.CONNECT.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.CONNECT); + } else if (V1HTTPRouteSpecMatches.MethodEnum.TRACE.toString().equals(method)) { + matchesItemCopy.setMethod(V1HTTPRouteSpecMatches.MethodEnum.TRACE); + } else { + throw new IllegalArgumentException("Unsupported method type: " + method); + } + matches.add(matchesItemCopy); + } + + httpRoute.getSpec().getRules().get(0).setMatches(matches); + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + RoutePredicate predicate1 = new RoutePredicate(); + predicate1.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + predicate1.setMatchValue("/exact"); + predicate1.setCaseSensitive(null); + expectedRoute.setPath(predicate1); + + expectedRoute.setMethods(methods); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithUrlRewrite() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecFilters filter = new V1HTTPRouteSpecFilters(); + filter.setType(V1HTTPRouteSpecFilters.TypeEnum.URLREWRITE); + V1HTTPRouteSpecUrlRewrite urlRewrite = new V1HTTPRouteSpecUrlRewrite(); + urlRewrite.setHostname("example.com"); + V1HTTPRouteSpecUrlRewritePath path = new V1HTTPRouteSpecUrlRewritePath(); + path.setType(V1HTTPRouteSpecUrlRewritePath.TypeEnum.REPLACEPREFIXMATCH); + path.setReplacePrefixMatch("/new"); + urlRewrite.setPath(path); + filter.setUrlRewrite(urlRewrite); + httpRoute.getSpec().getRules().get(0).addFiltersItem(filter); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + RewriteConfig rewriteConfig = new RewriteConfig(); + rewriteConfig.setEnabled(true); + rewriteConfig.setHost("example.com"); + rewriteConfig.setPath("/new"); + expectedRoute.setRewrite(rewriteConfig); + + Assertions.assertEquals(expectedRoute, route); + } + + + @Test + public void httpRoute2RouteTestWithRequestHeaderModifier() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecFilters filter = new V1HTTPRouteSpecFilters(); + filter.setType(V1HTTPRouteSpecFilters.TypeEnum.REQUESTHEADERMODIFIER); + V1HTTPRouteSpecRequestHeaderModifier headerModifier = new V1HTTPRouteSpecRequestHeaderModifier(); + V1HTTPRouteSpecRequestHeaderModifierAdd addHeader = new V1HTTPRouteSpecRequestHeaderModifierAdd(); + addHeader.setName("X-Custom-Header"); + addHeader.setValue("CustomValue"); + headerModifier.addAddItem(addHeader); + headerModifier.addSetItem(addHeader); + headerModifier.addRemoveItem("X-Remove-Header"); + filter.setRequestHeaderModifier(headerModifier); + httpRoute.getSpec().getRules().get(0).addFiltersItem(filter); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + HeaderControlConfig headerControl = new HeaderControlConfig(); + headerControl.setEnabled(true); + HeaderControlStageConfig requestConfig = new HeaderControlStageConfig(); + List
configAdd = new ArrayList<>(); + requestConfig.setAdd(configAdd); + List
configSet = new ArrayList<>(); + requestConfig.setSet(configSet); + List remove = new ArrayList<>(); + requestConfig.setRemove(remove); + configAdd.add(new Header("X-Custom-Header", "CustomValue")); + configSet.add(new Header("X-Custom-Header", "CustomValue")); + remove.add("X-Remove-Header"); + headerControl.setRequest(requestConfig); + expectedRoute.setHeaderControl(headerControl); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithResponseHeaderModifier() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + V1HTTPRouteSpecFilters filter = new V1HTTPRouteSpecFilters(); + filter.setType(V1HTTPRouteSpecFilters.TypeEnum.RESPONSEHEADERMODIFIER); + V1HTTPRouteSpecResponseHeaderModifier headerModifier = new V1HTTPRouteSpecResponseHeaderModifier(); + V1HTTPRouteSpecRequestHeaderModifierAdd addHeader = new V1HTTPRouteSpecRequestHeaderModifierAdd(); + addHeader.setName("X-Custom-Response-Header"); + addHeader.setValue("CustomResponseValue"); + headerModifier.addAddItem(addHeader); + headerModifier.addSetItem(addHeader); + headerModifier.addRemoveItem("X-Remove-Response-Header"); + filter.setResponseHeaderModifier(headerModifier); + httpRoute.getSpec().getRules().get(0).addFiltersItem(filter); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + HeaderControlConfig headerControl = new HeaderControlConfig(); + headerControl.setEnabled(true); + HeaderControlStageConfig responseConfig = new HeaderControlStageConfig(); + List
configAdd = new ArrayList<>(); + responseConfig.setAdd(configAdd); + List
configSet = new ArrayList<>(); + responseConfig.setSet(configSet); + List remove = new ArrayList<>(); + responseConfig.setRemove(remove); + configAdd.add(new Header("X-Custom-Response-Header", "CustomResponseValue")); + configSet.add(new Header("X-Custom-Response-Header", "CustomResponseValue")); + remove.add("X-Remove-Response-Header"); + headerControl.setResponse(responseConfig); + expectedRoute.setHeaderControl(headerControl); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithBothFilters() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + + // URL Rewrite filter + V1HTTPRouteSpecFilters urlRewriteFilter = new V1HTTPRouteSpecFilters(); + urlRewriteFilter.setType(V1HTTPRouteSpecFilters.TypeEnum.URLREWRITE); + V1HTTPRouteSpecUrlRewrite urlRewrite = new V1HTTPRouteSpecUrlRewrite(); + urlRewrite.setHostname("example.com"); + V1HTTPRouteSpecUrlRewritePath path = new V1HTTPRouteSpecUrlRewritePath(); + path.setType(V1HTTPRouteSpecUrlRewritePath.TypeEnum.REPLACEPREFIXMATCH); + path.setReplacePrefixMatch("/new"); + urlRewrite.setPath(path); + urlRewriteFilter.setUrlRewrite(urlRewrite); + + // Request Header Modifier filter + V1HTTPRouteSpecFilters headerModifierFilter = new V1HTTPRouteSpecFilters(); + headerModifierFilter.setType(V1HTTPRouteSpecFilters.TypeEnum.REQUESTHEADERMODIFIER); + V1HTTPRouteSpecRequestHeaderModifier headerModifier = new V1HTTPRouteSpecRequestHeaderModifier(); + V1HTTPRouteSpecRequestHeaderModifierAdd addHeader = new V1HTTPRouteSpecRequestHeaderModifierAdd(); + addHeader.setName("X-Custom-Header"); + addHeader.setValue("CustomValue"); + headerModifier.addAddItem(addHeader); + headerModifier.addSetItem(addHeader); + headerModifier.addRemoveItem("X-Remove-Header"); + headerModifierFilter.setRequestHeaderModifier(headerModifier); + + httpRoute.getSpec().getRules().get(0).setFilters(Arrays.asList(urlRewriteFilter, headerModifierFilter)); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + + // Expected URL Rewrite config + RewriteConfig rewriteConfig = new RewriteConfig(); + rewriteConfig.setEnabled(true); + rewriteConfig.setHost("example.com"); + rewriteConfig.setPath("/new"); + expectedRoute.setRewrite(rewriteConfig); + + // Expected Header Control config + HeaderControlConfig headerControl = new HeaderControlConfig(); + headerControl.setEnabled(true); + HeaderControlStageConfig requestConfig = new HeaderControlStageConfig(); + List
configAdd = new ArrayList<>(); + requestConfig.setAdd(configAdd); + List
configSet = new ArrayList<>(); + requestConfig.setSet(configSet); + List remove = new ArrayList<>(); + requestConfig.setRemove(remove); + configAdd.add(new Header("X-Custom-Header", "CustomValue")); + configSet.add(new Header("X-Custom-Header", "CustomValue")); + remove.add("X-Remove-Header"); + headerControl.setRequest(requestConfig); + expectedRoute.setHeaderControl(headerControl); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithRegularExpressionHeaders() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + + // Case 1: Regex starts with "^\Q" and ends with "\E.*" -> PREFIX + V1HTTPRouteSpecHeaders header1 = new V1HTTPRouteSpecHeaders(); + header1.setName("X-Prefix-Header"); + header1.setType(V1HTTPRouteSpecHeaders.TypeEnum.REGULAREXPRESSION); + header1.setValue("^"+ Pattern.quote("prefix")+".*"); + + // Case 2: Other regex pattern + V1HTTPRouteSpecHeaders header2 = new V1HTTPRouteSpecHeaders(); + header2.setName("X-Regex-Header"); + header2.setType(V1HTTPRouteSpecHeaders.TypeEnum.REGULAREXPRESSION); + header2.setValue("regex-pattern"); + + httpRoute.getSpec().getRules().get(0).getMatches().get(0).setHeaders(Arrays.asList(header1, header2)); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + + List expectedHeaders = new ArrayList<>(); + + // Expected result for Case 1 + KeyedRoutePredicate expectedHeader1 = new KeyedRoutePredicate(); + expectedHeader1.setKey("X-Prefix-Header"); + expectedHeader1.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + expectedHeader1.setMatchValue("prefix"); + expectedHeaders.add(expectedHeader1); + + // Expected result for Case 2 + KeyedRoutePredicate expectedHeader2 = new KeyedRoutePredicate(); + expectedHeader2.setKey("X-Regex-Header"); + expectedHeader2.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + expectedHeader2.setMatchValue("regex-pattern"); + expectedHeaders.add(expectedHeader2); + + expectedRoute.setHeaders(expectedHeaders); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void httpRoute2RouteTestWithRegularExpressionQueryParams() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + + // Case 1: Regex starts with "^" and ends with ".*" + V1HTTPRouteSpecQueryParams param1 = new V1HTTPRouteSpecQueryParams(); + param1.setName("prefix_param"); + param1.setType(V1HTTPRouteSpecQueryParams.TypeEnum.REGULAREXPRESSION); + param1.setValue("^"+ Pattern.quote("prefix")+".*"); + + // Case 2: Other regex pattern + V1HTTPRouteSpecQueryParams param2 = new V1HTTPRouteSpecQueryParams(); + param2.setName("regex_param"); + param2.setType(V1HTTPRouteSpecQueryParams.TypeEnum.REGULAREXPRESSION); + param2.setValue("regex-pattern"); + + httpRoute.getSpec().getRules().get(0).getMatches().get(0).setQueryParams(Arrays.asList(param1, param2)); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + expectedRoute.setServices(Collections.singletonList(new UpstreamService())); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + + List expectedUrlParams = new ArrayList<>(); + + // Expected result for Case 1 + KeyedRoutePredicate expectedParam1 = new KeyedRoutePredicate(); + expectedParam1.setKey("prefix_param"); + expectedParam1.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + expectedParam1.setMatchValue("prefix"); + expectedUrlParams.add(expectedParam1); + + // Expected result for Case 2 + KeyedRoutePredicate expectedParam2 = new KeyedRoutePredicate(); + expectedParam2.setKey("regex_param"); + expectedParam2.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + expectedParam2.setMatchValue("regex-pattern"); + expectedUrlParams.add(expectedParam2); + + expectedRoute.setUrlParams(expectedUrlParams); + + Assertions.assertEquals(expectedRoute, route); + } + @Test + public void httpRoute2RouteTestWithBackendRefs() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + + // Case 1: BackendRef with namespace and port and weight + V1HTTPRouteSpecBackendRefs backendRef1 = new V1HTTPRouteSpecBackendRefs(); + backendRef1.setName("service1"); + backendRef1.setNamespace("custom-namespace"); + backendRef1.setPort(8080); + backendRef1.setWeight(20); + + // Case 2: BackendRef with namespace but no port + V1HTTPRouteSpecBackendRefs backendRef2 = new V1HTTPRouteSpecBackendRefs(); + backendRef2.setName("service2"); + backendRef2.setNamespace("another-namespace"); + + // Case 3: BackendRef without namespace but with port + V1HTTPRouteSpecBackendRefs backendRef3 = new V1HTTPRouteSpecBackendRefs(); + backendRef3.setName("service3"); + backendRef3.setPort(9090); + + // Case 4: BackendRef without namespace and port + V1HTTPRouteSpecBackendRefs backendRef4 = new V1HTTPRouteSpecBackendRefs(); + backendRef4.setName("service4"); + + // Case 5: Mcp Bridge + V1HTTPRouteSpecBackendRefs backendRef5 = new V1HTTPRouteSpecBackendRefs(); + backendRef5.setName("service5-provider.DEFAULT-GROUP.public.nacos"); + backendRef5.setGroup(V1McpBridge.API_GROUP); + + httpRoute.getSpec().getRules().get(0).setBackendRefs(Arrays.asList(backendRef1, backendRef2, backendRef3, backendRef4, backendRef5)); + + Route route = converter.httpRoute2Route(httpRoute); + + Route expectedRoute = buildBasicRoute(); + expectedRoute.setIsIngressMode(false); + RoutePredicate pathPredicate = expectedRoute.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + expectedRoute.setCors(null); + expectedRoute.setCustomConfigs(null); + + List expectedServices = new ArrayList<>(); + expectedServices.add(new UpstreamService("service1.custom-namespace.svc.cluster.local:8080", null, null, 20)); + expectedServices.add(new UpstreamService("service2.another-namespace.svc.cluster.local", null, null, null)); + expectedServices.add(new UpstreamService("service3.default.svc.cluster.local:9090", null, null, null)); + expectedServices.add(new UpstreamService("service4.default.svc.cluster.local", null, null, null)); + expectedServices.add(new UpstreamService("service5-provider.DEFAULT-GROUP.public.nacos", null, null, null)); + + expectedRoute.setServices(expectedServices); + + Assertions.assertEquals(expectedRoute, route); + } + + @Test + public void route2HttpRouteTestBasic() { + Route route = buildBasicRoute(); + route.setName("test-route"); + RoutePredicate pathPredicate = route.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + + expectedHttpRoute.getMetadata().setName("test-route"); + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(null); + + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + } + + @Test + public void route2HttpRouteTestWithDomain() { + Route route = buildBasicRoute(); + route.setDomains(Collections.singletonList("example.com")); + RoutePredicate pathPredicate = route.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + expectedHttpRoute.getMetadata().setLabels(Collections.singletonMap("higress.io/domain_example.com", "true")); + + expectedHttpRoute.getSpec().setHostnames(Collections.singletonList("example.com")); + V1HTTPRouteSpecParentRefs parentRefs = new V1HTTPRouteSpecParentRefs(); + parentRefs.setName(converter.domainName2GatewayName("example.com")); + expectedHttpRoute.getSpec().setParentRefs(Collections.singletonList(parentRefs)); + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(null); + + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + } + + @Test + public void route2HttpRouteTestWithSinglePath() { + Route route = buildBasicRoute(); + RoutePredicate pathPredicate = new RoutePredicate(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + pathPredicate.setMatchValue("/exact"); + pathPredicate.setCaseSensitive(null); + route.setPath(pathPredicate); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + + V1HTTPRouteSpecPath path = new V1HTTPRouteSpecPath(); + path.setType(V1HTTPRouteSpecPath.TypeEnum.EXACT); + path.setValue("/exact"); + V1HTTPRouteSpecMatches match = new V1HTTPRouteSpecMatches(); + match.setPath(path); + expectedHttpRoute.getSpec().getRules().get(0).setMatches(Collections.singletonList(match)); + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(null); + + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + } + + @Test + public void route2HttpRouteTestWithHeaders() { + Route route = buildBasicRoute(); + RoutePredicate pathPredicate = route.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + + List headers = new ArrayList<>(); + + // Case 1: Exact match + KeyedRoutePredicate header1 = new KeyedRoutePredicate(); + header1.setKey("X-Exact-Header"); + header1.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + header1.setMatchValue("exact-value"); + headers.add(header1); + + // Case 2: Prefix match + KeyedRoutePredicate header2 = new KeyedRoutePredicate(); + header2.setKey("X-Prefix-Header"); + header2.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + header2.setMatchValue("prefix"); + headers.add(header2); + + // Case 3: Regular expression match + KeyedRoutePredicate header3 = new KeyedRoutePredicate(); + header3.setKey("X-Regex-Header"); + header3.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + header3.setMatchValue("regex-pattern"); + headers.add(header3); + + route.setHeaders(headers); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + + + List expectedHeaders = new ArrayList<>(); + + // Expected result for Case 1 + V1HTTPRouteSpecHeaders expectedHeader1 = new V1HTTPRouteSpecHeaders(); + expectedHeader1.setName("X-Exact-Header"); + expectedHeader1.setType(V1HTTPRouteSpecHeaders.TypeEnum.EXACT); + expectedHeader1.setValue("exact-value"); + expectedHeaders.add(expectedHeader1); + + // Expected result for Case 2 + V1HTTPRouteSpecHeaders expectedHeader2 = new V1HTTPRouteSpecHeaders(); + expectedHeader2.setName("X-Prefix-Header"); + expectedHeader2.setType(V1HTTPRouteSpecHeaders.TypeEnum.REGULAREXPRESSION); + expectedHeader2.setValue("^" + Pattern.quote("prefix") + ".*"); + expectedHeaders.add(expectedHeader2); + + // Expected result for Case 3 + V1HTTPRouteSpecHeaders expectedHeader3 = new V1HTTPRouteSpecHeaders(); + expectedHeader3.setName("X-Regex-Header"); + expectedHeader3.setType(V1HTTPRouteSpecHeaders.TypeEnum.REGULAREXPRESSION); + expectedHeader3.setValue("regex-pattern"); + expectedHeaders.add(expectedHeader3); + + expectedHttpRoute.getSpec().getRules().get(0).getMatches().get(0).setHeaders(expectedHeaders); + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(null); + + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + } + + @Test + public void route2HttpRouteTestWithQueryParams() { + Route route = buildBasicRoute(); + RoutePredicate pathPredicate = route.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + + List urlParams = new ArrayList<>(); + + // Case 1: Exact match + KeyedRoutePredicate param1 = new KeyedRoutePredicate(); + param1.setKey("exact_param"); + param1.setMatchType(RoutePredicateTypeEnum.EQUAL.toString()); + param1.setMatchValue("exact-value"); + urlParams.add(param1); + + // Case 2: Prefix match + KeyedRoutePredicate param2 = new KeyedRoutePredicate(); + param2.setKey("prefix_param"); + param2.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + param2.setMatchValue("prefix"); + urlParams.add(param2); + + // Case 3: Regular expression match + KeyedRoutePredicate param3 = new KeyedRoutePredicate(); + param3.setKey("regex_param"); + param3.setMatchType(RoutePredicateTypeEnum.REGULAR.toString()); + param3.setMatchValue("regex-pattern"); + urlParams.add(param3); + + route.setUrlParams(urlParams); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + + + List expectedParams = new ArrayList<>(); + + // Expected result for Case 1 + V1HTTPRouteSpecQueryParams expectedParam1 = new V1HTTPRouteSpecQueryParams(); + expectedParam1.setName("exact_param"); + expectedParam1.setType(V1HTTPRouteSpecQueryParams.TypeEnum.EXACT); + expectedParam1.setValue("exact-value"); + expectedParams.add(expectedParam1); + + // Expected result for Case 2 + V1HTTPRouteSpecQueryParams expectedParam2 = new V1HTTPRouteSpecQueryParams(); + expectedParam2.setName("prefix_param"); + expectedParam2.setType(V1HTTPRouteSpecQueryParams.TypeEnum.REGULAREXPRESSION); + expectedParam2.setValue("^" + Pattern.quote("prefix") + ".*"); + expectedParams.add(expectedParam2); + + // Expected result for Case 3 + V1HTTPRouteSpecQueryParams expectedParam3 = new V1HTTPRouteSpecQueryParams(); + expectedParam3.setName("regex_param"); + expectedParam3.setType(V1HTTPRouteSpecQueryParams.TypeEnum.REGULAREXPRESSION); + expectedParam3.setValue("regex-pattern"); + expectedParams.add(expectedParam3); + + expectedHttpRoute.getSpec().getRules().get(0).getMatches().get(0).setQueryParams(expectedParams); + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(null); + + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + } + + @Test + public void route2HttpRouteTestWithUrlRewrite() { + Route route = buildBasicRoute(); + RoutePredicate pathPredicate = route.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + RewriteConfig rewriteConfig = new RewriteConfig(); + rewriteConfig.setEnabled(true); + rewriteConfig.setHost("example.com"); + rewriteConfig.setPath("/new"); + route.setRewrite(rewriteConfig); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + + V1HTTPRouteSpecFilters filter = new V1HTTPRouteSpecFilters(); + filter.setType(V1HTTPRouteSpecFilters.TypeEnum.URLREWRITE); + V1HTTPRouteSpecUrlRewrite urlRewrite = new V1HTTPRouteSpecUrlRewrite(); + urlRewrite.setHostname("example.com"); + V1HTTPRouteSpecUrlRewritePath path = new V1HTTPRouteSpecUrlRewritePath(); + path.setType(V1HTTPRouteSpecUrlRewritePath.TypeEnum.REPLACEPREFIXMATCH); + path.setReplacePrefixMatch("/new"); + urlRewrite.setPath(path); + filter.setUrlRewrite(urlRewrite); + expectedHttpRoute.getSpec().getRules().get(0).addFiltersItem(filter); + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(null); + + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + } + + @Test + public void route2HttpRouteTestWithMethods() { + Route route = buildBasicRoute(); + RoutePredicate pathPredicate = route.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + route.setMethods(Arrays.asList("GET", "POST", "TRACE")); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + + V1HTTPRouteSpecPath path = new V1HTTPRouteSpecPath(); + path.setType(V1HTTPRouteSpecPath.TypeEnum.PATHPREFIX); + path.setValue("/"); + + V1HTTPRouteSpecMatches match1 = new V1HTTPRouteSpecMatches(); + match1.setMethod(V1HTTPRouteSpecMatches.MethodEnum.GET); + match1.setPath(path); + V1HTTPRouteSpecMatches match2 = new V1HTTPRouteSpecMatches(); + match2.setMethod(V1HTTPRouteSpecMatches.MethodEnum.POST); + match2.setPath(path); + V1HTTPRouteSpecMatches match3 = new V1HTTPRouteSpecMatches(); + match3.setMethod(V1HTTPRouteSpecMatches.MethodEnum.TRACE); + match3.setPath(path); + expectedHttpRoute.getSpec().getRules().get(0).setMatches(Arrays.asList(match1, match2, match3)); + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(null); + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + } + + @Test + public void route2HttpRouteTestWithHeaderControl() { + Route route = buildBasicRoute(); + RoutePredicate pathPredicate = route.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + HeaderControlConfig headerControl = new HeaderControlConfig(); + headerControl.setEnabled(true); + HeaderControlStageConfig requestConfig = new HeaderControlStageConfig(); + requestConfig.setAdd(Collections.singletonList(new Header("X-Custom-Header", "CustomValue"))); + requestConfig.setSet(Collections.singletonList(new Header("X-Custom-Header", "CustomValue"))); + requestConfig.setRemove(Collections.singletonList("X-Remove-Header")); + headerControl.setRequest(requestConfig); + route.setHeaderControl(headerControl); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + + V1HTTPRouteSpecFilters filter = new V1HTTPRouteSpecFilters(); + filter.setType(V1HTTPRouteSpecFilters.TypeEnum.REQUESTHEADERMODIFIER); + V1HTTPRouteSpecRequestHeaderModifier headerModifier = new V1HTTPRouteSpecRequestHeaderModifier(); + V1HTTPRouteSpecRequestHeaderModifierAdd addHeader = new V1HTTPRouteSpecRequestHeaderModifierAdd(); + addHeader.setName("X-Custom-Header"); + addHeader.setValue("CustomValue"); + headerModifier.addAddItem(addHeader); + headerModifier.addSetItem(addHeader); + headerModifier.addRemoveItem("X-Remove-Header"); + filter.setRequestHeaderModifier(headerModifier); + expectedHttpRoute.getSpec().getRules().get(0).addFiltersItem(filter); + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(null); + + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + } + + @Test + public void route2HttpRouteTestWithBackendRefs() { + Route route = buildBasicRoute(); + RoutePredicate pathPredicate = route.getPath(); + pathPredicate.setMatchType(RoutePredicateTypeEnum.PRE.toString()); + pathPredicate.setMatchValue("/"); + pathPredicate.setCaseSensitive(null); + + List services = new ArrayList<>(); + + // Case 1: Normal Kubernetes service in default namespace + UpstreamService service1 = new UpstreamService(); + service1.setName("service1.default.svc.cluster.local:8080"); + service1.setWeight(50); + services.add(service1); + + // Case 2: Kubernetes service in different namespace + UpstreamService service2 = new UpstreamService(); + service2.setName("service2.custom-namespace.svc.cluster.local"); + services.add(service2); + + // Case 3: MCP service + UpstreamService service3 = new UpstreamService(); + service3.setName("mcp-service.DEFAULT-GROUP.public.nacos"); + services.add(service3); + + route.setServices(services); + route.setCors(null); + route.setCustomConfigs(null); + + V1HTTPRoute httpRoute = converter.route2HttpRoute(route); + + V1HTTPRoute expectedHttpRoute = buildBasicSupportedHttpRoute(); + + + List expectedBackendRefs = new ArrayList<>(); + + // Expected result for Case 1 + V1HTTPRouteSpecBackendRefs expectedBackend1 = new V1HTTPRouteSpecBackendRefs(); + expectedBackend1.setName("service1"); + expectedBackend1.setPort(8080); + expectedBackend1.setWeight(50); + expectedBackend1.setNamespace("default"); + expectedBackendRefs.add(expectedBackend1); + + // Expected result for Case 2 + V1HTTPRouteSpecBackendRefs expectedBackend2 = new V1HTTPRouteSpecBackendRefs(); + expectedBackend2.setName("service2"); + expectedBackend2.setNamespace("custom-namespace"); + expectedBackendRefs.add(expectedBackend2); + + // Expected result for Case 3 + V1HTTPRouteSpecBackendRefs expectedBackend3 = new V1HTTPRouteSpecBackendRefs(); + expectedBackend3.setName("mcp-service.DEFAULT-GROUP.public.nacos"); + expectedBackend3.setGroup(V1McpBridge.API_GROUP); + expectedBackendRefs.add(expectedBackend3); + + expectedHttpRoute.getSpec().getRules().get(0).setBackendRefs(expectedBackendRefs); + + V1ObjectMeta expectedMetadata = expectedHttpRoute.getMetadata(); + expectedMetadata.setNamespace(null); + expectedMetadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Assertions.assertEquals(expectedHttpRoute, httpRoute); + + } + + @Test + public void domain2GatewayTestWithHttpDomain() { + Domain domain = new Domain(); + domain.setName("example.com"); + domain.setVersion("1"); + Map portAndCertMap = new HashMap<>(); + portAndCertMap.put(80, ""); + domain.setPortAndCertMap(portAndCertMap); + + V1Gateway gateway = converter.domain2Gateway(domain); + + V1Gateway expectedGateway = new V1Gateway(); + V1ObjectMeta metadata = new V1ObjectMeta(); + metadata.setName("example.com"); + metadata.setResourceVersion("1"); + expectedGateway.setMetadata(metadata); + + V1GatewaySpec spec = new V1GatewaySpec().addDefaultAddress(); + spec.setGatewayClassName(V1GatewayClass.DEFAULT_NAME); + V1GatewaySpecListeners listener = new V1GatewaySpecListeners(); + listener.setName("example.com-80"); + listener.setPort(80); + listener.setHostname("example.com"); + listener.setProtocol("HTTP"); + listener.setAllowedRoutes(V1GatewaySpecListeners.getDefaultAllowedRoutes()); + spec.setListeners(Collections.singletonList(listener)); + expectedGateway.setSpec(spec); + + Assertions.assertEquals(expectedGateway, gateway); + } + + @Test + public void domain2GatewayTestWithHttpsDomain() { + Domain domain = new Domain(); + domain.setName("secure.example.com"); + domain.setVersion("2"); + Map portAndCertMap = new HashMap<>(); + portAndCertMap.put(443, "test-cert"); + domain.setPortAndCertMap(portAndCertMap); + + V1Gateway gateway = converter.domain2Gateway(domain); + + V1Gateway expectedGateway = new V1Gateway(); + V1ObjectMeta metadata = new V1ObjectMeta(); + metadata.setName("secure.example.com"); + metadata.setResourceVersion("2"); + expectedGateway.setMetadata(metadata); + + V1GatewaySpec spec = new V1GatewaySpec().addDefaultAddress(); + spec.setGatewayClassName(V1GatewayClass.DEFAULT_NAME); + V1GatewaySpecListeners listener = new V1GatewaySpecListeners(); + listener.setName("secure.example.com-443"); + listener.setPort(443); + listener.setHostname("secure.example.com"); + listener.setProtocol("HTTPS"); + listener.setTls(V1GatewaySpecListeners.getDefaultTls("test-cert")); + listener.setAllowedRoutes(V1GatewaySpecListeners.getDefaultAllowedRoutes()); + spec.setListeners(Collections.singletonList(listener)); + expectedGateway.setSpec(spec); + + Assertions.assertEquals(expectedGateway, gateway); + } + + @Test + public void domain2GatewayTestWithWildcardDomain() { + Domain domain = new Domain(); + domain.setName("*"); + domain.setVersion("3"); + Map portAndCertMap = new HashMap<>(); + portAndCertMap.put(80, ""); + portAndCertMap.put(443, "wildcard-cert"); + domain.setPortAndCertMap(portAndCertMap); + + V1Gateway gateway = converter.domain2Gateway(domain); + + V1Gateway expectedGateway = new V1Gateway(); + V1ObjectMeta metadata = new V1ObjectMeta(); + metadata.setName(HigressConstants.DEFAULT_DOMAIN); + metadata.setResourceVersion("3"); + expectedGateway.setMetadata(metadata); + + V1GatewaySpec spec = new V1GatewaySpec().addDefaultAddress(); + spec.setGatewayClassName(V1GatewayClass.DEFAULT_NAME); + List listeners = new ArrayList<>(); + + V1GatewaySpecListeners httpListener = new V1GatewaySpecListeners(); + httpListener.setName(HigressConstants.DEFAULT_DOMAIN+ Separators.DASH +"80"); + httpListener.setPort(80); + httpListener.setProtocol("HTTP"); + httpListener.setAllowedRoutes(V1GatewaySpecListeners.getDefaultAllowedRoutes()); + listeners.add(httpListener); + + V1GatewaySpecListeners httpsListener = new V1GatewaySpecListeners(); + httpsListener.setName(HigressConstants.DEFAULT_DOMAIN+ Separators.DASH +"443"); + httpsListener.setPort(443); + httpsListener.setProtocol("HTTPS"); + httpsListener.setTls(V1GatewaySpecListeners.getDefaultTls("wildcard-cert")); + httpsListener.setAllowedRoutes(V1GatewaySpecListeners.getDefaultAllowedRoutes()); + listeners.add(httpsListener); + + spec.setListeners(listeners); + expectedGateway.setSpec(spec); + + Assertions.assertEquals(expectedGateway, gateway); + } + + @Test + public void domain2GatewayTestWithEmptyPortAndCertMap() { + Domain domain = new Domain(); + domain.setName("empty.example.com"); + domain.setVersion("4"); + domain.setPortAndCertMap(new HashMap<>()); + + V1Gateway gateway = converter.domain2Gateway(domain); + + Assertions.assertNull(gateway); + } + + @Test + void httpRoute2RouteTestNotReadonly() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + + V1ObjectMeta metadata = httpRoute.getMetadata(); + metadata.setName("test-route"); + metadata.setNamespace(DEFAULT_NAMESPACE); + metadata.setResourceVersion("1"); + KubernetesUtil.setLabel(metadata, KubernetesConstants.Label.RESOURCE_DEFINER_KEY, + KubernetesConstants.Label.RESOURCE_DEFINER_VALUE); + + Route route = converter.httpRoute2Route(httpRoute); + + Assertions.assertNotNull(route); + Assertions.assertFalse(route.getReadonly()); + } + + @Test + void httpRoute2RouteTestReadonlyDueToNonSysNs() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + + V1ObjectMeta metadata = httpRoute.getMetadata(); + metadata.setName("test-route"); + metadata.setNamespace("test-ns"); + metadata.setResourceVersion("1"); + KubernetesUtil.setLabel(metadata, KubernetesConstants.Label.RESOURCE_DEFINER_KEY, + KubernetesConstants.Label.RESOURCE_DEFINER_VALUE); + + Route route = converter.httpRoute2Route(httpRoute); + + Assertions.assertNotNull(route); + Assertions.assertTrue(route.getReadonly()); + } + + @Test + void httpRoute2RouteTestReadonlyDueToIncorrectDefiner() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + + V1ObjectMeta metadata = httpRoute.getMetadata(); + metadata.setName("test-route"); + metadata.setNamespace(DEFAULT_NAMESPACE); + metadata.setResourceVersion("1"); + KubernetesUtil.setLabel(metadata, KubernetesConstants.Label.RESOURCE_DEFINER_KEY, + "bad-definer"); + + Route route = converter.httpRoute2Route(httpRoute); + + Assertions.assertNotNull(route); + Assertions.assertTrue(route.getReadonly()); + } + + @Test + void httpRoute2RouteTestReadonlyDueToMissingDefiner() { + V1HTTPRoute httpRoute = buildBasicSupportedHttpRoute(); + + V1ObjectMeta metadata = httpRoute.getMetadata(); + metadata.setName("test-route"); + metadata.setNamespace(DEFAULT_NAMESPACE); + metadata.setResourceVersion("1"); + metadata.getLabels().remove(KubernetesConstants.Label.RESOURCE_DEFINER_KEY); + + Route route = converter.httpRoute2Route(httpRoute); + + Assertions.assertNotNull(route); + Assertions.assertTrue(route.getReadonly()); + } + private V1alpha1WasmPluginSpec createSpecWithGlobalConfig() { V1alpha1WasmPluginSpec spec = new V1alpha1WasmPluginSpec(); spec.setDefaultConfig(Collections.singletonMap("key", "value")); diff --git a/frontend/ice.config.mts b/frontend/ice.config.mts index acc663f4..a4133d16 100644 --- a/frontend/ice.config.mts +++ b/frontend/ice.config.mts @@ -15,7 +15,7 @@ export default defineConfig(() => ({ }, proxy: { "/api": { - target: "http://demo.higress.io/", + target: "http://localhost:8080/", changeOrigin: true, pathRewrite: { "^/api": "" }, }, diff --git a/frontend/src/components/AvatarDropdown/index.tsx b/frontend/src/components/AvatarDropdown/index.tsx index 2334854e..54f85202 100644 --- a/frontend/src/components/AvatarDropdown/index.tsx +++ b/frontend/src/components/AvatarDropdown/index.tsx @@ -1,6 +1,6 @@ import { logout } from '@/services'; import store from '@/store'; -import { LockOutlined, LogoutOutlined } from '@ant-design/icons'; +import { LockOutlined, LogoutOutlined, SettingOutlined } from '@ant-design/icons'; import { Avatar, Dropdown } from 'antd'; import { definePageConfig, history } from 'ice'; import type { MenuInfo } from 'rc-menu/lib/interface'; @@ -36,6 +36,10 @@ const AvatarDropdown: React.FC = ({ name, avatar }) => { history?.push(`/user/changePassword`); }, []); + const onHigressConfigClick = useCallback((event: MenuInfo) => { + history?.push(`/higress-configs`); + }, []); + const menu = { items: [ { @@ -52,6 +56,13 @@ const AvatarDropdown: React.FC = ({ name, avatar }) => { onClick: onLogoutClick, className: styles.menu, }, + { + key: 'higressConfig', + label: t('higressConfig.title'), + icon: , + onClick: onHigressConfigClick, + className: styles.menu, + }, ], }; return ( diff --git a/frontend/src/interfaces/domain.ts b/frontend/src/interfaces/domain.ts index 6a535be0..ed52c89d 100644 --- a/frontend/src/interfaces/domain.ts +++ b/frontend/src/interfaces/domain.ts @@ -1,14 +1,16 @@ export interface Domain { name: string; version?: string; - certIdentifier?: string; + isIngressMode?: boolean; enableHttps?: string; + portAndCertMap?: Record; [propName: string]: any; } export const Protocol = { http: 'HTTP', https: 'HTTPS', + both: 'HTTP', }; export enum EnableHttpsValue { diff --git a/frontend/src/interfaces/route.ts b/frontend/src/interfaces/route.ts index 335dcbf3..843d376e 100644 --- a/frontend/src/interfaces/route.ts +++ b/frontend/src/interfaces/route.ts @@ -69,6 +69,7 @@ export interface Route { version?: string; domains: string[]; path: RoutePredicate; + isIngressMode?: boolean; methods?: string[]; headers?: KeyedRoutePredicate[]; urlParams?: KeyedRoutePredicate[]; diff --git a/frontend/src/locales/en-US/translation.json b/frontend/src/locales/en-US/translation.json index 7583f7d1..3a7cbe91 100644 --- a/frontend/src/locales/en-US/translation.json +++ b/frontend/src/locales/en-US/translation.json @@ -19,6 +19,7 @@ "llmProviderManagement": "LLM Provider Management", "domainManagement": "Domain Management", "certManagement": "Certificate Management", + "config": "Higress Config", "consumerManagement": "Consumer Management", "pluginManagement": "Plugin Management" }, @@ -99,7 +100,8 @@ "name": "Domain", "protocol": "Protocol", "certificate": "Certificate", - "action": "Action" + "action": "Action", + "port": "Port" }, "defaultDomain": "Default Domain", "createDomain": "Create Domain", @@ -119,7 +121,15 @@ "certificatePlaceholder": "Please input the certificate identifier.", "mustHttps": "Force HTTPS", "mustHttpsTooltip": "Only certificates provided by SSL Certificates Service of Alibaba Clound are supported now.", - "mustHttpsCheckboxTooltip": "Only HTTPS (port 443) service is available. Requests sent to HTTP (port 80) service will be redirected to the corresponding HTTPS (port 443) service" + "mustHttpsCheckboxTooltip": "Only HTTPS (port 443) service is available. Requests sent to HTTP (port 80) service will be redirected to the corresponding HTTPS (port 443) service", + "portAndCertMap": "Port and Certificate Mapping", + "portAndCertMapTooltip": "The port and certificate mapping is used to specify the port and certificate for each port. If the port is not specified, the default certificate will be used.", + "addPortAndCert": "add", + "portAndCertMapRequired": "Please add at least one port and certificate mapping.", + "portRequired": "Port cannot be empty", + "portDuplicate": "Port cannot be duplicated", + "certificateRequiredWhenHttps": "Certificate is required when using HTTPS protocol", + "ingressModeConstraint": "Ingress mode only supports port 80 (HTTP) or port 443 (HTTPS)" } }, "dashboard": { @@ -484,7 +494,9 @@ "customConfigsTip": "Click this \"?\" to view the configuration guide.", "targetService": "Target Service", "targetServiceRequired": "Please choose the target service.", - "targetServiceNamedPlaceholder": "Search target service by name. Multiple selections are allowed." + "targetServiceNamedPlaceholder": "Search target service by name. Multiple selections are allowed.", + "weightTip": "Weight of the target service", + "weightMustBeNumber": "Weight must be a number" } }, "chart": { @@ -636,5 +648,17 @@ "switchToForm": "Form View", "isRequired": "is required", "invalidSchema": "Since schema information cannot be properly parsed, this plugin only supports YAML editing." + }, + "higressConfig": { + "title": "Work Mode Configuration", + "higressWorkMode": "Higress Work Mode", + "modeRequired": "Please select Higress Work Mode", + "gatewayMode": "Gateway API Mode", + "ingressMode": "Ingress Mode", + "fetchError": "Failed to fetch Higress Work Mode", + "updateSuccess": "Successfully updated Higress Work Mode", + "updateError": "Failed to update Higress Work Mode", + "alertTitle": "Gateway API Mode Notes", + "alertDescription": "Gateway API Mode is currently in Alpha stage. The following features are still being adapted: CORS (Cross-Origin Resource Sharing), request retry policy, case-insensitive URL paths, and HTTPS forced redirection." } } \ No newline at end of file diff --git a/frontend/src/locales/zh-CN/translation.json b/frontend/src/locales/zh-CN/translation.json index 4565b30b..53d5effe 100644 --- a/frontend/src/locales/zh-CN/translation.json +++ b/frontend/src/locales/zh-CN/translation.json @@ -19,8 +19,9 @@ "llmProviderManagement": "AI服务提供者管理", "domainManagement": "域名管理", "certManagement": "证书管理", + "pluginManagement": "插件配置", "consumerManagement": "消费者管理", - "pluginManagement": "插件配置" + "config": "Higress 配置" }, "index": { "title": "Higress Console" @@ -102,7 +103,8 @@ "name": "域名", "protocol": "协议", "certificate": "证书", - "action": "操作" + "action": "操作", + "port": "端口" }, "defaultDomain": "缺省域名", "createDomain": "创建域名", @@ -122,7 +124,15 @@ "certificatePlaceholder": "请输入证书", "mustHttps": "是否强制HTTPS", "mustHttpsTooltip": "目前支持阿里云SSL证书服务上的证书", - "mustHttpsCheckboxTooltip": "只生效 HTTPS(443端口),HTTP(80端口)访问将被重定向至 HTTPS(443端口)" + "mustHttpsCheckboxTooltip": "只生效 HTTPS(443端口),HTTP(80端口)访问将被重定向至 HTTPS(443端口)", + "portAndCertMap": "端口和证书映射", + "portAndCertMapTooltip": "端口和证书映射用于指定每个端口的证书。如果是ingress模式,默认对80端口使用HTTP协议,443端口使用HTTPS协议。", + "addPortAndCert": "添加", + "portAndCertMapRequired": "请至少添加一个端口和证书映射", + "portRequired": "端口号不能为空", + "portDuplicate": "不能有重复的端口号", + "certificateRequiredWhenHttps": "使用HTTPS协议时必须选择证书", + "ingressModeConstraint": "Ingress模式下只能使用80端口(HTTP)或443端口(HTTPS)" } }, "dashboard": { @@ -488,7 +498,9 @@ "customConfigsTip": "点击问号查看注解配置说明", "targetService": "目标服务", "targetServiceRequired": "请选择目标服务", - "targetServiceNamedPlaceholder": "搜索服务名称选择服务,可多选" + "targetServiceNamedPlaceholder": "搜索服务名称选择服务,可多选", + "weightTip": "目标服务的权重", + "weightMustBeNumber": "权重必须为数字" } }, "chart": { @@ -640,5 +652,17 @@ "switchToForm": "表单视图", "isRequired": "是必填的", "invalidSchema": "由于 schema 信息无法正常解析,本插件只支持 YAML 编辑方式。" + }, + "higressConfig": { + "title": "配置工作模式", + "higressWorkMode": "Higress 工作模式", + "modeRequired": "请选择 HigressWorkMode 工作模式", + "gatewayMode": "gateway api模式", + "ingressMode": "ingress模式", + "fetchError": "获取 Higress 工作模式失败", + "updateSuccess": "更新 Higress 工作模式成功", + "updateError": "更新 Higress 工作模式失败", + "alertTitle": "Gateway API 模式功能适配说明", + "alertDescription": "Gateway API 模式目前处于 Alpha 阶段,以下功能仍在适配中:CORS(跨域资源共享)、请求重试策略、URL 路径忽略大小写、HTTPS 强制跳转。" } } \ No newline at end of file diff --git a/frontend/src/pages/_defaultProps.tsx b/frontend/src/pages/_defaultProps.tsx index ec6ad13a..b1a2a5c7 100644 --- a/frontend/src/pages/_defaultProps.tsx +++ b/frontend/src/pages/_defaultProps.tsx @@ -8,6 +8,7 @@ import { UnorderedListOutlined, UserOutlined, WindowsOutlined, + SettingOutlined, } from '@ant-design/icons'; export default { @@ -37,6 +38,17 @@ export default { }, ], }, + { + name: 'higressConfig.title', + path: '/higress-configs', + hideFromMenu: true, + routes: [ + { + name: 'higressConfig.title', + path: '/higress-configs', + }, + ], + }, { name: 'menu.dashboard', path: '/dashboard', diff --git a/frontend/src/pages/domain/components/DomainForm/index.tsx b/frontend/src/pages/domain/components/DomainForm/index.tsx index b56583a4..38b57036 100644 --- a/frontend/src/pages/domain/components/DomainForm/index.tsx +++ b/frontend/src/pages/domain/components/DomainForm/index.tsx @@ -7,13 +7,19 @@ import { useRequest } from 'ahooks'; import { Checkbox, Form, Input, Select, Tooltip } from 'antd'; import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import DomainGroup from '../DomainGroup'; const { Option } = Select; -const DomainForm: React.FC = forwardRef((props, ref) => { +interface DomainFormProps { + value?: any; + isIngressMode?: boolean; +} + +const DomainForm = forwardRef((props: DomainFormProps, ref) => { const { t } = useTranslation(); - const { value } = props; + const { value, isIngressMode } = props; const [form] = Form.useForm(); const [protocol, setProtocol] = useState(Protocol.http); const [certificateOptions, setCertificateOptions] = useState([]); @@ -32,12 +38,12 @@ const DomainForm: React.FC = forwardRef((props, ref) => { setCertificateOptions(_certificateOptions); if (value) { - const { name, enableHttps } = value; + const { name, enableHttps, portAndCertMap } = value; const protocolValue = enableHttps !== EnableHttpsValue.off ? Protocol.https : Protocol.http; setProtocol(protocolValue); const values = { name, protocol: protocolValue }; - if (value.certIdentifier) { - Object.assign(values, { certIdentifier: value.certIdentifier }); + if (portAndCertMap) { + Object.assign(values, { portAndCertMap }); } if (enableHttps === EnableHttpsValue.force) { Object.assign(values, { mustHttps: [true] }); @@ -90,7 +96,7 @@ const DomainForm: React.FC = forwardRef((props, ref) => { ) } - { ) : null - } - + } */} + { + if (!portAndCertMapValue || portAndCertMapValue.length === 0) { + return Promise.reject(new Error(String(t('domain.domainForm.portAndCertMapRequired')))); + } + + // Check for empty ports + const hasEmptyPort = portAndCertMapValue.some(item => !item.port); + if (hasEmptyPort) { + return Promise.reject(new Error(String(t('domain.domainForm.portRequired')))); + } + + // Check for duplicate ports + const ports = portAndCertMapValue.map(item => item.port); + const hasDuplicates = ports.length !== new Set(ports).size; + if (hasDuplicates) { + return Promise.reject(new Error(String(t('domain.domainForm.portDuplicate')))); + } + + // Check if HTTPS protocol has certificate + const hasHttpsWithoutCert = portAndCertMapValue.some((item) => { + return item.protocol === 'HTTPS' && !item.certificate; + }); + if (hasHttpsWithoutCert) { + return Promise.reject(new Error(String(t('domain.domainForm.certificateRequiredWhenHttps')))); + } + + // Validate ingress mode constraints + if (isIngressMode) { + const invalidPorts = portAndCertMapValue.some((item) => { + return (item.port !== 80 && item.port !== 443) || + (item.port === 80 && item.protocol !== 'HTTP') || + (item.port === 443 && item.protocol !== 'HTTPS'); + }); + if (invalidPorts) { + return Promise.reject(new Error(String(t('domain.domainForm.ingressModeConstraint')))); + } + } + + return Promise.resolve(); + }, + }, + ]} + > + + + ); }); diff --git a/frontend/src/pages/domain/components/DomainGroup/index.tsx b/frontend/src/pages/domain/components/DomainGroup/index.tsx new file mode 100644 index 00000000..4fe6b6b6 --- /dev/null +++ b/frontend/src/pages/domain/components/DomainGroup/index.tsx @@ -0,0 +1,281 @@ +import React, { useContext, useState } from 'react'; +import { Table, Form, InputNumber, Select, Button, message, Checkbox, Tooltip } from 'antd'; +import { PlusOutlined, DeleteOutlined, QuestionCircleOutlined } from '@ant-design/icons'; +import { useTranslation } from 'react-i18next'; +import { uniqueId } from 'lodash'; + +const { Option } = Select; + +const EditableContext = React.createContext(null); + +interface DomainGroupProps { + value?: Record; + onChange?: (value: any[]) => void; + certificateOptions: Array<{ label: string; value: string }>; + isIngressMode?: boolean; + mustHttps?: boolean[]; + onMustHttpsChange?: (value: boolean[]) => void; +} + +interface DataType { + uid: string; + port: number; + protocol: 'HTTP' | 'HTTPS'; + certificate: string; +} + +const EditableRow: React.FC = ({ ...props }) => { + const [form] = Form.useForm(); + return ( +
+ + + +
+ ); +}; + +interface EditableCellProps { + title: React.ReactNode; + editable: boolean; + children: React.ReactNode; + dataIndex: keyof DataType; + record: DataType; + handleSave: (record: DataType) => void; + inputType: 'number' | 'select'; + selectOptions?: Array<{ label: string; value: string }>; + isIngressMode?: boolean; +} + +const EditableCell: React.FC = ({ + title, + editable, + children, + dataIndex, + record, + handleSave, + inputType, + selectOptions, + isIngressMode, + ...restProps +}) => { + const form = useContext(EditableContext); + + const save = async () => { + try { + const values = await form.validateFields(); + if (values.protocol === 'HTTP') { + values.certificate = ''; + } + if (isIngressMode) { + if (values.port === 80) { + values.protocol = 'HTTP'; + values.certificate = ''; + } + } + handleSave({ ...record, ...values }); + } catch (errInfo) { + // eslint-disable-next-line no-console + console.log('Save failed:', errInfo); + } + }; + + let childNode = children; + + if (editable) { + const isDisabled = dataIndex === 'certificate' && record.protocol === 'HTTP'; + + childNode = ( + + {inputType === 'number' ? ( + + ) : ( + + )} + + ); + } + + return {childNode}; +}; + +const DomainGroup: React.FC = ({ value, onChange, certificateOptions, isIngressMode, mustHttps, onMustHttpsChange }) => { + const { t } = useTranslation(); + + // change value to array + const valueArray: DataType[] = value ? Object.keys(value).map((port): DataType => ({ + uid: uniqueId(), + port: Number(port), + protocol: value[Number(port)] ? 'HTTPS' : 'HTTP' as const, + certificate: value[Number(port)] || '', + })) : []; + + const [dataSource, setDataSource] = useState(valueArray); + + const handleDelete = (uid: string) => { + const newData = dataSource.filter((item) => item.uid !== uid); + setDataSource(newData); + onChange?.(newData); + }; + + const handleAdd = () => { + const newData: DataType = { + uid: uniqueId(), + port: isIngressMode ? 80 : 80, + protocol: 'HTTP', + certificate: '', + }; + setDataSource([...dataSource, newData]); + onChange?.([...dataSource, newData]); + }; + + const handleSave = (row: DataType) => { + const newData = [...dataSource]; + const index = newData.findIndex((item) => row.uid === item.uid); + const item = newData[index]; + newData.splice(index, 1, { ...item, ...row }); + setDataSource(newData); + onChange?.(newData); + }; + + const hasHttp80AndHttps443 = dataSource.some(item => item.port === 80 && item.protocol === 'HTTP') && + dataSource.some(item => item.port === 443 && item.protocol === 'HTTPS'); + + const columns = [ + { + title: t('domain.columns.port'), + dataIndex: 'port', + width: '30%', + editable: true, + }, + { + title: t('domain.columns.protocol'), + dataIndex: 'protocol', + width: '30%', + editable: true, + }, + { + title: t('domain.columns.certificate'), + dataIndex: 'certificate', + width: '30%', + editable: true, + }, + { + title: t('domain.columns.action'), + dataIndex: 'action', + render: (_: any, record: DataType) => { + return dataSource.length >= 1 ? ( + handleDelete(record.uid)} /> + ) : null; + }, + }, + ]; + + const components = { + body: { + row: EditableRow, + cell: EditableCell, + }, + }; + + const columnsWithProps = columns.map((col) => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: (record: DataType) => ({ + record, + editable: col.editable, + dataIndex: col.dataIndex, + title: col.title, + inputType: col.dataIndex === 'port' ? 'number' : 'select', + selectOptions: (() => { + if (col.dataIndex === 'protocol') { + return [ + { label: 'HTTP', value: 'HTTP' }, + { label: 'HTTPS', value: 'HTTPS' }, + ]; + } + if (col.dataIndex === 'certificate') { + return certificateOptions; + } + return undefined; + })(), + handleSave, + isIngressMode, + }), + }; + }); + + return ( +
+ 'editable-row'} + bordered + dataSource={dataSource} + columns={columnsWithProps as any} + pagination={false} + /> + + {hasHttp80AndHttps443 && ( + + + {t('domain.domainForm.mustHttps')} + + + + + ), + value: true, + }, + ]} + /> + + )} + + ); +}; + +export default DomainGroup; diff --git a/frontend/src/pages/domain/index.tsx b/frontend/src/pages/domain/index.tsx index 2a339295..bedd28f8 100644 --- a/frontend/src/pages/domain/index.tsx +++ b/frontend/src/pages/domain/index.tsx @@ -1,11 +1,11 @@ /* eslint-disable */ // @ts-nocheck import { DEFAULT_DOMAIN, Domain, DomainResponse, EnableHttpsValue, Protocol } from '@/interfaces/domain'; -import { addGatewayDomain, deleteGatewayDomain, getGatewayDomains, updateGatewayDomain } from '@/services'; +import { getIngressWorkMode, addGatewayDomain, deleteGatewayDomain, getGatewayDomains, updateGatewayDomain } from '@/services'; import { ExclamationCircleOutlined, RedoOutlined } from '@ant-design/icons'; import { PageContainer } from '@ant-design/pro-layout'; import { useRequest } from 'ahooks'; -import { Button, Col, Drawer, Form, Modal, Row, Space, Table } from 'antd'; +import { Button, Col, Drawer, Form, Modal, Row, Space, Table, message } from 'antd'; import React, { useEffect, useRef, useState } from 'react'; import { useTranslation, Trans } from 'react-i18next'; import DomainForm from './components/DomainForm'; @@ -20,7 +20,7 @@ interface DomainFormProps { const DomainList: React.FC = () => { const { t } = useTranslation(); - + const { data: currentIngressMode } = useRequest(getIngressWorkMode); const columns = [ { title: t('domain.columns.name'), @@ -38,9 +38,13 @@ const DomainList: React.FC = () => { }, { title: t('domain.columns.certificate'), - dataIndex: 'certIdentifier', - key: 'certIdentifier', - render: (value) => value || '-', + dataIndex: 'portAndCertMap', + key: 'portAndCertMap', + render: (value) => { + if (!value) return '-'; + const certs = Object.values(value).filter(cert => cert !== ''); + return certs.length ? certs.join(', ') : '-'; + }, }, { title: t('domain.columns.action'), @@ -68,7 +72,7 @@ const DomainList: React.FC = () => { const [openDrawer, setOpenDrawer] = useState(false); const [openModal, setOpenModal] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false); - + const [ingressMode, setIngressMode] = useState(false); const getDomainList = async (factor): Promise => getGatewayDomains(factor); const { loading, run, refresh } = useRequest(getDomainList, { manual: true, @@ -119,31 +123,45 @@ const DomainList: React.FC = () => { const onEditDrawer = (domain: Domain) => { setCurrentDomain(domain); + setIngressMode(domain.isIngressMode); setOpenDrawer(true); }; const onShowDrawer = () => { setOpenDrawer(true); setCurrentDomain(null); + setIngressMode(currentIngressMode); }; const handleDrawerOK = async () => { try { const values: DomainFormProps = formRef.current && (await formRef.current.handleSubmit()); - const { name, certIdentifier } = values; + // message.info(JSON.stringify(values)); + const { name, portAndCertMap } = values; const data = { name: name || currentDomain?.name }; let enableHttps = EnableHttpsValue.off; - if (values.protocol === Protocol.https) { - if (values.certIdentifier) { - Object.assign(data, { certIdentifier }); + + if (portAndCertMap) { + // Convert array format to Record + const portCertRecord: Record = {}; + portAndCertMap.forEach((item: any) => { + portCertRecord[item.port] = item.certificate; + }); + Object.assign(data, { portAndCertMap: portCertRecord }); + + // Check if any port uses HTTPS + const hasHttps = Object.values(portCertRecord).some(cert => cert); + if (hasHttps) { + enableHttps = values.mustHttps?.length ? EnableHttpsValue.force : EnableHttpsValue.on; } - enableHttps = values.mustHttps?.length ? EnableHttpsValue.force : EnableHttpsValue.on; } + Object.assign(data, { enableHttps }); if (currentDomain?.version) { - await updateGatewayDomain({ version: currentDomain.version, ...data } as Domain); + await updateGatewayDomain({ version: currentDomain.version, isIngressMode: currentDomain.isIngressMode, ...data } as Domain); } else { - await addGatewayDomain(data as Domain); + // set domain isIngressMode + await addGatewayDomain({ ...data, isIngressMode: currentIngressMode } as Domain); } setOpenDrawer(false); refresh(); @@ -155,6 +173,7 @@ const DomainList: React.FC = () => { const handleDrawerCancel = () => { setOpenDrawer(false); setCurrentDomain(null); + setIngressMode(currentIngressMode); }; const onShowModal = (domain: Domain) => { @@ -176,6 +195,7 @@ const DomainList: React.FC = () => { const handleModalCancel = () => { setOpenModal(false); setCurrentDomain(null); + setIngressMode(currentIngressMode); }; return ( @@ -240,7 +260,7 @@ const DomainList: React.FC = () => { } > - + ); diff --git a/frontend/src/pages/higress-configs/index.tsx b/frontend/src/pages/higress-configs/index.tsx new file mode 100644 index 00000000..9b62b713 --- /dev/null +++ b/frontend/src/pages/higress-configs/index.tsx @@ -0,0 +1,97 @@ +/* eslint-disable */ +// @ts-nocheck +import React from 'react'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Form, Select, Button, message, Alert } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { useRequest } from 'ahooks'; +import { getIngressWorkMode, setIngressWorkMode } from '@/services'; + +const { Option } = Select; + +interface IngressWorkModeConfig { + mode: boolean; +} + +const HigressConfig: React.FC = () => { + const { t } = useTranslation(); + const [form] = Form.useForm(); + + const getIngressWorkModeConfig = async (): Promise => { + const mode = await getIngressWorkMode(); + return { mode }; + }; + const { loading, data, refresh } = useRequest(getIngressWorkModeConfig, { + manual: false, + onSuccess: (result) => { + form.setFieldsValue({ ingressWorkMode: result.mode }); + }, + }); + + const { run: updateIngressWorkMode, loading: updateLoading } = useRequest(setIngressWorkMode, { + manual: true, + onSuccess: () => { + message.success(t('higressConfig.updateSuccess')); + refresh(); + }, + onError: () => { + message.error(t('higressConfig.updateError')); + }, + }); + + const onFinish = (values: { ingressWorkMode: boolean }) => { + updateIngressWorkMode(values.ingressWorkMode); + }; + + return ( + +
+ +
+ + + + + + + +
+
+ ); +}; + +export default HigressConfig; \ No newline at end of file diff --git a/frontend/src/pages/route/components/RouteForm/index.tsx b/frontend/src/pages/route/components/RouteForm/index.tsx index 656eee97..3bf435a2 100644 --- a/frontend/src/pages/route/components/RouteForm/index.tsx +++ b/frontend/src/pages/route/components/RouteForm/index.tsx @@ -5,7 +5,7 @@ import { upstreamServiceToString } from '@/interfaces/route'; import { getGatewayDomains, getGatewayServices } from '@/services'; import { InfoCircleOutlined, QuestionCircleFilled, QuestionCircleOutlined } from '@ant-design/icons'; import { useRequest } from 'ahooks'; -import { Checkbox, Form, Input, Select, Tooltip } from 'antd'; +import { Checkbox, Form, Input, message, Select, Tooltip, InputNumber } from 'antd'; import { uniqueId } from "lodash"; import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -26,16 +26,23 @@ const MethodOptions = [ { label: "CONNECT", value: "CONNECT" }, ]; -const RouteForm: React.FC = forwardRef((props, ref) => { +interface RouteFormProps { + value?: any; + isIngressMode?: boolean; +} + +const RouteForm: React.FC = forwardRef((props: RouteFormProps, ref) => { const { t } = useTranslation(); - const { value } = props; + const { value, isIngressMode } = props; const [form] = Form.useForm(); const [serviceOptions, setServiceOptions] = useState([]); const [domainOptions, setDomainOptions] = useState([]); const servicesRef = useRef(new Map()); + const serviceWeightsRef = useRef(new Map()); const { data: _services = [] } = useRequest(getGatewayServices); const { data: _domains = [] } = useRequest(getGatewayDomains); + const [serviceWeights, setServiceWeights] = useState>(new Map()); useEffect(() => { form.resetFields(); @@ -72,6 +79,14 @@ const RouteForm: React.FC = forwardRef((props, ref) => { const customConfigArray = customConfigs ? Object.keys(customConfigs).map((key) => { return { uid: uniqueId(), key, value: customConfigs[key] }; }) : []; + + if (services) { + services.forEach(service => { + const key = upstreamServiceToString(service).split(':')[0]; + serviceWeightsRef.current.set(key, service.weight); + }); + } + form.setFieldsValue({ name, domains: domains || [], @@ -86,7 +101,10 @@ const RouteForm: React.FC = forwardRef((props, ref) => { }, [_services, _domains, value]); useImperativeHandle(ref, () => ({ - reset: () => form.resetFields(), + reset: () => { + form.resetFields(); + setServiceWeights(new Map()); + }, handleSubmit: async () => { const values = await form.validateFields(); if (values.domains && !Array.isArray(values.domains)) { @@ -101,6 +119,16 @@ const RouteForm: React.FC = forwardRef((props, ref) => { } values.customConfigs = customConfigsObj; } + if (values.services) { + values.services = values.services.map(service => { + const serviceKey = service.split(':')[0]; + const weight = serviceWeights.get(serviceKey); + return { + service, + weight: weight || 1, + }; + }); + } return values; }, })); @@ -252,13 +280,57 @@ const RouteForm: React.FC = forwardRef((props, ref) => { }, ]} > - + ) : ( +