Share
Embed
Руслан Черемин — Тестирование конфигурации для Java-разработчиков: практический опыт
На одном из Heisenbug Андрей Сатарин рассказывал, как можно покрывать тестами не только код, но и конфигурацию.
С его подачи последние 3 года в Deutsche Bank используют этот подход в своих проектах.
И да, тесты для конфигурации существенно уменьшают количество ошибок при развертывании приложения.
Но писать и поддерживать такие тесты может быть непросто, это новая и непривычная задача для тестирования, со своими тонкостями.
Руслан поделится своим опытом: с чего начинать, какие есть подводные камни,
какие решения оказались удобными и полезными при разработке таких тестов на Java.
Moscow JUG
June 07, 2018
Transcript
89 slides
-
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Safe harbour Данный
материал не является предложением или предоставлением какой- либо услуги. Данный материал предназначен исключительно для информационных и иллюстративных целей и не предназначен для распространения в рекламных целях. Любой анализ третьих сторон не предполагает какого-либо одобрения или рекомендации. Мнения, выраженные в данном материале, являются актуальными на текущий момент, появляются только в этом материале и могут быть изменены без предварительного уведомления. Эта информация предоставляется с пониманием того, что в отношении материала, предоставленного здесь, вы будете принимать самостоятельное решение в отношении любых действий в связи с настоящим материалом, и это решение является основанным на вашем собственном суждении, и что вы способны понять и оценить последствия этих действий. ООО "Дойче Банк Техцентр" не несет никакой ответственности за любые убытки любого рода, относящихся к этому материалу. !2 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Простыми словами -Все
персонажи выдуманы -Пользуйтесь с осторожностью -Похороны за свой счет !3 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Есть важный код
src/main/java/com/db/app/ GainMoney.java … !5 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Для важного кода
есть тесты src/main/java/com/db/app/ GainMoney.java src/test/java/com/db/app/ GainMoneyTest.java … !6 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Есть конфигурация src/main/java/com/db/app/
GainMoney.java src/test/java/com/db/app/ GainMoneyTest.java src/main/resources/ app-lab.properties app-uat.properties app-prod.properties !7 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Почему ее важно
тестировать? - ошибки конфигурации вредят бизнесу так же, как и ошибки кода - конфигурация не проверяется компилятором/IDE - а часто недостаточно проверяется и при использовании - UAT не гарантирует корректности PROD- конфигурации - и даже в PROD многие ошибки проявляются не сразу !8 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Тесты для конфигурации:
This page was intentionally left blank !9 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Общий план -
Что можно успеть до обеда в понедельник - простые полезные примеры, “так можно делать” - Понедельник, два года спустя: - где и как можно сделать лучше - Поддержка для рефакторинга конфигурации - как добиться плотного покрытия - программная модель конфигурации !11 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre JMX ports conflict
issue env-uat.properties: configchanger.jmx.port = 18501 … spotserver.jmx.port = 18503 … republisher.jmx.port = 18504 … ratefan.jmx.port = 18505 … newservice.jmx.port = 18505 !13 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre JMX ports conflict
issue @Test public void jmxPortsAreUnique() { File configsFolder = new File(CONFIGS_FOLDER); for( File configFile : configsFolder.listFiles(…) ) { Properties config = load( configFile ); List<String> ports = filterValues( config, name -> name.contains( "jmx.port" ) ); assertThat( ports, itemsAreUnique() ); } } !14 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre JMX ports conflict
issue @Test public void jmxPortsAreUnique() { File configsFolder = new File(CONFIGS_FOLDER); for( File configFile : configsFolder.listFiles(…) ) { Properties config = load( configFile ); List<String> ports = filterValues( config, name -> name.contains( "jmx.port" ) ); assertThat( ports, itemsAreUnique() ); } } !15 WTFА что, так можно было? -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Ports are all
valid @Test public void portsAreValid() { File configsFolder = new File(CONFIGS_FOLDER); for( File configFile : configsFolder.listFiles(…) ) { Properties config = load( configFile ); List<String> ports = filterValues( config, name -> name.endsWith( ".port" ) ); assertThat( ports, eachItem(intValue(is(validNetworkPort()))) ); } } !16 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что проверяем здесь?
@Test public void obscured() { File configsFolder = new File(CONFIGS_FOLDER); for( File configFile : configsFolder.listFiles(…) ) { if( configFile.getName().contains( “prod" ) ) { Properties config = load( configFile ); List<String> passwords = filterValues( config, name -> name.contains( ".passw" ) ); assertThat( passwords, everyItem(is(placeholder()))); } } } !17 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что проверяем здесь?
@Test public void obscured() { File configsFolder = new File(CONFIGS_FOLDER); for( File configFile : configsFolder.listFiles(…) ) { if( configFile.getName().contains( “prod" ) ) { Properties config = load( configFile ); List<String> passwords = filterValues( config, name -> name.contains( ".passw" ) ); assertThat( passwords, everyItem(is(placeholder()))); } } } !18 jdbc.password = ${very.secret.password} -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre — А что
считать конфигурацией? !19 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre — А что
считать конфигурацией? — Да все, что хочется !20 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: EOF для
SQL-plus 14365-insert-important-data.sql: … INSERT INTO … VALUES … … / <<EOF>> must be on new line! !21 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: EOF для
SQL-plus @RunWith( Theories.class ) public class SQLPlusScriptsTest { @Theory public void scriptHasCorrectEnding( File sqlFile ) { String sql = Files.toString( sqlFile, US_ASCII ); assertThat( sql, endsWith( "\n" ) ); assertThat( sql.trim(), endsWith( "/" ) ); } @DataPoints public static File[] sqlScripts() { return SQL_PATCHES_FOLDER.listFiles( ( dir, name ) -> name.endsWith( ".sql" ) ); } } !22 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: crontabs lab-sg.crontab:
0 12 * * 0 …/startAllServices.sh 0 10 * * 6 …/stopAllServices.sh !23 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: crontabs lab-sg.crontab:
0 12 * * 0 …/startAllServices.sh 0 10 * * 6 …/stopAllServices.sh <<EOF>> !24 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: crontabs lab-sg.crontab:
0 12 * * 0 …/startAllServices.sh 0 10 * * 6 …/stopAllServices.sh <<EOF>> !25 @Theory schedulesAreValid(File crontab) @Theory EOF_isOnNewLine(File crontab) -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: shell-скрипты env-uat-us.sh:
… JAVA_HOME=… LD_LIBRARY_PATH=… AGGRESSIVE=1 … !26 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: shell-скрипты env-uat-us.sh:
… JAVA_HOME=… LD_LIBRARY_PATH=… AGGRESSIVE=1 … !27 @Theory javaHomeIsDefined(File envFile) @Theory NDALib_IsInLD_LIBRARY_PATH(File envFile) -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Итого - Тесты
конфигурации поначалу смущают - Потом пишутся легко и весело - Много easy wins/low hanging fruits - Уменьшают затраты на обнаружение и исправление ошибок конфигурации - Дарят вторую молодость !28 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Per-file tests .../resources
app-lab-uk.properties app-uat-us.properties app-prod-uk.properties !29 @Theory forEachFileSomethingIsTrue(File envFile) -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Сценарии посложнее -
UI-приложение соединяется с сервером своего environment-а - Все сервисы одного environment-а соединяются с одним и тем же management-сервером - Все сервисы одного environment-а используют одну и ту же базу данных !30 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre !31 pricing-server: lab-uk.properties
uat-us.properties prod-uk.properties client-api: client-lab-uk.properties client-uat-us.properties client-prod-uk.properties control-server: lab-uk.properties uat-us.properties prod-uk.properties rate-fan: lab-uk.properties uat-us.properties prod-uk.properties risk-management: arm-lab-uk.properties arm-uat-us.properties arm-prod-uk.properties vdc: vdc-lab-uk.properties vdc-uat-us.properties vdc-prod-uk.properties monitoring-bridge: mon-lab-uk.properties mon-uat-us.properties mon-prod-uk.properties dashboard: lab-uk.properties uat-us.properties prod-uk.properties common serverside: common-lab-uk.properties common-uat-us.properties common-prod-uk.properties -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Как было бы
удобнее? @Theory public void eachEnvironmentIsXXX( Environment environment ) { for( Server server : environment.servers() ) { for( Service service : server.services() ) { Properties config = buildConfigFor( environment, server, service ); //… check {something} about config } } } !32 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Deployment layout !33
environment server#1 service#1 config#1 config#2 config#3 server#2 server#3 service#2 service#3 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Deployment layout !34
environment server#1 service#1 config#1 config#2 config#3 server#2 server#3 service#2 service#3 +rack, +region, +data center… -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Где взять deployment
layout? 1. Сначала — захардкодить 2. Потом (может быть когда-нибудь) заинтегрироваться с существующей системой IM !35 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Deployment layout public
enum Environment { PROD( PROD_UK_PRIMARY, PROD_UK_BACKUP, PROD_US_PRIMARY, PROD_US_BACKUP, PROD_SG_PRIMARY, PROD_SG_BACKUP ) … public Server[] servers() {…} } public enum Server { PROD_UK_PRIMARY(“rflx-ldn-1"), PROD_UK_BACKUP("rflx-ldn-2"), PROD_US_PRIMARY(“rflx-nyc-1"), PROD_US_BACKUP("rflx-nyc-2"), PROD_SG_PRIMARY(“rflx-sng-1"), PROD_SG_BACKUP("rflx-sng-2"), public Service[] services() {…} } !36 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Deployment layout public
enum Environment { PROD( PROD_UK_PRIMARY, PROD_UK_BACKUP, PROD_US_PRIMARY, PROD_US_BACKUP, PROD_SG_PRIMARY, PROD_SG_BACKUP ) … public Server[] servers() {…} } public enum Server { PROD_UK_PRIMARY(“rflx-ldn-1"), PROD_UK_BACKUP("rflx-ldn-2"), PROD_US_PRIMARY(“rflx-nyc-1"), PROD_US_BACKUP("rflx-nyc-2"), PROD_SG_PRIMARY(“rflx-sng-1"), PROD_SG_BACKUP("rflx-sng-2"), public Service[] services() {…} } !37 startServices-prod-uk-bcp.sh: … $START control-server $START pricing-server $START rate-fan … -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Приятные бонусы на
сдачу public enum Environment { PROD( PROD_UK_PRIMARY, PROD_UK_BACKUP, PROD_US_PRIMARY, PROD_US_BACKUP, PROD_SG_PRIMARY, PROD_SG_BACKUP ) … public Server[] servers() {…} } public enum Server { PROD_UK_PRIMARY(“rflx-ldn-1"), PROD_UK_BACKUP("rflx-ldn-2"), PROD_US_PRIMARY(“rflx-nyc-1"), PROD_US_BACKUP("rflx-nyc-2"), PROD_SG_PRIMARY(“rflx-sng-1"), PROD_SG_BACKUP("rflx-sng-2"), public Service[] services() {…} } !38 @Theory eachEnvironmentHasCoreServices(Environment) @Theory criticalServicesHaveBackupInstances(Environment) -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Поддерживаем актуальность public
class HardCodedLayoutConsistencyTest { @Theory eachHardCodedEnvironmentHasConfigFiles(Environment env){ … } @Theory eachConfigFileHasHardCodedEnvironment(File configFile){ … } } !39 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Итого: deployment layout
- Упрощает написание сложных тестов - Делает их нагляднее и читабельнее - В ходе его создания выясняются многие сакральные знания о deployment-е - Хорошо дополняет документацию (особенно если ее нет) !40 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Проблема assertThat( filterProperties(
properties, name -> name.contains(".port") ), eachItem(intValue(is(validNetworkPort()))) ); —> Error: 123456 is not valid network port !41 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Нет контекста ошибки
assertThat( filterProperties( properties, name -> name.contains(".port") ), eachItem(intValue(is(validNetworkPort()))) ); —> Error: 123456 is not valid network port !42 file, propertyName?.. Чинить — где? -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre “Декларативные” тесты SELECT
environment, server, component, configLocation, propertyName, propertyValue FROM configuration(environment, server, component) WHERE propertyName like “%.port%” and propertyValue is not validNetworkPort() !43 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre “Декларативные” тесты SELECT
environment, server, component, configLocation, propertyName, propertyValue FROM configuration(environment, server, component) WHERE propertyName like “%.port%” and propertyValue is not validNetworkPort() !44 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre “Декларативные” тесты SELECT
environment, server, component, configLocation, propertyName, propertyValue FROM configuration(environment, server, component) WHERE propertyName like “%.port%” and propertyValue is not validNetworkPort() !45 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre “Декларативные” тесты SELECT
environment, server, component, configLocation, propertyName, propertyValue FROM configuration(environment, server, component) WHERE propertyName like “%.port%” and propertyValue is not validNetworkPort() !46 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Use Streams, Luke
ValueWithContext[] incorrectPorts = flattenedProperties( environment ) .filter( propertyNameContains( ".port" ) ) .filter( !isInteger( propertyValue ) || !isValidNetworkPort( propertyValue ) ) .toArray(); assertThat( incorrectPorts, emptyArray() ); !47 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Use Streams, Luke
ValueWithContext[] incorrectPorts = flattenedProperties( environment ) .filter( propertyNameContains( ".port" ) ) .filter( !isInteger( propertyValue ) || !isValidNetworkPort( propertyValue ) ) .toArray(); assertThat( incorrectPorts, emptyArray() ); !48 ValueWithContext { environment, server, service, configPath, propertyName, propertyValue } -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Use Streams, Luke
ValueWithContext[] incorrectPorts = flattenedProperties( environment ) .filter( propertyNameContains( ".port" ) ) .filter( !isInteger( propertyValue ) || !isValidNetworkPort( propertyValue ) ) .toArray(); assertThat( incorrectPorts, emptyArray() ); !49 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Use Streams, Luke
ValueWithContext[] incorrectPorts = flattenedProperties( environment ) .filter( propertyNameContains( ".port" ) ) .filter( !isInteger( propertyValue ) || !isValidNetworkPort( propertyValue ) ) .toArray(); assertThat( incorrectPorts, emptyArray() ); !50 Expected: Empty array got: [<PROD/PROD_UK/vdc:vdc-prod—uk.properties:vdc.jmx.port = 123456>] -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Тестирование как поддержка
для рефакторинга !51 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre С чем имеем
дело xxx.address=${${hostname}.bus.address} … xxx.name=${Seq-${hostname}}.${${hostname}.shortname} … xxx.mcast=${Seq.udp.${seq.${hostname}.order}.recv.mcast} # и так до самого низа… !52 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre С чем имеем
дело apps/sequencer.properties include logging.properties include logging-${OS}.properties include network.properties include admin.properties include ports.properties include env/${env}.properties include hosts/${host-1}.properties include hosts/${host-2}.properties include hosts/${host-3}.properties !53 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Конфигурация 1 сервиса
10 include-ов глубиной 3 ~450 параметров всего Нужны этому сервису только 10-15% !54 * Автор — гений, но его с нами больше нет -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется !55 -
Конфигурацию отрефакторить и упростить -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется !56 -
Конфигурацию отрефакторить и упростить - После рефакторинга каждый сервис имеет все необходимые параметры -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется !57 -
Конфигурацию отрефакторить и упростить - После рефакторинга каждый сервис имеет все необходимые параметры - …и не имеет лишних параметров -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется !58 -
Конфигурацию отрефакторить и упростить - После рефакторинга каждый сервис имеет все необходимые параметры - …и не имеет лишних параметров - Сервисы успешно соединяются в кластер -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется - Конфигурацию
отрефакторить и упростить - После рефакторинга каждый сервис имеет все необходимые параметры - …и не имеет лишних параметров - Сервисы успешно соединяются в кластер - (…но неизвестно, соединяются ли они сейчас!) !59 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Программа-минимум !60 -
Проверить корректность каждого параметра каждого сервиса в изоляции - Проверить ключевые взаимосвязи между параметрами -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Нужна модель конфигурации
Service1: parameters in use { TCP.Address: string( IP | hostname ) TCP.Port: int( 2000 … 32000, not 2378, 13654 ) UDP.Multicast: string( e.g. IP class D ) UDP.TTL: int( >0 ), default 4 LOG.DIR: string( path ) default “./logs” TMP.DIR: string( path ) default ${java.io.tmp} … } !61 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Чем она нам
поможет? !62 Configuration( Full ){ ~450 параметров } Model( Service1 ){ 26 описаний параметров } Configuration( Service1 ){ 26 параметров + типизированные + значения по-умолчанию + валидированные } ConfigurationException{ “TCP.Port = ${…}” is not valid network port } -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Модель конфигурации в
коде interface IConfigurationModel { @Override List<FetchedValue<?>> fetch( Configuration config ); } class FetchedValue<T> { public final String propertyName; public final T propertyValue; … } !63 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Начнем с простого
public class SimpleComponent { … public void configure( final Configuration conf ) { int port = conf.getInt( "Port", -1 ); if( port < 0 ) throw new ConfigurationException(); String ip = conf.getString( "Address", null ); if( ip == null ) throw new ConfigurationException(); … } … } !65 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Начнем с простого
public class SimpleComponent { … public void configure( final Configuration conf ) { int port = conf.getInt( "Port", -1 ); if( port < 0 ) throw new ConfigurationException(); String ip = conf.getString( "Address", null ); if( ip == null ) throw new ConfigurationException(); … } … } !66 name default value validation type -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Элементарные свойства interface
IProperty<T> { /* (name, defaultValue, matcher…) */ /** lookup (or use default), * convert type, * validate value against matcher */ FetchedValue<T> fetch( Configuration config ) } class FetchedValue<T> { public final String propertyName; public final T propertyValue; … } !67 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Элементарные свойства IProperty<Integer>
PORT = intProperty("Port") .withDefaultValue( -1 ) .matchedWith( validNetworkPort() ); IProperty<String> ADDRESS = stringProperty("Address") .withDefaultValue( null ) .matchedWith( validIPAddress() ); !68 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Модель конфигурации class
SimpleComponentModel implements IConfigurationModel{ IProperty<Integer> PORT = intProperty("Port") .withDefaultValue( -1 ) .matchedWith( validNetworkPort() ); IProperty<String> ADDRESS = stringProperty("Address") .withDefaultValue( null ) .matchedWith( validIPAddress() ); @Override List<FetchedValue<?>> fetch( Configuration config ){ return asList( PORT.fetch( config ), ADDRESS.fetch( config ) ); } } !69 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Задача решена? Да,
но: - Модель громоздкая (20-60 полей) - Сложно поддерживать: нет прямого соответствия между кодом и его моделью - Что делать с ветвлениями и полиморфизмом? !70 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Задача решена? Да,
но: - Модель громоздкая (20-60 полей) - Сложно поддерживать: нет прямого соответствия между кодом и его моделью - Что делать с ветвлениями и полиморфизмом? Похоже, нужна декомпозиция - конфигурируемый класс => ConfigurationModel !71 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Модели комбинируемы class
ComplexComponentModel implements IConfigurationModel{ final IConfigurationModel subComponent1 = …; final IConfigurationModel subComponent2 = …; @Override List<FetchedValue<?>> fetch(Configuration config){ return union( subComponent1.fetch(config), subComponent2.fetch(config) ); } } !72 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что получается -
Иерархия объектов в сервисе соответствует (+/-) иерархии ConfigurationModels - Есть (почти) прямое соответствие между классом и его моделью !73 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Чего это стоило
- 12 сервисов - 70 конфигурируемых классов => 70 ConfigurationModels (~60 тривиальны) - 2 человеко-недели !74 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Как с этой
хренью взлетать @Theory public void propertiesValidInIsolation( Environment environment ){ for( Server server : environment.servers() ) { for( Service service : server.services() ) { Configuration config = fetchConfigFor( environment, server, service ); IConfigurationModel model = service.configurationModel(); model.fetch( config ); } } } !75 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Взаимозависимости сервисов !76
Большинство зависимостей это сетевые связи: “Сервис А слушает именно тот адрес, на который отправляет пакеты сервис Б” -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Network endpoints IProperty<IEndpoint>
TCP_REQUEST = outcomingTCP( // (+matchers, +default values) “TCP.Request.Address”, “TCP.Request.Port” ); class OutcomingTCPEndpoint implements IEndpoint { //(localInterface, localAddress, multicastGroup, port) @Override boolean matches( IEndpoint other); } !77 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Проверка связности кластера
ValueWithContext[] allEndpoints = flattenedConfigurationValues(environment) .filter( valueIsEndpoint() ) .toArray(); ValueWithContext[] unpairedEndpoints = Arrays.stream( allEndpoints ) .filter( e -> !hasMatchedEndpoint(e, allEndpoints) ) .toArray(); assertThat( unpairedEndpoints, emptyArray() ); !78 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Швейцарский нож на
сдачу… ConfigurationModel позволяет: - Конвертировать в другой формат - Выполнять запросы к конфигурации (“все udp-порты, используемые сервисами на данном сервере”) - Экспортировать сетевые связи между сервисами в виде диаграммы !79 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Выводы - Тестировать
конфигурацию важно - Поле непаханое, а порог входа низкий - От самых простых тестов уже много пользы - Тестировать сложные взаимосвязи тоже возможно - На простых тестах возможности не заканчиваются - Можно решать и сложные задачи - Получая на сдачу полезные инструменты !80 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что дальше? !81
Дальше пока не придумал, импровизируй -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что плохо !82
“...подход разочаровал. Вместо систематического контроля конфигов мы наделали еще кучу кода на каждый чих“ *Из отзывов с Гейзенбага 2018 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Модель конфигурации Service1
{ TCP.Address: string( IP | hostname ) TCP.Port: int( 2000 … 32000, not 2378, 13654 ) UDP.Multicast: string( e.g. IP class D ) UDP.TTL: int( >0 ), default 4 LOG.DIR: string( path ) default “./logs” TMP.DIR: string( path ) default ${java.io.tmp} … } !83 -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Модель конфигурации Service1
{ TCP.Address: string( IP | hostname ) TCP.Port: int( 2000 … 32000, not 2378, 13654 ) UDP.Multicast: string( e.g. IP class D ) UDP.TTL: int( >0 ), default 4 LOG.DIR: string( path ) default “./logs” TMP.DIR: string( path ) default ${java.io.tmp} … } !84 Создана для тестирования конфигурации. А почему не для самой конфигурации? -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что могло бы
быть лучше? !85 - Валидаторы конфигурации - Например XML/JSON-schema - Описывают валидацию конфигурации с помощью кода - Может быть сразу конфигурировать приложение с помощью кода? -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Configuration as code
!86 - Я не имею в виду bash code - Я не имею в виду “что-то, что лежит в VCS” - Я имею в виду код: - Строго типизированный, компилируемый - С поддержкой IDE - С возможностью использовать доменные объекты основного приложения -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Configuration as code
!87 - Я не имею в виду bash/JavaScript - Я не имею в виду “что-то, что лежит в VCS” - Я имею в виду код: - Строго типизированный, компилируемый - С поддержкой IDE - С возможностью использовать доменные объекты основного приложения - То есть Java/Kotlin :) -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Идея бродит в
воздухе !88 - “Автоматизация экспериментов с Kotlin DSL” /Aleksandr Tarasov - “Kotlin DSL: теория и практика” /Иван Осипов -
Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Для жалоб и
предложений Руслан Черемин [email protected], [email protected] blog: dev.cheremin.info !89