Это последний из серии сообщений о непрерывной доставке, основанных на моем локальном стеке Docker (см. Первый и второй посты здесь).  В этом посте я использую простой проект Spring Boot, чтобы показать, как использовать концепцию « конвейер как код ».  Пожалуйста, обратите внимание, что это только пример, и гораздо больше возможно.  Приложение, которое я использую, взято с сайта Spring Boot .  Jenkinsfile вдохновлен тем, что в этом посте, но мне пришлось изменить некоторые вещи, чтобы он работал с моим стеком.  Источники моего проекта можно найти здесь .  Я объясню наиболее важные фрагменты в этом посте. 
  Конвейер, который я использую, содержит следующие этапы: 
  этап строительства 
  На этапе сборки я использую плагин GitLab для проверки источников моего проекта.  Я также поместил текущий commitId в textFile в рабочем каталоге.  Затем я использую Maven (тот, который мы назвали «M3» в конфигурации Jenkins, как я описал здесь ) для упаковки кода.  Я также проверяю, что commitId передается в качестве параметра в Maven. 
  этап развертывания 
  на этапе развертывания я закрываю работающий экземпляр приложения, отправляя «true» в путь / shutdown.  Затем я просто запускаю банку, которую я построил на предыдущем шаге.  После этого задание ожидает, пока приложение ответит на простой запрос. 
  тест на дым 
  На этом простом шаге тестирования я сравниваю возвращенный commitId моего развернутого сервиса с commitId, который мы получили, когда я извлек последний код фиксации.  Если все прошло хорошо, эти два идентификатора должны совпадать, если не что-то в цепочке пошло не так. 
Вот и все для этого примера. Давайте посмотрим, что это значит для исходного кода. Поскольку это проект Maven, я начинаю с pom.xml:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | <dependencies>  <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId>  </dependency>  <dependency>    <!-- used for metrics like status, health etc -->    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-actuator</artifactId>  </dependency>  <dependency>    <!-- used for unit tests -->    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope>  </dependency></dependencies> | 
  Никаких особых зависимостей для этого проекта не требуется.  Spring-boot-starter-web используется для нашего REST-контроллера.  « Sprint-boot-starter-привод » может использоваться для проверки работоспособности и многого другого . 
  Наконец, ‘spring-boot-starter-test’ используется для того, чтобы иметь возможность (модульно) протестировать контроллер. 
  Давайте посмотрим на источники Java.  Приложение просто запускает приложение Spring Boot.  Класс Controller также очень прост: 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 | packagehello;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.bind.annotation.RequestMapping;@RestControllerpublicclassHelloController {     @RequestMapping("/")  publicString index() {    return"Greetings from Spring Boot!";  }    } | 
Как видите, я просто возвращаю фиксированную строку, когда GET-запрос поступает в ‘/’. Тестовый класс имеет следующий тестовый код:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | /** * Created by pascal on 19/01/2017. */@RunWith(SpringRunner.class)@SpringBootTest@AutoConfigureMockMvcpublicclassHelloControllerTest {    @Autowired    privateMockMvc mvc;    @Test    publicvoidgetHello() throwsException {        mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))                .andExpect(status().isOk())                .andExpect(content().string(equalTo("Greetings from Spring Boot!")));    }} | 
Это также просто, я думаю, я ожидаю, что фиксированная строка будет ответом на запрос GET. Рядом с кодом Java находится файл application.properties:
| 1 2 3 4 5 6 | server.port=8888info.app.name=@project.name@info.app.description=@project.description@info.app.version=@project.version@info.app.commitid=@commitid@endpoints.shutdown.enabled=true | 
Помимо двух функциональных свойств: порта, на котором мы запускаем приложение (8888), и возможности завершать работу приложения, вызывая конечную точку (endpoints.shutdown.enabled = true), остальные должны отображаться при вызове конечной точки ‘/ Информация’. Параметры @… @ будут заменены реальными значениями на Maven, поскольку мы фильтруем ресурсы:
| 01 02 03 04 05 06 07 08 09 10 | ...<resources>  <!-- used for variable substitution in application.properties -->  <resource>    <directory>src/main/resources</directory>    <filtering>true</filtering>  </resource></resources>... | 
Наконец у нас есть Jenkinsfile в проекте:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | importgroovy.json.JsonSlurper; properties([[$class: 'GitLabConnectionProperty', gitLabConnection: 'my-gitlab-connection']])node{    stage 'Build, Test and Package'    env.PATH = "${tool 'M3'}/bin:${env.PATH}"    checkout scm    // workaround, taken from https://github.com/jenkinsci/pipeline-examples/blob/master/pipeline-examples/gitcommit/gitcommit.groovy    def commitid = sh(returnStdout: true, script: 'git rev-parse HEAD').trim()    def workspacePath = pwd()    sh "echo ${commitid} > ${workspacePath}/expectedCommitid.txt"        withMaven(                maven: 'M3',                mavenSettingsConfig: 'a1adf035-653b-410d-b5a6-16b6da77b322',                mavenLocalRepo: '.repository') {                // Run the maven build            sh "mvn clean package -Dcommitid=${commitid}"        }} node{    stage 'Stop, Deploy and Start'    // shutdown    // copy file to target location    sh 'cp target/*.jar /tmp/'    // start the application    sh 'nohup java -jar /tmp/*.jar &'    // wait for application to respond} node{    stage 'Smoketest'    def workspacePath = pwd()    if(deploymentOk()){        return0    } else{        return1    }} def deploymentOk(){    def workspacePath = pwd()    expectedCommitid = newFile("${workspacePath}/expectedCommitid.txt").text.trim()    actualCommitid = readCommitidFromJson()    println "expected commitid from txt: ${expectedCommitid}"    println "actual commitid from json: ${actualCommitid}"    returnexpectedCommitid == actualCommitid} def readCommitidFromJson() {    def workspacePath = pwd()    def slurper = newJsonSlurper()    def json = slurper.parseText(newFile("${workspacePath}/info.json").text)    def commitid = json.app.commitid    returncommitid} | 
Я описал работу сценария ранее. Есть три важных константы, которые должны соответствовать нашей установке Jenkins:
-  В операторе: properties([[$class: 'GitLabConnectionProperty', gitLabConnection: 'my-gitlab-connection']])‘ my-gitlab-connection ‘ соответствует имени, которое я дал своему gitlabConnection в плагине Jenkins, как я описал здесь .
-   Как я описал перед «M3» в заявлении: 
 env.PATH = "${tool 'M3'}/bin:${env.PATH}"должен соответствовать установке Maven в Jenkins, как я описал здесь .
-   Наконец, есть строка mavenSettingsConfig: 'a1adf035-653b-410d-b5a6-16b6da77b322'. Упомянутый здесь идентификатор скопирован из файла настроек, который я настроил с помощью плагина Config File Provider, как описано здесь .
  Это все об источниках проекта.  Позвольте мне показать вам, как создать конвейерную работу в Jenkins.  В панели управления выберите создание нового задания типа «конвейер»: 
 
  Далее настройте эту работу, где самое важное — использовать Jenkinsfile, полученный из git.  Чтобы настроить это, мы должны использовать имя пользователя / пароль для входа в Gitlab (я пока не нашел способа использовать плагин Gitlab. Вы также можете использовать другое хранилище здесь, если вы хотите, чтобы ваши Jenkinsfiles были отделены от вашего проекта. источники): 
 
  Теперь, когда я запускаю задание, на последнем шаге произойдет сбой со следующей ошибкой: 
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: сценариям не разрешено использовать новый java.io.File java.lang.String
в org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.StaticWhitelist.rejectNew (StaticWhitelist.java:187)
….
  Существует одна последняя настройка, чтобы сделать эту работу успешной.  По умолчанию определенные действия не разрешены конвейерным заданием, поэтому я должен сказать Дженкинсу, что в этом случае они разрешены. 
  Для этого перейдите к «Управление Jenkins» и перейдите к «Утверждение сценариев в процессе»: 
 
  Существует упоминание о возможной уязвимости безопасности, которую вы должны утвердить, прежде чем задание позволит выполнить действие: 
 
  После нажатия кнопки «Подтвердить» и повторного запуска задания будет еще одна уязвимость, которая должна быть утверждена для успешного завершения задания. 
  Теперь сборка будет успешной для всех трех этапов, как показано на панели инструментов: 
 
  На этом завершается пример непрерывной доставки и конвейерной обработки в виде кода.  Как упоминалось ранее, это просто очень простой пример конвейера, но вы можете использовать его, чтобы начать работу с концепцией и извлечь из нее гораздо больше. 
| Ссылка: | Передайте в виде кода приложение Spring Boot от нашего партнера по JCG Паскаля Альмы в блоге Pragmatic Integrator . |