SystemFlagsDeployer { SystemFlagsDeployResult deployFlags(SystemFlagsDataArchive archive, boolean dryRun) { Map<FlagsTarget, Future<SystemFlagsDeployResult>> futures = new HashMap<>(); for (FlagsTarget target : targets) { futures.put(target, executor.submit(() -> deployFlags(target, archive.flagData(target), dryRun))); } List<SystemFlagsDeployResult> results = new ArrayList<>(); futures.forEach((target, future) -> { try { results.add(future.get()); } catch (InterruptedException | ExecutionException e) { log.log(Level.SEVERE, String.format("Failed to deploy flags for target '%s': %s", target, e.getMessage()), e); throw new RuntimeException(e); } }); try { archive.validateAllFilesAreForTargets(system, targets); } catch (IllegalArgumentException e) { results.add(new SystemFlagsDeployResult(List.of(OperationError.archiveValidationFailed(e.getMessage())))); } return SystemFlagsDeployResult.merge(results); } SystemFlagsDeployer(ServiceIdentityProvider identityProvider, SystemName system, Set<FlagsTarget> targets);   SystemFlagsDeployer(FlagsClient client, SystemName system, Set<FlagsTarget> targets);   }
@Test public void deploys_flag_data_to_targets() throws IOException { FlagsClient flagsClient = mock(FlagsClient.class); when(flagsClient.listFlagData(controllerTarget)).thenReturn(List.of()); when(flagsClient.listFlagData(prodUsWest1Target)).thenReturn(List.of(flagData("existing-prod.us-west-1.json"))); FlagData existingProdUsEast3Data = flagData("existing-prod.us-east-3.json"); when(flagsClient.listFlagData(prodUsEast3Target)).thenReturn(List.of(existingProdUsEast3Data)); FlagData defaultData = flagData("flags/my-flag/main.json"); FlagData prodUsEast3Data = flagData("flags/my-flag/main.prod.us-east-3.json"); SystemFlagsDataArchive archive = new SystemFlagsDataArchive.Builder() .addFile("main.json", defaultData) .addFile("main.prod.us-east-3.json", prodUsEast3Data) .build(); SystemFlagsDeployer deployer = new SystemFlagsDeployer(flagsClient, SYSTEM, Set.of(controllerTarget, prodUsWest1Target, prodUsEast3Target)); SystemFlagsDeployResult result = deployer.deployFlags(archive, false); verify(flagsClient).putFlagData(controllerTarget, defaultData); verify(flagsClient).putFlagData(prodUsEast3Target, prodUsEast3Data); verify(flagsClient, never()).putFlagData(prodUsWest1Target, defaultData); List<FlagDataChange> changes = result.flagChanges(); assertThat(changes).containsOnly( FlagDataChange.created(FLAG_ID, controllerTarget, defaultData), FlagDataChange.updated(FLAG_ID, prodUsEast3Target, prodUsEast3Data, existingProdUsEast3Data)); }
@Test public void dryrun_should_not_change_flags() throws IOException { FlagsClient flagsClient = mock(FlagsClient.class); when(flagsClient.listFlagData(controllerTarget)).thenReturn(List.of()); when(flagsClient.listDefinedFlags(controllerTarget)).thenReturn(List.of(new FlagId("my-flag"))); FlagData defaultData = flagData("flags/my-flag/main.json"); SystemFlagsDataArchive archive = new SystemFlagsDataArchive.Builder() .addFile("main.json", defaultData) .build(); SystemFlagsDeployer deployer = new SystemFlagsDeployer(flagsClient, SYSTEM, Set.of(controllerTarget)); SystemFlagsDeployResult result = deployer.deployFlags(archive, true); verify(flagsClient, times(1)).listFlagData(controllerTarget); verify(flagsClient, never()).putFlagData(controllerTarget, defaultData); verify(flagsClient, never()).deleteFlagData(controllerTarget, FLAG_ID); assertThat(result.flagChanges()).containsOnly( FlagDataChange.created(FLAG_ID, controllerTarget, defaultData)); assertThat(result.errors()).isEmpty(); }
@Test public void creates_error_entries_in_result_if_flag_data_operations_fail() throws IOException { FlagsClient flagsClient = mock(FlagsClient.class); UncheckedIOException exception = new UncheckedIOException(new IOException("I/O error message")); when(flagsClient.listFlagData(prodUsWest1Target)).thenThrow(exception); when(flagsClient.listFlagData(prodUsEast3Target)).thenReturn(List.of()); when(flagsClient.listDefinedFlags(prodUsEast3Target)).thenReturn(List.of(new FlagId("my-flag"))); FlagData defaultData = flagData("flags/my-flag/main.json"); SystemFlagsDataArchive archive = new SystemFlagsDataArchive.Builder() .addFile("main.json", defaultData) .build(); SystemFlagsDeployer deployer = new SystemFlagsDeployer(flagsClient, SYSTEM, Set.of(prodUsWest1Target, prodUsEast3Target)); SystemFlagsDeployResult result = deployer.deployFlags(archive, false); assertThat(result.errors()).containsOnly( OperationError.listFailed(exception.getMessage(), prodUsWest1Target)); assertThat(result.flagChanges()).containsOnly( FlagDataChange.created(FLAG_ID, prodUsEast3Target, defaultData)); }
@Test public void creates_error_entry_for_invalid_flag_archive() throws IOException { FlagsClient flagsClient = mock(FlagsClient.class); FlagData defaultData = flagData("flags/my-flag/main.json"); SystemFlagsDataArchive archive = new SystemFlagsDataArchive.Builder() .addFile("main.prod.unknown-region.json", defaultData) .build(); SystemFlagsDeployer deployer = new SystemFlagsDeployer(flagsClient, SYSTEM, Set.of(controllerTarget)); SystemFlagsDeployResult result = deployer.deployFlags(archive, false); assertThat(result.flagChanges()) .isEmpty(); assertThat(result.errors()) .containsOnly(OperationError.archiveValidationFailed("Unknown flag file: flags/my-flag/main.prod.unknown-region.json")); }
@Test public void creates_error_entry_for_flag_data_of_undefined_flag() throws IOException { FlagData prodUsEast3Data = flagData("flags/my-flag/main.prod.us-east-3.json"); FlagsClient flagsClient = mock(FlagsClient.class); when(flagsClient.listFlagData(prodUsEast3Target)) .thenReturn(List.of()); when(flagsClient.listDefinedFlags(prodUsEast3Target)) .thenReturn(List.of()); SystemFlagsDataArchive archive = new SystemFlagsDataArchive.Builder() .addFile("main.prod.us-east-3.json", prodUsEast3Data) .build(); SystemFlagsDeployer deployer = new SystemFlagsDeployer(flagsClient, SYSTEM, Set.of(prodUsEast3Target)); SystemFlagsDeployResult result = deployer.deployFlags(archive, true); assertThat(result.errors()) .containsOnly(SystemFlagsDeployResult.OperationError.createFailed("Flag not defined in target zone", prodUsEast3Target, prodUsEast3Data)); }
@Test public void creates_warning_entry_for_existing_flag_data_for_undefined_flag() throws IOException { FlagData prodUsEast3Data = flagData("flags/my-flag/main.prod.us-east-3.json"); FlagsClient flagsClient = mock(FlagsClient.class); when(flagsClient.listFlagData(prodUsEast3Target)) .thenReturn(List.of(prodUsEast3Data)); when(flagsClient.listDefinedFlags(prodUsEast3Target)) .thenReturn(List.of()); SystemFlagsDataArchive archive = new SystemFlagsDataArchive.Builder() .addFile("main.prod.us-east-3.json", prodUsEast3Data) .build(); SystemFlagsDeployer deployer = new SystemFlagsDeployer(flagsClient, SYSTEM, Set.of(prodUsEast3Target)); SystemFlagsDeployResult result = deployer.deployFlags(archive, true); assertThat(result.warnings()) .containsOnly(SystemFlagsDeployResult.Warning.dataForUndefinedFlag(prodUsEast3Target, new FlagId("my-flag"))); }