Friday, August 20, 2021

Cloud Build - CI/CD for a Java Project

In a previous blog post I went over the basics of what it takes to create a configuration for Cloud Build. This post will expand on it by creating a functional CI/CD pipeline for a java project using Cloud Build. Note that I am claiming the pipeline will be functional but far from optimal, a follow up post at some point will go over potential optimizations to the pipeline.


Continuous Integration

The objective of Continuous integration is to ensure that developers regularly merge quality code into a common place. The quality is ascertained using automation, which is where a tool like Cloud Build comes in during the CI process.

Consider a flow where developers work on feature branches and when ready send a pull request to the main branch

Now to ensure quality, checks should be run on the developers feature branch before it is allowed to be merged into the "main" branch. This means two things:

1. Running quality checks on the developers feature branch
2. Merges to main branch should not be permitted until checks are run.


Let's start with Point 1 - Running quality checks on a feature branch

Running quality checks on a feature branch

This is where integration of Cloud Build with the repo comes into place. I am using this repository - https://github.com/bijukunjummen/hello-cloud-build, to demonstrate this integration with Cloud Build. If you have access to a Google Cloud environment, a new integration of Cloud build build with a repository looks something like this:



Once this integration is in place, a Cloud Build "trigger" should be created to act on a new pull request to the repository:




Here is where the Cloud Build configuration comes into play, it specifies what needs to happen when a Pull Request is made to the repository. This is a Java based project with gradle as the build tool, I want to run tests and other checks, which is normally done through a gradle task called "check", a build configuration which does this is simple:




steps:
  - name: openjdk:11
    id: test
    entrypoint: "./gradlew"
    args: [ "check" ]

Onto the next objective - Merges to the main branch should not be allowed until the checks are clean

Merges to main branch only with a clean build

This is done on the repository side on github, through settings that look like this - 

The settings protects the "main" branch by only allowing in merges after the checks in the PR branch is clean. It also prevents checking in code directly to the main branch.


With these two considerations, checking the feature branch before merges are allowed, and allowing merges to "main" branch after checks should ensure that quality code should get into the "main" branch. 

Onto the Continuous Deployment side of the house. 


Continuous Deployment

So now presumably a clean code has made its way to the main branch and we want to deploy it to an environment. 

In Cloud Build this translates to a "trigger", that acts on commits to specific branches and looks like this for me:


and again the steps expressed as a Cloud Build configuration, has steps to re-run the checks and deploy the code to Cloud Run 


steps:
  - name: openjdk:11
    id: test
    entrypoint: "/bin/bash"
    args:
      - '-c'
      - |-
        ./gradlew check

  - name: openjdk:11
    id: build-image
    entrypoint: "/bin/bash"
    args:
      - '-c'
      - |-
        ./gradlew jib --image=gcr.io/$PROJECT_ID/hello-cloud-build:$SHORT_SHA
 
  - name: 'gcr.io/cloud-builders/gcloud'
    id: deploy
    args: [ 'run', 'deploy', "--image=gcr.io/$PROJECT_ID/hello-cloud-build:$SHORT_SHA", '--platform=managed', '--project=$PROJECT_ID', '--region=us-central1', '--allow-unauthenticated', '--memory=256Mi', '--set-env-vars=SPRING_PROFILES_ACTIVE=gcp', 'hello-cloud-build' ]

Here I am using Jib to create the image.

Wrapup


With this tooling in place, a developer flow looks like this. A PR triggers checks and shows up like this on the github side:


and once checks are complete, allows the branch to be merged in:


After merge the code gets cleanly deployed.


Tuesday, August 10, 2021

Google Cloud Build - Hello World

I have been exploring Google Cloud Build recently and this post is a simple introduction to this product. You can think of it as a tool that enables automation of deployments. This post though will not go as far as automating deployments, instead just covering the basics of what it involves in getting a pipeline going. A follow up post will show a continuous deployment pipeline for a java application. 

Steps

The basic steps to set-up a Cloud Build in your GCP project is explained here. Assuming that the Cloud Build has been set-up, I will be using this github project to create a pipeline.

Cloud pipeline is typically placed as a yaml configuration in a file named by convention as "cloudbuild.yaml". The pipeline is described as a series of steps, each step runs in a docker container and the name of the step points to the docker image. So for eg. a step which echo's a message looks like this:

Here the name "bash" points to the docker image named "bash" in docker hub

The project does not need to be configured in Google Cloud Build to run it, instead a utility called "cloud-build-local" can be used for running the build file. 


git clone git@github.com:bijukunjummen/hello-cloud-build.git
cd hello-cloud-build
cloud-build-local .
Alright, now to add a few more steps. Consider a build file with 2 steps: Here the two steps will run serially, first Step A, then Step B. A sample output looks like this on my machine:

Starting Step #0 - "A"
Step #0 - "A": Already have image (with digest): bash
Step #0 - "A": Step A
Finished Step #0 - "A"
2021/08/10 12:50:23 Step Step #0 - "A" finished
Starting Step #1 - "B"
Step #1 - "B": Already have image (with digest): bash
Step #1 - "B": Step B
Finished Step #1 - "B"
2021/08/10 12:50:25 Step Step #1 - "B" finished
2021/08/10 12:50:26 status changed to "DONE"

Concurrent Steps

A little more complex, say if I wanted to execute a few steps concurrently, the way to do it is using waitFor property of a step.


Here "waitFor" value of "-" indicates the start of the build, so essentially Step A and B will run concurrently and an output in my machine looks like this:

Starting Step #1 - "B"
Starting Step #0 - "A"
Step #1 - "B": Already have image (with digest): bash
Step #0 - "A": Already have image (with digest): bash
Step #1 - "B": Step B
Step #0 - "A": Step A
Finished Step #1 - "B"
2021/08/10 12:54:21 Step Step #1 - "B" finished
Finished Step #0 - "A"
2021/08/10 12:54:21 Step Step #0 - "A" finished

One more example where Step A is executed first and then Step B and Step C concurrently:

Passing Data

A root volume at path "/workspace" carries through the build, so if a step wants to pass data to another step then it can be passed through this "/workspace" folder. Here Step A is writing to a file and Step B is reading from the same file.

Conclusion

This covers the basics of the steps in a Cloud Build configuration file. In a subsequent post I will be using these to create a pipeline to deploy a java based application to Google Cloud Run.

Friday, July 2, 2021

Kotlin "Result" type for functional exception handling

In a previous post I had gone over how a "Try" type can be created in Kotlin from scratch to handle exceptions in a functional way. There is no need however to create such a type in Kotlin, a type called "Result" already handles the behavior of "Try" and this post will go over how it works. I will be taking the scenario from my previous post, having two steps:
  1. Parsing a Url
  2. Fetching from the Url
Either of these steps can fail
  • the URL may not be well formed, and 
  • fetching from a remote url may have network issues
So onto the basics of how such a call can be made using the Result type. You can imagine that parsing URL can return this Result type, capturing any exception that may result from such a call:
fun parseUrl(url: String): Result<URL> = 
        kotlin.runCatching { URL(url) }

Kotlin provides the "runCatching" function which accepts the block that can result in an exception and traps the result OR the exception in the "Result" type. Now that a "Result" is available, some basic checks can be made on it, I can check that the call succeeded using the "isSuccess" and "isFailure" properties:
val urlResult: Result<URL> = parseUrl("http://someurl")
urlResult.isSuccess == true
urlResult.isFailure == false

I can get the value using various "get*" methods:
urlResult.getOrNull() // Returns null if the block completed with an exception
urlResult.getOrDefault(URL("http://somedefault")) // Returns a default if the block completed with an exception
urlResult.getOrThrow() // Throws an exception if the block completed with an exception

The true power of "Result" type is however in chaining operations on it. So for eg, if you wanted to retrieve the host name given the url:
val urlResult: Result<URL> = parseUrl("http://someurl")
val hostResult: Result<String> = urlResult.map { url -> url.host }

Or a variant "mapCatching" which can trap any exception when using map operation and capture that as a "Result":
val getResult: Result<String> = urlResult.mapCatching { url -> throw RuntimeException("something failed!") }

All very neat! One nit that I have with the current "Result" is a missing "flatMap" operation, so for eg. consider a case where I have these two functions:
fun parseUrl(url: String): Result<URL> =
    kotlin.runCatching { URL(url) }
    
fun getFromARemoteUrl(url: URL): Result<String> {
    return kotlin.runCatching { "a result" }
}

I would have liked to be able to chain these two operations, along these lines:
val urlResult: Result<URL> = parseUrl("http://someurl")
val getResult: Result<String> = urlResult.flatMap { url -> getFromARemoteUrl(url)}

but a operator like "flatMap" does not exist (so far, as of Kotlin 1.5.20) 

I can do today is a bit of hack:
val urlResult: Result<URL> = parseUrl("http://someurl")
val getResult: Result<String> = urlResult.mapCatching { url -> getFromARemoteUrl(url).getOrThrow() }
OR even better, create an extension function which makes flatMap available to "Result" type, this way and use it:
fun <T, R> Result<T>.flatMap(block: (T) -> (Result<R>)): Result<R> {
    return this.mapCatching {
        block(it).getOrThrow()
    }
}
val urlResult: Result<URL> = parseUrl("http://someurl")
val getResult: Result<String> = urlResult.flatMap { url -> getFromARemoteUrl(url)}
This concludes my exploration of the Result type and the ways to use it. I have found it to be a excellent type to have in my toolbelt.

Thursday, June 3, 2021

Spring Endpoint to handle Json Patch and Json Merge Patch

In a previous blog post I went over the basics of Json Patch and Json Merge Patch and how a code that performs these operations looks like. In this post I will go over the details of how to expose a Spring based endpoint to accept a Json Patch or Json Merge Patch body and patch and save an entity. The entity that I want to update is a Book, and a sample book looks like this in a json form:
{
  "title": "Goodbye!",
  "author": {
    "givenName": "John",
    "familyName": "Doe"
  },
  "tags": [
    "example",
    "sample"
  ],
  "content": "This will be unchanged"
}
A kotlin representation of this entity is the following:
data class Book(
    val title: String,
    val author: Author,
    val tags: List<String>,
    val content: String,
    val phoneNumber: String? = null
)

data class Author(
    val givenName: String,
    val familyName: String? = null
)
Let's start with an endpoint that performs a Json Patch The endpoint should accept the patch in a request body, should accept a content type of "application/json-patch+json": A sample kotlin code of such an endpoint is the following:
import com.github.fge.jsonpatch.JsonPatch
...
...
@PatchMapping(path = ["/{id}"], consumes = ["application/json-patch+json"])
fun jsonPatchBook(
    @PathVariable id: String,
    @RequestBody patch: JsonNode
): Mono<ResponseEntity<Book>> {
    return Mono.fromSupplier {
        val jsonPatch: JsonPatch = JsonPatch.fromJson(patch)
        val original: JsonNode = objectMapper.valueToTree(getBook(id))
        val patched: JsonNode = jsonPatch.apply(original)
        val patchedBook: Book =
            objectMapper.treeToValue(patched) ?: throw RuntimeException("Could not convert json back to book")
        updateBook(patchedBook)
        ResponseEntity.ok(patchedBook)
    }
}
All that is involved is to : 
  1. Take in the Json Patch body and convert it into the JsonPatch type 
  2. Retrieve the Book entity for the identifier 
  3. Convert the Book entity into a Json representation 
  4. Apply the patch and convert the resulting json back into the Book entity

For an endpoint that performs Json Merge patch, along the same lines, the endpoint should accept the json merge patch request body with a content type of "application/merge-patch+json":


@PatchMapping(path = ["/{id}"], consumes = ["application/merge-patch+json"])
fun jsonMergePatchBook(
    @PathVariable id: String,
    @RequestBody patch: JsonNode
): Mono<ResponseEntity<Book>> {
    return Mono.fromSupplier {
        val original: JsonNode = objectMapper.valueToTree(getBook(id))
        val patched: JsonNode = JsonMergePatch.fromJson(patch).apply(original)
        val patchedBook: Book =
            objectMapper.treeToValue(patched) ?: throw RuntimeException("Could not convert json back to book")
        updateBook(patchedBook)
        ResponseEntity.ok(patchedBook)
    }
}
Steps are:
  1. Take in the Json Merge Patch body
  2. Retrieve the Book entity for the identifier 
  3. Convert the Book entity into a Json representation 
  4. Apply the merge patch and convert the resulting json back into the Book entity
All fairly straightforward thanks to the easy way that Spring Web allows to expose an endpoint and the way json-patch library provides support for the Json Patch and Json Merge Patch operations. If you need a complete working example with all the dependencies pulled in, here is a sample in my github repository - https://github.com/bijukunjummen/coroutine-cities-demo/blob/main/src/test/kotlin/samples/geo/patch/BookController.kt

Saturday, May 8, 2021

Json Patch and Json Merge Patch in Java

 Json Patch and Json Merge Patch both do one job well - a way to represent a change to a source json structure.  

Json Patch does it as a series of operations which transforms a source document and Json Merge Patch represents the change as a lite version of the source document.

It is easier to show these as an example, and this is straight from the Json Merge Patch's RFC.

Let's start with a source document:

{
  "title": "Goodbye!",
  "author": {
    "givenName": "John",
    "familyName": "Doe"
  },
  "tags": [
    "example",
    "sample"
  ],
  "content": "This will be unchanged"
}
and the objective is to transform it to this document:
{
  "title": "Hello!",
  "author": {
    "givenName": "John"
  },
  "tags": [
    "example"
  ],
  "content": "This will be unchanged",
  "phoneNumber": "+01-123-456-7890"
}
Which may be easier to visualize in a diff view:


The consolidated set of changes are:
  1. The title is being changed
  2. Author/familyName is removed
  3. One of the tags is removed
  4. A phone number is added

Json Patch

This change can be represented the following way using Json Patch document:
[
  { "op": "replace", "path": "/title", "value": "Hello!"},
  { "op": "remove", "path": "/author/familyName"},
  { "op": "add", "path": "/phoneNumber", "value": "+01-123-456-7890"},
  { "op": "replace", "path": "/tags", "value": ["example"]}
]
A series of operations transforms the source document into the target document. An operation can be one of "add", "remove", "replace", "move", "copy" or "test" and in the example exactly matches the diff.

 

Json Merge Patch

A Json merge patch for the change looks like this:
{
  "title": "Hello!",
  "author": {
    "familyName": null
  },
  "phoneNumber": "+01-123-456-7890",
  "tags": [
    "example"
  ]
}   
There is a little bit of interpretation required on how the change gets applied, it is very intuitive though: 1. The presence of "title" with a new value indicates that the title needs to be changed. 2. An explicit "null" for the family name indicates that the field should be removed 3. A phoneNumber field indicates that a new field needs to be added 4. Updated tags indicates that the tags need to be modified.
 

Using Json Patch with Java

json-patch is an awesome java library that provides support for both Json Patch and Json Merge Patch. It integrates with the excellent Jackson library and provides patch tooling on top of the the library. The sample is in kotlin:
val s = """
{
    "title": "Goodbye!",
    "author": {
      "givenName": "John",
      "familyName": "Doe"
    },
    "tags": [
      "example",
      "sample"
    ],
    "content": "This will be unchanged"
}        
""".trimIndent()


val patch = """
    [
        { "op": "replace", "path": "/title", "value": "Hello!"},
        { "op": "remove", "path": "/author/familyName"},
        { "op": "add", "path": "/phoneNumber", "value": "+01-123-456-7890"},
        { "op": "replace", "path": "/tags", "value": ["example"]}
    ]
""".trimIndent()
val jsonPatch: JsonPatch = JsonPatch.fromJson(objectMapper.readTree(patch))
val target = jsonPatch.apply(objectMapper.readTree(s))

Using Json Merge Patch with Java

The library makes using Json Merge patch equally easy:
val s = """
{
    "title": "Goodbye!",
    "author": {
      "givenName": "John",
      "familyName": "Doe"
    },
    "tags": [
      "example",
      "sample"
    ],
    "content": "This will be unchanged"
}        
""".trimIndent()


val patch = """
{
    "title": "Hello!",
    "author": {
      "familyName": null
    },
    "phoneNumber": "+01-123-456-7890",
    "tags": ["example"]
}   
""".trimIndent()

val jsonMergePatch: JsonMergePatch = JsonMergePatch.fromJson(objectMapper.readTree(patch))
val target = jsonMergePatch.apply(objectMapper.readTree(s))

Conclusion

Json Patch and Json Merge Patch are ways to represent a change to a json document. Both approaches do it a little differently but both are equally intuitive.

Tuesday, March 23, 2021

Project reactor and Caching with Caffeine

So you have a function which takes a key and returns a project reactor Mono type. 

Mono<String> get(String key) {
    Random random = ThreadLocalRandom.current();
    return Mono.fromSupplier(() -> key + random.nextInt());
}
And you want to cache the retrieval of this Mono type by key, a good way to do that is to use the excellent Caffeine library. Caffeine natively does not support reactor types however, but it is fairly easy to use Caffeine with reactor the following way:
public static <T> Function<String, Mono<T>> ofMono(@NotNull Duration duration,
                                                    @NotNull Function<String, Mono<T>> fn) {
    final Cache<String, T> cache = Caffeine.newBuilder()
            .expireAfterWrite(duration)
            .recordStats()
            .build();

    return key -> {
        T result = cache.getIfPresent(key);
        if (result != null) {
            return Mono.just(result);
        } else {
            return fn.apply(key).doOnNext(n -> cache.put(key, n));
        }
    };
}
It essentially wraps a function returning the Mono, and uses Caffeine to get the value from a cache defined via a closure. If value is present in the cache it is returned otherwise when the Mono emits a value, the value in the cache is set from that. So how can this be used..here is a test with this utility:
Function<String, Mono<String>> fn = (k) -> get(k);
Function<String, Mono<String>> wrappedFn = CachingUtils.ofMono(Duration.ofSeconds(10), fn);
StepVerifier.create(wrappedFn.apply("key1"))
        .assertNext(result1 -> {
            StepVerifier.create(wrappedFn.apply("key1"))
                    .assertNext(result2 -> {
                        assertThat(result2).isEqualTo(result1);
                    })
                    .verifyComplete();
            StepVerifier.create(wrappedFn.apply("key1"))
                    .assertNext(result2 -> {
                        assertThat(result2).isEqualTo(result1);
                    })
                    .verifyComplete();

            StepVerifier.create(wrappedFn.apply("key2"))
                    .assertNext(result2 -> {
                        assertThat(result2).isNotEqualTo(result1);
                    })
                    .verifyComplete();
        })
        .verifyComplete();
Here I am using Project Reactors StepVerifier utility to run a test on this wrapped function and ensure that cached value is indeed returned for repeating keys. The full sample is available in this gist

Coroutine based Spring boot webflux application

I have worked with Spring Framework for ages and it still manages to surprise me with how cutting edge it continues to be but at the same time enabling a developer to put together a fairly sane app.

The most recent surprise was with how it enables programming a web application with Kotlin coroutines. Coroutines is a fairly complicated concept to get my head around, but it is starting to click now and while trying out some samples I thought it may be a good idea to put an end to end web application in place. 

Thanks to the excellent Spring Boot starters it was not difficult at all. Along the way I also decided to experiment with r2dbc which is another involved technology to interact with a database using reactive streams. Combining reactive streams for interacting with the database but using coroutines in the rest of the layers was not difficult at all. In this post I will not be covering the nuances of what I had to do to get the sample to work, but will cover one thin slice of what it looks like. The sample is here in my github repo and should be fairly self explanatory. 


I have to acknowledge that Nicolas Frankel's blog post provided me a lot of pointers in getting the working code just right


A Slice of functionality

The slice of functionality that I will consider in this post is to return a list of entities and an entity from an embedded database that I have used for the application. 

Let's start from bottom up. So at the lowest level I have to query the database and return a list of entities, this is made dirt simple using the Spring Data based repositories. This is the entirety of the repository code that returns coroutine types.


import org.springframework.data.repository.kotlin.CoroutineCrudRepository
import samples.geo.domain.City

interface CityRepo : CoroutineCrudRepository<City, Long>
Just by doing this the CRUD operations now become suspendable functions. So to return a list of an entity or a specific entity, the signature looks something like this:


fun getCities(): Flow<City> {
    return cityRepo.findAll()
}

suspend fun getCity(id: Long): City? {
    return cityRepo.findById(id)
}
Any list operations now return the Coroutine Flow type and getting an entity is a suspendable function.


Moving to the web layer(I have a service layer, but it is just a passthrough to the repo in this instance), I like to have an handler for handling the Webflux ServerRequest and ServerResponse types the following way:
suspend fun getCities(request: ServerRequest): ServerResponse {
    val cities = cityService.getCities()
        .toList()
    return ServerResponse.ok().bodyValueAndAwait(cities)
}

suspend fun getCity(request: ServerRequest): ServerResponse {
    val id = request.pathVariable("id").toLong()
    val city = cityService.getCity(id)

    return city
        ?.let { ServerResponse.ok().bodyValueAndAwait(it) }
        ?: ServerResponse.notFound().buildAndAwait()
}
which is then composed at the web layer the following way:
object AppRoutes {
    fun routes(cityHandler: CityHandler): RouterFunction<*> = coRouter {
        accept(MediaType.APPLICATION_JSON).nest {
            GET("/cities", cityHandler::getCities)
            GET("/cities/{id}", cityHandler::getCity)
            ...
        }
    }
}
The "coRouter" dsl above provides the functionality to convert the Kotlin coroutine types to the Spring WebFlux RouterFunction type This is essentially it. The code and tests for all this fairly sophisticated set of technology(r2dbc, coroutines, webflux, reactive streams etc) that this encompasses is fairly small as can be seen from the github repository

Conclusion

Getting a working end to end web application with Coroutines and Spring Webflux is just a "Spring" board to further exploration of Coroutines for me and I hope to gain deeper insights into this fascinating technology over time. Having been involved in the Spring community for so long, it is fascinating to note that it continues to remain one of the best frameworks to develop applications in, mainly because of the constant innovation and its focus on developer happiness.