Handling same context path for multiple micro service in zuul configuration - netflix-zuul

We are building platform on top of Spring netflix OSS to host multiple use-cases micro service api. One of the use case have 2 API (2 code base building 2 jars) they want it to be hosted on 2 different service Id but both are having same context url
eg "/loan/card/v1/individual/"
AP11-> /credit_decision/payment_plan/
API2 -> /history_decision/payment_plan/
How should I configure the rout path so that any call coming via url
/loan/card/v1/individual/credit_decision/payment_plan/ should go to API1 and
/loan/card/v1/individual/history_decision/payment_plan/ should go to API2

You will have to define the routing like below. I have the strip-prefix to false in case you need it you can make it true. For more info here is the link to the DOC.
zuul:
routes:
API1:
path: /loan/card/v1/individual/credit_decision/**
service-id: API1
strip-prefix: false
API2:
path: /loan/card/v1/individual/history_decision/**
service-id: API2
strip-prefix: false

Related

Domain Mapping to point to a tag inside a service on Cloud Run

right now I'm deploying to cloud run and run
gcloud run deploy myapp --tag pr123 --no-traffic
I can then access the app via
https://pr123---myapp-jo5dg6hkf-ez.a.run.app
Now I would like to have a custom domain mapping going to this tag. I know how to point a custom domain to the service but I don't know how to point it to the tagged version of my service.
Can I add labels to the DomainMapping that would cause the mapping to got this version of my cloud run service? Or is there a routeName, eg. myapp#pr123 that would do the trick there?
In the end I would like to have
https://pr123.dev.mydomain.com
being the endpoint for this service.
With a custom domain, you configure a DNS to point to a service, not a revision/tag of the service. So, you can't by this way.
The solution is to use a load balancer with a serverless NEG. The most important is to define the URL mask that you want to map the tag and service from the URL which is received by the Load Balancer.
I ended up building the loadbalancer with a network endpoint group (as suggested). For further reference, here is my terraform snippet to create it. The part is then the traffic tag you assign to your revision.
resource "google_compute_region_network_endpoint_group" "api_neg" {
name = "api-neg"
network_endpoint_type = "SERVERLESS"
region = "europe-west3"
cloud_run {
service = data.google_cloud_run_service.api_dev.name
url_mask = "<tag>.preview.mydomain.com"
}
}

Open Api swagger docs with service names of the format ***-service

I have a java spring boot service mesh of services.
I am using open api for swagger documentation.
When I run my spring cloud gateway and access http://localhost:{my-port}/swagger-ui.html, it works fine and loads the swagger ui which mentions /v3/api-docs in the "Explore" title bar.
Also, when I add the service name to the url like so /v3/api-docs/my-service, it works fine.
However, I want to try a list of dropdown services instead of manually appending the service name for each service. I have tried this grouping code in my gateway:
#Bean
fun apis(): List<GroupedOpenApi> {
val groups = ArrayList<GroupedOpenApi>()
val definitions = locator!!.routeDefinitions.collectList().block()
definitions!!.stream().filter { routeDefinition -> routeDefinition.id.matches(".*-service".toRegex()) }.forEach { routeDefinition ->
val name = routeDefinition.id.replace("-service".toRegex(), "")
GroupedOpenApi.builder().pathsToMatch("/$name/**").setGroup(name).build()
}
return groups
}
Now, after this when I access http://localhost:{my-port}/swagger-ui.html, it shows a drop down list of all the service with the "-service" removed. But after selection, the docs don't show up.
Although, if I access it in a separate tab with http://localhost:{my-port}/v3/api-docs/my-service, it shows me the json of all the paths and works fine. Just doesn't load in the swagger ui.
Here's gateway route for one of the service and openapi:
- id: my-service
uri: lb://my-service
predicates:
- Path=/api/v1/my/**
filters:
- name: CircuitBreaker
args:
name: my-service
fallbackuri: forward:/myServiceFallBack

How to set swagger config properties from config file

I am using Azure API Management to host three versions of an API - dev, qa, stage. These are basically three different build configurations of the api, so when imported to APIM - "MyAPI-dev", "MyAPI-qa", "MyAPI-stage".
I am using swagger for documentation. When I trigger a revision in Terraform to build/re-create the API definitions, i am getting error:
"my-ApiM-dev" / Resource Group "rg-myApim"): apimanagement.APIClient#CreateOrUpdate: Failure sending
request: StatusCode=409 -- Original Error: Code="IdentifierAlreadyInUse" Message="Resource already exists."
I am 99% sure this is due to the "title" in SwaggerConfig.cs file, it is the same value for all configurations. Thus deploying two of the APIs with the same title is throwing the error.
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "MyApiTitle");
}
How can I get the title to be unique based on the configuration?
I tried creating config values in web.config value for each configuration and referencing the key in the config file, but it didn't work, SwaggerUi picked up the default value in web.config file only.
web.dev.config:
<add key="BuildConfig" value="dev" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
SwaggerConfig.cs:
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "MyApiTitle-" + ConfigurationManager.AppSettings["BuildConfig"]);
}
Another option is to script deleting the API, import the API and rename the title, but I would do that as a last resort.
Would like to do this dynamically in the project code though.
I am able to achieve this with PowerShell, please check if that helps to you
https://medium.com/#rakesh.suryawanshi/deploy-azure-web-api-into-azure-api-management-with-powershell-3d14d1610b07
also, check if you are able to deploy it manually with your approach.

Failing to create services on Google Cloud Run with API using Java SDK

I create a Cloud Run client, however, couldn't find a way to list a service that is deployed with Cloud Run on GKE (for Anthos).
Create the client:
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleCredentials credential = GoogleCredentials.getApplicationDefault();
credential.createScoped("https://www.googleapis.com/auth/cloud-platform");
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credential);
CloudRun.Builder builder = new CloudRun.Builder(httpTransport, jsonFactory, requestInitializer);
return builder.setApplicationName(applicationName)
.setRootUrl(cloudRunRootUrl)
.build();
} catch (IOException e) {
e.printStackTrace();
}
try to list services:
services = cloudRun.namespaces().services()
.list("namespaces/default")
.execute()
.getItems();
My "hello" service is deploy on a GKE cluster under the namespace default. The above code doesn't work because the client always see "default" as project_id and complains about permission stuff. If I put the project_id rather than "default", permission errors are gone, but no services will be found.
I tried another project that does have Google fully-managed cloud run services, the same code returns result (with .list("namespaces/")).
How to access the service on GKE?
And my next question would be, how to programmatically create Cloud Run services on GKE?
Edit - for creating a service
As I couldn't figure out how to interact with Cloud Run on GKE, I took a step back to try fully managed one. The following code to create a service fails, and the error message just doesn't provide much useful insight, how to make it work?
Service deployedService = null;
// Map<String,String> annotations = new HashMap<>();
// annotations.put("client.knative.dev/user-image","gcr.io/cloudrun/hello");
ServiceSpec spec = new ServiceSpec();
List<Container> containers = new ArrayList<>();
containers.add(new Container().setImage("gcr.io/cloudrun/hello"));
spec.setTemplate(new RevisionTemplate().setMetadata(new ObjectMeta().setName("hello-fully-managed-v0.1.0"))
.setSpec(new RevisionSpec().setContainerConcurrency(20)
.setContainers(containers)
.setTimeoutSeconds(100)
)
);
helloService.setApiVersion("serving.knative.dev/v1")
.setMetadata(new ObjectMeta().setName("hello-fully-managed")
.setNamespace("data-infrastructure-test-env")
// .setAnnotations(annotations)
)
.setSpec(spec)
.setKind("Service");
try {
deployedService = cloudRun.namespaces().services()
.create("namespaces/data-infrastructure-test-env",helloService)
.execute();
} catch (IOException e) {
e.printStackTrace();
response.add(e.toString());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
Error message I got:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "The request has errors",
"reason" : "badRequest"
} ],
"message" : "The request has errors",
"status" : "INVALID_ARGUMENT"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:150)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
And the base_url is: https://europe-west1-run.googleapis.com
Your question is quite detailed (and is about Java which I am no expert in) and there are actually too many questions in there (ideally, please ask only 1 question here). However, I'll try to answer a few things you asked:
First, Cloud Run (managed, and on GKE) both implement the Knative Serving API. I've explained this at https://ahmet.im/blog/cloud-run-is-a-knative/ In fact, Cloud Run on GKE is just the open source Knative components installed to your cluster.
And my next question would be, how to programmatically create Cloud Run services on GKE?
You will have a very hard time (if possible at all) using the Cloud Run API client libraries (e.g. new CloudRun above) because these are designed for *.googleapis.com endpoints.
The Knative API part of "Cloud Run on GKE" is actually just your Kubernetes (GKE) master API endpoint (which runs on an IP address, with a TLS certificate that isn't trusted by root CAs, but you can find the CA cert in GKE GetCluster API call to verify the cert.) The TLS is part is why it's so hard to use the API Client libraries.
Knative APIs are just Kubernetes objects. So your best bet is one of these:
See Kubernetes java client (https://github.com/kubernetes-client/java) actually allows dynamic objects. (Go implementation does) and try to use that to create Knative CRDs.
Use kubectl apply.
Ask Knative Serving open source repository for help (they should be providing client libraries, maybe they're already there I'm not sure)
To program Cloud Run (managed) with the API Client Libraries, you need to explicitly override the API endpoint to the region e.g. us-central1-run.googleapis.com. (This is documented on each API call's REST API reference documentation.)
I have written a blog post in detail (with sample code in Go) on how to create/update services on Cloud Run (managed) using the Knative Serving API here: https://ahmet.im/blog/gcloud-run-deploy/
If you want to see how gcloud run deploy works, and which APIs it calls, you can pass --log-http option to observe the request/response traffic.
As for the error you got, it seems like the error message isn't helpful, but it might be coming from anywhere (as you're trying to imitate Knative API in GCP client libraries). I recommend reading my blog posts and sample code in depth.
UPDATES: Our engineering team's looking at the issue, it appears that there's currently a bug not adding the "details" field to the error. That's being worked on.
In your case, we see the following errors from requests:
field: "spec.template.spec"
description: "Missing template spec."
Means you are not properly filling up the spec field as I shown in my blog post and sample code.
field: "metadata.name"
description: "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -"
Make sure the name you are specifying adheres the patterns specified in API docs. Try to create that name manually perhaps in the UI or gcloud CLI.
field: "api_version"
description: "Unsupported API version \'serving.knative.dev/v1\'. Expected \'serving.knative.dev/v1alpha1\'"
Do not use v1alpha1 API, use v1 directly.
We'll try to get the details to the error message, however it appears that you need to study the sample code I linked in my blog post more in detail:
https://github.com/GoogleCloudPlatform/cloud-run-button/blob/a52c7fbaae33a3e06c112206c7227a0ef9649647/cmd/cloudshell_open/deploy.go#L26-L112
The Java SDK is automatically generated from the fact that the Cloud Run (fully managed) API is public. It does not support Cloud Run for Anthos.
(gcloud.run.deploy) The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -revision name
revision name name should be 65 character then problem will be resolved in Automation pipeline with GCP revision suffix should be less revision name is the combination of (service name +revision suffix) will automatically created by GCP.

Identity Server Endpoints OIDC

I am using Identity server and hosting it under IIS. It was working fine when hosted directly under http://localhost:44431
Step 1: call http://localhost:44431/account/login?returnUrl=/connect/authorize/login?respone_type....
Step 2: Then it goes to the Authorize Endpoint and a return a token
Probelm hosting under localhost\id:
However, when I deploy the application on IIS under Default Web site as localhost\id. It stops working.
Step 1: Calling http://localhost/id/account/login?returnUrl=/connect/authorize/login?respone_type....
>> Inspecting the Request Headers:
>> Response Header:
>> Open Id Configuration at http://localhost/id/.well-known/openid-configuration
"authorization_endpoint":"http://localhost/id/connect/authorize",
Step 2: Calling the /connect/authorize endpoint:
>> Inspecting the Headers:
It didn't include the id virtual directory, that's why it is failing. where in the process I have to fix this?
I'm not able to reproduce your problem, but I did start from scratch hosting IdentityServer4 in IIS. The steps I followed for setup are below.
Cloned IdentityServer4.Samples. Launch Quickstarts/3_ImplicitFlowAuthentication solution:
https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/3_ImplicitFlowAuthentication
Created an application in IIS with the path as '/id' with the AppPool set to 'No Managed Code'
Ran 'dotnet publish' on the IdentityServer4 project and moved the output to the IIS app root's folder
Changed the Authority URL in the MvcClient project to point to localhost/id
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = "http://localhost/id",
RequireHttpsMetadata = false,
ClientId = "mvc",
SaveTokens = true
});
Load the MvcClient application and navigate to a route with the 'Authorize' filter. The redirect occurred properly with the appropriate virtual directory
Check to see if the proper path is being output by IdentityServer by going to the openid-configuration page: http://localhost/id/.well-known/openid-configuration
Are you running IdentityServer4 and an MVC app in the same project? If so, are you using relative paths for the OpenIdConnectOptions.Authority property? Try changing it to an absolute path and see if that fixes the problem. I'm thinking this might be the case, because your request URL does not include the /id path in the redirect uri:
http://localhost/id/account/login?**returnUrl=/connect/authorize/login**?respone_type
The correct path of course should be:
http://localhost/id/account/login?**returnUrl=/id/connect/authorize/login**?respone_type
Hope this helps! Please let me know

Resources