Skip to content

Commit

Permalink
Add config deployer changes for gRPC APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
sgayangi committed Aug 23, 2024
1 parent d0abcd6 commit 4dccd03
Show file tree
Hide file tree
Showing 21 changed files with 1,912 additions and 901 deletions.
168 changes: 161 additions & 7 deletions runtime/config-deployer-service/ballerina/APIClient.bal

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions runtime/config-deployer-service/ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ groupId = "joda-time"
artifactId = "joda-time"
version = "2.10.2"

# transitive dependency of com.google.protobuf:protobuf-java:3.21.7
[[platform.java11.dependency]]
groupId = "com.google.protobuf"
artifactId = "protobuf-java"
version = "3.21.7"

# transitive dependency of org.wso2.apk:org.wso2.apk.config:1.1.0-SNAPSHOT
[[platform.java11.dependency]]
Expand All @@ -385,3 +390,8 @@ version = "20231013"
groupId = "commons-lang"
artifactId = "commons-lang"
version = "2.4"

[[platform.java11.dependency]]
groupId = "org.wso2.apk"
artifactId = "org.wso2.apk.config"
version = "1.1.0-SNAPSHOT"
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@ groupId = "org.reactivestreams"
artifactId = "reactive-streams"
version = "1.0.3"

# transitive dependency of com.google.protobuf:protobuf-java:3.21.7
[[platform.java11.dependency]]
groupId = "com.google.protobuf"
artifactId = "protobuf-java"
version = "3.21.7"

# transitive dependency of org.wso2.apk:org.wso2.apk.config:1.0.0-SNAPSHOT
[[platform.java11.dependency]]
groupId = "com.fasterxml.jackson.core"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import wso2/apk_common_lib as commons;
import ballerina/http;
import config_deployer_service.org.wso2.apk.config.api as runtimeapi;
import config_deployer_service.org.wso2.apk.config.model as runtimeModels;
import config_deployer_service.java.util as utilapis;
import config_deployer_service.org.wso2.apk.config as runtimeUtil;
import ballerina/mime;
import ballerina/jballerina.java;
import config_deployer_service.model;
import ballerina/io;
import config_deployer_service.org.wso2.apk.config as runtimeUtil;
import config_deployer_service.org.wso2.apk.config.api as runtimeapi;
import config_deployer_service.org.wso2.apk.config.model as runtimeModels;

import ballerina/file;
import ballerina/http;
import ballerina/io;
import ballerina/jballerina.java;
import ballerina/log;
import ballerina/mime;
import ballerina/uuid;

import wso2/apk_common_lib as commons;

public class ConfigGeneratorClient {

public isolated function getGeneratedAPKConf(http:Request request) returns OkAnydata|commons:APKError|BadRequestError {
Expand Down Expand Up @@ -93,7 +95,7 @@ public class ConfigGeneratorClient {
private isolated function validateAndRetrieveDefinition(string 'type, string? url, byte[]? content, string? fileName) returns runtimeapi:APIDefinitionValidationResponse|runtimeapi:APIManagementException|error|commons:APKError {
runtimeapi:APIDefinitionValidationResponse|runtimeapi:APIManagementException|error validationResponse;
boolean typeAvailable = 'type.length() > 0;
string[] ALLOWED_API_DEFINITION_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL, "ASYNC"];
string[] ALLOWED_API_DEFINITION_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL, API_TYPE_GRPC, API_TYPE_ASYNC];
if !typeAvailable {
return e909005("type");
}
Expand Down
13 changes: 6 additions & 7 deletions runtime/config-deployer-service/ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ modules = [
[[package]]
org = "ballerina"
name = "crypto"
version = "2.6.2"
version = "2.6.3"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "time"}
Expand All @@ -70,7 +70,7 @@ modules = [
[[package]]
org = "ballerina"
name = "http"
version = "2.10.12"
version = "2.10.15"
dependencies = [
{org = "ballerina", name = "auth"},
{org = "ballerina", name = "cache"},
Expand Down Expand Up @@ -102,7 +102,7 @@ modules = [
[[package]]
org = "ballerina"
name = "io"
version = "1.6.0"
version = "1.6.1"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.value"}
Expand Down Expand Up @@ -273,7 +273,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "observe"
version = "1.2.2"
version = "1.2.3"
dependencies = [
{org = "ballerina", name = "jballerina.java"}
]
Expand Down Expand Up @@ -302,7 +302,7 @@ modules = [
[[package]]
org = "ballerina"
name = "sql"
version = "1.11.1"
version = "1.11.2"
dependencies = [
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
Expand Down Expand Up @@ -424,8 +424,7 @@ dependencies = [
modules = [
{org = "wso2", packageName = "apk_common_lib", moduleName = "apk_common_lib"},
{org = "wso2", packageName = "apk_common_lib", moduleName = "apk_common_lib.java.io"},
{org = "wso2", packageName = "apk_common_lib", moduleName = "apk_common_lib.java.lang"},
{org = "wso2", packageName = "apk_common_lib", moduleName = "apk_common_lib.org.wso2.apk.common"}
{org = "wso2", packageName = "apk_common_lib", moduleName = "apk_common_lib.java.lang"}
]

[[package]]
Expand Down
94 changes: 87 additions & 7 deletions runtime/config-deployer-service/ballerina/DeployerClient.bal
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import ballerina/mime;
import config_deployer_service.model;

import ballerina/http;
import wso2/apk_common_lib as commons;
import ballerina/log;
import ballerina/lang.value;
import ballerina/log;
import ballerina/mime;

import wso2/apk_common_lib as commons;

public class DeployerClient {
public isolated function handleAPIDeployment(http:Request request, commons:Organization organization) returns commons:APKError|http:Response {
Expand Down Expand Up @@ -82,6 +84,7 @@ public class DeployerClient {

private isolated function deployAPIToK8s(model:APIArtifact apiArtifact) returns commons:APKError|model:API {
do {
log:printInfo("DEPLOYING API TO K8s");
model:Partition apiPartition;
model:API? existingAPI;
model:Partition|() availablePartitionForAPI = check partitionResolver.getAvailablePartitionForAPI(apiArtifact.uniqueId, apiArtifact.organization);
Expand All @@ -95,6 +98,7 @@ public class DeployerClient {
apiArtifact.namespace = apiPartition.namespace;
if existingAPI is model:API {
check self.deleteHttpRoutes(existingAPI, <string>apiArtifact?.organization);
check self.deleteGrpcRoutes(existingAPI, <string>apiArtifact?.organization);
check self.deleteAuthenticationCRs(existingAPI, <string>apiArtifact?.organization);
_ = check self.deleteScopeCrsForAPI(existingAPI, <string>apiArtifact?.organization);
check self.deleteBackends(existingAPI, <string>apiArtifact?.organization);
Expand All @@ -121,9 +125,9 @@ public class DeployerClient {
check self.deployInterceptorServiceCRs(apiArtifact, ownerReference);
check self.deployBackendJWTConfigs(apiArtifact, ownerReference);
check self.deployAPIPolicyCRs(apiArtifact, ownerReference);

check self.deployRoutes(apiArtifact.productionHttpRoutes, apiArtifact.productionGqlRoutes, <string>apiArtifact?.namespace, ownerReference);
check self.deployRoutes(apiArtifact.sandboxHttpRoutes, apiArtifact.sandboxGqlRoutes, <string>apiArtifact?.namespace, ownerReference);
log:printInfo("REACHED THE POINT OF DEPLOYING ROUTES");
check self.deployRoutes(apiArtifact.productionHttpRoutes, apiArtifact.productionGqlRoutes, apiArtifact.productionGrpcRoutes, <string>apiArtifact?.namespace, ownerReference);
check self.deployRoutes(apiArtifact.sandboxHttpRoutes, apiArtifact.sandboxGqlRoutes, apiArtifact.sandboxGrpcRoutes, <string>apiArtifact?.namespace, ownerReference);

return deployK8sAPICrResult;
} on fail var e {
Expand All @@ -138,12 +142,15 @@ public class DeployerClient {
return e909028();
}
} else {
log:printInfo("NOT AN API??");
return e909028();
}
} on fail var e {
if e is commons:APKError {
return e;
}
log:printInfo("DEPLOYMENT FAAAAILED");

log:printError("Internal Error occured while deploying API", e);
return e909028();
}
Expand Down Expand Up @@ -197,6 +204,30 @@ public class DeployerClient {
}
}

private isolated function deleteGrpcRoutes(model:API api, string organization) returns commons:APKError? {
do {
model:GRPCRouteList|http:ClientError grpcRouteListResponse = check getGrpcRoutesForAPIs(api.spec.apiName, api.spec.apiVersion, <string>api.metadata?.namespace, organization);
if grpcRouteListResponse is model:GRPCRouteList {
foreach model:GRPCRoute item in grpcRouteListResponse.items {
http:Response|http:ClientError grpcRouteDeletionResponse = deleteGrpcRoute(item.metadata.name, <string>api.metadata?.namespace);
if grpcRouteDeletionResponse is http:Response {
if grpcRouteDeletionResponse.statusCode != http:STATUS_OK {
json responsePayLoad = check grpcRouteDeletionResponse.getJsonPayload();
model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status);
check self.handleK8sTimeout(statusResponse);
}
} else {
log:printError("Error occured while deleting GrpcRoute", grpcRouteDeletionResponse);
}
}
return;
}
} on fail var e {
log:printError("Error occured deleting grpcRoutes", e);
return e909022("Error occured deleting grpcRoutes", e);
}
}

private isolated function deleteBackends(model:API api, string organization) returns commons:APKError? {
do {
model:BackendList|http:ClientError backendPolicyListResponse = check getBackendPolicyCRsForAPI(api.spec.apiName, api.spec.apiVersion, <string>api.metadata?.namespace, organization);
Expand Down Expand Up @@ -300,6 +331,7 @@ public class DeployerClient {
if k8sAPIByNameAndNamespace is model:API {
k8sAPI.metadata.resourceVersion = k8sAPIByNameAndNamespace.metadata.resourceVersion;
http:Response deployAPICRResult = check updateAPICR(k8sAPI, <string>apiArtifact?.namespace);
log:printInfo(deployAPICRResult.statusCode.toString());
if deployAPICRResult.statusCode == http:STATUS_OK {
json responsePayLoad = check deployAPICRResult.getJsonPayload();
log:printDebug("Updated K8sAPI Successfully" + responsePayLoad.toJsonString());
Expand All @@ -312,8 +344,10 @@ public class DeployerClient {
model:StatusCause[] 'causes = details.'causes;
foreach model:StatusCause 'cause in 'causes {
if 'cause.'field == "spec.basePath" {
log:printError("Error occured while updating K8sAPI due to base path ", e909015(k8sAPI.spec.basePath));
return e909015(k8sAPI.spec.basePath);
} else if 'cause.'field == "spec.apiName" {
log:printError("Error occured while updating K8sAPI due to base path ", e909015(k8sAPI.spec.basePath));
return e909016(k8sAPI.spec.apiName);
}
}
Expand Down Expand Up @@ -349,7 +383,8 @@ public class DeployerClient {
return e909022("Internal error occured", e = error("Internal error occured"));
}
}
private isolated function deployRoutes(model:HTTPRoute[]? httproutes, model:GQLRoute[]? gqlroutes, string namespace, model:OwnerReference ownerReference) returns error? {
private isolated function deployRoutes(model:HTTPRoute[]? httproutes, model:GQLRoute[]? gqlroutes, model:GRPCRoute[]? grpcroutes,
string namespace, model:OwnerReference ownerReference) returns error? {
if httproutes is model:HTTPRoute[] && httproutes.length() > 0 {
model:HTTPRoute[] deployReadyHttproutes = httproutes;
model:HTTPRoute[]|commons:APKError orderedHttproutes = self.createHttpRoutesOrder(httproutes);
Expand Down Expand Up @@ -408,6 +443,51 @@ public class DeployerClient {
}
}
}
} else if grpcroutes is model:GRPCRoute[] && grpcroutes.length() > 0 {
model:GRPCRoute[] deployReadyGrpcRoutes = grpcroutes;
model:GRPCRoute[]|commons:APKError orderedGrpcRoutes = self.createGrpcRoutesOrder(grpcroutes);
if orderedGrpcRoutes is model:GRPCRoute[] {
deployReadyGrpcRoutes = orderedGrpcRoutes;
}
foreach model:GRPCRoute grpcRoute in deployReadyGrpcRoutes {
grpcRoute.metadata.ownerReferences = [ownerReference];
if grpcRoute.spec.rules.length() > 0 {
http:Response deployGrpcRouteResult = check deployGrpcRoute(grpcRoute, namespace);
if deployGrpcRouteResult.statusCode == http:STATUS_CREATED {
log:printDebug("Deployed GrpcRoute Successfully" + grpcRoute.toString());
} else if deployGrpcRouteResult.statusCode == http:STATUS_CONFLICT {
log:printDebug("GrpcRoute already exists" + grpcRoute.toString());
model:GRPCRoute grpcRouteFromK8s = check getGrpcRoute(grpcRoute.metadata.name, namespace);
grpcRoute.metadata.resourceVersion = grpcRouteFromK8s.metadata.resourceVersion;
http:Response grpcRouteCR = check updateGrpcRoute(grpcRoute, namespace);
if grpcRouteCR.statusCode != http:STATUS_OK {
json responsePayLoad = check grpcRouteCR.getJsonPayload();
model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status);
check self.handleK8sTimeout(statusResponse);
}
} else {
json responsePayLoad = check deployGrpcRouteResult.getJsonPayload();
model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status);
check self.handleK8sTimeout(statusResponse);
}
}
}
}
}

public isolated function createGrpcRoutesOrder(model:GRPCRoute[] grpcRoutes) returns model:GRPCRoute[]|commons:APKError {
do {
foreach model:GRPCRoute route in grpcRoutes {
model:GRPCRouteRule[] routeRules = route.spec.rules;
model:GRPCRouteRule[] sortedRouteRules = from var routeRule in routeRules
order by <string>(routeRule.matches[0].method.'service) descending
select routeRule;
route.spec.rules = sortedRouteRules;
}
return grpcRoutes;
} on fail var e {
log:printError("Error occured while sorting grpcRoutes", e);
return e909022("Error occured while sorting grpcRoutes", e);
}
}

Expand Down
44 changes: 36 additions & 8 deletions runtime/config-deployer-service/ballerina/K8sClient.bal
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import config_deployer_service.model as model;

import ballerina/crypto;
import ballerina/http;
//
// Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com).
//
Expand All @@ -15,13 +19,11 @@
// specific language governing permissions and limitations
// under the License.
//

import ballerina/io;
import ballerina/log;
import ballerina/url;
import ballerina/http;

import wso2/apk_common_lib as commons;
import ballerina/crypto;
import config_deployer_service.model as model;

const string K8S_API_ENDPOINT = "/api/v1";
final string token = check io:fileReadString(k8sConfiguration.serviceAccountPath + "/token");
Expand Down Expand Up @@ -56,7 +58,7 @@ isolated function getConfigMapValueFromNameAndNamespace(string name, string name
}

isolated function deleteAPICR(string name, string namespace) returns http:Response|http:ClientError {
string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis/" + name;
string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + name;
return k8sApiServerEp->delete(endpoint, targetType = http:Response);
}

Expand Down Expand Up @@ -111,12 +113,13 @@ isolated function deleteConfigMap(string name, string namespace) returns http:Re
}

isolated function deployAPICR(model:API api, string namespace) returns http:Response|http:ClientError {
string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis";
string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis";
return k8sApiServerEp->post(endpoint, api, targetType = http:Response);
}

isolated function updateAPICR(model:API api, string namespace) returns http:Response|http:ClientError {
string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis/" + api.metadata.name;
string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + api.metadata.name;
log:printInfo("UPDATING API CR");
return k8sApiServerEp->put(endpoint, api, targetType = http:Response);
}

Expand Down Expand Up @@ -151,7 +154,7 @@ isolated function updateGqlRoute(model:GQLRoute gqlroute, string namespace) retu
}

public isolated function getK8sAPIByNameAndNamespace(string name, string namespace) returns model:API?|commons:APKError {
string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis/" + name;
string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + name;
do {
http:Response response = check k8sApiServerEp->get(endpoint);
if response.statusCode == 200 {
Expand Down Expand Up @@ -344,3 +347,28 @@ isolated function getBackendJWTCrsForAPI(string apiName, string apiVersion, stri
string endpoint = "/apis/dp.wso2.com/v1alpha1/namespaces/" + namespace + "/backendjwts?labelSelector=" + check generateUrlEncodedLabelSelector(apiName, apiVersion, organization);
return k8sApiServerEp->get(endpoint, targetType = model:BackendJWTList);
}

isolated function getGrpcRoute(string name, string namespace) returns model:GRPCRoute|http:ClientError {
string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + name;
return k8sApiServerEp->get(endpoint, targetType = model:GRPCRoute);
}

isolated function deleteGrpcRoute(string name, string namespace) returns http:Response|http:ClientError {
string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + name;
return k8sApiServerEp->delete(endpoint, targetType = http:Response);
}

isolated function deployGrpcRoute(model:GRPCRoute grpcRoute, string namespace) returns http:Response|http:ClientError {
string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes";
return k8sApiServerEp->post(endpoint, grpcRoute, targetType = http:Response);
}

isolated function updateGrpcRoute(model:GRPCRoute grpcRoute, string namespace) returns http:Response|http:ClientError {
string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + grpcRoute.metadata.name;
return k8sApiServerEp->put(endpoint, grpcRoute, targetType = http:Response);
}

public isolated function getGrpcRoutesForAPIs(string apiName, string apiVersion, string namespace, string organization) returns model:GRPCRouteList|http:ClientError|error {
string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/?labelSelector=" + check generateUrlEncodedLabelSelector(apiName, apiVersion, organization);
return k8sApiServerEp->get(endpoint, targetType = model:GRPCRouteList);
}
Loading

0 comments on commit 4dccd03

Please sign in to comment.