рджреБрдирд┐рдпрд╛ 2 рдкрд╛рдЧрд▓ рд╣реЛ рд░рд╣реА рд╣реИ, 2 + 2 рдХреИрд▓рдХреБрд▓реЗрдЯрд░ рдХреЛ рдмрд╛рджрд▓реЛрдВ рдореЗрдВ рдзрдХреЗрд▓ рд░рд╣реА рд╣реИред рд╣рдо рдХреНрдпреЛрдВ рдмрджрддрд░ рд╣реИрдВ? рдЖрдЗрдП рд╣реИрд▓реЛ рд╡рд░реНрд▓реНрдб рдХреЛ рддреАрди рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕реЗрдЬ рдореЗрдВ рдзрдХреЗрд▓реЗрдВ, рдПрдХ рджреЛ рдЯреЗрд╕реНрдЯ рд▓рд┐рдЦреЗрдВ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдбреЙрдХреНрдпреВрдореЗрдВрдЯреЗрд╢рди рдкреНрд░рджрд╛рди рдХрд░реЗрдВ, рдПрдХ рд╕реБрдВрджрд░ рдЕрд╕реЗрдВрдмрд▓реА рдкрд╛рдЗрдк рд▓рд╛рдЗрди рдЦреАрдВрдЪреЗрдВ рдФрд░ рдПрдХ рд╕рд╢рд░реНрдд рдХреНрд▓рд╛рдЙрдб рдмрд┐рдХреНрд░реА рдкрд░ рдПрдХ рддреИрдирд╛рддреА рдкреНрд░рджрд╛рди рдХрд░реЗрдВ рдпрджрд┐ рдкрд░реАрдХреНрд╖рдг рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рдкрд╛рд╕ рд╣реЛ рдЬрд╛рддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, рдпрд╣ рд▓реЗрдЦ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджрд┐рдЦрд╛рдПрдЧрд╛ рдХрд┐ рдЙрддреНрдкрд╛рдж рд╡рд┐рдХрд╛рд╕ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рд╕реЗ рддреИрдирд╛рддреА рддрдХ рдХреЗ рдЙрддреНрдкрд╛рджреЛрдВ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред Ineteresno? рддрдм рдореИрдВ рдХрд╛рдд рдХреЗ рдиреАрдЪреЗ рдкреВрдЫрддрд╛ рд╣реВрдВ
рд░реВрдУ рдХрд╣рд╛рдБ рд╕реЗ рд╢реБрд░реВ рд╣реЛрддреА рд╣реИ ...?
рдПрдХ рдорд╛рддреГрднреВрдорд┐ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рдЙрддреНрдкрд╛рдж рд╣реИред рдпрд╣ рд╕рд╣реА рд╣реИ, рдЙрддреНрдкрд╛рдж рдПрдХ рд╡рд┐рдЪрд╛рд░ рд╕реЗ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИред рддреЛ рд╡рд┐рдЪрд╛рд░ рдпрд╣ рд╣реИ:
- рдРрд╕реА рд╕реЗрд╡рд╛ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдЬреЛ REST API рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ 'рд╣реИрд▓реЛ рд╡рд░реНрд▓реНрдб' рд▓реМрдЯрд╛рдП
- 'рд╣реИрд▓реЛ' рд╢рдмреНрдж рдПрдХ microservice рджреЗрддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ рдФрд░ рдХрдорд╛рдВрдб 1 рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ
- рд╢рдмреНрдж 'рд╡рд░реНрд▓реНрдб' рджреВрд╕рд░рд╛ рджреЗрддрд╛ рд╣реИ, рдЬреЛ рдЯреАрдо рджреНрд╡рд╛рд░рд╛ рдЪрд▓рд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ ред2
- рдЯреАрдо_3 'рд╣реИрд▓реЛ' рдФрд░ 'рд╡рд░реНрд▓реНрдб' рдХреЗ рд▓рд┐рдП рдПрдХ рдПрдХреАрдХрд░рдг рд╕реЗрд╡рд╛ рд▓рд┐рдЦрддреА рд╣реИ
- рдУрдПрд╕ (рдбреЗрд╕реНрдХрдЯреЙрдк) - рдбреЗрдмрд┐рдпрди 9 рдЦрд┐рдВрдЪрд╛рд╡
- IDE - IntelliJ IDEA 2019.1
- рдЧрд┐рдЯ рд░реЗрдкреЛ - рдЧрд┐рдЯрд╣рдм
- CI - рд╕рдореНтАНрдорд┐рд▓рди 5.4.0
- рдорд╛рд╡реЗрди рд░реЗрдкреЛ - рдиреЗрдХреНрд╕рд╕
- Openjdk 11
- рдорд╛рд╡реЗрди 3.6.0
- рдХреБрдмреЗрд░рдиреЗрдЯреНрд╕ 1.14 (1 рдорд╛рд╕реНрдЯрд░ + 1 рдХрд╛рд░реНрдпрдХрд░реНрддрд╛): рдХреИрд▓рд┐рдХреЛ рдиреЗрдЯрд╡рд░реНрдХ, рдиреЗрдЧреНрдиреЗрдХреНрд╕-рдЗрдВрдЧреНрд░реЗрд╕-рдирд┐рдпрдВрддреНрд░рдХ
рдорд╣рддреНрд╡рдкреВрд░реНрдг рдиреЛрдЯ: рд▓реЗрдЦ рд╕реБрдВрджрд░ рдХреЛрдб (рдХреЛрдбрд╕реНрдЯрд╛рдЗрд▓, рдЪреЗрдХрд╕реНрдЯрд╛рдЗрд▓, рдЬреЗрд╡рд╛рдбреЙрдХреНрд╕, рдПрд╕рдУрдПрд▓рдЖрдИрдбреА рдФрд░ рдЕрдиреНрдп buzzwords) рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдирд╣реАрдВ рд╣реИ рдФрд░ рдкреВрд░реНрдгрддрд╛ рдХреЗ рд▓рд┐рдП рдкрд╛рд▓реЗ рдЧрдП рд╕рдорд╛рдзрд╛рди (рдЖрдк рдЕрдВрддрд╣реАрди рд╣реИрд▓реЛ рд╡рд░реНрд▓реНрдб рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдВрддрд╣реАрди рдмрд╛рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ)ред рдпрд╣ рдПрдХ рд╕рд╛рде рдХреЛрдб, рд╡рд┐рдирд┐рд░реНрджреЗрд╢реЛрдВ, рдкрд╛рдЗрдкрд▓рд╛рдЗрди рдЕрд╕реЗрдВрдмрд▓реА рдФрд░ рд╕рдмрдХреБрдЫ рдХреА рдбрд┐рд▓реАрд╡рд░реА рдХреЛ рдПрдХ рд╕рд╛рде рдХрд░рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИред
рд╕реЗрд╡рд╛ рд╕реЗ рдХреНрдпрд╛ рдмрдирддрд╛ рд╣реИ?
рдЕрдВрддрд┐рдо рдЙрддреНрдкрд╛рдж рдХреЗ рд░реВрдк рдореЗрдВ рд╕реЗрд╡рд╛ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП:
- OpenAPI рдорд╛рдирдХ рдХреЗ рдпрд╛рдореНрд▓-рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд┐рдирд┐рд░реНрджреЗрд╢рди рдФрд░ рдЕрдиреБрд░реЛрдз рдкрд░ рджреЗрдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛ (GET / dL)
- рдкрд╣рд▓реЗ рдкреИрд░рд╛рдЧреНрд░рд╛рдл рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдПрдкреАрдЖрдИ рддрд░реАрдХреЗ
- рд╕реЗрд╡рд╛ рд╢реБрд░реВ рдХрд░рдиреЗ рдФрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреЗ рд╕рд╛рде README.md
рд╣рдо рдХреНрд░рдо рдореЗрдВ рд╕реЗрд╡рд╛рдУрдВ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░реЗрдВрдЧреЗред рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВ!
'рд╣реИрд▓реЛ' рдорд╛рдЗрдХреНрд░реЛрд╕рд┐рд╕реНрдЯ рд╕реЗрд╡рд╛
рд╡рд┐рд╢рд┐рд╖реНрдЯрддрд╛
рд╣рдо рд╕реНрд╡реИрдЧрд░ рд╕рдВрдкрд╛рджрдХ рдореЗрдВ рдРрдирдХ рд▓рд┐рдЦрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ рдУрдкрдирдПрдкреАрдЖрдИ рдпреБрдХреНрддрд┐ рдореЗрдВ рд░реВрдкрд╛рдВрддрд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВред рд╕реНрд╡реИрдЧрд░ рдПрдбрд┐рдЯрд░ рдХреЛ рдПрдХ рдХрдорд╛рдВрдб рдХреЗ рд╕рд╛рде рдбреЙрдХрдЯрд░ рдореЗрдВ рд▓реЙрдиреНрдЪ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рд╕реНрд╡реИрдЧрд░ рдбреЙрдХреНрд╕ рдХрд╛ рдУрдкрдиреИрдкреА-рдбреЙрдХ рдореЗрдВ рд░реВрдкрд╛рдВрддрд░рдг рдПрдбрд┐рдЯрд░ рдХреЗ рдпреВрдЖрдИ рдореЗрдВ рдПрдХ рдмрдЯрди рджрдмрд╛рдХрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рдХрд┐ POST / api / рдХрдиреНрд╡рд░реНрдЯ рд░рд┐рдХреНрд╡реЗрд╕реНрдЯ рдХреЛ http://lexverter.swagger.io рдкрд░ рднреЗрдЬрддрд╛ рд╣реИред рд╣реИрд▓реЛ рд╕реЗрд╡рд╛ рдХреА рдЕрдВрддрд┐рдо рд╡рд┐рд╢рд┐рд╖реНрдЯрддрд╛:
openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {}
рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
рдХреЛрдб рдХреЛ рд▓рд┐рдЦреЗ рдЬрд╛рдиреЗ рдХреЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ рд╕реЗрд╡рд╛ рдореЗрдВ 3 рд╡рд░реНрдЧ рд╢рд╛рдорд┐рд▓ рд╣реИрдВ:
- рд╕реЗрд╡рд╛ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдЗрдВрдЯрд░рдлреЗрд╕ (рд╡рд┐рдзрд┐ рдирд╛рдо рдСрдкрд░реЗрд╢рди рдХреЗ рд░реВрдк рдореЗрдВ рдХрд▓реНрдкрдирд╛ рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рд╣реИрдВ)
- рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
- рдпреБрдХреНрддрд┐ рдХреЛ рд╕реЗрд╡рд╛ рдХреЗ рд╕рд╛рде рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП vertx verticle (рдкрд╣рд▓реЗ рдкреИрд░рд╛рдЧреНрд░рд╛рдл рд╕реЗ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд╡рд┐рдзрд┐) - рдФрд░ http рд╕рд░реНрд╡рд░ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП
Src рдореЗрдВ рдлрд╝рд╛рдЗрд▓ рд╕рдВрд░рдЪрдирд╛ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреА рд╣реИ:

pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <properties> <main.verticle>io.bihero.hello.HelloVerticle</main.verticle> <vertx.version>3.8.1</vertx.version> <logback.version>1.2.3</logback.version> <junit-jupiter.version>5.3.1</junit-jupiter.version> <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version> <junit-platform-surefire-provider.version>1.1.0</junit-platform-surefire-provider.version> <assertj-core.version>3.8.0</assertj-core.version> <allure.version>2.8.1</allure.version> <allure-maven.version>2.10.0</allure-maven.version> <aspectj.version>1.9.2</aspectj.version> <mockito.version>2.21.0</mockito.version> <rest-assured.version>3.0.0</rest-assured.version> </properties> <groupId>io.bihero</groupId> <artifactId>hello-microservice</artifactId> <version>1.0.0-SNAPSHOT</version> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> ```1.8</source> <target>1.8</target> </configuration> <executions> <execution> <id>default-compile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedSourcesDirectory>src/main/generated</generatedSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/main</arg> </compilerArgs> </configuration> </execution> <execution> <id>default-testCompile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedTestSourcesDirectory>src/test/generated</generatedTestSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/test</arg> </compilerArgs> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <configuration> <properties> <property> <name>listener</name> <value>io.qameta.allure.junit5.AllureJunit5</value> </property> </properties> <includes> <include>**/*Test*.java</include> </includes> <argLine> -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Djdk.net.URLClassPath.disableClassPathURLCheck=true </argLine> <systemProperties> <property> <name>allure.results.directory</name> <value>${project.basedir}/target/allure-results</value> </property> <property> <name>junit.jupiter.extensions.autodetection.enabled</name> <value>true</value> </property> </systemProperties> <reportFormat>plain</reportFormat> </configuration> <dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit-platform-surefire-provider.version}</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> </dependency> </dependencies> </plugin> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <version>${allure-maven.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> <dependencies> <dependency> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-webdav-jackrabbit</artifactId> <version>2.8</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <Main-Class>io.vertx.core.Launcher</Main-Class> <Main-Verticle>${main.verticle}</Main-Verticle> </manifestEntries> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource> </transformer> </transformers> <artifactSet> </artifactSet> <outputFile>${project.build.directory}/${project.artifactId}-fat.jar</outputFile> </configuration> </execution> </executions> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/version.txt</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <excludes> <exclude>**/version.txt</exclude> </excludes> </resource> </resources> </build> <distributionManagement> <site> <id>reports</id> <url>dav:https://nexus.dev.techedge.pro:8443/repository/reports/${project.artifactId}/</url> </site> </distributionManagement> <reporting> <excludeDefaults>true</excludeDefaults> <plugins> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <configuration> <resultsDirectory>${project.build.directory}/allure-results</resultsDirectory> <reportDirectory>${project.reporting.outputDirectory}/${project.version}/allure</reportDirectory> </configuration> </plugin> </plugins> </reporting> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-api-service</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-codegen</artifactId> <version>${vertx.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-unit</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-junit5</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>${assertj-core.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.qameta.allure</groupId> <artifactId>allure-junit5</artifactId> <version>${allure.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> </dependencies> </project>
HelloService.java package io.bihero.hello; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; import io.vertx.ext.web.api.generator.WebApiServiceGen; @WebApiServiceGen public interface HelloService { static HelloService create(Vertx vertx) { return new DefaultHelloService(vertx); } void getHelloWord(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); }
DefaultHelloService.java package io.bihero.hello; import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; public class DefaultHelloService implements HelloService { private final Vertx vertx; public DefaultHelloService(Vertx vertx) { this.vertx = vertx; } @Override public void getHelloWord(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { resultHandler.handle(Future.succeededFuture(OperationResponse.completedWithPlainText(Buffer.buffer("Hello")))); } @Override public void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { vertx.fileSystem().readFile("doc.yaml", buffResult -> resultHandler.handle(Future.succeededFuture( OperationResponse.completedWithPlainText(buffResult.result())) )); } }
HelloVerticle.java package io.bihero.hello; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.api.contract.openapi3.OpenAPI3RouterFactory; import io.vertx.serviceproxy.ServiceBinder; public class HelloVerticle extends AbstractVerticle { private HttpServer server; private MessageConsumer<JsonObject> consumer; @Override public void start(Promise<Void> promise) { startHelloService(); startHttpServer().future().setHandler(promise); } @Override public void stop(){ this.server.close(); consumer.unregister(); } private void startHelloService() { consumer = new ServiceBinder(vertx).setAddress("service.hello") .register(HelloService.class, HelloService.create(getVertx())); } private Promise<Void> startHttpServer() { Promise<Void> promise = Promise.promise(); OpenAPI3RouterFactory.create(this.vertx, "/doc.yaml", openAPI3RouterFactoryAsyncResult -> { if (openAPI3RouterFactoryAsyncResult.succeeded()) { OpenAPI3RouterFactory routerFactory = openAPI3RouterFactoryAsyncResult.result();
рд╕реЗрд╡рд╛ рдХреЗ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдФрд░ рдЙрд╕рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди (@WebApiServiceGen рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рдЕрдкрд╡рд╛рдж рдХреЗ рд╕рд╛рде) рдореЗрдВ рдХреБрдЫ рднреА рдЕрд╕рд╛рдорд╛рдиреНрдп рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдЖрдк рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдкреНрд░рд▓реЗрдЦрди рдореЗрдВ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ), рд▓реЗрдХрд┐рди рдЖрдЗрдП рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рд╡рд░реНрдЯрд┐рдХрд▓ рдХреНрд▓рд╛рд╕ рдХреЛрдб рдХреЛ рджреЗрдЦреЗрдВред
рдКрд░реНрдзреНрд╡рд╛рдзрд░ рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рдХрд╣реЗ рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рджреЛ рддрд░реАрдХреЗ рджрд┐рд▓рдЪрд╕реНрдк рд╣реИрдВ:
- startHelloService рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд╕реНрддреБ рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдШрдЯрдирд╛ рдмрд╕ рдореЗрдВ рдкрддреЗ рдкрд░ рдмрд╛рдВрдзрддрд╛ рд╣реИ (рдКрдкрд░ рдХреЗ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рд╕реЗ рдкреИрд░рд╛рдореАрдЯрд░ x-vertx-event-bus -address рдпрд╛рдж рдХрд░реЗрдВ)
- startHttpServer рд╕реЗрд╡рд╛ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдПрдХ рд░рд╛рдЙрдЯрд░ рдлреИрдХреНрдЯреНрд░реА рдмрдирд╛рддрд╛ рд╣реИ, рдПрдХ http рд╕рд░реНрд╡рд░ рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ рд╕рднреА рдЖрдиреЗ рд╡рд╛рд▓реЗ http рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рд╣реИрдВрдбрд▓ рдХреЗ рд▓рд┐рдП рдмрдирд╛рдП рдЧрдП рд░рд╛рдЙрдЯрд░ рдХреЛ рд╣реБрдХ рдХрд░рддрд╛ рд╣реИ (рдпрджрд┐ рдпрд╣ gurbo рд╣реИ, рддреЛ GET / рдЕрдиреБрд░реЛрдз рд╡рд░реНрдЬрди рд╕рд░реНрд╡рд┐рд╕ рдмрд╕ рдореЗрдВ рдкрддрд╛ service.hello рдХреЗ рд╕рд╛рде рдЧрд┐рд░ рдЬрд╛рдПрдЧрд╛) рдФрд░ (рд╣рдо рд╡рд╣реАрдВ рдмрд╛рдБрдз рджреЗрддреЗ рд╣реИрдВ io.bihero.hello.HelloService рд╕реЗрд╡рд╛ рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди) рдФрд░ рд╕реЗрд╡рд╛ рд╡рд┐рдзрд┐ рдирд╛рдо getHelloWord рдХреЗ рд╕рд╛рде )
рдпрд╣ dzharnik рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдФрд░ рдЪрд▓рд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ:
mvn clean package
рд▓реЙрдиреНрдЪ рд▓рд╛рдЗрди рдореЗрдВ рджреЛ рдкреИрд░рд╛рдореАрдЯрд░ рджрд┐рд▓рдЪрд╕реНрдк рд╣реИрдВ:
- -Dlogback.configurationFile = / Src / conf / logback -sole.xml - logback рдХреЗ рд▓рд┐рдП рдХреЙрдиреНрдлрд╝рд┐рдЧ рдлрд╝рд╛рдЗрд▓ рдХрд╛ рдкрде (рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдирд┐рд░реНрднрд░рддрд╛ рдореЗрдВ slf4j рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ slf4j-api рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд░реВрдк рдореЗрдВ рд▓реЙрдЧрдмреИрдХ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП)
- -conf ./src/conf/config.json - рд╕реЗрд╡рд╛ рдХреЙрдиреНрдлрд┐рдЧ, рд╡рд╣ рдкреЛрд░реНрдЯ рдЬрд╣рд╛рдВ http REST API рдЦреЛрд▓рд╛ рдЬрд╛рдПрдЧрд╛, рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ:
{ "type": "file", "format": "json", "scanPeriod": 5000, "config": { "path": "/home/slava/JavaProjects/hello-world-to-cloud/hellomicroservice/src/conf/config.json" }, "serverPort": 8081, "serverHost": "0.0.0.0" }
рдорд╛рд╡реЗрди рдХрд╛ рдЖрдЙрдЯрдкреБрдЯ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рд╕реЗрд╡рд╛ рдХреИрд╕реЗ рд╢реБрд░реВ рд╣реБрдИ, ( io.netty рдкреИрдХреЗрдЬ рдХреЗ рд▓рд┐рдП рд▓рдХрдбрд╝рд╣рд╛рд░рд╛ рдХреА рд╕реЗрдЯрд┐рдВрдЧ рдореЗрдВ, рд╕реНрддрд░ = "INFO" рд╕реЗрдЯ рд╣реИ )
рд╕реЗрд╡рд╛ рдХреИрд╕реЗ рд╢реБрд░реВ рдХрд░реЗрдВ 2019-10-03 20:52:45,159 [vert.x-worker-thread-0] DEBUG isvpOpenAPIV3Parser: Loaded raw data: openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 2019-10-03 20:52:45,195 [vert.x-worker-thread-0] DEBUG isvpOpenAPIV3Parser: Parsed rootNode: {"openapi":"3.0.1","info":{"title":"Hello ;)","description":"Hello microservice","version":"1.0.0"},"servers":[{"url":"https://demo1.bihero.io/api/hello"}],"tags":[{"name":"hello","description":"Everything about saying 'Hello'"}],"paths":{"/":{"x-vertx-event-bus":{"address":"service.hello","timeout":"1000c"},"get":{"tags":["hello"],"summary":"Get 'Hello' word","operationId":"getHelloWord","responses":{"200":{"description":"OK"}}}},"/doc":{"x-vertx-event-bus":{"address":"service.hello","timeout":"1000c"},"get":{"tags":["hello_doc"],"summary":"Get 'Hello' microservice documentation","operationId":"getDoc","responses":{"200":{"description":"OK"}}}}},"components":{}} Oct 03, 2019 8:52:45 PM io.vertx.core.impl.launcher.commands.VertxIsolatedDeployer INFO: Succeeded in deploying verticle
рд╣реБрд░реНрд░реЗ! рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдп рдХрд░ рд░рд╣реА рд╣реИ, рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ:
curl http://127.0.0.1:8081/ Hello curl -v http://127.0.0.1:8081/doc openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {}
рд╕реЗрд╡рд╛ GET / рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП рд╣реИрд▓реЛ рд╢рдмреНрдж рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░рддреА рд╣реИ, рдЬреЛ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рд╕реЗ рдореЗрд▓ рдЦрд╛рддреА рд╣реИ, рдФрд░ рдпрд╣ рдмрд╛рдд рдХрд░ рд╕рдХрддреА рд╣реИ рдХрд┐ рдпрд╣ рдХреНрдпрд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, GET / doc рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рджреЗ рд░рд╣рд╛ рд╣реИред рдХреВрд▓, рдЪрд▓реЛ рдЙрддреНрдкрд╛рджреЛрдВ рдкрд░ рдЬрд╛рдПрдВ!
рдпрд╣рд╛рдБ рдХреБрдЫ рдЧрдбрд╝рдмрдбрд╝ рд╣реИ ...
рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ, рдореИрдВрдиреЗ рд▓рд┐рдЦрд╛ рд╣реИ рдХрд┐ рдЕрд╕реЗрдВрдмрд▓реА рдХреЗ рджреМрд░рд╛рди рдорд╛рд╡реЗрди рдХрд╛ рдЙрддреНрдкрд╛рджрди рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдмрд╣реБрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рдирд╣реАрдВ рд╣реИред рдореИрдВрдиреЗ рдЭреВрда рдмреЛрд▓рд╛, рдирд┐рд╖реНрдХрд░реНрд╖ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдФрд░ рдмрд╣реБрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИред рд╣рдореЗрдВ рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдорд╛рд╡реЗрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ рдФрд░ рдЬрдм рдкрд░реАрдХреНрд╖рдг рдЧрд┐рд░ рдЬрд╛рддреЗ рд╣реИрдВ, рддреЛ рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдЧрд┐рд░ рдЬрд╛рддреА рд╣реИред рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдКрдкрд░ рдкрд╛рд░рд┐рдд рдХрд░ рджрд┐рдпрд╛, рдФрд░ рдпрд╣ рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдпрд╛ рддреЛ рдкрд░реАрдХреНрд╖рдг рдкрд╛рд░рд┐рдд рдХрд░ рджрд┐рдпрд╛, рдпрд╛ рд╡реЗ рдирд╣реАрдВ рд╣реИрдВред рдмреЗрд╢рдХ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреЛрдИ рднреА рдкрд░реАрдХреНрд╖рдг рдирд╣реАрдВ рд╣реИ, рдпрд╣ рдЙрдиреНрд╣реЗрдВ рд▓рд┐рдЦрдиреЗ рдХрд╛ рд╕рдордп рд╣реИ (рдпрд╣рд╛рдВ рдЖрдк рдХрд╛рд░реНрдпрдкреНрд░рдгрд╛рд▓реА рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рддрд░реНрдХ рджреЗ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХрдм, рдХреИрд╕реЗ рдФрд░ рдХреИрд╕реЗ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦрдиреЗ рд╣реИрдВ, рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╕реЗ рдкрд╣рд▓реЗ рдпрд╛ рдмрд╛рдж рдореЗрдВ, рд▓реЗрдХрд┐рди рд╣рдо рд▓реЗрдЦ рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдиреЛрдЯ рдпрд╛рдж рд░рдЦреЗрдВрдЧреЗ рдФрд░ рдЖрдЧреЗ рдмрдврд╝реЗрдВрдЧреЗ - рд╣рдо рдХреБрдЫ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦреЗрдВрдЧреЗ)ред
рдкрд╣рд▓рд╛ рдкрд░реАрдХреНрд╖рдг рд╡рд░реНрдЧ рд╣реИ, рдЗрд╕рдХреА рдкреНрд░рдХреГрддрд┐ рд╕реЗ, рдПрдХ рдЗрдХрд╛рдИ рдкрд░реАрдХреНрд╖рдг рдЬреЛ рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛ рдХреЗ рджреЛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рддрд░реАрдХреЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рддрд╛ рд╣реИ:
HelloServiceTest.java package io.bihero.hello; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.api.OperationRequest; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(VertxExtension.class) public class HelloServiceTest { private HelloService helloService = HelloService.create(Vertx.vertx()); @Test @DisplayName("Test 'getHelloWord' method returns 'Hello' word") public void testHelloMethod(VertxTestContext testContext) { helloService.getHelloWord(new OperationRequest(new JsonObject()), testContext.succeeding(it -> { assertThat(it.getStatusCode()).isEqualTo(200); assertThat(it.getPayload().toString()).isEqualTo("Hello"); testContext.completeNow(); })); } @Test @DisplayName("Test 'getDoc' method returns service documentation in OpenAPI format") public void testDocMethod(VertxTestContext testContext) { helloService.getDoc(new OperationRequest(new JsonObject()), testContext.succeeding(it -> { try { assertThat(it.getStatusCode()).isEqualTo(200); assertThat(it.getPayload().toString()).isEqualTo(IOUtils.toString(this.getClass() .getResourceAsStream("../../../doc.yaml"), "UTF-8")); testContext.completeNow(); } catch (IOException e) { testContext.failNow(e); } })); } }
рджреВрд╕рд░рд╛ рдкрд░реАрдХреНрд╖рдг рдПрдХ рдЧреИрд░-рдПрдХреАрдХрд░рдг рдкрд░реАрдХреНрд╖рдг рд╣реИ, рдЬреЛ рдпрд╣ рдЬрд╛рдБрдЪрддрд╛ рд╣реИ рдХрд┐ рдКрд░реНрдзреНрд╡рд╛рдзрд░ рдЙрдЧрддрд╛ рд╣реИ рдФрд░ рдЕрдкреЗрдХреНрд╖рд┐рдд рд╕реНрдерд┐рддрд┐ рдФрд░ рдкрд╛рда рдХреЗ рд╕рд╛рде рд╕рдВрдмрдВрдзрд┐рдд http рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд╛ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИ:
HelloVerticleTest.java package io.bihero.hello; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.codec.BodyCodec; import io.vertx.junit5.Checkpoint; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @ExtendWith(VertxExtension.class) public class HelloVerticleTest { @Test @DisplayName("Test that verticle is up and respond me by 'Hello' word and doc in OpenAPI format") public void testHelloVerticle(Vertx vertx, VertxTestContext testContext) { WebClient webClient = WebClient.create(vertx); Checkpoint deploymentCheckpoint = testContext.checkpoint(); Checkpoint requestCheckpoint = testContext.checkpoint(2); HelloVerticle verticle = spy(new HelloVerticle()); JsonObject config = new JsonObject().put("serverPort", 8081).put("serverHost", "0.0.0.0"); doReturn(config).when(verticle).config(); vertx.deployVerticle(verticle, testContext.succeeding(id -> { deploymentCheckpoint.flag();
рдпрд╣ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд╕рд╛рде рдПрдХ рд╕реЗрд╡рд╛ рдмрдирд╛рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ:
mvn clean package
рд╣рдо рдЕрдЪреВрдХ рдкреНрд▓рдЧрдЗрди рд▓реЙрдЧ рдореЗрдВ рдмрд╣реБрдд рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВ, рдпрд╣ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛ (рдЪрд┐рддреНрд░ рдХреНрд▓рд┐рдХ рдХрд░рдиреЗ рдпреЛрдЧреНрдп рд╣реИ):

рд╡рд╛рд╣! рд╕реЗрд╡рд╛ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдкрд░реАрдХреНрд╖рдг рдЪрд▓рддреЗ рд╣реИрдВ рдФрд░ рдЕрд╕рдлрд▓ рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВ (рдереЛрдбрд╝реА рджреЗрд░ рдмрд╛рдж рд╣рдо рд╕реБрдВрджрд░рддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдВрдЧреЗ рдХрд┐ рдкрд░реАрдХреНрд╖рдг рдХреЗ рдкрд░рд┐рдгрд╛рдо рдЕрдзрд┐рдХрд╛рд░рд┐рдпреЛрдВ рдХреЛ рдХреИрд╕реЗ рджрд┐рдЦрд╛рдП рдЬрд╛рддреЗ рд╣реИрдВ), рдпрд╣ рд╕реЛрдЪрдиреЗ рдХрд╛ рд╕рдордп рд╣реИ рдХрд┐ рд╣рдо рдЗрд╕реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдХреИрд╕реЗ рд╡рд┐рддрд░рд┐рдд рдХрд░реЗрдВрдЧреЗ (рдЕрд░реНрдерд╛рдд, рд╕рд░реНрд╡рд░ рдкрд░)ред рдпрд╛рд░реНрдб рдореЗрдВ, 2019 рдХреЗ рдЕрдВрдд рдореЗрдВ, рдФрд░, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рд╣рдо рдПрдХ рдбреЙрдХрдЯрд░ рдЫрд╡рд┐ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдмрдВрдбрд▓ рдХрд░реЗрдВрдЧреЗред рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВ!
рдбреЙрдХрд░ рдФрд░ рд╕рдм рд╕рдм
рд╣рдо рдЕрдкрдиреА рдкрд╣рд▓реА рд╕реЗрд╡рд╛ рдХреЗ рд▓рд┐рдП рдПрдХ adoptopenjdk/openjdk11
рдЫрд╡рд┐ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░реЗрдВрдЧреЗ рдЬреЛ рдХрд┐ adoptopenjdk/openjdk11
рдЖрдзрд╛рд░ рдкрд░ adoptopenjdk/openjdk11
ред рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХреЗ рд╕рд╛рде рд╣рдорд╛рд░реЗ рдЗрдХрдЯреНрдареЗ dzharnik рдХреА рдЫрд╡рд┐ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдбреЙрдХрдЯрд░ рдореЗрдВ рд▓рд┐рдЦреЗрдВ рдХрдВрдЯреЗрдирд░ рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдорд╛рдВрдб рджрд░реНрдЬ рдХрд░реЗрдВред рдкрд░рд┐рдгрд╛рдореА Dockerfile рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрд╛рдИ рджреЗрдЧреА:
FROM adoptopenjdk/openjdk11:alpine-jre COPY target/hello-microservice-fat.jar app.jar COPY src/conf/config.json . COPY src/conf/logback-console.xml . COPY run.sh . RUN chmod +x run.sh CMD ["./run.sh"]
Run.sh рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреА рд╣реИ:
рд╣рдореЗрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЕрднреА рддрдХ рдЗрд╕ рд╕реНрддрд░ рдкрд░ JVM_OPTS рдкрд░реНрдпрд╛рд╡рд░рдг рдЪрд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдереЛрдбрд╝реА рджреЗрд░ рдмрд╛рдж рд╣рдо рдЗрд╕реЗ рд╕рдХреНрд░рд┐рдп рд░реВрдк рд╕реЗ рдмрджрд▓ рджреЗрдВрдЧреЗ рдФрд░ рд╡рд░реНрдЪреБрдЕрд▓ рдорд╢реАрди рдФрд░ рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛рдУрдВ рдХреЗ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдЯреНрдпреВрди рдХрд░реЗрдВрдЧреЗред рдпрд╣ рдЫрд╡рд┐ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдФрд░ рдХрдВрдЯреЗрдирд░ рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЪрд▓рд╛рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ:
docker build -t="hellomicroservice" . docker run -dit --name helloms hellomicroservice
рд╣рдо рдХрдВрдЯреЗрдирд░ рдХрд╛ рдЖрдИрдкреА-рдкрддрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдХрдВрдЯреЗрдирд░ рдХреЗ рдЕрдВрджрд░ рд╕реЗрд╡рд╛ рдХреЗ рд╕рдВрдЪрд╛рд▓рди рдХреА рдЬрд╛рдВрдЪ рдХрд░рддреЗ рд╣реИрдВ:
docker inspect helloms | grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "172.17.0.2", "IPAddress": "172.17.0.2",
curl http://172.17.0.2:8081/
рддреЛ, рдХрдВрдЯреЗрдирд░ рдореЗрдВ рд╕реЗрд╡рд╛ рд╢реБрд░реВ рд╣реЛрддреА рд╣реИред рд▓реЗрдХрд┐рди рд╣рдордиреЗ рдЗрд╕реЗ рдЕрдкрдиреЗ рд╣рд╛рдереЛрдВ рд╕реЗ рдЗрд╕ рддрд░рд╣ (docker run) рдкреНрд░реЛрдбрдХреНрд╢рди рдХреЗ рдорд╛рд╣реМрд▓ рдореЗрдВ рдирд╣реАрдВ рдЪрд▓рд╛рдпрд╛, рдЗрд╕рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдЕрджреНрднреБрдд рдХреБрдмреЗрд░рдиреЗрдЯ рд╣реИрдВред рдХреБрдмреЗрд░рдиреЗрдЯреНрд╕ рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдПрдХ рдЯреЗрдореНрдкреНрд▓реЗрдЯ, рдПрдХ yml рдлрд╝рд╛рдЗрд▓ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдЬрд┐рд╕рдХреЗ рд╡рд┐рд╡рд░рдг рдХреЗ рд╕рд╛рде рдХрд┐ рд╣рдо рдХреМрди рд╕реЗ рд╕рдВрд╕рд╛рдзрдиреЛрдВ (рддреИрдирд╛рддреА, рд╕реЗрд╡рд╛, рдкреНрд░рд╡реЗрд╢, рдЖрджрд┐) рдХреЛ рдЪрд▓рд╛рдПрдВрдЧреЗ рдФрд░ рдХрд┐рд╕ рдХрдВрдЯреЗрдирд░ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИрдВред рд▓реЗрдХрд┐рди, рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рдХрд┐ рд╣рдо k8s рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЯреЗрдореНрдкрд▓реЗрдЯ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░реЗрдВ, рдкрд╣рд▓реЗ рд╕реЗ рдЗрдХрдЯреНрдареЗ рдХреА рдЧрдИ рдЫрд╡рд┐ рдХреЛ рдбреЙрдХрд╣реИрдм рдкрд░ рдзрдХреЗрд▓ рджреЗрдВ:
docker tag hello bihero/hello docker push bihero/hello
рд╣рдо рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдХреБрдмреЗрд░рдиреЗрдЯреНрд╕ рдореЗрдВ рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЯреЗрдореНрдкрд▓реЗрдЯ рд▓рд┐рдЦрддреЗ рд╣реИрдВ (рд▓реЗрдЦ рдХреЗ рдврд╛рдВрдЪреЗ рдХреЗ рднреАрддрд░, рд╣рдо рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реЗрд▓реНрдбрд░ рдирд╣реАрдВ рд╣реИрдВ рдФрд░ "рдХреЛрд╖реЗрд░" рдЯреЗрдореНрдкрд▓реЗрдЯ рд╣реЛрдиреЗ рдХрд╛ рджрд┐рдЦрд╛рд╡рд╛ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ):
apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: io.bihero.hello.service: bihero-hello name: bihero-hello spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 template: metadata: labels: io.bihero.hello.service: bihero-hello spec: containers: - image: bihero/hello:${HELLO_SERVICE_IMAGE_VERSION} name: bihero-hello ports: - containerPort: 8081 imagePullPolicy: Always resources: {} restartPolicy: Always --- apiVersion: v1 kind: Service metadata: labels: io.bihero.hello.service: bihero-hello name: bihero-hello spec: ports: - name: "8081" port: 8081 targetPort: 8081 selector: io.bihero.hello.service: bihero-hello status: loadBalancer: {} --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: bihero-hello annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/secure-backends: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" nginx.ingress.kubernetes.io/rewrite-target: /$2 kubernetes.io/tls-acme: "true" namespace: default spec: tls: - hosts: - ${ID_DOMAIN} secretName: bihero rules: - host: ${ID_DOMAIN} http: paths: - path: /api/hello(/|$)(.*) backend: serviceName: bihero-hello servicePort: 8081
рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рд╣рдо рдЯреЗрдореНрдкрд▓реЗрдЯ рдореЗрдВ рдХреНрдпрд╛ рджреЗрдЦрддреЗ рд╣реИрдВ:
- рдкрд░рд┐рдирд┐рдпреЛрдЬрди : рдпрд╣рд╛рдВ рд╣рдо рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рд╣рдо рдХрд┐рд╕ рдЫрд╡рд┐ рд╕реЗ рдФрд░ рдХрд┐рддрдиреЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рд╕реЗ рдЕрдкрдиреА рд╕реЗрд╡рд╛ рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐рдХреГрддрд┐ рд╕реЗрдЯ рдмрдирд╛рддреЗ рд╣реИрдВред рдореЗрдЯрд╛рдбреЗрдЯрд╛.рд▓реИрдмрд▓реНрд╕ рдкрд░ рдзреНрдпрд╛рди рджреЗрдирд╛ рднреА рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ - рдЙрди рдкрд░ рд╣рдо рд╕реЗрд╡рд╛ рдХреЛ рддреИрдирд╛рддреА рдХреЗ рд▓рд┐рдП рдмрд╛рдзреНрдп рдХрд░реЗрдВрдЧреЗ
- рд╕реЗрд╡рд╛ : рд╣рдо рд╕реЗрд╡рд╛ рдХреЛ рдкрд░рд┐рдирд┐рдпреЛрдЬрди / рдкреНрд░рддрд┐рдХреГрддрд┐рдпреЛрдВ рд╕реЗ рдмрд╛рдБрдзрддреЗ рд╣реИрдВред рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, k8s рдореЗрдВ рдПрдХ рд╕реЗрд╡рд╛ рдРрд╕реА рдЪреАрдЬ рд╣реИ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдХреНрд▓рд╕реНрдЯрд░ рдХреЗ рдЕрдВрджрд░ http рдЕрдиреБрд░реЛрдз рднреЗрдЬрдирд╛ рдкрд╣рд▓реЗ рд╕реЗ рд╕рдВрднрд╡ рд╣реИ (рдФрд░ рд╣рд╛рдВ - рдЪрдпрдирдХрд░реНрддрд╛ рдкрд░ рдзреНрдпрд╛рди рджреЗрдВ)
- рдкреНрд░рдЧрддрд┐ : рдмрд╛рд╣рд░реА рджреБрдирд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рд╕реЗрд╡рд╛ рдХреЛ рдЙрдЬрд╛рдЧрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рд╡реЗрд╢ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рд╣рдо рдЕрдкрдиреА рд╣реИрд▓реЛ рд╕реЗрд╡рд╛ ( https://domain.com/api/hello -> http: //bihero-hello.service.internal.domain.local: 8081 / ) рдкрд░ / api / hello рд╕реЗ рд╢реБрд░реВ рд╣реЛрдиреЗ рд╡рд╛рд▓реЗ рд╕рднреА рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рд▓рдкреЗрдЯреЗрдВрдЧреЗред
рдЯреЗрдореНрдкрд▓реЗрдЯ рдореЗрдВ рджреЛ рдкрд░реНрдпрд╛рд╡рд░рдг рдЪрд░ рднреА рд╢рд╛рдорд┐рд▓ рд╣реИрдВ:
- $ {HELLO_SERVICE_IMAGE_VERSION} - рд╕реЗрд╡рд╛ рдХреЗ рд╕рд╛рде docker рдЫрд╡рд┐ рдХрд╛ рдЯреИрдЧ рдЬрд┐рд╕рд╕реЗ рд╣рдо рдЕрдкрдиреА рдкрд╣рд▓реА рддреИрдирд╛рддреА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВрдЧреЗ
- $ {ID_DOMAIN} - рд╡рд╣ рдбреЛрдореЗрди рдЬрд┐рд╕ рдкрд░ рд╣рдо рдЕрдкрдиреА рд╕реЗрд╡рд╛рдПрдБ рддреИрдирд╛рдд рдХрд░реЗрдВрдЧреЗ
Https рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд
рдЯреЗрд╕реНрдЯ рдХреНрд▓рд╕реНрдЯрд░ рдореЗрдВ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдПрдХ рдЧреБрдкреНрдд рдирд╛рдо рдмрд╛рдпрд╣рд░реЛ рд╣реИ , рдЬрд┐рд╕реЗ LetsEncrypt рд╕реЗ рд╡рд╛рдЗрд▓реНрдбрдХрд╛рд░реНрдб рдкреНрд░рдорд╛рдгрдкрддреНрд░ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, рдЯреАрдо рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреА рд╣реИ
kubectl create secret tls bihero --key keys/privkey.pem --cert keys/fullchain.pem
рдЬрд╣рд╛рдВ privkey.pem рдФрд░ fullchain.pem Letencrypt рджреНрд╡рд╛рд░рд╛ рдЙрддреНрдкрдиреНрди рдлрд╛рдЗрд▓реЗрдВ рд╣реИрдВ
рдЖрдк рд▓рд┐рдВрдХ рдХрд╛ рдЕрдиреБрд╕рд░рдг рдХрд░рдХреЗ k8 рдореЗрдВ tls рдХреЗ рд▓рд┐рдП рдПрдХ рд░рд╣рд╕реНрдп рдмрдирд╛рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ
рдпрд╣ k8s рдореЗрдВ рддреИрдирд╛рддреА рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ :) рдЪрд▓реЛ рдЪрд▓реЗрдВ!
export HELLO_SERVICE_IMAGE_VERSION=latest export ID_DOMAIN=demo1.bihero.io cat k8s.yaml | envsubst | kubectl apply -f -
рд╕реНрдЯрдбрдЖрдЙрдЯ рдореЗрдВ рдЖрдкрдХреЛ рдпрд╣ рджреЗрдЦрдирд╛ рдЪрд╛рд╣рд┐рдП:
deployment.extensions/bihero-hello created service/bihero-hello created ingress.extensions/bihero-hello created
рдЦреИрд░, рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдХреБрдмреЗрд░рдиреЗрдЯ рд╣рдореЗрдВ рд╡рд╣рд╛рдВ рдорд┐рд▓рд╛:
kubectl get po

рдЬреИрд╕рд╛ рдХрд┐ рдЕрдкреЗрдХреНрд╖рд┐рдд рдерд╛ - 3 рдЪреВрд▓реНрд╣рд╛
рдЖрдЗрдП рдПрдХ рдЪреВрд▓реНрд╣рд╛ рдХрд╛ рд╡рд┐рд╡рд░рдг рджреЗрдЦреЗрдВ
kubectl describe po bihero-hello-5b4759d55b-bf4qc

рд╕реЗрд╡рд╛ рдХреИрд╕реЗ рдХрд░ рд░рд╣рд╛ рд╣реИ?
kubectl describe service bihero-hello

рдкреНрд░рд╡реЗрд╢ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреНрдпрд╛?
kubectl describe ing bihero-hello

рд╡рд╛рд╣! рд╕реЗрд╡рд╛ k8s рдореЗрдВ рдЪрд▓рддреА рд╣реИ рдФрд░ рдХрд▓реНрдкрдирд╛ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рдХреБрдЫ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рд╕рд╛рде рдЬрд╛рдВрдЪ рдХреА рдЬрд╛рддреА рд╣реИред
curl https://demo1.bihero.io/api/hello Hello
curl https://demo1.bihero.io/api/hello/doc openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {}
A - рд╕реНрд╡рдЪрд╛рд▓рди
рдкреЗ ... рд╣рдо рд╕рдмрд╕реЗ рд╕реНрд╡рд╛рджрд┐рд╖реНрдЯ рдФрд░ рд░реЛрдорд╛рдВрдЪрдХ рддрдХ рдкрд╣реБрдБрдЪ рдЧрдПред рдмрд╣реБрдд рд╕реЗ рдХрд╛рдо рдХрд┐рдП рдЧрдП рдереЗ рдФрд░ рдкреНрд░рддреНрдпреЗрдХ рдЪрд░рдг рдореЗрдВ рдХреБрдЫ рдЙрдкрдХрд░рдгреЛрдВ рдХреЗ рдореИрдиреБрдЕрд▓ рд▓реЙрдиреНрдЪ рдХреЗ рд╕рд╛рде, рдкреНрд░рддреНрдпреЗрдХ рдЪрд░рдг рдореЗрдВ рд╕реНрд╡рдпрдВ рдХрд╛ рдерд╛ред , , k8s . , !
, , CI-.
?
- , , ( ), git-
- (mvn), (surefire, allure) тАФ fat-jar
- docker- (docker build)
- Push docker- ( docker registry) (docker push)
- k8s (kubectl apply)
CI- ?
, ( ), . :

:
- , master ( master , , , merge' merge request' )
- , dev- (telegram-bot)
- ,
- тАФ maven repository ( nexus blob store )
- fat-jar (mvn package, , тАФ )
- docker image . , , , ( ). registry k8s
- k8s
- , k8s .
- 4- , , maven repository
- ,
Concourse CI
CI- Concourse . Concourse CI:
- UI ( yaml-, , fly ): тАФ , (mvn, docker, fly, kubectl), , ( tg- )
- docker container', ( worker- , - environment- ) тАФ , , .
, , :
pipeline.yaml resource_types: - name: telegram type: docker-image source: repository: vtutrinov/concourse-telegram-resource tag: latest - name: kubernetes type: docker-image source: repository: zlabjp/kubernetes-resource tag: 1.16 - name: metadata type: docker-image source: repository: olhtbr/metadata-resource tag: 2.0.1 resources: - name: metadata type: metadata - name: sources type: git source: branch: master uri: git@github.com:bihero-io/hello-microservice.git private_key: ((deployer-private-key)) - name: docker-image type: docker-image source: repository: bihero/hello username: ((docker-registry-user)) password: ((docker-registry-password)) - name: telegram type: telegram source: bot_token: ((telegram-ci-bot-token)) chat_id: ((telegram-group-to-report-build)) ci_url: ((ci_url)) command: "/build_hello_ms" - name: kubernetes-demo type: kubernetes source: server: https://178.63.194.241:6443 namespace: default kubeconfig: ((kubeconfig-demo)) jobs: - name: build-hello-microservice serial: true public: true plan: - in_parallel: - get: sources trigger: true - get: telegram trigger: true - put: metadata - put: telegram params: status: Build In Progress - task: unit-tests config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: sources outputs: - name: tested-workspace run: path: /bin/sh args: - -c - | output_dir=tested-workspace cp -R ./sources/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" clean test caches: - path: ~/.m2/ on_failure: do: - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message run: path: /bin/sh args: - -c - | output_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${output_dir}/pom.xml" site-deploy version=$(cat $output_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - put: telegram params: status: Build Failed (unit-tests) message_file: message/msg - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message - name: tested-workspace run: path: /bin/sh args: - -c - | work_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${work_dir}/pom.xml" site-deploy version=$(cat $work_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - task: package config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace - name: metadata outputs: - name: app-packaged-workspace - name: metadata run: path: /bin/sh args: - -c - | output_dir=app-packaged-workspace cp -R ./tested-workspace/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" package -Dmaven.main.skip -DskipTests env tag="-"$(cat metadata/build_name) echo $tag >> ${output_dir}/target/classes/version.txt cat ${output_dir}/target/classes/version.txt > metadata/version caches: - path: ~/.m2/ on_failure: do: - put: telegram params: status: Build Failed (package) - put: docker-image params: build: app-packaged-workspace tag_file: app-packaged-workspace/target/classes/version.txt tag_as_latest: true get_params: skip_download: true - task: make-k8s-app-template config: platform: linux image_resource: type: docker-image source: repository: bhgedigital/envsubst inputs: - name: sources - name: metadata outputs: - name: k8s run: path: /bin/sh args: - -c - | export DOMAIN=demo1.bihero.io export HELLO_SERVICE_IMAGE_VERSION=$(cat metadata/version) cat sources/k8s.yaml | envsubst > k8s/hello_app_template.yaml cat k8s/hello_app_template.yaml - put: kubernetes-demo params: kubectl: apply -f k8s/hello_app_template.yaml - put: telegram params: status: Build Success message_file: message/msg
:
- resource_types , , . ( , docker-, ): telegram tg- , kubernetes k8s- metadata ( , ..)
- resources , . , , docker-registry docker- , . input- ,
- jobs , . put- tg-. тАФ get (, git-), тАФ put тАФ (docker image) , (metadata). task тАФ docker- docker-image', image_resource
- ((parameter-name)) тАФ , , , ( docker-registry).
:
fly -t bih sp -p hello-microservice -c pipeline.yaml -l credentials.yaml
credentials.yaml :
docker-registry-user: <dockerhub-user> docker-registry-password: <dockerhub-password> docker-registry-uri: <private-docker-registry-url> docker-private-registry-user: <private-docker-registry-user> docker-private-registry-password: <private-docker-registry-passwordl> telegram-ci-bot-token: <telegram-bot-token> telegram-group-to-report-build: <telegram-group-id> ci_url: <ci-server-url> deployer-private-key: | -----BEGIN OPENSSH PRIVATE KEY----- github-deploy-key -----END OPENSSH PRIVATE KEY----- kubeconfig-demo: | apiVersion: v1 clusters: - cluster: certificate-authority-data: <kube-cert-data> server: <kube-api-server-url> name: kubernetes contexts: - context: cluster: kubernetes user: kubernetes-admin name: kubernetes-admin@kubernetes current-context: kubernetes-admin@kubernetes kind: Config preferences: {} users: - name: kubernetes-admin user: client-certificate-data: <kube-client-cert-data> client-key-data: <kube-client-key-data>
. :
- CI-, :
- ( 1), c fly , CI-:
fly -t bih tj -j hello-microservice/build-hello-microservice -w
- /build_hello_ms -, telegram-group-to-report-build credentials.yaml
- master- (, , : master тАФ , тАФ ;) )
( ) -:
- :

- :

, UI CI-:
рд╣реБрд░реНрд░реЗ! , - , , k8s . :
- docker-hub'


- , 2-

- :
curl https://demo1.bihero.io/api/hello -v curl https://demo1.bihero.io/api/hello -v юВ▓ яАМ юВ▓ 5350 юВ▓ 14:59:04 яАЧ * Trying 178.63.194.243... * TCP_NODELAY set * Connected to demo1.bihero.io (178.63.194.243) port 443 (
, , . , . , ( testcontainers). TODO- ( ). рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВ!
'World' microservice
Service specification openapi: 3.0.1 info: title: World ;) description: "'World' word microservice" version: 1.0.0 servers: - url: https://demo1.bihero.io/api/world tags: - name: world description: Everything about 'World' word paths: /: x-vertx-event-bus: address: service.world timeout: 1000 get: tags: - world summary: Get 'World' word operationId: getWorldWord responses: 200: description: OK content: {} /doc: x-vertx-event-bus: address: service.world timeout: 1000c get: tags: - world summary: Get 'World' microservice documentation operationId: getDoc responses: 200: description: OK components: {}
pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <properties> <main.verticle>io.bihero.world.WorldVerticle</main.verticle> <vertx.version>3.8.1</vertx.version> <logback.version>1.2.3</logback.version> <junit-jupiter.version>5.3.1</junit-jupiter.version> <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version> <junit-platform-surefire-provider.version>1.1.0</junit-platform-surefire-provider.version> <assertj-core.version>3.8.0</assertj-core.version> <allure.version>2.8.1</allure.version> <allure-maven.version>2.10.0</allure-maven.version> <aspectj.version>1.9.2</aspectj.version> <mockito.version>2.21.0</mockito.version> <rest-assured.version>3.0.0</rest-assured.version> </properties> <groupId>io.bihero</groupId> <artifactId>world-microservice</artifactId> <version>1.0.0-SNAPSHOT</version> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> ```11</source> <target>11</target> </configuration> <executions> <execution> <id>default-compile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedSourcesDirectory>src/main/generated</generatedSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/main</arg> </compilerArgs> </configuration> </execution> <execution> <id>default-testCompile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedTestSourcesDirectory>src/test/generated</generatedTestSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/test</arg> </compilerArgs> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <configuration> <properties> <property> <name>listener</name> <value>io.qameta.allure.junit5.AllureJunit5</value> </property> </properties> <includes> <include>**/*Test*.java</include> </includes> <argLine> -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Djdk.net.URLClassPath.disableClassPathURLCheck=true </argLine> <systemProperties> <property> <name>allure.results.directory</name> <value>${project.basedir}/target/allure-results</value> </property> <property> <name>junit.jupiter.extensions.autodetection.enabled</name> <value>true</value> </property> </systemProperties> <reportFormat>plain</reportFormat> </configuration> <dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit-platform-surefire-provider.version}</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> </dependency> </dependencies> </plugin> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <version>${allure-maven.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> <dependencies> <dependency> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-webdav-jackrabbit</artifactId> <version>2.8</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <Main-Class>io.vertx.core.Launcher</Main-Class> <Main-Verticle>${main.verticle}</Main-Verticle> </manifestEntries> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource> </transformer> </transformers> <artifactSet> </artifactSet> <outputFile>${project.build.directory}/${project.artifactId}-fat.jar</outputFile> </configuration> </execution> </executions> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/version.txt</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <excludes> <exclude>**/version.txt</exclude> </excludes> </resource> </resources> </build> <distributionManagement> <site> <id>reports</id> <url>dav:https://nexus.dev.techedge.pro:8443/repository/reports/${project.artifactId}/</url> </site> </distributionManagement> <reporting> <excludeDefaults>true</excludeDefaults> <plugins> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <configuration> <resultsDirectory>${project.build.directory}/allure-results</resultsDirectory> <reportDirectory>${project.reporting.outputDirectory}/${project.version}/allure</reportDirectory> </configuration> </plugin> </plugins> </reporting> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-api-service</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-codegen</artifactId> <version>${vertx.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-unit</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-junit5</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>${assertj-core.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.qameta.allure</groupId> <artifactId>allure-junit5</artifactId> <version>${allure.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> </dependencies> </project>
WorldService.java package io.bihero.world; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; import io.vertx.ext.web.api.generator.WebApiServiceGen; @WebApiServiceGen public interface WorldService { static WorldService create(Vertx vertx) { return new DefaultWorldService(vertx); } void getWorldWord(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); }
DefaultWorldService.java package io.bihero.world; import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; public class DefaultWorldService implements WorldService { private final Vertx vertx; public DefaultWorldService(Vertx vertx) { this.vertx = vertx; } public void getWorldWord(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { resultHandler.handle(Future.succeededFuture(OperationResponse.completedWithPlainText(Buffer.buffer("World")))); } @Override public void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { vertx.fileSystem().readFile("doc.yaml", buffResult -> resultHandler.handle(Future.succeededFuture( OperationResponse.completedWithPlainText(buffResult.result())) )); } }
WorldVerticle.java package io.bihero.world; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.api.contract.openapi3.OpenAPI3RouterFactory; import io.vertx.serviceproxy.ServiceBinder; public class WorldVerticle extends AbstractVerticle { HttpServer server; MessageConsumer<JsonObject> consumer; public void startWorldService() { consumer = new ServiceBinder(vertx).setAddress("service.world") .register(WorldService.class, WorldService.create(getVertx())); } private Promise<Void> startHttpServer() { Promise<Void> promise = Promise.promise(); OpenAPI3RouterFactory.create(this.vertx, "/doc.yaml", openAPI3RouterFactoryAsyncResult -> { if (openAPI3RouterFactoryAsyncResult.succeeded()) { OpenAPI3RouterFactory routerFactory = openAPI3RouterFactoryAsyncResult.result();
WorldServiceTest.java package io.bihero.world; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.api.OperationRequest; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(VertxExtension.class) public class WorldServiceTest { private WorldService worldService = WorldService.create(Vertx.vertx()); @Test @DisplayName("Test 'getWorldWord' method returns 'World' word") public void testHelloMethod(VertxTestContext testContext) { worldService.getWorldWord(new OperationRequest(new JsonObject()), testContext.succeeding(it -> { assertThat(it.getStatusCode()).isEqualTo(200); assertThat(it.getPayload().toString()).isEqualTo("World"); testContext.completeNow(); })); } @Test @DisplayName("Test 'getDoc' method returns service documentation in OpenAPI format") public void testDocMethod(VertxTestContext testContext) { worldService.getDoc(new OperationRequest(new JsonObject()), testContext.succeeding(it -> { try { assertThat(it.getStatusCode()).isEqualTo(200); assertThat(it.getPayload().toString()).isEqualTo(IOUtils.toString(this.getClass() .getResourceAsStream("../../../doc.yaml"), "UTF-8")); testContext.completeNow(); } catch (IOException e) { testContext.failNow(e); } })); } }
WorldVerticleTest.java package io.bihero.world; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.codec.BodyCodec; import io.vertx.junit5.Checkpoint; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @ExtendWith(VertxExtension.class) public class WorldVerticleTest { @Test @DisplayName("Test that verticle is up and respond me by 'World' word and doc in OpenAPI format") public void testHelloVerticle(Vertx vertx, VertxTestContext testContext) { WebClient webClient = WebClient.create(vertx); Checkpoint deploymentCheckpoint = testContext.checkpoint(); Checkpoint requestCheckpoint = testContext.checkpoint(2); WorldVerticle verticle = spy(new WorldVerticle()); JsonObject config = new JsonObject().put("serverPort", 8082).put("serverHost", "0.0.0.0"); doReturn(config).when(verticle).config(); vertx.deployVerticle(verticle, testContext.succeeding(id -> { deploymentCheckpoint.flag();
Dockerfile FROM adoptopenjdk/openjdk11:alpine-jre COPY target/world-microservice-fat.jar app.jar COPY src/conf/config.json . COPY src/conf/logback-console.xml . COPY run.sh . RUN chmod +x run.sh CMD ["./run.sh"]
pipeline.yaml resource_types: - name: telegram type: docker-image source: repository: vtutrinov/concourse-telegram-resource tag: latest - name: kubernetes type: docker-image source: repository: zlabjp/kubernetes-resource tag: 1.16 - name: metadata type: docker-image source: repository: olhtbr/metadata-resource tag: 2.0.1 resources: - name: metadata type: metadata - name: sources type: git source: branch: master uri: git@github.com:bihero-io/worldmicroservice.git private_key: ((deployer-private-key)) - name: docker-image type: docker-image source: repository: bihero/world username: ((docker-registry-user)) password: ((docker-registry-password)) - name: telegram type: telegram source: bot_token: ((telegram-ci-bot-token)) chat_id: ((telegram-group-to-report-build)) ci_url: ((ci_url)) command: "/build_world_ms" - name: kubernetes-demo type: kubernetes source: server: ((k8s-api-server)) namespace: default kubeconfig: ((kubeconfig-demo)) jobs: - name: build-world-microservice serial: true public: true plan: - in_parallel: - get: sources trigger: true - get: telegram trigger: true - put: metadata - put: telegram params: status: Build In Progress - task: unit-tests config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: sources outputs: - name: tested-workspace run: path: /bin/sh args: - -c - | output_dir=tested-workspace cp -R ./sources/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" clean test caches: - path: ~/.m2/ on_failure: do: - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message run: path: /bin/sh args: - -c - | output_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${output_dir}/pom.xml" site-deploy version=$(cat $output_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - put: telegram params: status: Build Failed (unit-tests) message_file: message/msg - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message - name: tested-workspace run: path: /bin/sh args: - -c - | work_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${work_dir}/pom.xml" site-deploy version=$(cat $work_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/world-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - task: package config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace - name: metadata outputs: - name: app-packaged-workspace - name: metadata run: path: /bin/sh args: - -c - | output_dir=app-packaged-workspace cp -R ./tested-workspace/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" package -Dmaven.main.skip -DskipTests tag="-"$(cat metadata/build_name) echo $tag >> ${output_dir}/target/classes/version.txt cat ${output_dir}/target/classes/version.txt > metadata/version caches: - path: ~/.m2/ on_failure: do: - put: telegram params: status: Build Failed (package) - put: docker-image params: build: app-packaged-workspace tag_file: app-packaged-workspace/target/classes/version.txt tag_as_latest: true get_params: skip_download: true - task: make-k8s-app-template config: platform: linux image_resource: type: docker-image source: repository: bhgedigital/envsubst inputs: - name: sources - name: metadata outputs: - name: k8s run: path: /bin/sh args: - -c - | export DOMAIN=demo1.bihero.io export WORLD_SERVICE_IMAGE_VERSION=$(cat metadata/version) cat sources/k8s.yaml | envsubst > k8s/world_app_template.yaml cat k8s/world_app_template.yaml - put: kubernetes-demo params: kubectl: apply -f k8s/world_app_template.yaml - put: telegram params: status: Build Success message_file: message/msg
k8s app template apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: io.bihero.hello.service: bihero-world name: bihero-world spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 template: metadata: labels: io.bihero.hello.service: bihero-world spec: containers: - image: bihero/world:${WORLD_SERVICE_IMAGE_VERSION} name: bihero-world ports: - containerPort: 8082 imagePullPolicy: Always resources: {} restartPolicy: Always --- apiVersion: v1 kind: Service metadata: labels: io.bihero.hello.service: bihero-world name: bihero-world spec: ports: - name: "8082" port: 8082 targetPort: 8082 selector: io.bihero.hello.service: bihero-world status: loadBalancer: {} --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: bihero-world annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/secure-backends: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" nginx.ingress.kubernetes.io/rewrite-target: /$2 kubernetes.io/tls-acme: "true" namespace: default spec: tls: - hosts: - ${DOMAIN} secretName: bihero rules: - host: ${DOMAIN} http: paths: - path: /api/world(/|$)(.*) backend: serviceName: bihero-world servicePort: 8082
'HelloWorld' microservice
, . , , . testcontainers, .
Service specification openapi: 3.0.1 info: title: Hello World ;) description: "Hello World microservice. Aggregate 'Hello World' by hellomicroservice and worldmicroservice" version: 1.0.0 servers: - url: https://demo1.bihero.io/api/helloworld tags: - name: helloworld description: Everything about 'Hello World' paths: /: x-vertx-event-bus: address: service.helloworld timeout: 1000 get: tags: - helloworld summary: Aggregate 'Hello World' operationId: getHelloWorld responses: 200: description: OK content: {} /doc: x-vertx-event-bus: address: service.helloworld timeout: 1000c get: tags: - world summary: Get 'Hello World' microservice documentation operationId: getDoc responses: 200: description: OK components: {}
pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <properties> <main.verticle>io.bihero.helloworld.HelloWorldVerticle</main.verticle> <vertx.version>3.8.1</vertx.version> <logback.version>1.2.3</logback.version> <junit-jupiter.version>5.3.1</junit-jupiter.version> <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version> <junit-platform-surefire-provider.version>1.1.0</junit-platform-surefire-provider.version> <assertj-core.version>3.8.0</assertj-core.version> <allure.version>2.8.1</allure.version> <allure-maven.version>2.10.0</allure-maven.version> <aspectj.version>1.9.2</aspectj.version> <mockito.version>2.21.0</mockito.version> <rest-assured.version>3.0.0</rest-assured.version> <testcontainers.version>1.12.3</testcontainers.version> </properties> <groupId>io.bihero</groupId> <artifactId>hello-world-microservice</artifactId> <version>1.0.0-SNAPSHOT</version> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> ```11</source> <target>11</target> </configuration> <executions> <execution> <id>default-compile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedSourcesDirectory>src/main/generated</generatedSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/main</arg> </compilerArgs> </configuration> </execution> <execution> <id>default-testCompile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedTestSourcesDirectory>src/test/generated</generatedTestSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/test</arg> </compilerArgs> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <configuration> <properties> <property> <name>listener</name> <value>io.qameta.allure.junit5.AllureJunit5</value> </property> </properties> <includes> <include>**/*Test.java</include> </includes> <argLine> -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Djdk.net.URLClassPath.disableClassPathURLCheck=true </argLine> <systemProperties> <property> <name>allure.results.directory</name> <value>${project.basedir}/target/allure-results</value> </property> <property> <name>junit.jupiter.extensions.autodetection.enabled</name> <value>true</value> </property> </systemProperties> <reportFormat>plain</reportFormat> </configuration> <dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit-platform-surefire-provider.version}</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> </dependency> </dependencies> </plugin> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <version>${allure-maven.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> <dependencies> <dependency> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-webdav-jackrabbit</artifactId> <version>2.8</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <Main-Class>io.vertx.core.Launcher</Main-Class> <Main-Verticle>${main.verticle}</Main-Verticle> </manifestEntries> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource> </transformer> </transformers> <artifactSet> </artifactSet> <outputFile>${project.build.directory}/${project.artifactId}-fat.jar</outputFile> </configuration> </execution> </executions> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/version.txt</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <excludes> <exclude>**/version.txt</exclude> </excludes> </resource> </resources> </build> <distributionManagement> <site> <id>reports</id> <url>dav:https://nexus.dev.techedge.pro:8443/repository/reports/${project.artifactId}/</url> </site> </distributionManagement> <reporting> <excludeDefaults>true</excludeDefaults> <plugins> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <configuration> <resultsDirectory>${project.build.directory}/allure-results</resultsDirectory> <reportDirectory>${project.reporting.outputDirectory}/${project.version}/allure</reportDirectory> </configuration> </plugin> </plugins> </reporting> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-api-service</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-codegen</artifactId> <version>${vertx.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-unit</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-junit5</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>${assertj-core.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.qameta.allure</groupId> <artifactId>allure-junit5</artifactId> <version>${allure.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>${testcontainers.version}</version> </dependency> </dependencies> </project>
HelloWorldService.java package io.bihero.helloworld; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; import io.vertx.ext.web.api.generator.WebApiServiceGen; @WebApiServiceGen public interface HelloWorldService { static HelloWorldService create(Vertx vertx, JsonObject config) { return new DefaultHelloWorldService(vertx, config); } void getHelloWorld(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); }
DefaultHelloWorldService.java package io.bihero.helloworld; import io.vertx.core.*; import io.vertx.core.buffer.Buffer; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; import io.vertx.ext.web.client.WebClient; public class DefaultHelloWorldService implements HelloWorldService { private final Vertx vertx; private final JsonObject config; private final WebClient webClient; public DefaultHelloWorldService(Vertx vertx, JsonObject config) { this.vertx = vertx; this.config = config; this.webClient = WebClient.create(this.vertx); } @Override public void getHelloWorld(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { getHelloWord().compose(this::getHelloWorld).setHandler(v -> resultHandler.handle( Future.succeededFuture(OperationResponse.completedWithPlainText(Buffer.buffer(v.result()))) )); } @Override public void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { vertx.fileSystem().readFile("doc.yaml", buffResult -> resultHandler.handle(Future.succeededFuture( OperationResponse.completedWithPlainText(buffResult.result())) )); } private Future<String> getHelloWord() { Future<String> future = Future.future(); webClient.get(config.getInteger("hello-service-port"), config.getString("hello-service-host"), "/").send(ar -> future.handle(Future.succeededFuture(ar.result().bodyAsString()))); return future; } private Future<String> getHelloWorld(String helloWord) { Future<String> future = Future.future(); webClient.get(config.getInteger("world-service-port"), config.getString("world-service-host"), "/").send(ar -> future.handle(Future.succeededFuture(helloWord + " " + ar.result().bodyAsString()))); return future; } }
HelloWorldVerticle.java package io.bihero.helloworld; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.api.contract.openapi3.OpenAPI3RouterFactory; import io.vertx.serviceproxy.ServiceBinder; public class HelloWorldVerticle extends AbstractVerticle { HttpServer server; MessageConsumer<JsonObject> consumer; public void startWorldService() { consumer = new ServiceBinder(vertx).setAddress("service.helloworld") .register(HelloWorldService.class, HelloWorldService.create(vertx, config())); } private Promise<Void> startHttpServer() { Promise<Void> promise = Promise.promise(); OpenAPI3RouterFactory.create(this.vertx, "/doc.yaml", openAPI3RouterFactoryAsyncResult -> { if (openAPI3RouterFactoryAsyncResult.succeeded()) { OpenAPI3RouterFactory routerFactory = openAPI3RouterFactoryAsyncResult.result();
HelloWorldServiceTest.java package io.bihero.helloworld; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.codec.BodyCodec; import io.vertx.junit5.Checkpoint; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.spy; @Testcontainers @ExtendWith(VertxExtension.class) public class HelloWorldServiceTest { @Container private static final GenericContainer helloServiceContainer = new GenericContainer("bihero/hello") .withExposedPorts(8081); @Container private static final GenericContainer worldServiceContainer = new GenericContainer("bihero/world") .withExposedPorts(8082); @Test @DisplayName("Test 'helloworld' microservice respond by 'Hello World' string and doc in OpenAPI format") public void testHelloWorld(Vertx vertx, VertxTestContext testContext) { WebClient webClient = WebClient.create(vertx); Checkpoint deploymentCheckpoint = testContext.checkpoint(); Checkpoint requestCheckpoint = testContext.checkpoint(2); HelloWorldVerticle verticle = spy(new HelloWorldVerticle()); JsonObject config = new JsonObject().put("serverPort", 8083) .put("serverHost", "0.0.0.0") .put("hello-service-host", helloServiceContainer.getContainerIpAddress()) .put("world-service-host", worldServiceContainer.getContainerIpAddress()) .put("hello-service-port", helloServiceContainer.getMappedPort(8081)) .put("world-service-port", worldServiceContainer.getMappedPort(8082)); DeploymentOptions deploymentOptions = new DeploymentOptions().setConfig(config); vertx.deployVerticle(verticle, deploymentOptions, testContext.succeeding(id -> { deploymentCheckpoint.flag();
Dockerfile тАФ / , /usr/local , ConfigMap' k8s
Dockerfile FROM adoptopenjdk/openjdk11:alpine-jre COPY target/hello-world-microservice-fat.jar app.jar COPY src/conf/config.json /usr/local/config.json COPY src/conf/logback-console.xml . COPY run.sh . RUN chmod +x run.sh CMD ["./run.sh"]
, , CI . Concourse , , worker- docker-compose' ( ui- postgresql). тАФ docker-, docker docker'. testcontainers ( hello world ). ? : ! docker', maven' 11- . , Dockerfile:
FROM alpine:3.7 ENV DOCKER_CHANNEL=stable \ DOCKER_VERSION=17.12.1-ce \ DOCKER_COMPOSE_VERSION=1.19.0 \ DOCKER_SQUASH=0.2.0 # Install Docker, Docker Compose, Docker Squash RUN apk --update --no-cache add \ bash \ curl \ device-mapper \ py-pip \ iptables \ util-linux \ ca-certificates \ maven \ openjdk11 --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community \ && \ apk upgrade && \ curl -fL "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/x86_64/docker-${DOCKER_VERSION}.tgz" | tar zx && \ mv /docker/* /bin/ && chmod +x /bin/docker* && \ pip install docker-compose==${DOCKER_COMPOSE_VERSION} && \ curl -fL "https://github.com/jwilder/docker-squash/releases/download/v${DOCKER_SQUASH}/docker-squash-linux-amd64-v${DOCKER_SQUASH}.tar.gz" | tar zx && \ mv /docker-squash* /bin/ && chmod +x /bin/docker-squash* && \ rm -rf /var/cache/apk/* && \ rm -rf /root/.cache COPY repository /root/.m2/repository # -, COPY settings.xml /root/.m2/settings.xml # maven' COPY entrypoint.sh /bin/entrypoint.sh # -, ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk/ ENTRYPOINT ["entrypoint.sh"]
entrypoint.sh , :
pipeline.yaml resource_types: - name: telegram type: docker-image source: repository: vtutrinov/concourse-telegram-resource tag: latest - name: kubernetes type: docker-image source: repository: zlabjp/kubernetes-resource tag: 1.16 - name: metadata type: docker-image source: repository: olhtbr/metadata-resource tag: 2.0.1 resources: - name: metadata type: metadata - name: sources type: git source: branch: master uri: git@github.com:bihero-io/helloworldmicroservice.git private_key: ((deployer-private-key)) - name: docker-image type: docker-image source: repository: bihero/helloworld username: ((docker-registry-user)) password: ((docker-registry-password)) - name: telegram type: telegram source: bot_token: ((telegram-ci-bot-token)) chat_id: ((telegram-group-to-report-build)) ci_url: ((ci_url)) command: "/build_helloworld_ms" - name: kubernetes-demo type: kubernetes source: server: ((k8s-api-server)) namespace: default kubeconfig: ((kubeconfig-demo)) jobs: - name: build-helloworld-microservice serial: true public: true plan: - in_parallel: - get: sources trigger: true - get: telegram trigger: true - put: metadata - put: telegram params: status: Build In Progress - task: tests privileged: true config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/dind # , , , maven 11- tag: latest username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: sources outputs: - name: tested-workspace run: path: entrypoint.sh args: - bash - -ceux - | # testcontainers output_dir=tested-workspace cp -R ./sources/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" clean test caches: - path: ~/.m2/ on_failure: do: - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message run: path: /bin/sh args: - -c - | output_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${output_dir}/pom.xml" site-deploy version=$(cat $output_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-world-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - put: telegram params: status: Build Failed (unit-tests) message_file: message/msg - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message - name: tested-workspace run: path: /bin/sh args: - -c - | work_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${work_dir}/pom.xml" site-deploy version=$(cat $work_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-world-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - task: package config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace - name: metadata outputs: - name: app-packaged-workspace - name: metadata run: path: /bin/sh args: - -c - | output_dir=app-packaged-workspace cp -R ./tested-workspace/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" package -Dmaven.main.skip -DskipTests tag="-"$(cat metadata/build_name) echo $tag >> ${output_dir}/target/classes/version.txt cat ${output_dir}/target/classes/version.txt > metadata/version caches: - path: ~/.m2/ on_failure: do: - put: telegram params: status: Build Failed (package) - put: docker-image params: build: app-packaged-workspace tag_file: app-packaged-workspace/target/classes/version.txt tag_as_latest: true get_params: skip_download: true - task: make-k8s-app-template config: platform: linux image_resource: type: docker-image source: repository: bhgedigital/envsubst inputs: - name: sources - name: metadata outputs: - name: k8s run: path: /bin/sh args: - -c - | export DOMAIN=demo1.bihero.io export HELLO_WORLD_SERVICE_IMAGE_VERSION=$(cat metadata/version) cat sources/k8s.yaml | envsubst > k8s/helloworld_app_template.yaml cat k8s/helloworld_app_template.yaml - put: kubernetes-demo params: kubectl: apply -f k8s/helloworld_app_template.yaml - put: telegram params: status: Build Success message_file: message/msg
k8s. hello world k8s-. k8s <service-name>..default.svc.cluster.local , , , API. , :
k8s apiVersion: v1 kind: ConfigMap metadata: name: hello-world-config data: config.json: | { "type": "file", "format": "json", "scanPeriod": 5000, "config": { "path": "/config.json" }, "serverPort": 8083, "serverHost": "0.0.0.0", "hello-service-host": "bihero-hello.default.svc.cluster.local", "hello-service-port": 8081, "world-service-host": "bihero-world.default.svc.cluster.local", "world-service-port": 8082 } --- apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: io.bihero.hello.service: bihero-helloworld name: bihero-helloworld spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 template: metadata: labels: io.bihero.hello.service: bihero-helloworld spec: containers: - image: bihero/helloworld:${HELLO_WORLD_SERVICE_IMAGE_VERSION} name: bihero-helloworld ports: - containerPort: 8083 imagePullPolicy: Always resources: {} volumeMounts: # /usr/local ConfigMap' - mountPath: /usr/local/ name: hello-world-config restartPolicy: Always volumes: - name: hello-world-config configMap: name: hello-world-config --- apiVersion: v1 kind: Service metadata: labels: io.bihero.hello.service: bihero-helloworld name: bihero-helloworld spec: ports: - name: "8083" port: 8083 targetPort: 8083 selector: io.bihero.hello.service: bihero-helloworld status: loadBalancer: {} --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: bihero-helloworld annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/secure-backends: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" nginx.ingress.kubernetes.io/rewrite-target: /$2 kubernetes.io/tls-acme: "true" namespace: default spec: tls: - hosts: - ${DOMAIN} secretName: bihero rules: - host: ${DOMAIN} http: paths: - path: /api/helloworld(/|$)(.*) backend: serviceName: bihero-helloworld servicePort: 8083
, тАФ , , , , :
curl https://demo1.bihero.io/api/helloworld Hello World
curl https://demo1.bihero.io/api/helloworld/doc openapi: 3.0.1 info: title: Hello World ;) description: "Hello World microservice. Aggregate 'Hello World' by hellomicroservice and worldmicroservice" version: 1.0.0 servers: - url: https://demo1.bihero.io/api/helloworld tags: - name: helloworld description: Everything about 'Hello World' paths: /: x-vertx-event-bus: address: service.helloworld timeout: 1000 get: tags: - helloworld summary: Aggregate 'Hello World' operationId: getHelloWorld responses: 200: description: OK content: {} /doc: x-vertx-event-bus: address: service.helloworld timeout: 1000c get: tags: - world summary: Get 'Hello World' microservice documentation operationId: getDoc responses: 200: description: OK components: {}
рд╣реБрд░реНрд░реЗ! ! , тАж
TODO' (backlog)
- тАФ parent pom .
- docker- docker-hub, тАФ , , private registry.
- "" (maven-release-plugin? concourse semver-resource ?), , , .
- API - ( HelloWorld, , , ). - , тАФ :)
, , , , :)
https://github.com/bihero-io/hello-microservice
https://github.com/bihero-io/worldmicroservice
https://github.com/bihero-io/helloworldmicroservice
[UPD] TODO'
- helm-, k8s , on-prem ,