diff --git a/operator/src/main/java/it/aboutbits/postgresql/core/BaseReconciler.java b/operator/src/main/java/it/aboutbits/postgresql/core/BaseReconciler.java index 849129a..fe7515f 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/core/BaseReconciler.java +++ b/operator/src/main/java/it/aboutbits/postgresql/core/BaseReconciler.java @@ -20,7 +20,7 @@ public abstract class BaseReconciler & Named, S protected abstract S newStatus(); public S initializeStatus(CR resource) { - S status = resource.getStatus(); + var status = resource.getStatus(); //noinspection ConstantConditions if (status == null) { diff --git a/operator/src/main/java/it/aboutbits/postgresql/core/SQLUtil.java b/operator/src/main/java/it/aboutbits/postgresql/core/SQLUtil.java index 419e15a..f458b25 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/core/SQLUtil.java +++ b/operator/src/main/java/it/aboutbits/postgresql/core/SQLUtil.java @@ -22,7 +22,7 @@ private static QueryPart concatenateQueryParts( List items, String separator ) { - int size = items.size(); + var size = items.size(); if (items.isEmpty()) { return sql(""); diff --git a/operator/src/main/java/it/aboutbits/postgresql/crd/database/DatabaseReconciler.java b/operator/src/main/java/it/aboutbits/postgresql/crd/database/DatabaseReconciler.java index 002db87..72d4f8a 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/crd/database/DatabaseReconciler.java +++ b/operator/src/main/java/it/aboutbits/postgresql/crd/database/DatabaseReconciler.java @@ -217,7 +217,10 @@ private UpdateControl reconcileWithoutTransaction( name ); - return UpdateControl.noUpdate(); + status.setPhase(CRPhase.READY) + .setMessage(null); + + return UpdateControl.patchStatus(resource); } log.info( diff --git a/operator/src/main/java/it/aboutbits/postgresql/crd/role/RoleReconciler.java b/operator/src/main/java/it/aboutbits/postgresql/crd/role/RoleReconciler.java index bbebb9a..875374e 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/crd/role/RoleReconciler.java +++ b/operator/src/main/java/it/aboutbits/postgresql/crd/role/RoleReconciler.java @@ -313,7 +313,10 @@ private UpdateControl reconcileInTransaction( name ); - return UpdateControl.noUpdate(); + status.setPhase(CRPhase.READY) + .setMessage(null); + + return UpdateControl.patchStatus(resource); } var changePassword = loginExpected && !passwordMatches; diff --git a/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaReconciler.java b/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaReconciler.java index 921ec35..968a973 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaReconciler.java +++ b/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaReconciler.java @@ -221,7 +221,10 @@ private UpdateControl reconcileInTransaction( name ); - return UpdateControl.noUpdate(); + status.setPhase(CRPhase.READY) + .setMessage(null); + + return UpdateControl.patchStatus(resource); } log.info( diff --git a/operator/src/test/java/it/aboutbits/postgresql/core/PasswordFingerprintServiceTest.java b/operator/src/test/java/it/aboutbits/postgresql/core/PasswordFingerprintServiceTest.java index e4fbc41..6a11638 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/core/PasswordFingerprintServiceTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/core/PasswordFingerprintServiceTest.java @@ -76,7 +76,9 @@ void whenSecretExists_shouldReuseKey() { } @Test - @DisplayName("When the key Secret is lost and the operator restarts, should create a new key and the fingerprints change") + @DisplayName( + "When the key Secret is lost and the operator restarts, should create a new key and the fingerprints change" + ) void whenSecretLost_shouldCreateNewKey() { // given var fingerprint = newService().fingerprint("password", PasswordEncryption.SCRAM_SHA_256); diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerTest.java index 65b0bb3..6af1cb8 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerTest.java @@ -27,6 +27,7 @@ import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; import java.util.Objects; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThat; @@ -175,6 +176,59 @@ void createsCustomResourceWithFileRef_andReconcilerStatusIsReady() throws IOExce } } + @Test + @DisplayName("When the server of a ClusterConnection is reachable again after an ERROR, the status should be ready") + void reachableAgainAfterError_setsReady() { + // given: a reconciled ClusterConnection + var clusterConnection = given.one() + .clusterConnection() + .withName("test-connection-error-to-ready") + .returnFirst(); + + var spec = clusterConnection.getSpec(); + var port = spec.getPort(); + + // and: the server is not reachable for a while + spec.setPort(1); + + var erroredClusterConnection = applyClusterConnection(clusterConnection); + + assertThat(erroredClusterConnection.getStatus().getPhase()).isEqualTo(CRPhase.ERROR); + + // when: the server is reachable again + spec.setPort(port); + + var updatedClusterConnection = applyClusterConnection(clusterConnection); + + // then + assertThat(updatedClusterConnection.getStatus().getPhase()).isEqualTo(CRPhase.READY); + assertThat(updatedClusterConnection.getStatus().getMessage()).startsWith("PostgreSQL"); + } + + private ClusterConnection applyClusterConnection(ClusterConnection clusterConnection) { + var namespace = kubernetesClient.getNamespace(); + + clusterConnection.getMetadata().setManagedFields(null); + clusterConnection.getMetadata().setResourceVersion(null); + + var applied = kubernetesClient.resources(ClusterConnection.class) + .inNamespace(namespace) + .resource(clusterConnection) + .serverSideApply(); + + var generation = applied.getMetadata().getGeneration(); + + //noinspection ConstantConditions + return kubernetesClient.resources(ClusterConnection.class) + .inNamespace(namespace) + .withName(applied.getMetadata().getName()) + .waitUntilCondition( + c -> c.getStatus() != null && c.getStatus().getObservedGeneration() >= generation, + 5, + TimeUnit.SECONDS + ); + } + private static void assertThatClusterConnectionHasExpectedStatus( ClusterConnection clusterConnection, CRStatus expectedStatus, diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/database/DatabaseReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/database/DatabaseReconcilerTest.java index 7ebc3e0..0d564d9 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/database/DatabaseReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/database/DatabaseReconcilerTest.java @@ -15,6 +15,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.util.concurrent.TimeUnit; import static it.aboutbits.postgresql.core.ReclaimPolicy.DELETE; import static org.assertj.core.api.Assertions.assertThat; @@ -72,6 +73,71 @@ void createDatabase_andStatusReady() { assertThat(databaseService.databaseExists(dsl, database.getSpec())).isTrue(); } + @Test + @DisplayName( + "When the ClusterConnection of an up-to-date Database is back, the Database should be reconciled to READY" + ) + void upToDateDatabase_afterPendingClusterConnection_setsReady() { + // given: a reconciled Database with an explicit owner, so that the next reconciles find it up to date + var clusterConnection = given.one() + .clusterConnection() + .withName("test-connection-database-pending-to-ready") + .returnFirst(); + + var database = given.one() + .database() + .withClusterConnectionName(clusterConnection.getMetadata().getName()) + .withReclaimPolicy(DELETE) + .returnFirst(); + + var spec = database.getSpec(); + var clusterRef = spec.getClusterRef(); + + var dsl = postgreSQLContextFactory.getDSLContext(clusterConnection); + + spec.setOwner(databaseService.fetchDatabaseOwner(dsl, spec)); + + // and: the ClusterConnection is not available for a while + clusterRef.setName("non-existing-cc"); + + var pendingDatabase = applyDatabase(database); + + assertThat(pendingDatabase.getStatus().getPhase()).isEqualTo(CRPhase.PENDING); + + // when: the ClusterConnection is available again + clusterRef.setName(clusterConnection.getMetadata().getName()); + + var updatedDatabase = applyDatabase(database); + + // then + assertThat(updatedDatabase.getStatus().getPhase()).isEqualTo(CRPhase.READY); + assertThat(updatedDatabase.getStatus().getMessage()).isNull(); + } + + private Database applyDatabase(Database database) { + var namespace = kubernetesClient.getNamespace(); + + database.getMetadata().setManagedFields(null); + database.getMetadata().setResourceVersion(null); + + var applied = kubernetesClient.resources(Database.class) + .inNamespace(namespace) + .resource(database) + .serverSideApply(); + + var generation = applied.getMetadata().getGeneration(); + + //noinspection ConstantConditions + return kubernetesClient.resources(Database.class) + .inNamespace(namespace) + .withName(applied.getMetadata().getName()) + .waitUntilCondition( + d -> d.getStatus() != null && d.getStatus().getObservedGeneration() >= generation, + 5, + TimeUnit.SECONDS + ); + } + private static void assertThatDatabaseHasExpectedStatus( Database database, CRStatus expectedStatus, diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java index 21094b5..5d92980 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java @@ -711,6 +711,67 @@ void defaultPrivilegeOnSequence() { } } + @Test + @DisplayName( + "When the ClusterConnection of an up-to-date DefaultPrivilege is back, the DefaultPrivilege should be reconciled to READY" + ) + void upToDateDefaultPrivilege_afterPendingClusterConnection_setsReady() { + // given: a reconciled DefaultPrivilege + var clusterConnectionMain = given.one() + .clusterConnection() + .returnFirst(); + + var database = given.one() + .database() + .withClusterConnectionName(clusterConnectionMain.getMetadata().getName()) + .withReclaimPolicy(DELETE) + .returnFirst(); + + // To create a Schema in the new database, we need a ClusterConnection pointing to it + var clusterConnectionDb = given.one() + .clusterConnection() + .withDatabase(database.getSpec().getName()) + .returnFirst(); + + var role = given.one() + .role() + .withClusterConnectionName(clusterConnectionMain.getMetadata().getName()) + .returnFirst(); + + var defaultPrivilege = given.one() + .defaultPrivilege() + .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) + .withRole(role.getSpec().getName()) + .withObjectType(SCHEMA) + .withPrivileges(USAGE) + .returnFirst(); + + var clusterRef = defaultPrivilege.getSpec().getClusterRef(); + + // and: the ClusterConnection is not available for a while + clusterRef.setName("non-existing-cc"); + + var pendingDefaultPrivilege = applyDefaultPrivilege( + defaultPrivilege, + dp -> dp.getStatus().getObservedGeneration() >= 2L + ); + + assertThat(pendingDefaultPrivilege.getStatus().getPhase()).isEqualTo(CRPhase.PENDING); + + // when: the ClusterConnection is available again + clusterRef.setName(clusterConnectionDb.getMetadata().getName()); + + var updatedDefaultPrivilege = applyDefaultPrivilege( + defaultPrivilege, + dp -> dp.getStatus().getObservedGeneration() >= 3L + ); + + // then + assertThat(updatedDefaultPrivilege.getStatus().getPhase()).isEqualTo(CRPhase.READY); + assertThat(updatedDefaultPrivilege.getStatus().getMessage()).isNull(); + } + private DefaultPrivilege applyDefaultPrivilege( DefaultPrivilege defaultPrivilege, Predicate condition diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java index 33e6aa8..29cc12d 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java @@ -1226,6 +1226,59 @@ void grantOnAllSequences() { } } + @Test + @DisplayName("When the ClusterConnection of an up-to-date Grant is back, the Grant should be reconciled to READY") + void upToDateGrant_afterPendingClusterConnection_setsReady() { + // given: a reconciled Grant + var clusterConnection = given.one() + .clusterConnection() + .returnFirst(); + + var role = given.one() + .role() + .withClusterConnectionName(clusterConnection.getMetadata().getName()) + .returnFirst(); + + var database = given.one() + .database() + .withClusterConnectionName(clusterConnection.getMetadata().getName()) + .withReclaimPolicy(DELETE) + .returnFirst(); + + var grant = given.one() + .grant() + .withClusterConnectionName(clusterConnection.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) + .withRole(role.getSpec().getName()) + .withObjectType(DATABASE) + .withPrivileges(CONNECT) + .returnFirst(); + + var clusterRef = grant.getSpec().getClusterRef(); + + // and: the ClusterConnection is not available for a while + clusterRef.setName("non-existing-cc"); + + var pendingGrant = applyGrant( + grant, + g -> g.getStatus().getObservedGeneration() >= 2L + ); + + assertThat(pendingGrant.getStatus().getPhase()).isEqualTo(CRPhase.PENDING); + + // when: the ClusterConnection is available again + clusterRef.setName(clusterConnection.getMetadata().getName()); + + var updatedGrant = applyGrant( + grant, + g -> g.getStatus().getObservedGeneration() >= 3L + ); + + // then + assertThat(updatedGrant.getStatus().getPhase()).isEqualTo(CRPhase.READY); + assertThat(updatedGrant.getStatus().getMessage()).isNull(); + } + private Grant applyGrant( Grant grant, Predicate condition diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerNonSuperuserTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerNonSuperuserTest.java index 489c7da..7700563 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerNonSuperuserTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerNonSuperuserTest.java @@ -214,7 +214,9 @@ void nonSuperuserAdmin_updatesLoginAndMembership() { } @Test - @DisplayName("When the admin is not a superuser, its implicit membership in a created Role should be ignored and kept") + @DisplayName( + "When the admin is not a superuser, its implicit membership in a created Role should be ignored and kept" + ) void nonSuperuserAdmin_implicitAdminMembership_isIgnoredAndKept() { // given var rootConnection = givenRootClusterConnection("test-connection-root-self-grant"); @@ -262,7 +264,9 @@ void nonSuperuserAdmin_implicitAdminMembership_isIgnoredAndKept() { @ParameterizedTest(name = "flag {0}") @MethodSource("provideSuperuserOnlyFlags") - @DisplayName("When the admin is not a superuser and the Role asks for a superuser-only flag, the status should be ERROR") + @DisplayName( + "When the admin is not a superuser and the Role asks for a superuser-only flag, the status should be ERROR" + ) void nonSuperuserAdmin_superuserOnlyFlag_setsError( String flagName, BiConsumer flagSetter @@ -301,7 +305,9 @@ void nonSuperuserAdmin_superuserOnlyFlag_setsError( } @Test - @DisplayName("When the admin is not a superuser and passwordEncryption is 'server', the server should hash the password") + @DisplayName( + "When the admin is not a superuser and passwordEncryption is 'server', the server should hash the password" + ) void nonSuperuserAdmin_serverPasswordEncryption_letsTheServerHash() { // given var rootConnection = givenRootClusterConnection("test-connection-root-server-encryption"); @@ -335,7 +341,9 @@ void nonSuperuserAdmin_serverPasswordEncryption_letsTheServerHash() { } @Test - @DisplayName("When the admin is not a superuser and the Secret contains a SCRAM-SHA-256 verifier, it should be stored verbatim") + @DisplayName( + "When the admin is not a superuser and the Secret contains a SCRAM-SHA-256 verifier, it should be stored verbatim" + ) void nonSuperuserAdmin_preHashedPassword_isStoredVerbatim() { // given var rootConnection = givenRootClusterConnection("test-connection-root-prehashed"); diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerTest.java index 38af55e..bb63af3 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerTest.java @@ -412,7 +412,9 @@ void secretRefChange_triggersReconciliation() { } @Test - @DisplayName("When an existing Role has no password fingerprint in its status, the Secret password should be applied once") + @DisplayName( + "When an existing Role has no password fingerprint in its status, the Secret password should be applied once" + ) void missingPasswordFingerprint_appliesSecretPasswordOnce() { // given: a Role created by the operator var clusterConnection = given.one() @@ -489,6 +491,40 @@ void missingPasswordFingerprint_appliesSecretPasswordOnce() { assertThat(PostgreSQLPasswordVerifier.storedVerifier(dsl, roleName)).isEqualTo(verifierAfterUpgrade); } + @Test + @DisplayName("When the ClusterConnection of an up-to-date Role is back, the Role should be reconciled to READY") + void upToDateRole_afterPendingClusterConnection_setsReady() { + // given: a reconciled Role + var clusterConnection = given.one() + .clusterConnection() + .withName("test-connection-role-pending-to-ready") + .returnFirst(); + + var role = given.one() + .role() + .withName("test-role-pending-to-ready") + .withClusterConnectionName(clusterConnection.getMetadata().getName()) + .returnFirst(); + + var clusterRef = role.getSpec().getClusterRef(); + + // and: the ClusterConnection is not available for a while + clusterRef.setName("non-existing-cc"); + + var pendingRole = applyRole(role); + + assertThat(pendingRole.getStatus().getPhase()).isEqualTo(CRPhase.PENDING); + + // when: the ClusterConnection is available again + clusterRef.setName(clusterConnection.getMetadata().getName()); + + var updatedRole = applyRole(role); + + // then + assertThat(updatedRole.getStatus().getPhase()).isEqualTo(CRPhase.READY); + assertThat(updatedRole.getStatus().getMessage()).isNull(); + } + @Test @DisplayName("When the Secret already contains a SCRAM-SHA-256 verifier, it should be stored verbatim") void preHashedPassword_isStoredVerbatim() { diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java index a301871..4d8beae 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java @@ -15,6 +15,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.util.concurrent.TimeUnit; import static it.aboutbits.postgresql.core.ReclaimPolicy.DELETE; import static org.assertj.core.api.Assertions.assertThat; @@ -85,6 +86,82 @@ void createSchema_andStatusReady() { assertThat(schemaService.schemaExists(dsl, schema.getSpec())).isTrue(); } + @Test + @DisplayName("When the ClusterConnection of an up-to-date Schema is back, the Schema should be reconciled to READY") + void upToDateSchema_afterPendingClusterConnection_setsReady() { + // given: a reconciled Schema with an explicit owner, so that the next reconciles find it up to date + var clusterConnectionMain = given.one() + .clusterConnection() + .withName("test-connection-schema-pending-to-ready") + .returnFirst(); + + var database = given.one() + .database() + .withClusterConnectionName(clusterConnectionMain.getMetadata().getName()) + .returnFirst(); + + // Creating a second ClusterConnection CR is only required for the tests + var clusterConnectionDb = given.one() + .clusterConnection() + .withName("test-connection-schema-pending-to-ready-db") + .withDatabase(database.getSpec().getName()) + .returnFirst(); + + var schema = given.one() + .schema() + .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) + .withReclaimPolicy(DELETE) + .returnFirst(); + + var spec = schema.getSpec(); + var clusterRef = spec.getClusterRef(); + + var dsl = postgreSQLContextFactory.getDSLContext(clusterConnectionDb); + + spec.setOwner(schemaService.fetchSchemaOwner(dsl, spec)); + + // and: the ClusterConnection is not available for a while + clusterRef.setName("non-existing-cc"); + + var pendingSchema = applySchema(schema); + + assertThat(pendingSchema.getStatus().getPhase()).isEqualTo(CRPhase.PENDING); + + // when: the ClusterConnection is available again + clusterRef.setName(clusterConnectionDb.getMetadata().getName()); + + var updatedSchema = applySchema(schema); + + // then + assertThat(updatedSchema.getStatus().getPhase()).isEqualTo(CRPhase.READY); + assertThat(updatedSchema.getStatus().getMessage()).isNull(); + } + + private Schema applySchema(Schema schema) { + var namespace = kubernetesClient.getNamespace(); + + schema.getMetadata().setManagedFields(null); + schema.getMetadata().setResourceVersion(null); + + var applied = kubernetesClient.resources(Schema.class) + .inNamespace(namespace) + .resource(schema) + .serverSideApply(); + + var generation = applied.getMetadata().getGeneration(); + + //noinspection ConstantConditions + return kubernetesClient.resources(Schema.class) + .inNamespace(namespace) + .withName(applied.getMetadata().getName()) + .waitUntilCondition( + s -> s.getStatus() != null && s.getStatus().getObservedGeneration() >= generation, + 5, + TimeUnit.SECONDS + ); + } + private static void assertThatSchemaHasExpectedStatus( Schema schema, CRStatus expectedStatus, diff --git a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java index e12a90d..005d692 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java @@ -87,7 +87,7 @@ void helmInstall_createsDeployment() throws IOException { // 1. Verify files exist and contain expected data // ./Chart.yaml - Map chartMetadata = serialization.unmarshal( + var chartMetadata = serialization.unmarshal( Files.newInputStream(chartPath.resolve("Chart.yaml")), new TypeReference>() { } @@ -96,7 +96,7 @@ void helmInstall_createsDeployment() throws IOException { assertThat(chartMetadata.get("name")).isEqualTo(chartName); // ./values.yaml - Map values = serialization.unmarshal( + var values = serialization.unmarshal( Files.newInputStream(chartPath.resolve("values.yaml")), new TypeReference>() { } @@ -215,7 +215,7 @@ void helmInstall_createsDeployment() throws IOException { .consumeLinesWith(65536, line -> installOutput.append(line).append(System.lineSeparator())) .run(); - int installExitCode = holder.exitCode; + var installExitCode = holder.exitCode; assertThat(installExitCode) .withFailMessage("Helm install failed with output:\n" + installOutput) .isZero();