How can I access and read parameters definied in PackageRoot/Settings/Settings.xml file from my stateful/stateless service code?
For example I have a section DocumentDbConfig with Parameter EndpointUrl:
<Section Name="DocumentDbConfig">
<Parameter Name="EndpointUrl" Value="{url}"/>
</Section>
And I would like to read it in my code:
public async Task<ServiceActionResult<Result>> GetResult()
{
_client = new Client({{ EndpointUrl }}); //HOW TO GET ENDPOINT URL FROM SETTINGS?
}
As long as your code has access to the ServiceContext you can access all of the configuration packages that were deployed with your service. For example:
serviceContext.CodePackageActivationContext.GetConfigurationPackageObject("Config")
where "Config" is the name of the configuration package. From there, you can access all of the sections and keys/values within each section. Be sure to refer to the ConfigurationPackage documentation as a guide on how to access this data, as well as how to listen to events that fire when the configuration package changes.
Related
I have a simple boot application where I have added open api swagger dependency
springdoc-openapi-ui
along with these properties
springdoc.swagger-ui.disable-swagger-default-url=true
springdoc.swagger-ui.configUrl=/v3/api-docs/swagger-config
springdoc.swagger-ui.path=/swagger-ui.html
I am getting these error (when calling http://localhost:8080/swagger-ui/index.html):
No API definition provided.
this is my controller :
#RestController
public class HelloWorld {
#GetMapping("sayHi")
public String sayHi(){
return "Hi Beno";
}
}
Any idea ?
When using a different endpoint to serve the OpenAPI Config, you'll need to set two properties
// This will set UI to fetch the config's URL from "somePath"
springdoc.swagger-ui.configUrl=somePath
// You also need to serve the config file from the endpoint at "somePath"
springdoc.api-docs.path=somePath
I'm trying to understand in what scenarios endpoint definitions are required in the ServiceManifest. I have a stateful service with the multiple service remoting listeners defined. My implementation of CreateServiceReplicaListeners:
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
return new[]
{
new ServiceReplicaListener(context => this.CreateServiceRemotingListener(context)),
new ServiceReplicaListener(context =>
{
return new FabricTransportServiceRemotingListener(context,
new CustomService<string>(),
new FabricTransportRemotingListenerSettings
{
EndpointResourceName = "ustdfdomgfasdf"
});
}, name: "CustomListener")
};
}
The endpoint resource name for the custom listener is garbage. I have not defined that endpoint in the service manifest's resources:
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpoint" />
<Endpoint Name="ReplicatorEndpoint" />
</Endpoints>
</Resources>
However, in testing I find I'm still able to get a proxy to CustomListener:
InventoryItem i = new InventoryItem(description, price, number, reorderThreshold, max);
var applicationInstance = FabricRuntime.GetActivationContext().ApplicationName.Replace("fabric:/", String.Empty);
var inventoryServiceUri = new Uri("fabric:/" + applicationInstance + "/" + InventoryServiceName);
//Doesn't fail
ICustomService customServiceClient = ServiceProxy.Create<ICustomService>(inventoryServiceUri,
i.Id.GetPartitionKey(),
listenerName: "CustomListener");
//Still doesn't fail
var added = await customServiceClient.Add(1, 2);
To me, this indicates endpoint definitions aren't required for service remoting as long as the endpoint and listener names are unique. Is that so? If not, why does my example work?
Endpoints are required to tell service fabric to allocate ports in the node for the services being started on that node, this will prevent port collision when many services are opening ports in the node.
Once allocated, these are create as Environment Variables in the service process, something like: Fabric_Endpoint_<EndpointName> : port
When you create the Listeners, they are responsible to open the ports, generally using the ports allocated via Endpoints, but not prevents you creating a custom listener to Open any port (If running with enough privilege to do so)
CreateServiceRemotingListener(context) creates the default listeners
EndpointResourceName setting tell which endpoint to be used by a listener, if not defined, DefaultEndpointResourceName setting is used as the default Endpoint, the default value is "ServiceEndpoint"
What I am not sure to answer right now is: if EndpointResourceName is not found, it uses DefaultEndpointResourceName, I assume so, need to check the code to confirm that.
When multiple listeners are using the same port, they generally have a path to identify each of them, something like: tcp://127.0.0.1:1234/endpointpath
I have Spring Boot Camel application where rest apis are exposed using camel-restlet
Sample route
#Component
public class AppRoute extends RouteBuilder{
public void configure(CamelContext context){
from("restlet:employee?restletMethods=GET").log("${body}");
}
}
The App runs perfect ( spring-boot:run ). but am unable to locate under which path the API is exposed. Log has no information.
Every API i hit returns 404. Log shows the route has been started. Under which path is it running. And how do I change it?
Note: Please dont suggest any XML based configuration. Anything that I can put under #Configuration would be perfect
I would go with the Rest DSL which is supported by the camel-restlet component like this
restConfiguration().component("restlet").port(8080);
rest("/rest")
.get("employee")
.route().log("${body}")
.endRest();
And this route will listen to the following url
http://localhost:8080/rest/employee
EDIT:
I guess you could do something like without using the Rest DSL
String host = InetAddress.getLocalHost().getHostName();
from("restlet:http://" + host + contextPath + "/employee?restletMethods=GET").log("${body}")
The port and context path are configurable with the following properties
camel.component.restlet.port=8686
server.servlet.context-path=/my-path
The context path can be injected in the routeBuilder with
#Value("${server.servlet.context-path}")
private String contextPath;
According to the documentation, the format of the URI in a restlet endpoint definition should be the following:
restlet:restletUrl[?options]
Where restletUrl should have the following format:
protocol://hostname[:port][/resourcePattern]
So in your case you could define the URI in the following way:
from("restlet:http://localhost/employee?restletMethods=GET")
This should make the endpoint available under the following URL:
http://localhost/employee
Which you can test e.g. in a web browser.
Use the first of the three configuration methods described here:
https://restlet.com/open-source/documentation/javadocs/2.0/jee/ext/org/restlet/ext/servlet/ServerServlet.html
You should be able to customize it using the Component:
https://restlet.com/open-source/documentation/javadocs/2.0/jee/api/org/restlet/Component.html?is-external=true
See in particular setServers() methods (or XML equivalent) to change the hostname and port.
I am trying to connect to spring vault using role based authentication (spring boot project).
As per documentation, I should be able to connect to spring vault only using approle (pull mode). However, I am getting secrect-id missing exception on application start up.
http://cloud.spring.io/spring-cloud-vault/single/spring-cloud-vault.html#_approle_authentication
When I pass, secret-id also, I am able to connect and properties/values are getting correctly autowired.
Is there any way I can connect with vault using "token + role/role-id" and spring generate secret-id for me automatically at run time using mentioned info.
spring.cloud.vault:
scheme: http
host: <host url>
port: 80
token : <token>
generic.application-name: vault/abc/pqr/test
generic.backend: <some value>
generic.default-context: vault/abc/pqr/test
token: <security token>
authentication: approle
app-role:
role-id: <role-id>
POM:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-starter-config</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
Please let me know in case any other info is required.
Update
#mp911de, I tried as per your suggestion, however spring-cloud-vault is picking properties set in bootstrap.yml and not one set inside "onApplicationEvent" and thus solution is not working. I tried setting property by "System.setProperty" method but that event didn't worked.
However, if I am setting properties in main before run method, it is working as expected. But I need to load application.properties first (need to pick some configuration from there) and thus don't want to write logic there.
Is there anything wrong in my approach ??
#Component public class LoadVaultProperties implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
private RestTemplate restTemplate = new RestTemplate();
#Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
try {
String roleId = getRoleIdForRole(event); //helper method
String secretId = getSecretIdForRoleId(event); //helper method
Properties properties = new Properties();
properties.put("spring.cloud.vault.app-role.secret-id", secretId);
properties.put("spring.cloud.vault.app-role.role-id", roleId);
event.getEnvironment().getPropertySources().addFirst(new PropertiesPropertySource(
PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME, properties));
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
Spring Vault's AppRole authentication supports two modes but not the pull mode:
Push mode in which you need to supply the secret_id
Authenticating without a secret_id by just passing role_id. This mode requires the role to be created without requiring the secret_id by setting bind_secret_id=false on role creation
Pull mode as mention in the Vault documentation requires the client to know about the secret_id, obtained from a wrapped response. Spring Vault does not fetch a wrapped secret_id but I think that would be a decent enhancement.
Update: Setting system properties before application start:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
System.setProperty("spring.cloud.vault.app-role.role-id", "…");
System.setProperty("spring.cloud.vault.app-role.secret-id", "…");
SpringApplication.run(MyApplication.class, args);
}
References:
Vault documentation on AppRole creation
Spring Cloud Vault documentation on AppRole authentication.
I am struggling to make the .Net backend of Mobile Services use the correct connectionString. When I publish the service I select the correct connection string for "MS_TableConnectionString". If I check the web.config on the server (via FTP) I see what I would expect:
web.config on server:
<connectionStrings>
<add name="MS_TableConnectionString" connectionString="Data Source=tcp:[ServerAddress].database.windows.net,1433;Initial Catalog=[MyMobileService_db];Integrated Security=False;User ID=[correctUserName];Password=[CorrectPassword];Connect Timeout=30;Encrypt=True" providerName="System.Data.SqlClient" />
In my context it is configured to use a connection string called MS_TableConnectionString:
private const string connectionStringName = "Name=MS_TableConnectionString";
public MyMobileServiceContext() : base(connectionStringName)
{
Schema = "MyMobileService";
}
To see what connection string is actually being used I added this to an example controller:
Example Client Code:
public class ExampleController : ApiController
{
MyMobileServiceContext context;
public ApiServices ApiServices { get; set; }
public ExampleController()
{
context = new MyMobileServiceContext();
}
public async Task<IHttpActionResult> PostExample(ExampleItem item)
{
ApiServices.Log.Warn("ConnectionString: " + context.Database.Connection.ConnectionString);
...
}
And when I look at the Log Entry on Mobile Services I see a different UserName and Password:
[2014-04-15T12:26:33.1410580Z] Level=Warn, Kind=Trace, Category='PostExampleItem', Id=00000000-0000-0000-0000-000000000000, Message='ConnectionString: Data Source=[SameServerAddress].database.windows.net;Initial Catalog=[SameDatabaseName];User ID=[DifferentUserName];Password=[DifferentPassword];Asynchronous Processing=True;TrustServerCertificate=False;'
The different username and password are the same as I see in the original .PublishSettings file that I downloaded under the name of SQLServerDBConnectionString but I have no idea where this is stored on the server?
Because of the different username and password I see the following exception in the log:
[2014-04-15T13:18:11.2007511Z] Level=Error, Kind=Trace, Category='App.Request', Id=d7ec6d25-f3b7-4e88-9024-217be40ae77f, Exception=System.Data.Entity.Core.ProviderIncompatibleException: An error occurred accessing the database. This usually means that the connection to the database failed. Check that the connection string is correct and that the appropriate DbContext constructor is being used to specify it or find it in the application's config file. See http://go.microsoft.com/fwlink/?LinkId=386386 for information on DbContext and connections. See the inner exception for details of the failure. ---> System.Data.Entity.Core.ProviderIncompatibleException: The provider did not return a ProviderManifestToken string. ---> System.Data.SqlClient.SqlException: Cannot open database "master" requested by the login. The login failed.
Login failed for user '[DifferentUserName]'.
This session has been assigned a tracing ID of '[GUID]'. Provide this tracing ID to customer support when you need assistance.
Any help would be much appreciated as at the moment I am having to hard code the whole connection string in the constructor of the Context to make it work.
Thanks
F
UPDATE: 15th April 2014 15:23
I deleted all my publisher profiles and created a copy of the original .PublishSettings file. From this I deleted all but one profile. I then deleted the SQLDBConnectionString attribute to confirm that it is not because I was sending this that was causing the problem. The result was no change, it is still using the DifferentUserName and Password so it must be reading it from the server somewhere.
We have a hole at the moment in that we pick up the connection string from the portal yet don't expose the ability to set or modify connection strings there.
The work-around is to set an application setting in the portal and then use that in your code using the ApiServices class, something like this (in your controller)
string connectionString = this.Services.Settings["YourConnectionStringAsAppSetting"];
I know it is confusing... we'll make it easier to access and modify the connection strings.
Henrik