Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Руслан Черемин — Тестирование конфигурации для ...

Руслан Черемин — Тестирование конфигурации для Java-разработчиков: практический опыт

На одном из Heisenbug Андрей Сатарин рассказывал, как можно покрывать тестами не только код, но и конфигурацию.

С его подачи последние 3 года в Deutsche Bank используют этот подход в своих проектах.
И да, тесты для конфигурации существенно уменьшают количество ошибок при развертывании приложения.
Но писать и поддерживать такие тесты может быть непросто, это новая и непривычная задача для тестирования, со своими тонкостями.

Руслан поделится своим опытом: с чего начинать, какие есть подводные камни,
какие решения оказались удобными и полезными при разработке таких тестов на Java.

Avatar for Moscow JUG

Moscow JUG

June 07, 2018
Tweet

More Decks by Moscow JUG

Other Decks in Programming

Transcript

  1. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Safe harbour Данный

    материал не является предложением или предоставлением какой- либо услуги. Данный материал предназначен исключительно для информационных и иллюстративных целей и не предназначен для распространения в рекламных целях. Любой анализ третьих сторон не предполагает какого-либо одобрения или рекомендации. Мнения, выраженные в данном материале, являются актуальными на текущий момент, появляются только в этом материале и могут быть изменены без предварительного уведомления. Эта информация предоставляется с пониманием того, что в отношении материала, предоставленного здесь, вы будете принимать самостоятельное решение в отношении любых действий в связи с настоящим материалом, и это решение является основанным на вашем собственном суждении, и что вы способны понять и оценить последствия этих действий. ООО "Дойче Банк Техцентр" не несет никакой ответственности за любые убытки любого рода, относящихся к этому материалу. !2
  2. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Простыми словами -Все

    персонажи выдуманы -Пользуйтесь с осторожностью -Похороны за свой счет !3
  3. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Для важного кода

    есть тесты src/main/java/com/db/app/ GainMoney.java src/test/java/com/db/app/ GainMoneyTest.java … !6
  4. Руслан Черемин 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
  5. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Почему ее важно

    тестировать? - ошибки конфигурации вредят бизнесу так же, как и ошибки кода - конфигурация не проверяется компилятором/IDE - а часто недостаточно проверяется и при использовании - UAT не гарантирует корректности PROD- конфигурации - и даже в PROD многие ошибки проявляются не сразу !8
  6. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Общий план -

    Что можно успеть до обеда в понедельник - простые полезные примеры, “так можно делать” - Понедельник, два года спустя: - где и как можно сделать лучше - Поддержка для рефакторинга конфигурации - как добиться плотного покрытия - программная модель конфигурации !11
  7. Руслан Черемин 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
  8. Руслан Черемин 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
  9. Руслан Черемин 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А что, так можно было?
  10. Руслан Черемин 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
  11. Руслан Черемин 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
  12. Руслан Черемин 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}
  13. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre — А что

    считать конфигурацией? — Да все, что хочется !20
  14. Руслан Черемин 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
  15. Руслан Черемин 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
  16. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: crontabs lab-sg.crontab:

    0 12 * * 0 …/startAllServices.sh 0 10 * * 6 …/stopAllServices.sh !23
  17. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Пример: crontabs lab-sg.crontab:

    0 12 * * 0 …/startAllServices.sh 0 10 * * 6 …/stopAllServices.sh <<EOF>> !24
  18. Руслан Черемин 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)
  19. Руслан Черемин 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)
  20. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Итого - Тесты

    конфигурации поначалу смущают - Потом пишутся легко и весело - Много easy wins/low hanging fruits - Уменьшают затраты на обнаружение и исправление ошибок конфигурации - Дарят вторую молодость !28
  21. Руслан Черемин 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)
  22. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Сценарии посложнее -

    UI-приложение соединяется с сервером своего environment-а - Все сервисы одного environment-а соединяются с одним и тем же management-сервером - Все сервисы одного environment-а используют одну и ту же базу данных !30
  23. Руслан Черемин 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
  24. Руслан Черемин 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
  25. Руслан Черемин 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
  26. Руслан Черемин 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…
  27. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Где взять deployment

    layout? 1. Сначала — захардкодить 2. Потом (может быть когда-нибудь) заинтегрироваться с существующей системой IM !35
  28. Руслан Черемин 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
  29. Руслан Черемин 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 …
  30. Руслан Черемин 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)
  31. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Поддерживаем актуальность public

    class HardCodedLayoutConsistencyTest {
 @Theory
 eachHardCodedEnvironmentHasConfigFiles(Environment env){ … }
 
 @Theory
 eachConfigFileHasHardCodedEnvironment(File configFile){ … }
 }
 !39
  32. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Итого: deployment layout

    - Упрощает написание сложных тестов - Делает их нагляднее и читабельнее - В ходе его создания выясняются многие сакральные знания о deployment-е - Хорошо дополняет документацию (особенно если ее нет) !40
  33. Руслан Черемин 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
  34. Руслан Черемин 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?.. Чинить — где?
  35. Руслан Черемин 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
  36. Руслан Черемин 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
  37. Руслан Черемин 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
  38. Руслан Черемин 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
  39. Руслан Черемин 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
  40. Руслан Черемин 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 }
  41. Руслан Черемин 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
  42. Руслан Черемин 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>]
  43. Руслан Черемин 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
  44. Руслан Черемин 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
  45. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Конфигурация 1 сервиса

    10 include-ов глубиной 3 ~450 параметров всего Нужны этому сервису только 10-15% !54 * Автор — гений, но его с нами больше нет
  46. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется !55 -

    Конфигурацию отрефакторить и упростить
  47. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется !56 -

    Конфигурацию отрефакторить и упростить - После рефакторинга каждый сервис имеет все необходимые параметры
  48. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется !57 -

    Конфигурацию отрефакторить и упростить - После рефакторинга каждый сервис имеет все необходимые параметры - …и не имеет лишних параметров
  49. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется !58 -

    Конфигурацию отрефакторить и упростить - После рефакторинга каждый сервис имеет все необходимые параметры - …и не имеет лишних параметров - Сервисы успешно соединяются в кластер
  50. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Хочется - Конфигурацию

    отрефакторить и упростить - После рефакторинга каждый сервис имеет все необходимые параметры - …и не имеет лишних параметров - Сервисы успешно соединяются в кластер - (…но неизвестно, соединяются ли они сейчас!) !59
  51. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Программа-минимум !60 -

    Проверить корректность каждого параметра каждого сервиса в изоляции - Проверить ключевые взаимосвязи между параметрами
  52. Руслан Черемин 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
  53. Руслан Черемин 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 }
  54. Руслан Черемин 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
  55. Руслан Черемин 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
  56. Руслан Черемин 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
  57. Руслан Черемин 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
  58. Руслан Черемин 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
  59. Руслан Черемин 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
  60. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Задача решена? Да,

    но: - Модель громоздкая (20-60 полей) - Сложно поддерживать: нет прямого соответствия между кодом и его моделью - Что делать с ветвлениями и полиморфизмом? !70
  61. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Задача решена? Да,

    но: - Модель громоздкая (20-60 полей) - Сложно поддерживать: нет прямого соответствия между кодом и его моделью - Что делать с ветвлениями и полиморфизмом? Похоже, нужна декомпозиция - конфигурируемый класс => ConfigurationModel !71
  62. Руслан Черемин 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
  63. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что получается -

    Иерархия объектов в сервисе соответствует (+/-) иерархии ConfigurationModels - Есть (почти) прямое соответствие между классом и его моделью !73
  64. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Чего это стоило

    - 12 сервисов - 70 конфигурируемых классов 
 => 70 ConfigurationModels (~60 тривиальны) - 2 человеко-недели !74
  65. Руслан Черемин 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
  66. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Взаимозависимости сервисов !76

    Большинство зависимостей это сетевые связи: “Сервис А слушает именно тот адрес, на который отправляет пакеты сервис Б”
  67. Руслан Черемин 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
  68. Руслан Черемин 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
  69. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Швейцарский нож на

    сдачу… ConfigurationModel позволяет: - Конвертировать в другой формат - Выполнять запросы к конфигурации
 (“все udp-порты, используемые сервисами на данном сервере”) - Экспортировать сетевые связи между сервисами в виде диаграммы !79
  70. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Выводы - Тестировать

    конфигурацию важно - Поле непаханое, а порог входа низкий - От самых простых тестов уже много пользы - Тестировать сложные взаимосвязи тоже возможно - На простых тестах возможности не заканчиваются - Можно решать и сложные задачи - Получая на сдачу полезные инструменты !80
  71. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что дальше? !81

    Дальше пока не придумал, импровизируй
  72. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что плохо !82

    “...подход разочаровал. Вместо систематического контроля конфигов мы наделали еще кучу кода на каждый чих“ *Из отзывов с Гейзенбага 2018
  73. Руслан Черемин 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
  74. Руслан Черемин 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 Создана для тестирования конфигурации. А почему не для самой конфигурации?
  75. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Что могло бы

    быть лучше? !85 - Валидаторы конфигурации - Например XML/JSON-schema - Описывают валидацию конфигурации с помощью кода - Может быть сразу конфигурировать приложение с помощью кода?
  76. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Configuration as code

    !86 - Я не имею в виду bash code - Я не имею в виду “что-то, что лежит в VCS” - Я имею в виду код: - Строго типизированный, компилируемый - С поддержкой IDE - С возможностью использовать доменные объекты основного приложения
  77. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Configuration as code

    !87 - Я не имею в виду bash/JavaScript - Я не имею в виду “что-то, что лежит в VCS” - Я имею в виду код: - Строго типизированный, компилируемый - С поддержкой IDE - С возможностью использовать доменные объекты основного приложения - То есть Java/Kotlin :)
  78. Руслан Черемин 18/05/2018 Deutsche Bank Technology Centre Идея бродит в

    воздухе !88 - “Автоматизация экспериментов с Kotlin DSL”
 /Aleksandr Tarasov - “Kotlin DSL: теория и практика” 
 /Иван Осипов