IIB - Proper/Standard Way To Set HTTP Request Base URL For Different Environments - soa

We are trying to implement our product with SOA and currently using IBM Integration Bus v9 as our ESB.
We have 3 different environments (Sets of servers used for different purposes) that we deploy our product on:
development: Used during the testing and development process
customer test: More stable builds for customer's approval before going for the main release
main/production: This is the final thing.
The challenge we have encountered: Setting Base URL for HTTP nodes of our message flows for different environments; without compromising the DRY principle!
It seems that it's only possible to set the whole URL at once in HTTP Request nodes with the mqsiapplybaroverride command. The problem is that multiple resources can be exposed from a single server and thus have a common base URL.
Using UDP seems to be a promising approach. We can set base URL for each of our services in a UDP and build HTTP Request URLs in compute nodes just before the HTTP Request node using the UDP. Then the UDPs can be overrided with the mqsiapplybaroverride... Problem? It seems that UDPs don't have a scope of more than a single message flow... so anytime I want to call a resource from a server I have to define a UDP for that message flow or the BAR override won't affect the base URL for that message flow... This would lead to base URLs being repeated in each message flow... DRY applies.
This should be a common problem in a typical SOA application... So is there any better way to solve it? Anything like JNDI feature in typical Java EE Containers?
IIB v10.0.0.6 seems to have introduced a RestRequest node which provides Base URL setting capability... Unfortunately, we don't have that luxury for the time being.

You can use a user defined configurable service to achieve this.
You can read and set URL from configurable service using a java compute node or using a mapping node and custom java.

A good solution is to set baseUrl in database for each environment and set value as bellow:
SET OutputLocalEnvironment.Destination.REST.Request.BaseURL = GetCachedOrFromDB('custom_service_baseUrl');
GetCachedOrFromDB -> define function to get value from cache or db (if not in cache)
'custom_service_baseUrl' - > define property as key in settings table for each environment.

Related

How to test different versions of backend services on Kubernetes?

I have a frontend instance (Angular app on nginx), which proxies calls to backend under a specific domain (let's say backend-app). Everything's easy when there is only one instance of both backend and frontend - I name the Service backend-app and DNS resolves to the correct backend Deployment.
Let's say I have another version of backend which I would like to test before merging to master. As nginx configuration of frontend instance is hardcoded to proxy to backend-api, creating another Service under the same name for a newer version of backend doesn't work.
I considered these options:
Making an environment variable and substituting domain name in nginx proxy configuration during runtime. This way I could be flexible about where do I want to route frontend calls to. Cons of this solution, as far as I have investigated, is that this approach beats the purpose of self-containment, that is, it becomes ambiguous what is the frontend's backend client and this type of configuration is prone to errors.
Creating different namespace every time I want to test things. While this allows spinning the whole stack without any problem or conflict, it seems to me that it's a huge overhead to create namespaces just for testing something once.
Having some fancy configurations combining labels and selectors. I couldn't think or find how to do it.
Any other opinions/suggestions you might have?
Try this approach
add label name:backend-1 to backend1 pod
add label name:backend-2 to backend2 pod
create a service using backend-1 selector.
to test against other backend, say backend2, all you have to do is edit the service yaml file and update the selector. you can toggle this way to test between backend1 and backend2
Are you using open shift. If yes then you can divide load between services by percentage using route.
Check blue/green and canary deployment options for more details

Dynamic NameResolver that the load balancer will honor?

So I am working on a gRPC based remoting framework and I have created my own service registry that my services registers in when they come up. Of course I would like the client to lookup the server from the using a custom NameResolver.
So this is working now but since I want to change the service registry at runtime (for example to support rolling upgrades), I would like the NameResolver to keep checking and when a change occurs I want it to update the registry by doing onUpdate. The question is, can I do this and will the gRPC framework honor the update?
So my understading is that the load balancer checks the list of ResolvedServerInfo so it knows what to load balance between. But from that point I am unclear how this is intendent to work. Does it check each time and is it allowed to keep updating the NameResolver. Listener?
I hope this is the case since it is a crucial part of the service discovery I am trying to implement.
Thanks
Magnus
NameResolver is designed to get push based updates rather than polling for changes in addresses. In order to accomplish what you want, you'll need to set up a timer to periodically refresh names in your customer name resolver.

Using Cloudfront to expose ElasticSearch REST API in read only (GET/HEAD)

I want to let my clients speak directly with ElasticSearch REST API, obviously preventing them from performing any data or configuration change.
I had a look at ElasticSearch REST interface and I noticed the pattern: HTTP GET requests are pretty safe (harmless queries and status of cluster).
So I thought I can use Cloudfront as a CDN/Proxy that only allows GET/HEAD methods (you can impose such restrict it in the main configuration).
So far so good, all is set up. But things don't work because I would need to open my EC2 security group to the world in order to be reachable from Cloudfront! I don't want this, really!
When I use EC2 with RDS, I can simply allow access to my EC2 security group in RDS security groups. Why can't I do this with CloudFront? Or can I?
Ideas?
edit: It's not documented, but ES accepts facets query, which involve a (JSON) body, not only with POST, but also with GET. This simply breaks HTTP recommendation (as for RFC3616) by not ignoring the body for GET request (source).
This relates because, as pointed out, exposing ES REST interface directly can lead to easy DOS attacks using complex queries. I'm still convinced though, having one less proxy is still worth it.
edit: Other option for me would be to skip CloudFront and adding a security layer as an ElasticSearch plugin as shown here
I ended coding with my own plugin. Surprisingly there was nothing quite like this around.
No proxies, no Jetty, no Tomcat.
Just a the original ES rest module and my RestFilter. Using a minimum of reflection to obtain the remote address of the requests.
enjoy:
https://github.com/sscarduzio/elasticsearch-readonlyrest-plugin
Note that even a GET request can be harmful in Elasticsearch. A query which simply takes up too much resources to compute will bring down your cluster. Facets are a good way to do this.
I'd recommend writing a simple REST API you place in front of ES so you get much more control over what hits your search cluster. If that's not an option you could consider running Nginx on your ES boxes to act as a local reverse proxy, which will give you the same control (and a whole lot more) as CloudFront does. Then you'd only have to open up Nginx to the world, instead of ES.
A way to do this in AWS would be:
Set up an Application Load Balancer in front of your ES cluster. Create a TLS cert for the ALB and serve https. Open the ES security group to the ALB.
Set up CloudFront and use the ALB as origin. Pass a custom header with a secret value (for WAF, see next point).
Set up WAF on your ALB to only allow requests that contain the custom header with the secret value. Now all requests have to go through CloudFront.
Set up a Lambda#Edge function on your CloudFront distribution to either remove the body from GET requests, or DENY such requests.
It’s quite some work, but there’s advantages over the plugin, e.g.:
CloudFront comes with free network DDOS protection
CloudFront gives your users lower latency to ES because of the fast CloudFront network and global PoP’s.
Opens many options to use CloudFront, WAF and Lamba#Edge to further protect your ES cluster.
I’m working on sample code in CDK to set all of this up. Will report back when that’s ready.

Cluster aware servlet development

How can one develop a cluster-aware servlet and what is the design criteria for the same?
This isn't a problem which is to be solved at code level, but rather at webserver level. So the Servlet code doesn't need to be aware of being clustered.
The code does not need to be aware of being clustered but the developer needs to be aware that the code may be clustered and the session replicated. Let me explain.
When you mark an webapp in web.xml you are telling the container that that this web-application can be clustered.
If the webapp is deployed on a cluster, each machine in the cluster will run a vm and this webapp inside it. As far as the client is concerned the request it sees one webapp though each request from the client can be serviced by a different vm in the cluster.
So if the webapp is storing any state, it must be made available to all the instances of the vms(in the cluster) running the webapp.
How can this be done ?
By marking the things that you put into the httpsession object as "Serializable". You are signaling to the container that it should replicate the state to the other vms (if you have setup session replication). It is accomplished in a couple of ways in weblogic. Everytime you use setAttribute() on the session, it triggers a sessionreplication event.
In WL There are two ways of replicating inmemory replication and using database for
replication . I would like to hear how this is done in other appservers.
As #BalusC said, this is primarily a server configuration task, and how to do it depends very much on which server you're using (and which you don't mention), but here's how to do it with Tomcat 6, for example.
There is one thing to keep in mind at the code side, though, which is that you have to be careful what objects you put into the HTTP session (using HttpSession.setAttribute(). For session replication to work, these objects have to be serializable in order to be transported across the network to the other servers in the cluster. If they are not serializable, then either the server may drop them, or it may throw an exception.
It's not uncommon for developers to use the HTTP session as a place to put large, complex business objects (to allow them to be accessed from JSPs, for example), and these things are very unlikely to be serializable. Other examples for form-binding objects which, while being simple form-data holders, are often not serializable.

Is it wrong to switch client logic in the service tier?

We have two client apps (a web app and an agent app) accessing methods on the same service, but with slightly different requirements. My team wants to control behaviour on the service side by passing in a ApplicationType parameter to every method - which is essentially an enum containing the name of the calling client application - which is then used as a key for a database lookup to configure the service with client-specific options.
Something about this makes me uneasy as I don't think the service should really have to be aware of which client is calling it. I'm being told that it's easier to do it this way than pass a load of options dynamically through the method call.
Is there anything wrong with the client application telling the service who they are? Or is there really no difference between passing a config key versus a set of parameterized options?
One immediate problem I can see is that if we ever opened the service to another client run by a third party, we'd have to maintain their configuration settings locally for them. At the moment we own both client apps so it's not so much of a problem.
How would you do it?
In a layered solution, you should always consider your layers as onion-like layers, and dependencies should always go inwards, never outwards.
So your GUI/App layer should depend on the businesslogic layer, the businesslogic layer should depend on the data access layer, and similar.
Unless you categorize the clients (web, win, wpf, cli), or generalize it with client profiles (which client applications can configure), I would never pass in the name of the calling application, as this would make the business logic layer aware of and dependent upon the outside layer.
What kind of differences are we talking about that would depend on the type of application? If you elaborate a bit on the differences here, perhaps someone can come up with some helpful advice on other ways to solve this.
But I would definitely look for other ways before going down your described path.
Can't you create two different services, one for each application? The two services will share a lot of code or call a single internal service with different parameterization depending on what outer service was called.
From a design perspective, this is no different than having users with different profiles. From a security perspective, I hope your applications are doing something to identify themselves, lest users of one application figure out a way to invoke the other applications logic as a hack. (Image a HR application being used by the mafia and a bank at the same time, one customer would be interesting in hacking the other customer's application on a shared application host)
In .net the design doesn't feel this way because the credentials live on the thread (i.e. when you set the IIPrincipal, that info rides on the thread-- it is communicated along with each method call, but not as a parameter.)
Maybe what you are looking for in terms of a more elegant design is an ApplicationIdentity attribute. You'd have to write a custom one, I don't know of one in the framework right now.
This is a hard topic to discuss without a solid example.
You are right for feeling that way. Sending in the client type to change behaviour is not correct. It's not a bad idea for logging... but that's about it.
Here is what I would do:
Review each method to see what needs to be different and why.
Create different methods for different usages. The method name should be self explanatory. If you ever need to break compatibility, you have more control (assuming you're not using a versioning system which would be overkill for an in-house-only service).
In some cases request parameters (flags/enum values) are more appropriate.
In some cases knowing the operating environment is more appropriate (especially for data security). The operating environment almost always sent during a login request. Something like "attended"/"secure" (agent client) vs "unattended"/"not secure" (web client). Now you must exchange a session key (HTTP cookie or an application level session id). Sessions obviously doesn't work if you need to be 100% stateless -- especially if you want to scale-out without session replication... if you have that requirement, send a structure in every request.
Think of requests like functions in your code. You wouldn't put a magic parameter that changes the behaviour of the function. You would create multiple functions that each behave differently. Whoever is using the function makes the decision which one to call.
So why is client type so wrong? Client type has no specific meaning on its own. It has many meanings and they may change over time. It's simply informational which is why it is a handy thing to log. An operating environment does have a specific meaning.
Here is a scenario to consider: What if a new client type is developed that is slightly different in a way that would break compatibility with the original request? Now you have two requests. 2 clients use Request A and 1 client uses Request B. If you pass in a client type to each request, the server is expected to work for every possible client type. Much harder to test and maintain!!

Resources