Application Metadata
Spring Boot provides support for bundling an application's configuration properties within the executable jar. In this section we also discuss bundling an application's configuraiton properties as a label in a container image. This packaging of metadata allows Data Flow to present an application's configuration properties in the UI and Shell. Whitelisting of application configuration properties in an important step in this process so that most important application properties to configure will be presented first by the UI and Shell.
The outline of the steps to create, whitelist and package an application's configuration properties in an executable jar and also as metadata in a container image.
The common steps between packaging configuration properties in an executable jar and container image are
- Add Boot's Configuration processor to your pom.xml as explained here
These configuration properties are packaged in a configuration metadata file.
Boot's auto-configuration libraries already contain these metadata files and is what enabled you to configure Boot's Common Application Properties, such as server.port as well as families of other properties specific to a given technology, for example JMX and logging that use the the prefix spring.jmx
and logging
.
- Whitelist properties that you want to have preferred visibility in the UI and Shell as explained here instead of viewing all of Boot's Common Application Properties.
- Configure the
spring-cloud-app-starter-metadata-maven-plugin
, as explained here, to generate metadata jar as well as theMETA-INF/spring-configuration-metadata-encoded.properties
file used to create Container Image labels.
The additional steps to create a label in a container image with the application metadata and whitelist are
- Configure the
properties-maven-plugin
, as explained here, to load theMETA-INF/spring-configuration-metadata-encoded.properties
as maven properties. Among others it will load theorg.springframework.cloud.dataflow.spring.configuration.metadata.json
property. - Extend the
jib-maven-plugin
(ordocker-maven-plugin
) configuration, as shown here, to add the content of theorg.springframework.cloud.dataflow.spring.configuration.metadata.json
property as a Container Image Label
Boot Configuration processor
For your own applications, you can easily generate your own configuration metadata file from items annotated with @ConfigurationProperties
by using the spring-boot-configuration-processor
library. This library includes a Java annotation processor which is invoked as your project is compiled.
The generated configuration metadata files are then stored inside the uber-jar under META-INF/spring-configuration-metadata.json
To use the processor, include a dependency on spring-boot-configuration-processor.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Whitelisting properties
In addition to this file, Data Flow can make use of an additional file that whitelists application properties that are the most important, usually your own application's @ConfigurationProperties
.
Without using a whitelist, the UI and Shell will present the hundreds of Common Application Properties resulting in a poor user experience.
The whitelist
file lists the primary application properties so that the shell and the UI can display them first when presenting options through TAB completion or in drop-down boxes.
To whitelist application properties, create a file named spring-configuration-metadata-whitelist.properties
under the META-INF
resource directory. There are two property keys that can be used inside this file. The first key is named configuration-properties.classes
. The value is a comma separated list of fully qualified @ConfigurationProperty
class names. The second key is configuration-properties.names
, whose value is a comma-separated list of property names. This can contain the full name of the property, such as server.port
, or a partial name to whitelist a category of property names, such as spring.jmx
.
The Spring Cloud Stream application starters are a good place to look for examples using whitelists. The following example comes from the file sink’s spring-configuration-metadata-whitelist.properties
file:
configuration-properties.classes=org.springframework.cloud.stream.app.file.sink.FileSinkProperties
If we also want to add server.port
to be white listed, it would become the following line:
configuration-properties.classes=org.springframework.cloud.stream.app.file.sink.FileSinkProperties
configuration-properties.names=server.port
Metadata Artifacts
Including the application metadata inside the uber-jar has the downside of needing to download a potentially very large uber-jar just to inspect the metadata. Creating a separate uber-jar, also called a 'companion artifact', that contains only the application metadata has several advantages which include:
- Being much smaller in size. The companion artifact is usually a few kilobytes, as opposed to megabytes for the actual application. Consequently, they are quicker to download, allowing quicker feedback when using Data Flow's UI and shell.
- A smaller size is also a benefit when being used in resource constrained environments, such as Cloud Foundry, where the local disk size is often very small.
- For environments that do not deal with Spring Boot uber jars directly, for example container based runtimes such as Kubernetes, this is the only way to provide metadata about the properties supported by the application.
Creating Metadata Artifacts
You can go a step further in the process of describing the main properties that your stream or task app supports by creating a additional application metadata. Depending on the runtime, the metadata is packaged either as an additional companion jar artifact or a configuration label inside the application's container image.
Configure App Starter Metadata Maven Plugin
The spring-cloud-app-starter-metadata-maven-plugin
plugin helps to prepare all necessary metadata files for your application:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-app-starter-metadata-maven-plugin</artifactId>
<version>2.0.0.RELEASE</version>
<executions>
<execution>
<id>aggregate-metadata</id>
<phase>compile</phase>
<goals>
<goal>aggregate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
This plugin needs to be used in addition to the spring-boot-configuration-processor
that creates the spring-configuration-metadata.json
files. Be sure to configure both of them.
Metadata Jar File
For the uber-jar packaged applications an additional metadata-jar is provided, that contains the Spring boot JSON file about configuration properties metadata and the whitelisting file described in the previous section. The following example shows the contents of such an artifact, for the canonical log sink:
$ jar tvf log-sink-rabbit-1.2.1.BUILD-SNAPSHOT-metadata.jar
373848 META-INF/spring-configuration-metadata.json
174 META-INF/spring-configuration-metadata-whitelist.properties
Note that the spring-configuration-metadata.json
file is quite large. This is because it contains the concatenation of all the properties that are available at runtime to the log sink (some of them come from spring-boot-actuator.jar, some of them come from spring-boot-autoconfigure.jar, some more from spring-cloud-starter-stream-sink-log.jar, and so on). Data Flow always relies on all those properties, even when a companion artifact is not available, but here all have been merged into a single file.
The spring-cloud-app-starter-metadata-maven-plugin
plugin generates ready to use application metadata.jar artifact. Just make sure the plugin is configured in your application's pom.
Metadata Container Image Label
For the container image packaged applications, the whitelisted properties are used at compile time to extract only the whitelisted configuration metadata form the spring-configuration-metadata.json
file and insert it as a configuration label in the generated application container image. Therefore the produced container image contains the application itself as well as the whitelisted configuration metadata for it. Not need for a companion artifact.
At compile time the spring-cloud-app-starter-metadata-maven-plugin
generates a META-INF/spring-configuration-metadata-encoded.properties
file with a single property inside: org.springframework.cloud.dataflow.spring.configuration.metadata.json
. The property value is the strigified, whitelisted subset of the configuration metadata.
org.springframework.cloud.dataflow.spring.configuration.metadata.json={\n \"groups\": [{\n \"name\": \"log\",\n \"type\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\",\n \"sourceType\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\"\n }],\n \"properties\": [\n {\n \"name\": \"log.expression\",\n \"type\": \"java.lang.String\",\n \"description\": \"A SpEL expression (against the incoming message) to evaluate as the logged message.\",\n \"sourceType\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\",\n \"defaultValue\": \"payload\"\n },\n {\n \"name\": \"log.level\",\n \"type\": \"org.springframework.integration.handler.LoggingHandler$Level\",\n \"description\": \"The level at which to log messages.\",\n \"sourceType\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\"\n },\n {\n \"name\": \"log.name\",\n \"type\": \"java.lang.String\",\n \"description\": \"The name of the logger to use.\",\n \"sourceType\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\"\n }\n ],\n \"hints\": []\n}
Properties Maven Plugin
To turn this property into a Docker label, first we need to load it as maven property using the properties-maven-plugin
plugin:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>apps-metadata</id>
<phase>process-classes</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>${project.build.outputDirectory}/META-INF/spring-configuration-metadata-encoded.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
Container Maven Plugin
Then with the help of the fabric8:docker-maven-plugin
or jib
maven plugins insert the org.springframework.cloud.dataflow.spring.configuration.metadata.json
property into a Docker label with the same name:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.0.0</version>
<configuration>
<from>
<image>springcloud/openjdk</image>
</from>
<to>
<image>springcloudstream/${project.artifactId}</image>
<tags>
<tag>3.0.0.BUILD-SNAPSHOT</tag>
</tags>
</to>
<container>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<format>Docker</format>
<labels>
<org.springframework.cloud.dataflow.spring-configuration-metadata.json>
${org.springframework.cloud.dataflow.spring.configuration.metadata.json}
</org.springframework.cloud.dataflow.spring-configuration-metadata.json>
</labels>
</container>
</configuration>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.33.0</version>
<configuration>
<images>
<image>
<name>springcloudstream/${project.artifactId}:2.1.3.BUILD-SNAPSHOT</name>
<build>
<from>springcloud/openjdk</from>
<volumes>
<volume>/tmp</volume>
</volumes>
<labels>
<org.springframework.cloud.dataflow.spring-configuration-metadata.json>
${org.springframework.cloud.dataflow.spring.configuration.metadata.json}
</org.springframework.cloud.dataflow.spring-configuration-metadata.json>
</labels>
<entryPoint>
<exec>
<arg>java</arg>
<arg>-jar</arg>
<arg>/maven/log-sink-kafka.jar</arg>
</exec>
</entryPoint>
<assembly>
<descriptor>assembly.xml</descriptor>
</assembly>
</build>
</image>
</images>
</configuration>
</plugin>
Using Application Metadata
Once you have the application metadata generated either as a separate, companion artifact or embedded in the application container image as a configuration label, you can make the Data Flow system aware of it so that it can be used.
Using Metadata Jar files
When registering a single app with app register, you can use the optional --metadata-uri
option in the shell, as follows:
dataflow:>app register --name log --type sink
--uri maven://org.springframework.cloud.stream.app:log-sink:2.1.0.RELEASE
--metadata-uri maven://org.springframework.cloud.stream.app:log-sink:jar:metadata:2.1.0.RELEASE
When registering several files by using the app import command, the file should contain a <type>.<name>.metadata
line in addition to each <type>.<name>
line. Strictly speaking, doing so is optional (if some apps have it but some others do not, it works), but it is best practice.
The following example shows a uber-jar app, where the metadata artifact is being hosted in a Maven repository (retrieving it through http://
or file://
would be equally possible).
source.http=maven://org.springframework.cloud.stream.app:log-sink:2.1.0.RELEASE
source.http.metadata=maven://org.springframework.cloud.stream.app:log-sink:jar:metadata:2.1.0.RELEASE
Using Metadata Container Image Labels
When registering a single docker app with app register, the Data Flow server will automatically check for metadata in the org.springframework.cloud.dataflow.spring-configuration-metadata.json
configuration label:
dataflow:>app register --name log --type sink --uri container:springcloudstream/log-sink-rabbit:2.1.13.RELEASE
Configurations specific for each target Container Registry provider/instance.
For a private container registry with volume mounted secretes, the registry configurations are automatically inferred from the secrets.
In addition the spring.cloud.dataflow.container.registry-configurations
has properties that let you explicitly configure multiple container registries like this:
- Docker Hub - public Docker Hub registry
- spring.cloud.dataflow.container.registry-configurations[default].registry-host=registry-1.docker.io
- spring.cloud.dataflow.container.registry-configurations[default].authorization-type=dockeroauth2
- spring.cloud.dataflow.container.registry-configurations[default].extra[registryAuthUri]=https://auth.docker.io/token?service=registry.docker.io&scope=repository:{repository}:pull&offline_token=1&client_id=shell
spring:
cloud:
dataflow:
container:
registry-configurations:
default:
registry-host: registry-1.docker.io
authorization-type: dockeroauth2
extra:
'registryAuthUri': 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:{repository}:pull&offline_token=1&client_id=shell'
This registry is used by default, if the image name doesn't provide the registry host prefix. The public Docker hub repositories don't require username/password authorization. The credentials though will be required for the private Docker Hub repositories.
- spring.cloud.dataflow.container.registry-configurations[myjfrog].registry-host=springsource-docker-private-local.jfrog.io
- spring.cloud.dataflow.container.registry-configurations[myjfrog].authorization-type=basicauth
- spring.cloud.dataflow.container.registry-configurations[myjfrog].user=[artifactory user]
- spring.cloud.dataflow.container.registry-configurations[myjfrog].secret=[artifactory encrypted password]
spring:
cloud:
dataflow:
container:
registry-configurations:
myjfrog:
registry-host: springsource-docker-private-local.jfrog.io
authorization-type: basicauth
user: [artifactory user]
secret: [artifactory encrypted password]
Note: you need to create an Encrypted Password in JFrog.
- spring.cloud.dataflow.container.registry-configurations[myecr].registry-host=283191309520.dkr.ecr.us-west-1.amazonaws.com
- spring.cloud.dataflow.container.registry-configurations[myecr].authorization-type=awsecr
- spring.cloud.dataflow.container.registry-configurations[myecr].user=[your AWS accessKey]
- spring.cloud.dataflow.container.registry-configurations[myecr].secret=[your AWS secretKey]
- spring.cloud.dataflow.container.registry-configurations[myecr].extra[region]=us-west-1
- spring.cloud.dataflow.container.registry-configurations[myecr].extra[registryIds]=283191309520
spring:
cloud:
dataflow:
container:
registry-configurations:
myecr:
registry-host: 283191309520.dkr.ecr.us-west-1.amazonaws.com
authorization-type: awsecr
user: [your AWS accessKey]
secret: [your AWS secretKey]
extra:
region: us-west-1
'registryIds': 283191309520
In addition to the credentials you have to provide the registry's region
through the extra properties configuration (e.g. .extra[region]=us-west-1
).
Optionally you can set the registry IDs via the .extra[registryIds]
property as a comma separated value.
- spring.cloud.dataflow.container.registry-configurations[myazurecr].registry-host=tzolovazureregistry.azurecr.io
- spring.cloud.dataflow.container.registry-configurations[myazurecr].authorization-type=basicauth
- spring.cloud.dataflow.container.registry-configurations[myazurecr].user=[your Azure registry username]
- spring.cloud.dataflow.container.registry-configurations[myazurecr].secret=[your Azure registry access password]
spring:
cloud:
dataflow:
container:
registry-configurations:
myazurecr:
registry-host: tzolovazureregistry.azurecr.io
authorization-type: basicauth
user: [your Azure registry username]
secret: [your Azure registry access password]
- spring.cloud.dataflow.container.registry-configurations[harbor].registry-host=demo.goharbor.io
- spring.cloud.dataflow.container.registry-configurations[harbor].authorization-type=dockeroauth2
- spring.cloud.dataflow.container.registry-configurations[harbor].user=admin
- spring.cloud.dataflow.container.registry-configurations[harbor].secret=Harbor12345
spring:
cloud:
dataflow:
container:
registry-configurations:
harbor:
registry-host: demo.goharbor.io
authorization-type: dockeroauth2
user: admin
secret: Harbor12345
The Harbor Registry configuration uses the OAuth2 Token authorization similar to DockerHub but on different registryAuthUri
. Later is automatically resolved at bootstrap, but you can override it like this:
- spring.cloud.dataflow.container.registry-configurations[harbor].extra[registryAuthUri]=https://demo.goharbor.io/service/token?service=harbor-registry&scope=repository:{repository}:pull
spring:
cloud:
dataflow:
container:
registry-configurations:
harbor:
extra:
'registryAuthUri': https://demo.goharbor.io/service/token?service=harbor-registry&scope=repository:{repository}:pull
- Overriding/Augmenting Volume Mounted Secrets
Properties can override or augment the configurations obtained via the registry secrets.
For example if you have created a Secret to access a registry running at address: my-private-registry:5000
, then you can disable SSL verification for this registry like this:
- spring.cloud.dataflow.container.registry-configurations[myregistry].registry-host=my-private-registry:5000
- spring.cloud.dataflow.container.registry-configurations[myregistry].disableSslVerification=true
spring:
cloud:
dataflow:
container:
registry-configurations:
myregistry:
registry-host: my-private-registry:5000
disableSslVerification: true
This is handy testing registries with self-signed Certificates.