Skip to content

Commit

Permalink
Chpater09: Add
Browse files Browse the repository at this point in the history
  • Loading branch information
leszko committed Feb 17, 2022
1 parent e26f09b commit c091c19
Show file tree
Hide file tree
Showing 67 changed files with 1,769 additions and 0 deletions.
156 changes: 156 additions & 0 deletions Chapter09/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Chapter 9: Advanced Continuous Delivery

All the instructions assumes that you have Java 8+ and Docker installed on your system.

## Code Samples

### Code Sample 1: Initial Flyway migration

The [sample1](sample1) includes a Calculator project together with the initial Flyway migration.

The initial migration SQL is defined in `src/main/resources/db/migration/V1__Create_calculation_table.sql`. You can have a look at that file and then apply the migration.

$ ./gradlew flywayMigrate -i

The command should automatically detect the migration script and update the database schema. You can now run the Calculator application.

$ ./gradlew bootRun

We can now make a call to our service.

$ curl localhost:8080/sum?a=1\&b=2
3

This should result in creating an entry in the database. You can check it by browsing the database at: http://localhost:8080/h2-console (use JDBC URL: `jdbc:h2:/tmp/calculator`).

### Code Sample 2: Backwards-compatible Flyway migration

The [sample2](sample2) includes a Calculator project together with the backwards-compatible Flyway migration.

The migration adds a new column `CREATED_AT` to the `CALCULATION` table. The migration is defined in `src/main/resources/db/migration/V2__Add_created_at_column.sql`. To apply the migration, run the following command.

$ ./gradlew flywayMigrate -i

Then, run again the application.

$ ./gradlew bootRun

Make a new call to the service.

$ curl localhost:8080/sum?a=2\&b=3
5

Observe the new entry in the database.

### Code Sample 3: Non-backwards-compatible Flyway migration

The [sample3](sample3) includes a Calculator project together with the non-backwards-compatible Flyway migration (which renames of the table column).

Renaming the column will be done in a few steps:
1. Adding a new column in the database
2. Changing the code to use both columns
3. Merging the data
4. Removing the old column from the code
5. Dropping the old column from the database

The first steps are already included, you can check they work fine by executing the `src/main/resources/db/migration/V3__Add_sum_column.sql` migration and starting the service.

$ ./gradlew flywayMigrate -i
$ ./gradlew bootRun

Make a new call to the service and observe the entries in the database. Note that all the changes we did so far are backwards-compatible.

Let's create the data merging migration (`src/main/resources/db/migration/V4__Copy_result_into_sum_column.sql`), which will copy the old column data to the new one.

update CALCULATION
set CALCULATION.sum = CALCULATION.result
where CALCULATION.sum is null;

Execute the migration.

$ ./gradlew flywayMigrate -i

The next step is to remove the old column from the code. You can remove all mentions of `result` from the `Calculation.java` class. Then, start the service and observe the data in the database.

$ ./gradlew bootRun

Finally, we can drop the old column from the database. Create `src/main/resources/db/migration/V5__Drop_result_column.sql` with the following content.

alter table CALCULATION
drop column RESULT;

After running the migration and starting the service, observe again the database.

$ ./gradlew flywayMigrate -i
$ ./gradlew bootRun

## Exercise solutions

### Exercise 1: Flyway to make non-backwards-compatible change in MySQL

The [exercise1](exercise1) directory contains the initial Flyway migration.

Start the MySQL docker container with the following command.

$ docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=abc123 -d mysql

Install MySQL client CLI (`mysql-client`) and the `flyway` command tool. Note that you can use the related client Docker images instead.

Create a sample database `test`.

$ mysql -h 127.0.0.1 -u root -p'abc123' -P 3306
mysql> create database test;
mysql> use test;

Run the initial migration which creates the `users` table.

$ flyway -user=root -password=abc123 -url=jdbc:mysql://127.0.0.1:3306/test -locations=filesystem:. migrate

Insert some data with the MySQL client.

mysql> INSERT INTO USERS(ID, EMAIL, PASSWORD) VALUES(1, 'rafal@leszko.com','ala123');
mysql> INSERT INTO USERS(ID, EMAIL, PASSWORD) VALUES(2, 'maria@leszko.com','123456');
mysql> INSERT INTO USERS(ID, EMAIL, PASSWORD) VALUES(3, 'roza@leszko.com','password');

Create the migration `V2__Create_hashed_password_column.sql` to create a new column.

alter table USERS
add HASHED_PASSWORD varchar(100);

Apply the migration.

$ flyway -user=root -password=abc123 -url=jdbc:mysql://127.0.0.1:3306/test -locations=filesystem:. migrate

Create a migration to copy the data from `PASSWORD` into `HASHED_PASSWORD`. Let's name it `V3__Copy_password_into_hashed_password.sql`.

update USERS
set USERS.HASHED_PASSWORD = MD5(USERS.PASSWORD);

Apply the migration.

$ flyway -user=root -password=abc123 -url=jdbc:mysql://127.0.0.1:3306/test -locations=filesystem:. migrate

Check using MySQL client that the data was copied.

Create a migration to drop the `PASSWORD` column. Let's name it `V4__Drop_password_column.sql`.

alter table USERS
drop column PASSWORD;

Apply the migration and check the results with the MySQL client.

$ flyway -user=root -password=abc123 -url=jdbc:mysql://127.0.0.1:3306/test -locations=filesystem:. migrate
mysql> select * from USERS;
+----+------------------+----------------------------------+
| ID | EMAIL | HASHED_PASSWORD |
+----+------------------+----------------------------------+
| 1 | rafal@leszko.com | 77da6e74372583260cc783e8bdbe5b37 |
| 2 | maria@leszko.com | e10adc3949ba59abbe56e057f20f883e |
| 3 | roza@leszko.com | 5f4dcc3b5aa765d61d8327deb882cf99 |
+----+------------------+----------------------------------+

### Exercise 2: Create Jenkins shared library to build and unit test Gradle projects

The [exercise2](exercise2) directory contains the code for the Jenkins shared library.

To use it, you need to copy it into a separate repository and point it in Jenkins.
5 changes: 5 additions & 0 deletions Chapter09/exercise1/V1__Create_users_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
create table USERS (
ID int not null,
EMAIL varchar(100) not null,
PASSWORD varchar(100) not null
);
6 changes: 6 additions & 0 deletions Chapter09/exercise2/vars/gradleBuild.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Build Gradle project.
*/
def call() {
sh './gradlew build'
}
6 changes: 6 additions & 0 deletions Chapter09/exercise2/vars/gradleTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Run unit tests in Gradle project.
*/
def call() {
sh './gradlew test'
}
68 changes: 68 additions & 0 deletions Chapter09/sample1/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
buildscript {
dependencies {
classpath('com.h2database:h2:1.4.199')
}
}

plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
id "org.flywaydb.flyway" version "5.2.4"
}

apply plugin: 'io.spring.dependency-management'
apply plugin: "jacoco"
apply plugin: 'checkstyle'

group = 'com.leszko'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.hazelcast:hazelcast-all:3.12'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation("io.cucumber:cucumber-java:4.2.6")
testImplementation("io.cucumber:cucumber-junit:4.2.6")
}

jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.2
}
}
}
}

checkstyle {
checkstyleTest.enabled = false
}

task acceptanceTest(type: Test) {
include '**/acceptance/**'
systemProperties System.getProperties()
}

task smokeTest(type: Test) {
include '**/acceptance/**'
systemProperties System.getProperties()
}

test {
exclude '**/acceptance/**'
exclude '**/smoke/**'
}

flyway {
url = 'jdbc:h2:file:/tmp/calculator'
user = 'sa'
}
12 changes: 12 additions & 0 deletions Chapter09/sample1/config/checkstyle/checkstyle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">

<module name="Checker">
<module name="TreeWalker">
<module name="JavadocType">
<property name="scope" value="public"/>
</module>
</module>
</module>
Binary file not shown.
5 changes: 5 additions & 0 deletions Chapter09/sample1/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit c091c19

Please sign in to comment.