Uncategorized

Configure a Spring Boot Application

Gain more understanding on how to build cloud native applications.

Learning outcomes

After completing the lab, you will be able to:

  • Summarize some of the ways to configure a Spring application
  • Use environment variables to configure an application running
    locally
  • Use cf logs to view the logs of an application running on
    Tanzu Application Service
  • Use environment variables to configure an application running on
    Tanzu Application Service
  • Explain when to use the CLI versus a manifest for
    application configuration

12 factor applications

If you completed the
Cloud Native Development learning path,
you should already be familiar with 12 factors
guidelines.

In practice, you will have to decide which ones to use and if it makes
sense to adhere to all of them.

In the previous lab, you covered the first two factors by setting up
your codebase in GitHub and using Gradle to explicitly declare your
dependencies.

This lab will focus on the third factor:
storing configuration in
the environment.

There are many options for how to externalize configuration for a cloud
native application.
Our first choice is to use environment variables.

Another choice, externalizing configuration using a Config Server,
will be introduced in later lessons.

Get started

  1. Review the
    Environment
    slides.

  2. You must have completed (or fast-forwarded to) the
    Deploying a Spring Boot Application lab.
    You must have your pal-tracker application associated with the
    spring-boot-solution codebase deployed and running on
    Tanzu Application Service.

  3. In a terminal window,
    make sure you start in the ~/workspace/pal-tracker directory.

  4. Pull in pre-authored tests to your codebase:

    git cherry-pick configuration-start
    

    You will see the following tests:

    [main 95c54c1] Add tests for configuration lab
    Author: <redacted>
    Date: Wed Jan 8 11:09:04 2020 -0500
    3 files changed, 67 insertions(+)
    create mode 100644 src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java
    create mode 100644 src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java
    create mode 100644 src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java
    

    Your goal is to get your tests to pass by the end of the lab,
    as well as deploy the updated code to Tanzu Application Service.

If you get stuck

If you get stuck within this lab,
you can either
view the solution,
or you can
fast-forward
to the configuration-solution tag.

Add test dependencies

Since you now have added tests to your codebase,
you need to set up the build to use a testing framework.
You will use Junit 5.

Add the following to your build.gradle file:

  1. Add the following line to the dependencies closure in your
    build.gradle file to enable the necessary test dependencies:

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    
  2. Add the following closure to the end of the build.gradle
    file:

    test {
    useJUnitPlatform()
    }
    
  3. Take a look at the solution if you get stuck

    git show configuration-solution:build.gradle
    

Environment variables

You will use the environment variable mechanism to provide configuration
to a process.

It does not matter if your application is written in
Java,
Ruby,
Golang, or
some other language,
they all have the capability of accessing environment variables during
their process start up time.

Tanzu Application Service can be configured to provide your application
with environment variables when it is executed.
In fact, it provides
several environment variables
that your application can use to uniquely identify its execution
environment.

You will extend your application to:

  • Configure the “hello” message from the environment.
  • Add a RESTful endpoint that returns some of the system-provided
    variables.

Externalize configuration

Spring Boot includes a mechanism to get configuration values.

  1. Extract the hello message to a field in the controller.

  2. Create a constructor that accepts a message parameter and assigns
    it to the field.

  3. Annotate that constructor parameter with @Value.

    @Value takes a specific format to reference an environment
    variable, for example:

    @Value("${welcome.message}")
    

    Take time to read about annotation-based configuration
    if you are new to Spring or if you need a refresher.
    There is more
    detailed documentation
    on using Spring expressions in bean definitions and annotations,
    if you are interested.

  4. If you get stuck,
    review the solution:

    git show configuration-solution:src/main/java/io/pivotal/pal/tracker/WelcomeController.java
    

Verify it works

Run your app with the bootRun Gradle task.
It will fail to start because Spring Boot is unable to find a value for
the requested variable.

From now on the WELCOME_MESSAGE environment variable must be set to
run your app.
One way to do this is to add the environment variable assignment before
the Gradle command.

WELCOME_MESSAGE=howdy ./gradlew bootRun

Notice you are using a different welcome message than the previous
statically configured message.

Why is the @Value name welcome.message?

You may be wondering why the name given in the @Value annotation
is welcome.message and not the name of the environment variable,
WELCOME_MESSAGE.

Spring can take configuration information from
many different sources.
In order to support this, and to have a consistent way of naming
configuration items from different sources, Spring uses what is known as
relaxed binding.
Among other things, this defines the rules that convert environment
variable names, such as WELCOME_MESSAGE,
to the common format represented by welcome.message.
This means that you could later change the source of the welcome message
text to, for example, a Java property without having to modify the code.

Manage local configuration

Running your application like this with environment variables in the
command line every time is tedious.

You can configure your Gradle build
to make this easier.

  1. Extend the bootRun and test tasks to set the required
    environment variable by adding the following to the end of your
    build.gradle file:

    bootRun.environment([
    "WELCOME_MESSAGE": "howdy",
    ])
    test.environment([
    "WELCOME_MESSAGE": "Hello from test",
    ])
    
  2. You can review the solution here:

    git show configuration-solution:build.gradle
    
  3. This will instruct Gradle to set that environment variable for you
    when you run the bootRun task, so you can go back to just using:

    ./gradlew bootRun
    

This has the added benefit of documenting required environment variables
and supporting multiple operating systems.

Tanzu Application Service environment variables

When Tanzu Application Service starts your application,
it will be running with a port provided by the runtime environment.
It will also have an instance ID set which will be distinct for
each instance, if the app has multiple instances.

Create an endpoint to see some of that information.

  1. Create another controller called EnvController.

  2. Annotate it with @RestController.

  3. Implement a method called getEnv as described in the
    EnvControllerTest.

  4. Annotate the getEnv method with @GetMapping("/env").

  5. Declare @Value annotated constructor arguments to be populated
    with values of the following environment variables:

    • PORT
    • MEMORY_LIMIT
    • CF_INSTANCE_INDEX
    • CF_INSTANCE_ADDR
  6. Add a default value to the variables above so that the app will
    still run correctly locally.
    For example:

    @Value("${cf.instance.index:NOT SET}") String cfInstanceIndex,
    
  7. Implement getEnv so that the test passes.

    Take a look at our solution if you get stuck:

    git show configuration-solution:src/main/java/io/pivotal/pal/tracker/EnvController.java
    
  8. Run your app and navigate to
    localhost:8080/env.
    Make sure you are getting a response.
    The variables will show NOT SET,
    which ensures the controller is valid.

  9. Navigate to localhost:8080/.
    Make sure the root route still has the welcome message you set above.

  10. Make sure all your tests pass before moving on by running the
    Gradle build task.

Deploy

  1. Push the app to Tanzu Application Service.
    The app will fail to start and will be reported as a crash.

    To see what the problem is, use the cf logs command.
    Logging in Tanzu Application Service works by taking everything
    from standard output and error
    (System.out and System.err in Java)
    and storing it in a small buffer.

  2. View the logs from your app through the cf logs
    command.
    By default, this will show you a stream of logs.
    To see logs from the recent past, use the --recent flag.

  3. Check the output and find the problem.
    You will see something about Spring not having a value for the
    welcome message.

  4. To fix this issue, use the cf set-env command to set the
    WELCOME_MESSAGE environment variable to
    Howdy from Tanzu Application Service in your app’s container on
    Tanzu Application Service.
    The CLI will give you a helpful tip:

    TIP: Use 'cf restage pal-tracker' to ensure your env variable changes take effect
    

    Actually, the CLI does not know whether you need to restage,
    or simply restart.
    Restaging is necessary if the environment variable is used in the
    buildpack’s compile stage.
    Restarting is sufficient if the environment variable is only used by
    the application at runtime.
    Restarting is usually faster,
    since the droplet does not need to be re-created.
    In this case, restarting is sufficient, so save yourself some time:

    cf restart pal-tracker
    
  5. Once your app is running on Tanzu Application Service,
    navigate to the root endpoint and check that it displays
    Howdy from Tanzu Application Service.

  6. Navigate to the /env endpoint and verify actual values are
    displayed instead of NOT SET.

Manifest

The initial deployment failure could have been avoided by using a
manifest.yml.

This file documents requirements for the application and configures
variables in the
environment
such as the WELCOME_MESSAGE and setting the JDK version to use when
running the application.

A Tanzu Application Service manifest is also an appropriate place to
describe Backing Services dependencies that your app requires.
You will see more about backing services in the
Backing Services and Database Migrations and
Data Access labs.

  1. Checkout the solution manifest.yml file:

    git checkout configuration-solution manifest.yml
    
  2. Notice the WELCOME_MESSAGE must be
    Hello from Tanzu Application Service.

To see the effect of using a manifest:

  1. Delete your app with the cf delete command.

  2. Push your app using your new manifest with push.
    The push command will automatically use your manifest file if you
    push from the same directory.

  3. Visit the root endpoint and /env endpoint.
    Be aware that the app URL has changed as a result of the
    random-route: true line in the manifest.

  4. Make a commit and push your code to GitHub once you are sure
    everything is working.

Wrap up

Review the
Tanzu Application Service Environment
slides about environment variable configuration on
Tanzu Application Service.

Now that you have completed the lab, you should be able to:

  • Summarize some of the ways to configure a Spring application
  • Use environment variables to configure an application running
    locally
  • Use cf logs to view the logs of an application running on Cloud
    Foundry
  • Use environment variables to configure an application running on
    Tanzu Application Service
  • Explain when to use the command line interface (CLI)
    versus a manifest for application configuration

Extras

Tanzu Application Service command line interface (CLI)

If you have additional time, explore the cf CLI by reading the
documentation or by
running cf help and cf <command> --help.

Explore the pal-tracker application environment

  1. Review the
    cf env command.

  2. Run the cf env command for the pal-tracker application.
    Review the system provided environment variables,
    as well as the user provided environment variables.

Explore the pal-tracker container

  1. Review the
    cf ssh command.

  2. Run the cf ssh command to create a secure shell connection to the
    only running instance (index 0) of your pal-tracker.

  3. Run the following and review the running pal-tracker container:

    • Tanzu Application Service environment variables:

      env | grep CF_

      Notice that Tanzu Application Service sets the IP address,
      ports,
      and host name (GUID) of the containers for you.

    • Review the running processes in the container:

      ps -ef

      Notice the Java process,
      how did Tanzu Application Service know what the run command is
      for the pal-tracker application?

      Notice the envoy and healthcheck processes.
      You will see discussion of these in later labs.

    • Review the following directory and its subdirectories:

      ll app/

      Where did the directory and files originate from?