Stateless Web API service with HTTPS endpoint throws health state error - c#

I want a https endpoint for my in a local service fabric (GA version) cluster hosted stateless Web API service. After achieving that, I want to deploy my cluster in Azure.
I followed the steps in the "Secure a Service Fabric cluster" article of the service fabric documentation and created a self-signed certificate and uploaded it to my key vault. I also imported my certificate to my machine's "trusted people" store with the Import-PfxCertificate commands on step 2.5.
AddCertToKeyVault:
Invoke-AddCertToKeyVault -SubscriptionId <Id> -ResourceGroupName 'ResourceGroupName' -Location 'West Europe' -VaultName 'VaultName' -CertificateName 'TestCert' -Password '****' -CreateSelfSignedCertificate -DnsName 'www.<clustername>.westeurope.cloudapp.azure.com' -OutputPath 'C:\MyCertificates'
Now I adjusted the ServiceManifest.xml, ApplicationManifest.xml (like in RunAs: Run a Service Fabric application with different security permissions) and my OwinCommunicationListener.cs:
ServiceManifest.xml (MasterDataServiceWebApi):
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="MasterDataServiceWebApiPkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<StatelessServiceType ServiceTypeName="MasterDataServiceWebApiType" />
</ServiceTypes>
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>MasterDataServiceWebApi.exe</Program>
</ExeHost>
</EntryPoint>
</CodePackage>
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpoint" Type="Input" Protocol="https" Port="5030" CertificateRef="TestCert"/>
</Endpoints>
</Resources>
</ServiceManifest>
ApplicationManifest:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ApplicationTypeName="exCHANGETestCluster2Type" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Parameters>
<Parameter Name="MasterDataServiceWebApi_InstanceCount" DefaultValue="-1" />
</Parameters>
<ServiceManifestImport>
<ServiceManifestRef ServiceManifestName="MasterDataServiceWebApiPkg" ServiceManifestVersion="1.0.0" />
<ConfigOverrides />
<Policies>
<EndpointBindingPolicy EndpointRef="ServiceEndpoint" CertificateRef="TestCert" />
</Policies>
</ServiceManifestImport>
<DefaultServices>
<Service Name="MasterDataServiceWebApi">
<StatelessService ServiceTypeName="MasterDataServiceWebApiType" InstanceCount="[MasterDataServiceWebApi_InstanceCount]">
<SingletonPartition />
</StatelessService>
</Service>
</DefaultServices>
<Certificates>
<EndpointCertificate X509FindValue="<Thumbprint>" Name="TestCert" />
</Certificates>
</ApplicationManifest>
OwinCommunicationListener.cs:
[...]
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
var serviceEndpoint = this.serviceContext.CodePackageActivationContext.GetEndpoint(this.endpointName);
int port = serviceEndpoint.Port; //NEW!
if (this.serviceContext is StatefulServiceContext)
{
[...]
}
else if (this.serviceContext is StatelessServiceContext)
{
var protocol = serviceEndpoint.Protocol;
this.listeningAddress = string.Format(
CultureInfo.InvariantCulture,
//"http://+:{0}/{1}",
"{0}://+:{1}/{2}", //NEW!
protocol,
port,
string.IsNullOrWhiteSpace(this.appRoot)
? string.Empty
: this.appRoot.TrimEnd('/') + '/');
}
else
{
throw new InvalidOperationException();
}
[...]
When I deploy the stateless service to my local cluster now, my service fabric explorer reports some very "expressive" errors and I am not able to access my service:
Kind Health State Description
=============================================================================
Services Error Unhealthy services: 100% (1/1), ServiceType='MasterDataServiceWebApiType', MaxPercentUnhealthyServices=0%.
Service Error Unhealthy service: ServiceName='fabric:/sfCluster/MasterDataServiceWebApi', AggregatedHealthState='Error'.
Partitions Error Unhealthy partitions: 100% (1/1), MaxPercentUnhealthyPartitionsPerService=0%.
Partition Error Unhealthy partition: PartitionId='e5635b85-3c23-426b-bd12-13ae56796f23', AggregatedHealthState='Error'.
Event Error Error event: SourceId='System.FM', Property='State'. Partition is below target replica or instance count.
Visual Studio isn't providing me with any further error details. It's quite the opposite. The stacktrace prints: fabric:/sfCluster/MasterDataServiceWebApi is ready.
What did I miss? Did I configured something wrong?
BTW: After that, I created a new cluster in Azure with my self-signed certificate, but when I try to acess the Service Fabric Explorer of this cluster I have no UI and a blank site..

I have learned, that Service Fabric uses the local machine store for the certificate validation. (https://github.com/Azure-Samples/service-fabric-dotnet-web-reference-app/issues/3)
So I had to import the certificate into my local machine store by this slightly modified powershell-script:
Import-PfxCertificate -Exportable -CertStoreLocation cert:\localMachine\my -FilePath C:\MyCertificates\TestCert.pfx -Password (Read-Host -AsSecureString -Prompt "Enter Certificate Password")
Before that, I imported my certificate into Cert:\CurrentUser\TrustedPeople and Cert:\CurrentUser\My. But the local Service Fabric cluster doesn't look up there.
BTW:
I still get a blank site, when I try to access the Service Fabric Explorer of my azure-hosted Service Fabric cluster, that I have secured with the same certification key. I will create another question for this problem. EDIT: Using the Internet Explorer instead of Firefox solved my blank site issue.

Related

Service Fabric service exists but returns 404 upon calling

I have a service fabric cluster that works fine locally but when deployed to azure the WebAPI stateless service returns
404 FABRIC_E_SERVICE_DOES_NOT_EXISTS
However the SF cluster shows that the service actually does exists and is up and running
Below is my service manifest for the service
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="SvcWebAPIPkg"
Version="1.0.9"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in
Program.cs. -->
<StatelessServiceType ServiceTypeName="SvcWebAPIType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.9">
<EntryPoint>
<ExeHost>
<Program>SvcWebApi.exe</Program>
<WorkingFolder>CodePackage</WorkingFolder>
</ExeHost>
</EntryPoint>
</CodePackage>
<ConfigPackage Name="Config" Version="1.0.9" />
<Resources>
<Endpoints>
<Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input"
Port="80" />
</Endpoints>
</Resources>
</ServiceManifest>
If that's the way how you access your service - "myaddress.com/api/BriefcaseApi/GetString" - it's incorrect. SF tries to resolve 'api' as a registered service, which is obviously does not exist. Right? Here is the way how you should construct the url -
http(s)://<Cluster FQDN | internal IP>:Port/<ServiceInstanceName>/<Suffix path>?PartitionKey=<key>&PartitionKind=<partitionkind>&ListenerName=<listenerName>&TargetReplicaSelector=<targetReplicaSelector>&Timeout=<timeout_in_seconds>
In your case, here is how it may look like -
http(s)://myaddress.com/SvcWebAPI/api/BriefcaseApi/GetString
Find more info here - Reverse proxy in Azure Service Fabric.

C# Consuming XML API - No REST

I was wondering how to consume a XML Service, which states: "CarTrawler’s OTA Server does not expose a Web Services interface - i.e. no SOAP-discoverable WSDL. All messages are stateless and no session is maintained between calls.".
The service have targets URL’s defined in order to send the requests.
One possible request may be:
<?xml version="1.0" encoding="UTF-8"?>
<OTA_VehAvailRateRQ
xmlns="http://www.opentravel.org/OTA/2003/05"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opentravel.org/OTA/2003/05 OTA_VehAvailRateRQ.xsd"
Target="Test" Version="1.005">
<POS>
<Source ISOCurrency="EUR">
<RequestorID Type="16" ID="#####" ID_Context="####" />
</Source>
</POS>
<VehAvailRQCore Status="Available">
<VehRentalCore PickUpDateTime="2016-04-01T07:00:00" ReturnDateTime="2016-04-09T19:00:00">
<PickUpLocation CodeContext="####" LocationCode="71" />
<ReturnLocation CodeContext="####" LocationCode="71" />
</VehRentalCore>
<DriverType Age='30'/>
</VehAvailRQCore>
<VehAvailRQInfo PassengerQty='3'>
<Customer>
<Primary>
<CitizenCountryName Code='IE' />
</Primary>
</Customer>
<TPA_Extensions>
<ConsumerIP>999.999.999.999</ConsumerIP>
</TPA_Extensions>
</VehAvailRQInfo>
</OTA_VehAvailRateRQ>
Maybe using HttpClient?, as it has no WSDL and I guess the service isn't REST.
This is no problem. WSDL is just metadata to help you figure out the kind of data you should send or receive. It doesn't mean that the service is not RESTful.
There are many tools that you can use to call a RESTful service. This is my REST client:
https://bitbucket.org/MelbourneDeveloper/restclient-.net
NuGet: Install-Package RESTClient.NET
You should try just doing a simple GET as a string and see what gets returned. If you post the Url of the API, I will try it.

Orleans Specify SqlServer for Liveness

I am trying to setup a test environment for Orleans that uses SQL Server for liveness. This is my server config file:
<?xml version="1.0" encoding="utf-8" ?>
<OrleansConfiguration xmlns="urn:orleans">
<Globals>
<Liveness LivenessType="SqlServer" DeploymentId="42783519-d64e-44c9-9c29-111111111133" DataConnectionString="Data Source=.\\SQLEXPRESS;Initial Catalog=Orleans;Integrated Security=True;" />
<!--<SeedNode Address="localhost" Port="11111" />-->
</Globals>
<Defaults>
<Networking Address="localhost" Port="11111" />
<ProxyingGateway Address="localhost" Port="30000" />
<Tracing DefaultTraceLevel="Info" TraceToConsole="true" TraceToFile="{0}-{1}.log">
<TraceLevelOverride LogPrefix="Application" TraceLevel="Info" />
</Tracing>
<Statistics MetricsTableWriteInterval="30s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" />
</Defaults>
<Override Node="Primary">
<Networking Address="localhost" Port="11111" />
<ProxyingGateway Address="localhost" Port="30000" />
</Override>
</OrleansConfiguration>
When I use this config I get this error when running:
MembershipTableGrain cannot run without Seed node - please check your
silo configuration file and make sure it specifies a SeedNode element.
Alternatively, you may want to use AzureTable for LivenessType.
Parameter name: grain = MembershipTableGrain Exception =
System.ArgumentException: MembershipTableGrain cannot run without Seed
node - please check your silo configuration file and make sure it
specifies a SeedNode element. Alternatively, you may want to use
AzureTable for LivenessType.
and further up, the logs say that the Liveness is MembershipTableGrain (which is the default and requires a SeeNode). What am I missing here?
My silo config for SQLServer membership looks like this
<?xml version="1.0" encoding="utf-8"?>
<OrleansConfiguration xmlns="urn:orleans">
<Globals>
<SystemStore SystemStoreType="SqlServer" DeploymentId="YYYYY" DataConnectionString="Server=THESERVER;Database=Orleans;User ID=USER;password=PASSWORD;"/>
</Globals>
<Defaults>
<Networking Address="" Port="11111"/>
<ProxyingGateway Address="" Port="30000"/>
</Defaults>
</OrleansConfiguration>
No need to specify the liveness type. It figures it out by looking at the SystemStoreType.
The client config does need the gateway specified
<ClientConfiguration xmlns="urn:orleans">
<SystemStore SystemStoreType ="SqlServer"
DeploymentId="YYY"
DataConnectionString="Server=THESERVER;Database=Orleans;User ID=USER;password=PASSWORD;" />
<GatewayProvider ProviderType="SqlServer"/>
</ClientConfiguration>
You can also use programmable API for configuration, instead of XML.
I found the problem. That is not how to change the Liveness type. It should be like this:
<SystemStore SystemStoreType="SqlServer" DeploymentId="42783519-d64e-44c9-9c29-111111111133" DataConnectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Orleans;Integrated Security=True;" />
<Liveness LivenessType="SqlServer" />
Also, you must make sure to ref "Microsoft.Orleans.OrleansSqlUtils" NuGet package and run this SQL Create Script

Azure Service Bus Topic Timeout exception

I am building a POC for Azure Service Bus Topics using the code given on this blog post: http://blogs.msdn.com/b/tomholl/archive/2011/10/09/using-service-bus-topics-and-subscriptions-with-wcf.aspx However, I am getting following error.
System.TimeoutException: The request has timed out after 00:00:00 milliseconds. The successful completion of the request cannot be determined. Additional queries should be made to determine whether or not the operation has succeeded.
I have done everything as per the Link. Here is my Code, I receive error on this line: ((IChannel)clientChannerl).Open();
var accountEventLog = new AccountEventLog()
{
AccountId = 123,
EventType = "BE",
Date = DateTime.Now
};
ChannelFactory<IAccountEventNotification> factory = null;
try
{
factory = new ChannelFactory<IAccountEventNotification>("Subscribers");
var clientChannerl = factory.CreateChannel();
((IChannel)clientChannerl).Open();
using (new OperationContextScope((IContextChannel)clientChannerl))
{
var bmp = new BrokeredMessageProperty();
bmp.Properties["AccountId"] = accountEventLog.AccountId;
bmp.Properties["EventType"] = accountEventLog.EventType;
bmp.Properties["Date"] = accountEventLog.Date;
OperationContext.Current.OutgoingMessageProperties.Add(BrokeredMessageProperty.Name, bmp);
clientChannerl.onEventOccurred(accountEventLog);
}
((IChannel)clientChannerl).Close();
factory.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Here is my config settings
<behaviors>
<endpointBehaviors>
<behavior name="securityBehavior">
<transportClientEndpointBehavior>
<tokenProvider>
<sharedSecret issuerName="RootManageSharedAccessKey" issuerSecret="Shared Key Here" />
</tokenProvider>
</transportClientEndpointBehavior>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<netMessagingBinding>
<binding name="messagingBinding" sendTimeout="00:03:00" receiveTimeout="00:03:00"
openTimeout="00:03:00" closeTimeout="00:03:00" sessionIdleTimeout="00:01:00"
prefetchCount="-1">
<transportSettings batchFlushInterval="00:00:01" />
</binding>
</netMessagingBinding>
</bindings>
<client>
<endpoint name="Subscribers"
address="sb://Namespace/topicname"
binding="netMessagingBinding"
bindingConfiguration="messagingBinding"
contract="My Contract"
behaviorConfiguration="securityBehavior" />
</client>
Any help will be highly appreciated
I am able to resolve the issue. However, I am going to describe what I learned in this whole exercise.
The token provider added in the behavior is used for Service Bus Authentication with ACS (Active Directory Service)
Namespaces created using Azure Portal don't create an ACS endpoint/ACS authentication by default. When you create a namespace it only creates SAS (Shared Access Signature) by default.
To Authenticate your wcf call with SAS use this Token provider: <sharedAccessSignature keyName="RootManageSharedAccessKey" key="key" />
In case you want to use ACS authentication then create namespace using Azure Power Shell. Following is the PS command to create Namespace with ACS Authentication enabled:
New-AzureSBNamespace "Namespace" "East US" -CreateACSNamespace $true -NamespaceType Messaging
So to resolve my issue I used the Point 3 described above and it started working.
Another thing to look out for, is has someone accidentally left the proxy enabled in your App.config or Web.config? This will produce a similar exception when sending.
Look for something like the following:
<system.net>
<defaultProxy enabled="true" useDefaultCredentials="true">
<proxy autoDetect="false" bypassonlocal="false" proxyaddress="http://127.0.0.1:8888" usesystemdefault="false" />
</defaultProxy>
</system.net>

Azure autoscaling exception when trying to read performance counter

I am working on an example from Microsoft that shows how to use autoscaling (http://www.windowsazure.com/en-us/develop/net/how-to-guides/autoscaling/). The worker role installed on the cloud generates a performance counter and locally, a console application reads this counter and applies autoscaling.
Everything looks fine, the performance counter is available from WADPerformanceCountersTable and the console application accesses correctly to the storage but it cannot find the worker role inside WADPerformanceCountersTable. This is the generated exception:
Autoscaling General Error: 2001 :
Microsoft.Practices.EnterpriseLibrary.WindowsAzure.Autoscaling.DataPointsCollection.DataPointsCollectionException:
Could not retrieve performance counter with name '\Processor(_Total)\% Processor Time'
for target 'WorkerRoleExample' from the WADPerformanceCountersTable table. --->
System.ArgumentOutOfRangeException: Could not retrieve the role with alias 'WorkerRoleExample' from the service information store.
Please review the service information store to fix this.
Configuration files of Autoscaling Application Block (both files are part of a console application):
rules.xml:
<?xml version="1.0" encoding="utf-8" ?>
<rules xmlns="http://schemas.microsoft.com/practices/2011/entlib/autoscaling/rules">
<constraintRules>
<rule name="default" enabled="true" rank="1" description="The default constraint rule">
<actions>
<range min="1" max="2" target="WorkerRoleExample"/>
</actions>
</rule>
</constraintRules>
<reactiveRules>
<rule name="ScaleUpOnHighUtilization" rank="10" description="Scale up the web role" enabled="true" >
<when>
<any>
<greaterOrEqual operand="WebRoleA_CPU_Avg_5m" than="60"/>
</any>
</when>
<actions>
<scale target="WorkerRoleExample" by="1"/>
</actions>
</rule>
<rule name="ScaleDownOnLowUtilization" rank="10" description="Scale up the web role" enabled="true" >
<when>
<all>
<less operand="WebRoleA_CPU_Avg_5m" than="60"/>
</all>
</when>
<actions>
<scale target="WorkerRoleExample" by="-1"/>
</actions>
</rule>
</reactiveRules>
<operands>
<performanceCounter alias="WebRoleA_CPU_Avg_5m"
performanceCounterName="\Processor(_Total)\% Processor Time"
source ="WorkerRoleExample"
timespan="00:05:00" aggregate="Average"/>
</operands>
</rules>
services.xml:
<?xml version="1.0" encoding="utf-8" ?>
<serviceModel xmlns="http://schemas.microsoft.com/practices/2011/entlib/autoscaling/serviceModel">
<subscriptions>
<subscription name="TestingWorkerRole"
certificateThumbprint="**************"
subscriptionId="**************"
certificateStoreLocation="CurrentUser"
certificateStoreName="My">
<services>
<service dnsPrefix="**************" slot="Staging">
<roles>
<role alias="AutoscalingApplicationRole"
roleName="WorkerRoleExample"
wadStorageAccountName="targetstorage"/>
</roles>
</service>
</services>
<storageAccounts>
<storageAccount alias="targetstorage"
connectionString="DefaultEndpointsProtocol=https;AccountName=*****;AccountKey=*******">
</storageAccount>
</storageAccounts>
</subscription>
</subscriptions>
</serviceModel>
Worker role running on the cloud generates a performance counter:
public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;
CreatePerformanceCounters();
return base.OnStart();
}
private static void CreatePerformanceCounters()
{
DiagnosticMonitorConfiguration diagConfig = DiagnosticMonitor.GetDefaultInitialConfiguration();
var procTimeConfig = new PerformanceCounterConfiguration();
procTimeConfig.CounterSpecifier = #"\Processor(_Total)\% Processor Time";
procTimeConfig.SampleRate = TimeSpan.FromSeconds(10);
diagConfig.PerformanceCounters.DataSources.Add(procTimeConfig);
diagConfig.PerformanceCounters.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", diagConfig);
}
In services.xml try changing this:
<role alias="AutoscalingApplicationRole"
roleName="WorkerRoleExample"
wadStorageAccountName="targetstorage"/>
To this:
<role alias="WorkerRoleExample"
roleName="WorkerRoleExample"
wadStorageAccountName="targetstorage"/>
In rules.xml the target attribute of the scale element looks for a matching alias attribute on the role element in services.xml - it was looking for WorkerRoleExample and couldn't find it.
It would also work if, instead of the above, in rules.xml you changed:
<scale target="WorkerRoleExample" by="1"/>
To this:
<scale target="AutoscalingApplicationRole" by="1"/>

Categories