Skip to content

Commit

Permalink
chore(java): Full java 17 only support (#2107)
Browse files Browse the repository at this point in the history
* chore(java): Full java 17 only

* chore(java): Full java 17 only

* chore(java): Full java 17 only

* chore(java): Let gradle plugin compat set the target and source versions

* chore(java): Attempt to fix the serialization behavior on linux when using Instant clocks and sql conversions of the JSON string from that conversion

* fix(sql): Attempting to fix the precision error in a slightly different manner

* fix(sql): Attempting to fix the precision error in a slightly different manner

* fix(sql): Attempting to fix the precision error by force setting mapping of instants

* fix(sql): Use base date time serializer with some mods

* fix(sql): Use less precision on comparison to an instant now

* fix(sql): Test ONLY with serializer vs serial and deserializer

* fix(sql): Now with tests passing remove the extraneous code

* fix(sql): Document the changes

* fix(sql): Add test verifying precision and adjust precision to 6 to match mysql

* fix(sql): Adjust comment and change to MICROS for tests
  • Loading branch information
jasonmcintosh authored Nov 15, 2024
1 parent 072ccd6 commit 784a3d1
Show file tree
Hide file tree
Showing 14 changed files with 107 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- uses: actions/setup-java@v4
with:
java-version: 11
java-version: 17
distribution: 'zulu'
cache: 'gradle'
- name: Prepare build variables
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- uses: actions/setup-java@v4
with:
java-version: 11
java-version: 17
distribution: 'zulu'
cache: 'gradle'
- name: Prepare build variables
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- uses: actions/setup-java@v4
with:
java-version: 11
java-version: 17
distribution: 'zulu'
cache: 'gradle'
- name: Assemble release info
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.compile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
FROM ubuntu:bionic
FROM ubuntu:jammy
LABEL maintainer="[email protected]"
RUN echo "mysql-server mysql-server/root_password password sa" | debconf-set-selections
RUN echo "mysql-server mysql-server/root_password_again password sa" | debconf-set-selections
RUN apt-get update && apt-get install -y \
openjdk-11-jdk \
openjdk-17-jdk \
mysql-server \
&& rm -rf /var/lib/apt/lists/*
ENV GRADLE_USER_HOME /workspace/.gradle
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.slim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM alpine:3.11
FROM alpine:3.20
LABEL maintainer="[email protected]"
RUN apk --no-cache add --update bash openjdk11-jre
RUN apk --no-cache add --update bash openjdk17-jre
RUN addgroup -S -g 10111 spinnaker
RUN adduser -S -G spinnaker -u 10111 spinnaker
COPY keel-web/build/install/keel /opt/keel
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.ubuntu
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM ubuntu:bionic
FROM ubuntu:jammy
LABEL maintainer="[email protected]"
RUN apt-get update && apt-get -y install openjdk-11-jre-headless wget
RUN apt-get update && apt-get -y install openjdk-17-jre-headless wget
RUN adduser --system --uid 10111 --group spinnaker
COPY keel-web/build/install/keel /opt/keel
RUN mkdir -p /opt/keel/plugins && chown -R spinnaker:nogroup /opt/keel/plugins
Expand Down
4 changes: 0 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ allprojects {
apply(plugin: "com.github.ben-manes.versions")
apply(plugin: "com.diffplug.spotless")

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

dependencies {
annotationProcessor(platform("io.spinnaker.kork:kork-bom:${korkVersion}"))
Expand Down
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
targetJava17=true
kotlinVersion=1.6.21
fiatVersion=1.50.0
korkVersion=7.243.0
Expand All @@ -9,6 +10,8 @@ okHttpVersion=4.5.0
resilience4jVersion=1.5.0
spinnakerGradleVersion=8.32.1

org.gradle.jvmargs=-Xmx4g -Xms4g

# Used to control whether to spin up docker to run liquibase before jooq
buildingInDocker=false

Expand Down
4 changes: 2 additions & 2 deletions gradle/kotlin.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ apply plugin: "kotlin-allopen"
compileKotlin {
kotlinOptions {
languageVersion = "1.6"
jvmTarget = "11"
jvmTarget = "17"
// see https://kotlinlang.org/docs/java-to-kotlin-interop.html#compatibility-mode-for-default-methods
freeCompilerArgs += "-Xjvm-default=enable"
}
Expand All @@ -14,7 +14,7 @@ compileKotlin {
compileTestKotlin {
kotlinOptions {
languageVersion = "1.6"
jvmTarget = "11"
jvmTarget = "17"
freeCompilerArgs += "-Xjvm-default=enable"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import strikt.assertions.isNull
import strikt.assertions.isTrue
import java.time.Clock
import java.time.Instant
import java.time.temporal.ChronoUnit

abstract class ArtifactRepositoryTests<T : ArtifactRepository> : JUnit5Minutests {
val publisher: ApplicationEventPublisher = mockk(relaxed = true)
Expand Down Expand Up @@ -649,7 +650,10 @@ abstract class ArtifactRepositoryTests<T : ArtifactRepository> : JUnit5Minutests
}

context("artifact creation timestamp exists") {
val createdAt = Instant.now()
// We truncate this since we're using a serialization to java that reduces the level of precision
// and later comparisons break otherwise. This is needed to work with generated columns in
// certain databases. See the PrecisionSqlSerializer class for more info
val createdAt = Instant.now().truncatedTo(ChronoUnit.MICROS)

before {
subject.register(versionedSnapshotDebian)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.netflix.spinnaker.keel.serialization;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializerBase;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;

/**
* This class overloads the default Serialization instance in the JavaTimeModule to a precision
* level that the database can process via str_to_date string conversion It's needed because we do
* JSON serialization to a column and then the column gets used to generate another field via string
* conversion. Specifically * triggered_at * dismissed_at are "generated" columns. NOTE this should
* ONLY impact running on Linux as the system clock on Linux systems returns a higher precision in
* Java 17+. It's also impactful as ISO_INSTANT has NO precision limitation giving inconsistent
* string output as a result
*
* <p>See
*
* <ul>
* <li>https://stackoverflow.com/questions/74781495/changes-in-instant-now-between-java-11-and-java-17-in-aws-ubuntu-standard-6-0
* <li>https://stackoverflow.com/a/38042457 for more information.
* </ul>
*
* <p>NOTE we're going to 6 digits to match <a
* href="https://www.w3schools.com/sql/func_mysql_str_to_date.asp">Mysql str_to_date function
* limits</a> on microseconds parsing. This is used in the SQL definition: <code>
* add column triggered_at datetime(3) generated always as (str_to_date(json->>'$.triggeredAt', '%Y-%m-%dT%T.%fZ'))
* </code> column in the 20210616-create-dismissible-notifications.yml liquibase change set.
*/
public class PrecisionSqlSerializer extends InstantSerializerBase<Instant> {
public PrecisionSqlSerializer() {
super(
Instant.class,
Instant::toEpochMilli,
Instant::getEpochSecond,
Instant::getNano,
new DateTimeFormatterBuilder().appendInstant(6).toFormatter());
}

@Override
protected InstantSerializerBase<?> withFormat(
Boolean aBoolean, DateTimeFormatter dateTimeFormatter, JsonFormat.Shape shape) {
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import com.netflix.spinnaker.keel.jackson.registerKeelApiModule
import de.huxhorn.sulky.ulid.ULID
import org.springframework.boot.jackson.JsonComponentModule
import java.text.SimpleDateFormat
import java.time.Instant
import java.util.TimeZone

/**
Expand All @@ -29,18 +29,20 @@ fun configuredObjectMapper(): ObjectMapper = ObjectMapper().configureForKeel()
* Factory method for [YAMLMapper]s configured how we like 'em.
*/
fun configuredYamlMapper(): YAMLMapper = YAMLMapper().configureForKeel().disable(USE_NATIVE_TYPE_ID)

fun <T : ObjectMapper> T.configureForKeel(): T =
apply {
fun <T : ObjectMapper> T.configureForKeel(): T {
val javaTimeModule = JavaTimeModule()
javaTimeModule.addSerializer(Instant::class.java,PrecisionSqlSerializer())
return apply {
registerKeelApiModule()
.registerKotlinModule()
.registerULIDModule()
.registerModule(JavaTimeModule())
.registerModule(javaTimeModule)
.configureSaneDateTimeRepresentation()
.disable(FAIL_ON_UNKNOWN_PROPERTIES)
.enable(ACCEPT_CASE_INSENSITIVE_ENUMS)
.setSerializationInclusion(NON_NULL)
}
}

private fun ObjectMapper.registerULIDModule(): ObjectMapper =
registerModule(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.netflix.spinnaker.keel.serialization

import dev.minutest.junit.JUnit5Minutests
import dev.minutest.rootContext
import org.assertj.core.api.Assertions.assertThat
import java.time.Instant
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder

class ObjectMapperSerializationTests : JUnit5Minutests {

class ObjectMapperSerializationTestsObject {
var date = Instant.from(DateTimeFormatter.ISO_INSTANT.parse("2024-03-10T12:28:19.228816801Z"))
}

fun tests() = rootContext {
test("Verify instant precision is no more than 6 characters") {
val mapper = configuredObjectMapper()
val sampleObject = ObjectMapperSerializationTestsObject()
val readBack = mapper.readValue(
mapper.writeValueAsString(sampleObject),
ObjectMapperSerializationTestsObject::class.java
)
// 6 precision is the max allowed via str_to_date in SQL. See
// https://dev.mysql.com/doc/refman/8.4/en/date-and-time-type-syntax.html#:~:text=MySQL%20permits%20fractional%20seconds%20for,microseconds%20(6%20digits)%20precision.
// SO we want to make sure waht we store matches this
val foramt = DateTimeFormatterBuilder().parseCaseInsensitive().appendInstant(6).parseStrict().toFormatter()
assertThat(readBack.date).isEqualTo(foramt.format(sampleObject.date))
}
}
}
2 changes: 1 addition & 1 deletion keel-sql/src/test/resources/testcontainers.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ryuk.container.image = public.ecr.aws/s4w6t4b6/testcontainers/ryuk:0.3.4
ryuk.container.image = testcontainers/ryuk:0.11.0

0 comments on commit 784a3d1

Please sign in to comment.