Here is the question - how could I resolve http gateway port and protocol for a particular node type in my service code? So far I did manage to get this info only by parsing the app manifest like this -
var fabricClient = new FabricClient();
var appManifest = await fabricClient.ClusterManager.GetClusterManifestAsync();
var document = XDocument.Parse(appManifest);
var nodeTypeObj = document.Root.Descendants()
.Where(d => d.Name.LocalName == "NodeTypes")
.Descendants()
.Where(e => e.Name.LocalName == "NodeType")
.First(e => e.Attributes().Any(a => a.Name.LocalName == "Name" && a.Value == this.Context.NodeContext.NodeType));
var gateway = nodeTypeObj.Descendants()
.Where(d => d.Name.LocalName == "HttpApplicationGatewayEndpoint")
.First();
var port = gateway.Attributes().First(a => a.Name.LocalName == "Port").Value;
var protocol = gateway.Attributes().First(a => a.Name.LocalName == "Protocol").Value;
Are there any simpler means to get these values? Something like a method I'd call passing node type along the way and getting back all the stuff that very node type is configured with? including HttpApplicationGatewayEndpoint?
Looks like you're scraping the cluster manifest - I probably wouldn't recommend going that far.
There isn't a specific API to get that information so I would either pick a port by convention and use it (the default 19008 would work), or put the port in an environment variable or a config package if you think it will change between environments.
Related
We're using EFCore 3.1 and trying to build a query using Exists by means of .Any() which spans 2 properties.
var selectionCriteria = someHugeList.Select(sh => new { sh.Id, sh.StatusCode }).ToList()
var resultsQry = _myContext.SomeClass
.Include(sc => sc.DetailRecords)
.Where(sc => selectionCriteria.Any(crit => crit.Id == sc.Id
&& crit.StatusCode == sc.StatusCode));
var results = await resultsQry.ToListAsync()
When running this query (even with a small amount (5 items) of selection criteria items it provides the following error message;
System.InvalidOperationException: LINQ expression 'DbSet
.Where(c => __selectionCriteria_0
.Any(crit => crit.Id == sc.Id && crit.StatusCode == sc.StatusCode))' could not be translated.
Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by
inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'
Seems the problem resides in the fact that there are 2 properties included in the .Any clause. A where exists in sql normally can do this without problem. EFCore seems to find this difficult.
Does anyone have an idea on how to solve this?
just found this; https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#linq-queries-are-no-longer-evaluated-on-the-client
long story short; client evaluation is no longer working in EFCore 3.1 meaning this type of query (comparing a clientside list to a serverside list) doesn't work. You need to bring it to the client. My collegue pointed out to me just now that I didn't appreciate the error message to it's full potential :).
Changed my query as follows (not optimal, but no other solution yet):
var selectionCriteria = someHugeList.Select(sh => new { sh.Id, sh.StatusCode }).ToList()
var resultsQry = _myContext.SomeClass
.Include(sc => sc.DetailRecords)
.AsEnumerable() // this is the important part, pulling all the records client side so we can execute the .Any on the client.
.Where(sc => selectionCriteria.Any(crit => crit.Id == sc.Id
&& crit.StatusCode == sc.StatusCode));
var results = await resultsQry.ToList() // no more async, because clientside
I am using IdentityServer4 with two external Idp's, one with WSFederation (ADFS) and one with SAML.
For the SAML implementation I use the commercial product ComponentSpace SAML 2 for ASP.Net Core. I use the middleware-based config.
Logging it with both Idp's works perfectly, but now I have the situation where, depending on the client, I need to pass extra parameters to the SAML AuthnRequest. I know how to pass this extra parameter in the request (I can use the OnAuthnRequestCreated from the middleware), but what I don't know is how to test at that point from where the request is coming, i.e. from which client.
I have control of the client so I could also pass extra acr_values (which I think can be used to pass custom data), but again I don't know how to get them in the OnAuthnRequestCreated event as shown in the code below.
Any help would be much appreciated.
services.AddSaml(Configuration.GetSection("SAML"));
services.AddAuthentication()
.AddWsFederation("adfs", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
//...rest of config (SSO is working)
})
.AddSaml("saml", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
//...rest of config (SSO is working)
options.OnAuthnRequestCreated = request =>
{
//Here I would need to know from which client the request is coming (either by client name or url or acr_values or whatever)
//to be able to perform conditional logic. I've checked on the request object itself but the info is not in there
return request;
};
});
The request parameter is the SAML AuthnRequest object. It doesn't include client information etc.
Instead of the OnAuthnRequestCreated event, in your Startup class you can add some middleware as shown below. You can call GetRequiredService to access any additional interfaces (eg IHttpContextAccessor) you need to retrieve the client information.
app.Use((context, next) =>
{
var samlServiceProvider =
context.RequestServices.GetRequiredService<ISamlServiceProvider>();
samlServiceProvider.OnAuthnRequestCreated += authnRequest =>
{
// Update authn request as required.
return authnRequest;
};
return next();
});
Thanks ComponentSpace for the reply. I didn't get it to work directly with your solution by using app.Use((context, next)) => ... but your comment on GetRequiredService pointed me into the direction to find the solution like below. Basically I'm getting the IHttpContextAccessor which I can then use to parse the query string. I then get the ReturnUrl from this query string and use the IIdentityServerInteractionService to get the AuthorizationContext object, which contains what I need to build my custom logic.
So thanks again for pointing me into the right direction.
//build and intermediate service provider so we can get already configured services further down this method
var sp = services.BuildServiceProvider();
services.AddAuthentication()
.AddSaml("SamlIdp", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.OnAuthnRequestCreated = request =>
{
var httpContextAccessor = sp.GetService<IHttpContextAccessor>();
var queryStringValues = HttpUtility.ParseQueryString(httpContextAccessor.HttpContext.Request.QueryString.Value);
var interactionService = sp.GetService<IIdentityServerInteractionService>();
var authContext = interactionService.GetAuthorizationContextAsync(queryStringValues["ReturnUrl"]).Result;
//authContext now contains client info and other useful stuff to help build further logic to customize the request
return request;
};
});
In c#, I want to get a list of service fabric node information where my stateless service runs. This will be useful in tests. I know how to do this for stateful service using FabricClient class and ActorServiceProxy class, but when it comes to stateless service, I couldn't find a way. Do you have an idea?
Thanks,
You can still use the FabricClient to get this information. Have a play with the QueryManager to check for the info you need
Here's some quick code I use to quickly query the latest version of our TenantApp Service then I check to see they're all running in a healthy state or they've been upgraded properly.
var currentAppTypes = await fabricClient.QueryManager.GetApplicationTypeListAsync();
var tenantAppTypes = currentAppTypes.Where(x => x.ApplicationTypeName.Equals("TenantAppsType"));
var latestTenantAppType = currentAppTypes.Where(x => x.ApplicationTypeName.Equals("TenantAppsType"))?
.OrderByDescending(x =>
{
var versions = x.ApplicationTypeVersion.Split('.');
if (versions.Length == 3)
{
return (int.Parse(versions[0]) * 1000000) +
(int.Parse(versions[1]) * 1000) +
int.Parse(versions[2]);
}
return 0;
})?.FirstOrDefault();
if (latestTenantAppType != null)
{
var currentSvcTypes = await fabricClient.QueryManager.GetServiceTypeListAsync(latestTenantAppType.ApplicationTypeName, latestTenantAppType.ApplicationTypeVersion);
// etc
}
Or if you just want to get all the applications running
var currentApps = await fabricClient.QueryManager.GetApplicationListAsync();
Once you have the service information you can check the nodes its on or you can check the nodes directly themselves
var currentNodes = fabricClient.QueryManager.GetNodeListAsync();
var nodeInfo = await fabricClient.QueryManager.GetNodeLoadInformationAsync("nodeName");
Hope this helps
For anyone still trying to do this, I had a timer requirement that required me to work out how many nodes were running my app on the fly. This is roughly the code I used:
string currentNodeName = ServiceContext.NodeContext.NodeName;
var fabricClient = new FabricClient();
var nodeList = (await fabricClient.QueryManager.GetNodeListAsync()).ToList();
var serviceName = ServiceContext.ServiceName.LocalPath.Split('/')[1];
var nodesRunningApplication = new List<Node>();
foreach (var node in nodeList)
{
var nodeApplicationList = await fabricClient.QueryManager.GetDeployedApplicationListAsync(node.NodeName);
var nodeApplication = nodeApplicationList.FirstOrDefault(p =>
p.ApplicationName.LocalPath.Split('/')[1] == serviceName);
if (nodeApplication != null)
{
nodesRunningApplication.Add(node);
}
}
I have been trying to create an observable tweeter feed using tweetsharp with the following
public IObservable<IEnumerable<TwitterStatus>> MakeTweetRequest(string screenName)
{
var service = new TwitterService();
var r = Observable.FromAsyncPattern<string, IEnumerable<TwitterStatus>>(
(x,y,g) => service.BeginListTweetsOnSpecifiedUserTimeline(x),
d => service.EndListTweetsOnSpecifiedUserTimeline(d) );
return r(screenName);
}
but i'm just unable to get it to work can anyone help?
There are a few examples using TweetSharp in my Rx-Demo
Each of these do the same thing, via different Rx methods, which is too ListTweetsOnHomeTimelineSince and all future tweets:
TwitterFeedAsync
TwitterFeedCreateSync
TwitterFeedGenerateSync
I have 3 node.js application (A, B, C). A is an entry point and I like all the request that gets to this entry point to be redirected toward B or C (depending upon the value of a parameter in the request).
Currently I'm doing so with a res.redirect in A (I'm using expressjs framework). That's working fine except that it's not really transparent (I can see from the outside that the original request has been redirected).
To solve this, I will have B and C listen on socket instead of port number but I do not know how to have A redirecting the request to the sockets used by B or C.
Any idea on how to have 2 node.js process communicating via sockets ?
** UPDATE **
I have changed the code of A to use node-proxy:
app.all('/port/path/*', function(req, res){
// Host to proxied to
var host = 'localhost';
// Retrieve port and build new URL
var arr_url = req.url.split('/');
var port = arr_url[1];
var new_url = '/' + arr_url.slice(2).join('/');
console.log("CURRENT URL:" + req.url); // url is /5000/test/...
console.log("NEW URL :" + new_url); // url is /test/...
// Change URL
req.url = new_url;
var proxy = new httpProxy.HttpProxy();
proxy.proxyRequest(req, res, {
host: host,
port: port,
enableXForwarded: false,
buffer: proxy.buffer(req)
});
console.log("Proxied to " + host + ':' + port + new_url);
// For example: the url is localhost:10000/5000/test
// the log tells me 'Proxied to localhost:5000/test' => that is correct
// But... I do not get any return
// If I issue 'curl -XGET http://localhost:5000/test' I get the return I expect
});
Any obvious mistake in this ?
You're on the right track with having the other processes listen on different ports. What you're talking about is called a reverse proxy. If its http, its pretty straightforward to get this going with node-http-proxy:
https://github.com/nodejitsu/node-http-proxy
You want to set something up like a proxy table:
var options = {
router: {
'foo.com/baz': '127.0.0.1:8001',
'foo.com/buz': '127.0.0.1:8002',
'bar.com/buz': '127.0.0.1:8003'
}
};
I just put in an issue for for the node-http-rpoxy module here https://github.com/nodejitsu/node-http-proxy/issues/104
Apparently it does in fact work with unix sockets right now, but like this (subject to change).
var options = {
router: {
'foo.com/baz': ':/tmp/nodeserver.sock',
}
};
All it needs is a colon to use the socket path as the port value.