Skip to content

Instantly share code, notes, and snippets.

@chriswk
Last active March 30, 2021 08:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriswk/f5a4e533ecde8c775efbd1441d4fa7ff to your computer and use it in GitHub Desktop.
Save chriswk/f5a4e533ecde8c775efbd1441d4fa7ff to your computer and use it in GitHub Desktop.

Bootstrap Unleash

We just released unleash-client-java 4.2.1. This version includes a new way to bootstrap Unleash using a JSON string matching the /api/client/features format.

First; update your Unleash dependency to 4.2.1

  • Maven
    <dependency>
        <groupId>no.finn.unleash</groupId>
        <artifactId>unleash-client-java</artifactId>
        <version>4.2.1</version>
    </dependency>  
  • Gradle
    implementation("no.finn.unleash:unleash-client-java:4.2.1")

Priorities/Load order

The Unleash client already performs regular backups of latest known state from the API. On startup, if a backup file is available, that will take priority over the Bootstrap provider. It is not currently possible to override this priority. If there's no backup file or it currently contains no features, the Unleash client proceeds to try loading state using the BootstrapHandler.

Using a file on disk

By default, Unleash configures a ToggleBootstrapFileProvider which checks the UNLEASH_BOOTSTRAP_FILE environment variable.

This variable can should be set to the location of a downloaded backup of your feature toggles.

A minimal Dockerfile to prebake latest known state from your Unleash instance at Docker image build time could look something like this:

FROM openjdk:slim

RUN apt-get update && apt-get -y install wget
WORKDIR /app
ENV UNLEASH_BOOTSTRAP_FILE /app/unleash-feature-toggles.json

# Your normal java configuration here so you cache as much as possible
# The wget for the backup file is not cached, so you'd like that to happen as late as possible


RUN wget -O /app/unleash-feature-toggles.json [YOUR_UNLEASH_INSTANCE]/api/client/features

# Start your application as you'd normally do

Using a file on classpath

The provided ToggleBootstrapFileProvider also supports loading from Java classpath, so if you're baking your feature toggle state into a fatjar that's what you'd use. Say your file is located at /toggles/bootstrap.json inside the jar, you'd set UNLEASH_BOOTSTRAP_FILE to classpath:/toggles/bootstrap.json and the SDK will handle the rest.

Using S3

To avoid bloating the core library with extra dependencies, only the file provider, which can use java core APIs are included. However, since the API for writing a custom provider is public and requires only the implementation of a single method returning a String, we can also provide toggles with other storage strategies.

For S3, add the S3 library to your classpath, if you're using maven that's

S3 Dependency

Maven
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>[version]</version> <!-- At the time of writing 2.16.29 -->
</dependency>
Gradle
implementation("software.amazon.awssdk:s3:${aws_version}") // at the time of writing 2.16.29

And then implement a custom ToggleBootstrapProvider. Something similar to

package ai.getunleash.aws;

import no.finn.unleash.repository.ToggleBootstrapProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.S3Exception;

import java.io.IOException;

public class ToggleBootstrapS3Provider implements ToggleBootstrapProvider {
    private final S3Client s3;

    public ToggleBootstrapS3Provider(S3Client s3) {
        this.s3 = s3;
    }

    @Override
    public String read() {
        try {
            GetObjectRequest featureToggleRequest = GetObjectRequest.builder()
                .key("KEY_OF_YOUR_BACKUP_FILE")
                .bucket("YOUR_BUCKET")
                .build();
            ResponseBytes<GetObjectResponse> featureToggleResponse = s3
                .getObjectAsBytes(featureToggleRequest);
            return featureToggleResponse.asUtf8String();
        } catch (S3Exception ioException) {
            LOGGER.warn("Failed to read file from aws", ioException);
        }
        return null;
    }
}

and then use this when configuring Unleash

import no.finn.unleash.DefaultUnleash;

public class Example {
    public static void main(String[] args) {
        S3Client s3 = S3Client.builder().build();
        UnleashConfig config = UnleashConfig.builder()
                .toggleBootstrapProvider(new ToggleBootstrapS3Provider(s3))
                .build();
        Unleash unleash = new DefaultUnleash(config);
    }
}

Examples

Both examples include an UnleashSubscriber that listens to the TogglesBootstrapped event and logs the amount of toggles found in the bootstrap file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment