jenkins

Jenkins Workshop 21-22 November 2020

Do pobrania

Google drive

sha1sum
51bb48ecdfb4ce210f1941e43eebe674fb524b91  Jenkins_workshop_5.ova
3c6bff4eea93c387bbdf193015cda7c04833e75f  logger-plugin.hpi
a9fa67ec8bb023cf0d99eccd47b9cca96b433500  old-data-plugin.hpi
9d97f5492b3ba5c9cc502b3e26aa4b93a1c32ec5  old-data-plugin_v2.hpi

Instalacja wirtualnej maszyny

Wymagania Zainstalowany Virtual Box

W VM VirtualBox Manager wybieramy: Plik -> Importuj urządzenie wirtualne.

W kolejnym oknie wybieramy pobrany obraz.

Import

W kolejnym oknie należy zwrócić uwagę na dwie opcje.

Import

Po zaimportowaniu i uruchomieniu powinniśmy otrzymać.

Import

Proszę, zwrócić uwagę na przydzielony adres IP powinien to być w waszej sieci, a nie adres localhost (127.0.0.1)!

Użytkownicy:

jenkins:12345678
root:12345678

Można się zalogować bezpośrednio w oknie powyżej, używając programu putty lub innego klienta ssh (np. git Bash).

ssh root@192.168.0.178
or
ssh jenkins@192.168.0.178

0: Środowisko

Zanim przejdziemy do dalszych działań, należy wygenerować klucze ssh na naszej wirtualnej maszynie. Następnie klucz publiczny przypisać do swojego konta w Bitbuckecie.

$ ssh-keygen -t rsa -C bklimas@acme.com

$ ll ~/.ssh/
razem 12
-rw-------. 1 jenkins jenkins 1843 11-15 10:08 id_rsa
-rw-r--r--. 1 jenkins jenkins  411 11-15 10:08 id_rsa.pub
-rw-r--r--. 1 jenkins jenkins  571 11-15 09:55 known_hosts

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzsDiyJ+4WZAwaYDVz43Zrm6RCxPTzFWXnUfuBdAx6BmM98gxnveU0k+AbiiF739ZTDKxmWHyBmgGKVMzrSYoldKAPvTii4X/FtQfdsUrvl3gL9E+UM2MJ3k+yaYS88RmfDH7TTYBAwvFYqP3+F7F9tX+Te2Purf/ZwU+r5ekPMP4gtkO9Pi1XtzdOAW2vDHrXm2QJqOQPa8Ppx6SalrLOnlCjo5RtpEIBhjTN3WkwhRYHxDZedTZE0YFyn8fNna4UlZiEhPaLRu6utykDTyfOOhFiONYj9naNzv0/wIAYgYAFreD5CCNZEVMKIYwuKlS9Tb0gyzpDQYBjNGAvks2v jenkins@localhost.localdomain

Bitbucket SSH keys Keys 1 Dodatkowo na wirtualnej maszynie, jak i na naszym komputerze (zapis hosta do ~/.known_hosts)

Bardzo pomocnym będzie także wgranie swojego klucza (z konta, którego teraz używacie) publicznego do Bitbucketa (nie ma się co obawiać to klucz publiczny!)

cd /tmp
git clone ssh://git@ovh.klimas7.pl:7999/lab/learn.git

$ git clone ssh://git@ovh.klimas7.pl:7999/lab/learn.git
Cloning into 'learn'...
The authenticity of host '[ovh.klimas7.pl]:7999 ([51.89.23.232]:7999)' can't be established.
RSA key fingerprint is SHA256:N9yD+ozjLANIDbEfFL/zuqv1smd1sBY58NJOSakHjuo.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[ovh.klimas7.pl]:7999,[51.89.23.232]:7999' (RSA) to the list of known hosts

1: Uruchomienie

Instalacja via dnf

Definicja repozytorium i import klucza

# wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.repo
# rpm --import https://pkg.jenkins.io/redhat/jenkins.io.key

Instalacja

# dnf install jenkins
# dnf install java

Uruchomienie serwisu

# systemctl status jenkins.service
# systemctl enable jenkins.service
# systemctl start jenkins.service

Test: http://192.168.0.178:8080/ otwieramy w przeglądarce

Firewall

# firewall-cmd --permanent --add-service=jenkins
# firewall-cmd --zone=public --add-service=http --permanent
# firewall-cmd --reload
# firewall-cmd --list-all

Test http://192.168.0.178:8080/ otwieramy w przeglądarce

Usunięcie

# systemctl stop jenkins.service
# dnf remove jenkins

Docker

$ docker pull jenkins/jenkins
# check version
$ docker image inspect docker.io/jenkins/jenkins
# start
$ docker run -d -v jenkins_home:/var/jenkins_home --name jenkins_workshop -p 8080:8080 -p 50000:50000 jenkins/jenkins &
$ docker ps

Test http://192.168.0.178:8080/ otwieramy w przeglądarce

$ docker exec jenkins_workshop cat /var/jenkins_home/secrets/initialAdminPassword

Stop and clean

$ docker stop jenkins_workshop
$ docker rm jenkins_workshop
$ docker volume rm jenkins_home

Uruchomienie via jar

$ cd /opt/tools/jenkins
$ java -jar jenkins.war &

Test http://192.168.0.178:8080/ otwieramy w przeglądarce

Stop and clean

$ ps aux | grep java
$ kill -9 1338 #JENKINS_PID
# or
$ killall java

rm -rf ~/.jenkins

Uruchomienie ze skryptu

$ cd /opt/tools/jenkins/
$ ls
jenkins.sh  jenkins.war  ssh
$ cat jenkins.sh 
#!/bin/bash

jh=/opt/tools/jenkins
jp=8080
mkdir -p $jh/log
mkdir -p $jh/war

command=$1

printUsage() {
    echo "Usage:"
    echo "jenkins.sh start"
    echo "jenkins.sh stop"
}

start() {
    jenkins_options="--logfile=$jh/log/jenkins.log --webroot=$jh/war"
    jenkins_options="$jenkins_options --ajp13Port=-1 --debug=5 \ 
                    --handlerCountMax=100 --handlerCountMaxIdle=20"
    
    #use http
    jenkins_options="$jenkins_options --httpPort=$jp"
    
    #use_https
    #jenkins_options="$jenkins_options --httpPort=-1 --httpsPort=8443 \ 
                       --httpsCertificate=$jh/ssh/cert.pem  \
                       --httpsPrivateKey=$jh/ssh/key.pem"

    java -Dcom.sun.akuma.Daemon=daemonized -Djava.awt.headless=true -DJENKINS_HOME=$jh \ 
         -jar $jh/jenkins.war $jenkins_options &
}

stop() {
    jenkinsPID=$(ps aux | grep java | grep $jh | awk '{print $2}')
    echo "kill jenkins process PID: "$jenkinsPID
    kill -9 $jenkinsPID 2>&1 > /dev/null
}

case $command in
    "start")
        start
        ;;
    "stop")
        stop
        ;;
    *)
        printUsage
esac

winstone, winstone 1, ajp, headless, com.sun.akuma, Jenkins initial-settings

$ ./jenkins.sh

Test http://192.168.0.178:8080/ otwieramy w przeglądarce

uruchomienie https

ssl files explanation

cd /opt/tools/jenkins/ssh
$ openssl genrsa -out key.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
..............................................+++++
..............+++++
e is 65537 (0x010001)

$ openssl req -new -key key.pem -out csr.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:PL
State or Province Name (full name) []:Lesser Poland
Locality Name (eg, city) [Default City]:KRK
Organization Name (eg, company) [Default Company Ltd]:K7Soft            
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:192.168.0.178
Email Address []:admin@k7soft.pl

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

#### Self-signed certificate ####
$ openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
Signature ok
subject=C = PL, ST = Lesser Poland, L = KRK, O = K7Soft, CN = 192.168.0.178, emailAddress = admin@k7soft.pl
Getting Private key

W jenkins.sh

#use http
#jenkins_options="$jenkins_options --httpPort=$jp"

#use_https
jenkins_options="$jenkins_options --httpPort=-1 --httpsPort=8443 --httpsCertificate=$jh/ssh/cert.pem --httpsPrivateKey=$jh/ssh/key.pem"

A tu w logach

2020-10-30 11:55:00.779+0000 [id=1]     WARNING winstone.Logger#logInternal: Using the --httpsPrivateKey/--httpsCertificate 
options currently relies on unsupported APIs in the Oracle JRE.
Please use --httpsKeyStore and related options instead.

Create keystore

$ openssl pkcs12 -export -in cert.pem -inkey key.pem -out jenkins.p12 -name jenkins
Enter Export Password:
Verifying - Enter Export Password:

#Password: 12345678

$ keytool -importkeystore -deststorepass 12345678 -destkeypass 12345678 \
          -destkeystore jenkins.keystore -srckeystore jenkins.p12 \ 
          -srcstoretype PKCS12 -srcstorepass 12345678 -alias jenkins
Importing keystore jenkins.p12 to jenkins.keystore...

$ keytool -list -keystore jenkins.keystore 
Enter keystore password:  
Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 1 entry

jenkins, 30 paź 2020, PrivateKeyEntry, 
Certificate fingerprint (SHA-256): D9:9C:7F:85:C5:1C:D8:32:82:0A:70:8E:8E:CF:59:15:B9:22:A2:9B:5D:20:35:6E:44:D8:FE:D1:EA:15:0D:0E

Dodatkowo w jenkins.sh

#jenkins_options="$jenkins_options --httpPort=-1 --httpsPort=8443 --httpsCertificate=$jh/ssh/cert.pem --httpsPrivateKey=$jh/ssh/key.pem"
jenkins_options="$jenkins_options --httpPort=-1 --httpsPort=8443 --httpsKeyStore=$jh/ssh/jenkins.keystore --httpsKeyStorePassword=12345678"

Firewall

## disabled
#
# systemctl status firewalld
# systemctl stop firewalld

## or open port
# firewall-cmd --get-active-zones
# firewall-cmd --permanent --zone=public --add-port=8443/tcp
# systemctl restart firewalld

Ćwiczenie 1.1

Uruchomienie ze skryptu

Ćwiczenie 1.2*

Uruchomienie ze skryptu po https

W każdym przypadku powinniśmy otrzymać stronę startową Start Hasło ze wskazanego pliku wklejamy w pole tekstowe

$ cat /opt/tools/jenkins/secrets/initialAdminPassword

Setup wizard Setup wizard

Jeżeli zamkniemy powyższe okno Setup wizard finish

Ważne!

Instalujemy sugerowane wtyczki Initial plugins

Admin page Admin page

Login: admin
Hasło: 12345678
Pełna nazwa: Administrator
Adres email: admin@acme.com

Url Wizard Finish setup

2: Katalog domowy

Instalacja via dnf

JENKINS_HOME=/var/lib/jenkins
logfile=/var/log/jenkins/jenkins.log
war=/usr/lib/jenkins/jenkins.war

Instalacja via jar

JENKINS_HOME=/home/jenkins/.jenkins

Struktura

├── config.xml              (jenkins root configuration)
├── *.xml                   (other site-wide configuration files)
├── jenkins.sh              (start stop script)
├── jenkins.war             (jenkins war :))
├── log                     (task log directory)
├── logs                    (logs)
├── nodes                   (nodes configuration)
├── plugins                 (stores plugins)
├── secrets                 (secretes needed when migrating credentials to other servers)
├── ssh                     (ssl configuration)
├── updates                 (updates config)
├── userContent             (files in this directory will be served under your http://server/userContent/)
├── users                   (users configuration)
│   ├── admin_4702638789067880202
│   └── users.xml
├── war                     (unpacked jenkins.war)
├── workflow-libs           (shared library)
├── jobs                    (jobs configuration and builds)
│   ├── Job A
│   │   ├── builds
│   │   │   ├── 1
│   │   │   │   ├── build.xml
│   │   │   │   ├── changelog.xml
│   │   │   │   └── log
│   │   │   ├── 2
│   │   │   │   ├── build.xml
│   │   │   │   ├── changelog.xml
│   │   │   │   └── log
│   │   │   ├── legacyIds
│   │   │   └── permalinks
│   │   ├── config.xml
│   │   └── nextBuildNumber
│   └── restCounter
│       ├── builds
│       │   ├── 1
│       │   │   ├── build.xml
│       │   │   └── log
│       │   ├── 2
│       │   │   ├── build.xml
│       │   │   ├── changelog.xml
│       │   │   ├── log
│       │   │   └── pl.klimas7$restCounter -> ../../modules/pl.klimas7$restCounter/builds/2
│       │   ├── 3
│       │   │   ├── build.xml
│       │   │   ├── changelog.xml
│       │   │   ├── log
│       │   │   └── pl.klimas7$restCounter -> ../../modules/pl.klimas7$restCounter/builds/3
│       │   ├── legacyIds
│       │   └── permalinks
│       ├── config.xml
│       ├── modules
│       │   └── pl.klimas7$restCounter
│       │       ├── builds
│       │       │   ├── 2
│       │       │   │   ├── archive
│       │       │   │   │   └── pl.klimas7
│       │       │   │   │       └── restCounter
│       │       │   │   │           └── 0.0.1-SNAPSHOT
│       │       │   │   │               ├── restCounter-0.0.1-SNAPSHOT.jar
│       │       │   │   │               └── restCounter-0.0.1-SNAPSHOT.pom
│       │       │   │   ├── build.xml
│       │       │   │   ├── junitResult.xml
│       │       │   │   └── log
│       │       │   ├── 3
│       │       │   │   ├── archive
│       │       │   │   │   └── pl.klimas7
│       │       │   │   │       └── restCounter
│       │       │   │   │           └── 0.0.1-SNAPSHOT
│       │       │   │   │               ├── restCounter-0.0.1-SNAPSHOT.jar
│       │       │   │   │               └── restCounter-0.0.1-SNAPSHOT.pom
│       │       │   │   ├── build.xml
│       │       │   │   ├── junitResult.xml
│       │       │   │   └── log
│       │       │   ├── legacyIds
│       │       │   └── permalinks
│       │       ├── config.xml
│       │       └── nextBuildNumber
│       └── nextBuildNumber
└── workspace               (working directory for the version control system)
    ├── Job A
    │   └── test_job_a.txt
    └── restCounter
        ├── pom.xml
        ├── README.md
        ├── restCounter.sh
        ├── src
        └── target

2.1 Ćwiczenie: Zmiana liczby egzekutorów

$ cd /opt/tools/jenkins/
$ vim config.xml
<?xml version='1.1' encoding='UTF-8'?>
<hudson>
  <disabledAdministrativeMonitors/>
  <version>2.235.5</version>
  <installStateName>RUNNING</installStateName>
  <numExecutors>4</numExecutors>                <!-- z 2 na 4 --> 
  <mode>NORMAL</mode>
  <useSecurity>true</useSecurity>
  <!-- ... -->
</hudson>

Jenkins -> Zarządzaj Jenkinsem -> Odczytaj ponownie konfigurację z dysku Reload configuration

vim:

2.2 Ćwiczenie: Odzyskanie dostępu

$ cd /opt/tools/jenkins/
$ ./jenkins.sh stop
$ vim config.xml
<hudson>
  <disabledAdministrativeMonitors/>
  <version>2.235.5</version>
  <installStateName>RUNNING</installStateName>
  <numExecutors>4</numExecutors>
  <mode>NORMAL</mode>
  <useSecurity>false</useSecurity>              <!-- z true na false -->
  <authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy">
    <denyAnonymousReadAccess>true</denyAnonymousReadAccess>
  </authorizationStrategy>
  <!-- ... -->
</hubson>
$ ./jenkins.sh start

Jenkins -> Zarządzaj Jenkinsem -> Konfiguruj ustawienia bezpieczeństwa Konfiguruj ustawienia bezpieczeństwa

Jenkins -> Użytkownicy -> admin -> Konfiguracja

admin -> Konfiguracja

2.3: Ćwiczenie* UserContent

Jenkins -> Nowy Projekt -> Ogólny projekt (nazwa UserContent) -> OK User Content Job

Budowanie -> Dodaj krok budowania -> Uruchom powłokę User Content Job Configuration

echo "Test" > ../../userContent/${BUILD_TAG}.txt

Wynik http://192.168.0.178:8080/userContent/ User Content Job Output

Udostępnianie ‘linków’

$ cd /opt/tools/jenkins/
$ ln -s /opt/tools/maven userContent/maven

User Content Job link

System Properties

$ ./jenkins.sh stop
$ vim jenkins.sh
    java_options="-Dhudson.model.DirectoryBrowserSupport.allowSymlinkEscape=true"
    java -Dcom.sun.akuma.Daemon=daemonized -Djava.awt.headless=true -DJENKINS_HOME=$jh $java_options -jar $jh/jenkins.war $jenkins_options &
$ ./jenkins.sh start

User Content Job link 2

3: Aktualizacja i instalacja wtyczek

Aktualizacja Jenkinsa dostępna ze strony głównej Update Jenkins

lub Jenkins -> Zarządzaj Jenkinsem Update Jenkins 2 Update Jenkins 3

Powrót do poprzedniej wersji Jenkins -> Zarządzaj Jenkinsem Downgrade Jenkins

Aktualizacja wtyczek Menedżer wtyczek Jenkins -> Zarządzaj Jenkinsem -> Zarządzaj wtyczkami Update plugins Update plugins Update plugins

Instalacja wtyczek spoza repozytorium Jenkins -> Zarządzaj Jenkinsem -> Zarządzaj wtyczkami Menedżer wtyczek -> Zaawansowane Install plugin

Ćwiczenie 3.1: Aktualizacja

Zainstaluj aktualizacje Jenkinsa, wtyczek oraz dwie wtyczki spoza repozytorium

logger-plugin.hpi
old-data-plugin.hpi

4: Status Information

4.1: Ćwiczenie

Tworzymy 4 projekty (joby) Jenkins -> Nowy Projekt -> Ogólny projekt (JobShedule[1..4])
Opcje

Kolejne 3 można stworzyć używając funkcji Kopiuj z Jenkins -> Nowy Projekt -> Kopiuj z Copy Job 1 Copy Job 2 Copy Job 3

4.2: Informacje o systemie

Dostępne informacje:

4.3: Informacje o Jenkinsie

Dostępne informacje:

4.4: Statystyki obciążenia

4.5: Dziennik systemowy

Źródła wtyczki logger-plugin

4.5.1: Ćwiczenie

Tworzymy projekt ogólny Logger w którym użyjemy kroku budowania Logger example
Jenkins -> Nowy Projekt -> Ogólny projekt (Logger)
Budowanie -> Logger example
Logger 1
Zapisz, Uruchom
Sprawdzamy co otrzymaliśmy w logach
Jenkins -> Zarządzaj Jenkinsem -> Dziennik systemwy -> Wszystkie zdarzenia Jenkinsa
Logger 2

lis 13, 2020 6:24:18 PM INFO io.jenkins.plugins.LoggerBuilder printLog
Log: INFO code: 800
lis 13, 2020 6:24:18 PM SEVERE io.jenkins.plugins.LoggerBuilder printLog
Log: SEVERE code: 1000
lis 13, 2020 6:24:18 PM WARNING io.jenkins.plugins.LoggerBuilder printLog
Log: WARNING code: 900

Dodanie dedykowanego loggera Jenkins -> Zarządzaj Jenkinsem -> Dziennik systemwy -> Dodaj nowy rejestrator logów
Nazwa: Logger
Logger 3
Loggers: Logger: io.jenkins.plugins Log level: ALL
Logger 4
Save, Uruchom ponownie projekt Logger w nowo dodanym logerze

lis 13, 2020 6:42:06 PM ALL io.jenkins.plugins.LoggerBuilder
Log: ALL code: -2147483648
lis 13, 2020 6:42:06 PM CONFIG io.jenkins.plugins.LoggerBuilder
Log: CONFIG code: 700
lis 13, 2020 6:42:06 PM FINE io.jenkins.plugins.LoggerBuilder
Log: FINE code: 500
lis 13, 2020 6:42:06 PM FINER io.jenkins.plugins.LoggerBuilder
Log: FINER code: 400
lis 13, 2020 6:42:06 PM FINEST io.jenkins.plugins.LoggerBuilder
Log: FINEST code: 300
lis 13, 2020 6:42:06 PM INFO io.jenkins.plugins.LoggerBuilder printLog
Log: INFO code: 800
lis 13, 2020 6:42:06 PM SEVERE io.jenkins.plugins.LoggerBuilder printLog
Log: SEVERE code: 1000
lis 13, 2020 6:42:06 PM WARNING io.jenkins.plugins.LoggerBuilder printLog
Log: WARNING code: 900

5: Troubleshooting

Jenkins -> Zarządzaj Jenkinsem -> Troubleshooting -> Zarządzanie starymi danymi
Mechanizm zabezpieczający Jenkinsa przed zmianami w konfiguracji np. wtyczek. Administrator powinien świadomie przeglądnąć zmiany i ewentualnie podjąć odpowiednie działania.

Przykład zmian w pluginie: Źródła wtyczki OldDataPlugin
Tworzymy projekt ogólny OldData w którym użyjemy kroku budowania Old Data
Jenkins -> Nowy Projekt -> Ogólny projekt (OldData)
Budowanie -> Old Data First, Second mogą być dowolne
Old Data 1
Save, Uruchom

Uruchomiono przez użytkownika Administrator
Running as SYSTEM
Building in workspace /opt/tools/jenkins/workspace/OldData
First: One Second: Two
Finished: SUCCESS

Instalujemy kolejną wersję wtyczki oldData old-data-plugin_v2.hpi Materiały
Jenkins -> Zarządzaj Jenkinsem -> Zarządzaj wtyczkami -> Zawansowane -> Prześlij wtyczkę (old-data-plugin_v2.hpi)
Old Data 2
Restart.
Old Data 3
Old Data 4

5.1: Ćwiczenie Wykonać powyższy przykład.

6: Skonfiguruj system

Miejsce w Jenkinsie gdzie znajdziemy globalne ustawienia samego Jenkinsa oraz wtyczek, zmienne środowiskowe …
Jenkins -> Zarządzaj Jenkinsem -> Skonfiguruj system

6.1: Ćwiczenie, Jenkins Location

Zdefiniować System Admin e-mail addres jenkins.klimas7@gmail.com Admin Email

6.2: Ćwiczenie, Global properties

Dodać kilka zmiennych środowiskowych

STAGE_1:false
TEST_BRANCH:master
TEST_SCRIPT:Test_script

Properties

6.3: Ćwiczenie, Global Build Discarders

Ustawić globalny log Rotation Maksymalna ilość zadań do przechowania: 5 Properties

6.4: Ćwiczenie, E-mail Notification

Powiadomienia e-mail z wykorzystaniem konta google

SMTP server:                smtp.gmail.com
Use SMTP Authentication:    true
User Name:                  jenkins.klimas7@gmail.com
Password:                   <?>
Use SSL:                    true
Use TLS:                    true
SMTP Port:                  465

e-mail
Konto Google
Google Account

6.5: Quiet period

Okres spokoju, cichy okres, czas w sekundach gdzie określamy ile projekt czeka z uruchomieniem po wyzwoleniu przez

Przykład 1: inny projekt

Jenkins -> Nowy Projekt -> Ogólny projekt (QP_Parent)

Jenkins -> Nowy Projekt -> Ogólny projekt (QP_Child)

Uruchamiamy projekt QP_Parent
Quiet period 4

Jeżeli w tym czasie uruchomimy QP_Parent kilka razy to projekt podrzędny z cichym okresem nie będzie uruchomiony, w informacji o wystartowaniu otrzymamy

Started by upstream project "QP_Parent" build number 6
originally caused by:
 Uruchomiono przez użytkownika Jenkins Administrator
Started by upstream project "QP_Parent" build number 7
originally caused by:
 Uruchomiono przez użytkownika Jenkins Administrator
Started by upstream project "QP_Parent" build number 8
originally caused by:
 Uruchomiono przez użytkownika Jenkins Administrator
Started by upstream project "QP_Parent" build number 9
originally caused by:
 Uruchomiono przez użytkownika Jenkins Administrator

Przykład 2: zmiany w repozytorium

Quiet period 6
Quiet period 5

7: Zarządzanie węzłami (nodes)

Dobre praktyki:

Jenkins -> Zarządzaj Jenkinsem -> Zarządzaj węzłami Node 1
Dodając nowy węzeł mamy możliwość dodania kolejnego lub utworzenie kopii już istniejącego. Node 2
Plan wykorzystania:

Metoda uruchomienia:

Availability:

7.1 Uruchomienie via JNLP

JNLP Java Network Launch Protocol
W pierwszej kolejności należy w ustawieniach bezpieczeństwa odblokować port dla agentów
Jenkins -> Zarządzaj Jenkinsem -> Konfiguruj ustawienia bezpieczeństwa -> Agents
TCP port for inbound agents -> Fixed: 8082
Node 7
Dodatkowo port należy odblokować w zaporze sieciowej

firewall-cmd --permanent --zone=public --add-port=8082/tcp
systemctl restart firewalld

Node 8
Jenkins -> Węzły -> Linux_2
Node 9

$ java -jar agent.jar -jnlpUrl http://192.168.0.178:8080/computer/Linux_2/slave-agent.jnlp -secret f9b49c94b7e9cae76ffb0b63be0f34a9e7ac52c14aec447a2d27a90ced8180d3 -workDir "/tmp/linux_2"
lis 15, 2020 11:52:30 PM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
INFO: Using /tmp/linux_2/remoting as a remoting work directory
lis 15, 2020 11:52:30 PM org.jenkinsci.remoting.engine.WorkDirManager setupLogging
INFO: Both error and output logs will be printed to /tmp/linux_2/remoting
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main createEngine
INFO: Setting up agent: Linux_2
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener <init>
INFO: Jenkins agent is running in headless mode.
lis 15, 2020 11:52:30 PM hudson.remoting.Engine startEngine
INFO: Using Remoting version: 4.5
lis 15, 2020 11:52:30 PM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
INFO: Using /tmp/linux_2/remoting as a remoting work directory
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Locating server among [http://192.168.0.178:8080/]
lis 15, 2020 11:52:30 PM org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver resolve
INFO: Remoting server accepts the following protocols: [JNLP4-connect, Ping]
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Agent discovery successful
  Agent address: 192.168.0.178
  Agent port:    8082
  Identity:      c9:b8:2c:81:18:d8:6a:ef:19:e8:d8:91:84:7a:ba:64
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Handshaking
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Connecting to 192.168.0.178:8082
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Trying protocol: JNLP4-connect
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Remote identity confirmed: c9:b8:2c:81:18:d8:6a:ef:19:e8:d8:91:84:7a:ba:64
lis 15, 2020 11:52:31 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Connected

Node 10

Odnośniki: java11-preview-availability, JENKINS-52282, Jenkins PR

7.3 Node Monitoring

Node 6

7.4 Uruchomienie via SSH, Ćwiczenie

Utworzyć 2 dodatkowe węzły na naszej wirtualnej maszynie przy wykorzystaniu ssh

#Tworzymy katalogi dla naszych węzłów
$ mkdir -p /work/node{1,2}

Tworzymy pierwszy węzeł
Node 3
Tworzymy drugi jako kopię pierwszego
Node 4
Z odpowiednimi modyfikacjami
Node 5

8: Globalne narzędzia do konfiguracji

8.1: Managed files

Managed files to miejsce, w którym możemy definiować pliki konfiguracyjne dostępne dla wszystkich węzłów i projektów
Global 2
Global 1

8.2: Globalne narzędzia do konfiguracji

Jenkins pozwala zdefiniować narzędzia wykorzystywane w projektach, takie jak maven, java, docker.
Jenkins -> Zarządzaj Jenkinsem -> Globalne narzędzia do konfiguracji

Maven Configuration

JDK

Nazwa       jdk8
JAVA_HOME   /opt/java/jdk8

Nazwa       jdk12
JAVA_HOME   /opt/java/jdk12

Maven

Name        mvn
MAVEN_HOME  /opt/tools/maven

Docker

Name                docker
Installation root   /usr/bin

8.2: Globalne narzędzia do konfiguracji dla węzłów

Global 3

8.3: Ćwiczenie

Zdefiniować jdk, maven oraz docker zgodnie z powyższym opisem

9: Konfiguruj ustawienia bezpieczeństwa

Jenkins -> Zarządzaj Jenkinsem -> (Security) -> Konfiguruj ustawienia bezpieczeństwa

9.1 Authentication

9.2 Authorization

Uwaga W przypadku Matrix-based security oraz Project-based Matrix Authorization Strategy nie jest możliwe permanentnie odebrać roli Administrator, jest ona przywracana ostatniej grupie lub użytkownikowi, który ją posiadał.

9.3 Agents

Definicja portu, z którego będą korzystać agenci do komunikacji z Jenkinsem
Agents port
Uruchomienie via JNLP

9.4 SSH Server

Definicja portu dla serwera ssh wbudowanego w Jenkinsa wykorzystywany do Jenkins CLI
SSH port
SSH

9.5 Ćwiczenie 1

Autentykacja za pomocą LDAP (Lightweight Directory Access Protocol). Nasz LDAP UI

Server:                         ovh.klimas7.pl
root DN:                        dc=acme,dc=com
User search filter:             uid={0}
Group membership:               Search for LDAP groups containing user
Manager DN:                     cn=admin,dc=acme,dc=com
Manager Password:               <?>
Display Name LDAP attribute:    displayname
Email Address LDAP attribute:   mail  

LDAP Configuration
LDAP Configuration 2

LDAP Hints!

Test: Test LDAP settings
LDAP Test LDAP Test

9.6 Ćwiczenie 2

Zalogowany użytkownik może wszystko, ananimowy użytkownik ma mieć możliwość odczytu
Authorization -> Logged-in users can do anything -> Allow anonymous read access
Authorization 1

9.7 Ćwiczenie 3

Przykład z ćwiczenia 2 zrealizować za pomocą Matrix-based security
Matrix configuration

9.8 Ćwiczenie 4

Hint!

Ćwiczenie:

Matrix configuration

9.9 Ćwiczenie 5

Wykujemy ćwiczenie 4 w kontekście Project-based Matrix Authorization Strategy
Matrix configuration 2
Dodajemy nowy projekty (nie musi robić nic)
Jenkins -> Nowy Projekt -> Ogólny projekt (Matrix_1)
Przy aktywowanym Project-based Matrix Authorization Strategy w każdym nowym projekcie mamy dostępną opcję Enable project-based security gdzie możemy określić konkretne uprawnienia w donośnie do konkretnego projektu

Hint!

Dla projektu matrix_1 zdefiniować następujące zasady bezpieczeństwa

Project Matrix configuration

10: Konsola skryptów

Kolejnym z przydatnych narzędzi dostępnych w Jenkinsie jest konsola skryptów.
Jenkins -> Zarządzaj Jenkinsem -> (Tools and Actions) -> Konsola skryptów Groovy 1

// Wyświetlenie dostępnych wtyczek 
println(Jenkins.instance.pluginManager.plugins)
// Pobranie treści wybranej strony
println 'http://www.google.com'.toURL().text

Jenkins javadoc

// Wypisanie wszystkich zmiennych globalnych na masterze i agentach
import jenkins.*
import jenkins.model.*
import hudson.*
import hudson.model.*

jenkins = Jenkins.getInstance()
nodeProperties = jenkins.getGlobalNodeProperties()
props = nodeProperties.getAll(hudson.slaves.EnvironmentVariablesNodeProperty.class)

for (prop in props) {
    prop.getEnvVars().each{ println "${it}"; }
}

//Agents

slaves = Jenkins.getInstance().slaves
for (slave in slaves) {
    props = slave.nodeProperties.getAll(hudson.slaves.EnvironmentVariablesNodeProperty.class)
    println slave.name
    for (prop in props) {
        prop.getEnvVars().each{ println "${it}"; }
    }
}


println "OK";
//Wypisanie tych projektów które nie mają ustawionego logRotator
jenkins = Jenkins.getInstance()
def jobs = jenkins.getItems(hudson.model.FreeStyleProject.class) //hudson.maven.MavenModuleSet.class

jobs.findAll{ !it.logRotator}.each {
    println it.name;
}

return "OK"
//Dodanie zmiennych globalnych
import jenkins.*
import jenkins.model.*
import hudson.*
import hudson.model.*

def addVariable(String key, String value, Map envVars){
    String oldValue = envVars.get(key);
    if (oldValue != null) {
        println "Global variable exists: " + key + " -> " + oldValue + " Update this!";
    }
    envVars.put(key, value)
    println "Add/Update global variable: " + key + " -> " + value;
}

jenkins = Jenkins.getInstance()
nodeProperties = jenkins.getGlobalNodeProperties()
props = nodeProperties.getAll(hudson.slaves.EnvironmentVariablesNodeProperty.class)


if ( props.size() != 1 ) {
    println("error: unexpected number of environment variable containers: " + props.size() + " expected: 1")
} else {
    envVars = props.get(0).getEnvVars();

    addVariable("TEST_SCRIPT", "Test_script", envVars)

    jenkins.save();
}


println "OK";
//Ustawienie logRotator w nie mają logRotator
import hudson.tasks.*

jenkins = Jenkins.getInstance()
def jobs = jenkins.getItems(hudson.model.FreeStyleProject.class) //hudson.maven.MavenModuleSet.class


jobs.findAll{ !it.buildDiscarder}.each {
    println it.name;
    println it.setBuildDiscarder(new LogRotator(-1, 20, -1, -1) )
    it.save()
}

return "OK"

10.1: Ćwiczenie, wypróbować powyższe przykłady

11: CLI

CLI Command Line Interface.

Dostęp do Jenkinsa z linii komend można zrealizować na 2 sposoby:

11.1: ssh

W pierwszej kolejności należy odblokować serwer ssh wbudowany w Jenkinsa (np. na porcie 8081).
Jenkins -> Zarządzaj Jenkinsem -> (Security) -> Konfiguruj ustawienia bezpieczeństwa -> SSH Server
SSH Server
Wybrany port należy odblokować w firewallu.

# firewall-cmd --permanent --zone=public --add-port=8081/tcp
# systemctl restart firewalld

Na naszym lokalnym komputerze możemy wykonać

$ ssh -l admin -p 8081 192.168.0.178 help
## Jendak w wyniku najprawdopodobniej otrzymamy
admin@192.168.0.178: Permission denied (publickey).

Nasz klucz publiczny należy dodać do listy kluczy wybranego użytkownika. W naszym przypadku może to być admin.

#Geracja kluczy rsa jeżeli wcześniej nie mieliśmy
$ ssh-keygen
$ ll ~/.ssh/
razem 12
-rw-------. 1 jenkins jenkins 1843 11-15 10:08 id_rsa
-rw-r--r--. 1 jenkins jenkins  411 11-15 10:08 id_rsa.pub
-rw-r--r--. 1 jenkins jenkins  571 11-15 09:55 known_hosts

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzsDiyJ+4WZAwaYDVz43Zrm6RCxPTzFWXnUfuBdAx6BmM98gxnveU0k+AbiiF739ZTDKxmWHyBmgGKVMzrSYoldKAPvTii4X/FtQfdsUrvl3gL9E+UM2MJ3k+yaYS88RmfDH7TTYBAwvFYqP3+F7F9tX+Te2Purf/ZwU+r5ekPMP4gtkO9Pi1XtzdOAW2vDHrXm2QJqOQPa8Ppx6SalrLOnlCjo5RtpEIBhjTN3WkwhRYHxDZedTZE0YFyn8fNna4UlZiEhPaLRu6utykDTyfOOhFiONYj9naNzv0/wIAYgYAFreD5CCNZEVMKIYwuKlS9Tb0gyzpDQYBjNGAvks2v jenkins@localhost.localdomain

Jenkins -> Użytkownicy -> admin -> Konfiguracja -> SSH Public Keys
SSH Public Keys
Hint: Możemy dodać dowolną ilość kluczy

$ ssh -l admin -p 8081 192.168.0.178 help
  add-job-to-view
    Adds jobs to view.
  build
    Builds a job, and optionally waits until its completion.

11.2: jenkins-cli.jar

Pobieramy jenkins-cli.jar ze strony naszego Jenkinsa
Jenkins -> Zarządzaj Jenkinsem -> (Tools and Actions) -> Wiersz poleceń Jenkinsa

$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/

ERROR: You must authenticate to access this Jenkins.
Jenkins CLI

$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:12345678
  add-job-to-view
    Adds jobs to view.
  build
    Builds a job, and optionally waits until its completion.

Jeżeli nie chcemy używać hasła możemy użyć tokena przypisanego do danego użytkownika
Jenkins -> Użytkownicy -> admin -> Konfiguracja -> API Token Api token

$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 help
  add-job-to-view
    Adds jobs to view.
  build
    Builds a job, and optionally waits until its completion.
# W tym przypadku musimy mieć zarejestrowany klucz publiczny 
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -ssh -user admin
lis 15, 2020 6:41:35 PM io.jenkins.cli.shaded.org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar getOrCreateProvider
INFO: getOrCreateProvider(EdDSA) created instance of io.jenkins.cli.shaded.net.i2p.crypto.eddsa.EdDSASecurityProvider
  add-job-to-view
    Adds jobs to view.
  build
    Builds a job, and optionally waits until its completion.
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -ssh -user admin list-jobs
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -ssh -user admin get-job 'Job A'

tworzymy nowy job CLI_Invoke na podstawie pustego szablonu (tworzymy plik template_job.xml)

<?xml version='1.1' encoding='UTF-8'?>
<!-- template_job.xml -->
<project>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <hudson.plugins.jira.JiraProjectProperty plugin="jira@3.1.3"/>
  </properties>
  <scm class="hudson.scm.NullSCM"/>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders/>
  <publishers/>
  <buildWrappers/>
</project>
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 create-job CLI_Invoke < template_job.xml
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 build CLI_Invoke
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 console CLI_Invoke 1
<?xml version='1.1' encoding='UTF-8'?>
<!-- update_job.xml -->
<project>
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <hudson.plugins.jira.JiraProjectProperty plugin="jira@3.1.3"/>
    <hudson.model.ParametersDefinitionProperty>
      <parameterDefinitions>
        <hudson.model.StringParameterDefinition>
          <name>param</name>
          <description></description>
          <defaultValue>default value</defaultValue>
          <trim>false</trim>
        </hudson.model.StringParameterDefinition>
      </parameterDefinitions>
    </hudson.model.ParametersDefinitionProperty>
  </properties>
  <scm class="hudson.scm.NullSCM"/>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
    <hudson.tasks.Shell>
      <command>echo &quot;${param}&quot;</command>
      <configuredLocalRules/>
    </hudson.tasks.Shell>
  </builders>
  <publishers/>
  <buildWrappers/>
</project>
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 update-job CLI_Invoke < update_job.xml
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 build CLI_Invoke
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 console CLI_Invoke 2
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 build CLI_Invoke -p param='111 222'
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 console CLI_Invoke 3

11.3: curl

Dodatkowo cześć rzeczy możemy wykonać wywołując odpowiedni url

curl -X POST 'http://admin:116f410f45f00c7b533ddc889e978fec37@192.168.0.178:8080/job/CLI_Invoke/buildWithParameters?param=abc'

11.4: Ćwiczenie.

Na podstawie powyższych informacji stworzyć nowy projekt CLI_Invoke wykonać jego aktualizacje oraz uruchomić z parametrami, jak i bez.

12: Rest and xml api

Praktycznie każdą informację, którą mamy prezentowaną w Jenkinsie możemy również zaprezentować w formacie json lub xml
Przykłady

Dokumentacja
Api

13: Ogólny projekt (Freestyle project)

Najbardziej podstawowy typ projektu dostępny w Jenkinsie. Możemy w nim wykonać cokolwiek.
Oficjalny opis: To jest podstawowa funkcja Jenkinsa. Jenkins stworzy projekt łączący dowolny SCM z dowolnym systemem budującym, może to być również wykorzystane do czegoś innego niż budowanie oprogramowania. najtrafniej oddaje istotę projektu.

Instalujemy wtyczki: Active Choices, Git Parameter, Conditional BuildStep, Parameterized Trigger

13.1 Istotne opcje

13.2 Repozytorium kodu

13.3 Wyzwalacze zadania

13.4 Budowanie

13.5 Akcje po zadaniu

13.6 Ćwiczenie 1

13.7 Ćwiczenie 2

13.8 lockable resources*

Definicja Jenkins -> Zarządzaj Jenkinsem -> (System Configuration) -> Skonfiguruj system -> Lockable Resources Manager

Resource:
  Name: Test_Resources
  Description: Example Test Resources
  Labels: test_resouce
  Reserved by: 

Lockable Resources 1
Monitoring Jenkins -> Lockable Resources
Lockable Resources 2
Dodajemy konfiguracje w projekcie
Lockable Resources 4

Uruchomiono przez użytkownika Jenkins Administrator
Running as SYSTEM
[lockable-resources] acquired lock on [Test_Resources]
Building on master in workspace /opt/tools/jenkins/workspace/Job_A

Lockable Resources 3
Jeżeli zasoby będą zablokowane to przy uruchomieniu projektu otrzymamy następujący komunikat
Lockable Resources 5

13.9 Throttle builds*

Definiujemy w projekcie
Throttle 1
Przy próbie wcześniejszego uruchomienia projektu lub jego wyzwolenia otrzymamy Throttle 2

13.10 Dodatkowe parametry*

Active Choices

To zadanie jest sparametryzowane -> Active Choices Parameter

Name:           param_a
Script:         Groovy Script
Groovy Script:  return ['a','b','c']
Choice Type:    Single Select

To zadanie jest sparametryzowane -> Active Choices Reactive Parameter

Name:           param_b
Script:         Groovy Script
Groovy Script:  def map = ['a': ['a1','a2','a3'],'b': ['b1','b2','b3'],'c': ['c1','c2','c3']]
                return map[param_a]
Choice Type:    Single Select
Referenced parameters:  param_a	

Active 1

Git Parameter

Na podstawie zdefiniowanego repozytorium git wtyczka umożliwia wybranie tag, branch, branch lub tag, Revision, Pull Request Git Parameter 1
Git Parameter 2
Git Parameter 3

14: Maven project

W porównaniu z ogólnym projektem Maven project ma odgórnie zdefiniowany główny krok budowania oraz kroki, które można wykonać przed (pre) jako i po (post). Maven project 1

14.1 Ćwiczenie

Dla projektu typu maven wykonać 13.7 Ćwiczenie 2

Hint!

15: Pipelines

Your Job is Your Code

Start:

15.1: Ćwiczenie, Zainstalować wtyczki Blue Ocean

Blue Ocean (BlueOcean Aggregator)
Blue Ocean 1

15.2: Scripted vs Declarative Pipeline

15.3: First pipeline!

Jenkins -> Nowy Projekt -> Pipeline (P_1) Pipeline 1

properties([
            parameters([
                string(defaultValue: 'test_a', description: '', name: 'TEST', trim: false)
            ])
          ])
node {
    stage('First stage') {
        echo "Sii Power People"
    }
    stage("Environments") {
        echo env.PATH
        echo env.BUILD_ID
        echo env.JOB_URL
    }
    stage('Parameters') {
        echo params.TEST
    }
    stage('currentBuild') {
        echo currentBuild.displayName
        echo currentBuild.currentResult
        //error 'Cos poszło nie tak'
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_2)

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Building..'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing..'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying....'
            }
        }
    }
}

W przypadku deklaratywnego stylu mamy do dyspozycji ponowne uruchomienie pipelinu z dowolnego etapu.
Pipeline 2 Restart Pipeline 3 Restart

Jenkins -> Nowy Projekt -> Pipeline (P_(3,4))

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Building..'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing..'
                //echo 1
                //message 'Test'
            }
        }
    }
}
//vs
node {
    stage('Build') {
        echo "Build ..."
    }
    stage("Test") {
        echo "Testing .."
        //echo 1
        //message 'Test'
    }
}

Declarative owszem jest walidowany na starcie, ale nie każdy przypadek.

15.4 Agent

Jenkins -> Nowy Projekt -> Pipeline (P_5)

pipeline {
    agent none

    stages {
        stage('Build') {
            steps {
                echo 'Building..'
            }
        }
    }
}

//next

pipeline {
    agent none

    stages {
        stage('Build') {
            steps {
                echo 'Building..'
                sh 'touch test.txt' //! próba utworzenia pliku spowoduje błąd
            }
        }
    }
}

Jeżeli w pipeline nie jest wykonywana żadna interakcja z agentem (zapis, odczyt itp.) wtedy taki pipeline może być uruchomiony z sukcesem

Jenkins -> Nowy Projekt -> Pipeline (P_6)

pipeline {
    agent {
        docker {
            image 'maven:3-alpine'
        }
    }
    stages {
        stage('Build') {
            steps {
                sh 'mvn -v'
            }
        }
    }
}

//next

pipeline {
    agent {
        docker {
            image 'maven:3.6.0-jdk-12-alpine'
            args '-v $HOME/.m2:/root/.m2'
        }
    }
    stages {
        stage('Git'){
            steps {
                git branch: 'master', url: 'https://github.com/klimas7/restCounter.git'
            }
        }
        stage('Build') {
            steps {
                sh 'mvn clean install'
            }
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_7)

pipeline {
    agent {
        label 'Linux_1'
    }

    stages {
        stage('Build') {
            steps {
                echo 'Building..'
            }
        }
    }
}

15.5: Tools

Jenkins -> Nowy Projekt -> Pipeline (P_8)

pipeline {
    agent any
    tools {
        maven 'mvn'
        jdk 'jdk8'
    }
    stages {
        stage('Stag1'){
            steps {
                sh 'java -version'
                sh 'mvn -version'
            }
        }
    }
}
//vs
pipeline {
    agent any
    tools {
        maven 'mvn'
        jdk 'jdk8'
    }
    stages {
        stage('Stage 1'){
            steps {
                sh 'java -version'
            }
        }
        stage('Stage 2'){
            tools {
                jdk 'jdk12'
            }
            steps {
                sh 'java -version'
            }
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_8_a)

node {
    stage("Stage 1") {
        def javaHome = tool name: 'jdk12', type: 'jdk'
        sh "${javaHome}/bin/java -version"
        
        def mavenHome = tool name: 'mvn', type: 'maven'
        sh "${mavenHome}/bin/mvn -version"
        
        withEnv(["PATH+JAVA=${tool name: 'jdk12', type: 'jdk'}/bin", 
                 "PATH+MAVEN=${tool name: 'mvn', type: 'maven'}/bin"]) {
            sh 'java -version'
            sh 'mvn -version'   
        }        
    }
}

15.6: Options

Jenkins -> Nowy Projekt -> Pipeline (P_9)

pipeline {
    agent any
    tools {
        jdk 'jdk8'
    }
    options {
        timestamps()
        buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '2')
        timeout(time: 15, unit: 'SECONDS')
    }
    stages {
        stage('Stag1'){
            options {
                timeout(time: 10, unit: 'SECONDS')
            }
            steps {
                sleep 9
            }
        }
        stage('Stag2'){
            steps {
                sleep 15
            }
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_9_a)

properties([
    buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '2'))
])
node {
    timestamps {
        timeout(time: 15, unit: 'SECONDS') {
            stage("Stage 1") {
                def javaHome = tool name: 'jdk12', type: 'jdk'
                sh "${javaHome}/bin/java -version"
                timeout(time: 10, unit: 'SECONDS') {
                    sleep 9
                }
            }
            stage("Stage 2") {
                echo "Stage 2"
                sleep 3
            }
        }   
    }
}

15.7: Triggers

Jenkins -> Nowy Projekt -> Pipeline (P_10)

pipeline {
    agent any
    triggers {
      cron '* * * * *'
      upstream 'p_9'
    }
    stages {
        stage('Stage 1') {
            steps {
                echo 'Stage 1'
                sleep 10
            }
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_10_a)

properties([
    pipelineTriggers([
        cron('* * * * *'), 
        upstream('p_9')
    ])
])
node {
    stage('Stage 1') {
        echo "Stage 1"
        sleep 10
    }
}

15.8: Condition

Dodać Global properties STAGE_1: false
Jenkins -> Nowy Projekt -> Pipeline (P_11)

pipeline {
    agent any
    stages {
        stage('Stage 1') {
            when {
              environment name: 'STAGE_1', value: 'true'
            }
            steps {
                echo 'Stage 1'
                sleep 10
            }
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_11_a)

pipeline {
    agent any
    parameters {
        booleanParam defaultValue: false, description: 'Czy coś ma się deployować', name: 'deploy'
    }
    stages {
        stage('Build') {
            steps {
                echo 'Building ...'
            }
        }
        stage('Test') {
            steps {
                echo 'Test ...'
                unstable 'Cos poszło nie najlepiej'
            }
        }
        stage('Deploy') {
            when {
                expression {params.deploy && currentBuild.currentResult == "SUCCESS" }
            }
            steps {
                echo 'Deploying ...'
            }
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_11_b)

node {
    stage('Stage 0') {
        echo env.STAGE_1
    }
    if (env.STAGE_1 == "true") {
        stage('Stage 1') {
            echo "Stage 1"
            sleep 10
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_11_c)

properties([
    parameters([
        booleanParam(defaultValue: false, description: 'Czy coś ma się deployować', name: 'deploy')
    ])
])
node {
    stage('Build') {
        echo 'Building ...'
    }
    stage('Test') {
        echo 'Test ...'
        //unstable 'Cos poszło nie najlepiej'
    }
    if (params.deploy && currentBuild.currentResult == "SUCCESS") {
        stage('Deploy') {
            echo 'Deploying ...'
        }
    }
}

15.9: Parallel

Jenkins -> Nowy Projekt -> Pipeline (P_12)

pipeline {
    agent none
    stages {
        stage('Run Tests') {
            parallel {
                stage('Test On Windows') {
                    agent {
                        label 'windows'
                    }
                    steps {
                        echo "Test on windows"
                        sleep 30
                    }
                }
                stage('Test On Linux') {
                    agent {
                        label "linux"
                    }
                    steps {
                        echo "Test on linux"
                        sleep 30
                    }
                }
            }
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_12_a)

pipeline {
    agent none
    stages {
        stage('Run Tests') {
            parallel {
                stage('Test On Windows') {
                    agent any
                    steps {
                        echo "Test on windows"
                        sleep 2
                        echo "currentResult: " + currentBuild.currentResult
                        echo "result: " + currentBuild.result
                    }
                }
                stage('Test On Linux') {
                    agent {
                        label "linux"
                    }
                    steps {
                        echo "Test on linux"
                        sleep 5
                        echo "currentResult: " + currentBuild.currentResult
                        echo "result: " + currentBuild.result
                        //error 'error'
                        //unstable 'unstable'
                    }
                }
            }
        }
        stage('Deploy') {
            agent any
            steps {
                echo 'Deploy ...'
                echo "currentResult: " + currentBuild.currentResult
                echo "result: " + currentBuild.result
            }
            post {
                always {
                    echo "currentResult: " + currentBuild.currentResult
                    echo "result: " + currentBuild.result
                }
            }
        }
    }
}

Jenkins -> Nowy Projekt -> Pipeline (P_12_b)

stage('Run Tests') {
    parallel linux: {
        node('linux') {
            stage('Test On Linux') {
                echo "Test on linux"
                sleep 30
            }
        }
    },
    windows: {
        node('windows') {
            stage('Test On Windows') {
                echo "Test on windows"
                sleep 30
            }
        }
    }
}

15.10: In-process Script Approval

Jenkins -> Nowy Projekt -> Pipeline (P_13)

pipeline {
    agent {
        label 'Linux_1'
    }
    stages {
        stage('Build') {
            steps {
                script {
                    readFile()
                }
            }
        }
    }
}


def readFile() {
    def fileContents = new File('/work/tag.txt').text
    echo fileContents
}
[Pipeline] {
Scripts not permitted to use new java.io.File java.lang.String. Administrators can decide whether to approve or reject this signature.
[Pipeline] }

Jenkins -> Zarządzaj Jenkinsem -> In-process Script Approval
In approval 1 In approval 2 In approval 3

15.11: Walidacja

ssh -l admin -p 8081 192.168.0.178 declarative-linter < Jenkinsfile

15.12: try-catch

Jenkins -> Nowy Projekt -> Pipeline (P_14)

pipeline {

    agent any

    stages {
        stage ('Build & Test') {
            steps {
                sh 'echo Building... Failure here will fail the build'
                script {
                    try {
                        echo 'Running tests...'
                        sh 'exit 1'
                    }
                    catch (exc) {
                        echo "Message: " + exc.getMessage()
                        echo 'Testing failed!'
                        currentBuild.result = 'UNSTABLE'
                    }
                }
            }
        }
    }
}

15.13: RestCounter project

W pierwszej kolejności na naszej wirtualnej maszynie musimy otworzyć port 8088 w zaporze sieciowej

# as root
firewall-cmd --permanent --zone=public --add-port=8088/tcp
systemctl restart firewalld
firewall-cmd --list-all

Jenkins -> Nowy Projekt -> Pipeline (RestCounter_pipeline)

pipeline {
    agent any
    tools {
        maven 'mvn'
        jdk 'jdk8'
    }
    stages {
        stage('Git'){
            steps {
                git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
            }
        }

        stage ('Build') {
            steps {
                sh 'mvn clean install'
            }
            post {
                success {
                    archive 'target/*.jar'
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
    }
}

Dodajemy opcje deploy wraz z etapem Deploy

pipeline {
    agent any
    parameters {
        booleanParam defaultValue: true, description: 'Deploy and run application', name: 'deploy'
    }
    tools {
        maven 'mvn'
        jdk 'jdk8'
    }
    stages {
        stage('Git'){
            steps {
                git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
            }
        }

        stage ('Build') {
            steps {
                sh 'mvn clean install'
            }
            post {
                success {
                    archive 'target/*.jar'
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
        
        stage ('Deply') {
            when {
                expression { params.deploy }
            }
            steps {
                echo "Deploy application"
            }
        }
    }
}

Rozbudowujemy etap Deploy

def applicationDir = '/opt/tools/restCounter'

pipeline {
    parameters {
      booleanParam defaultValue: true, description: 'Deploy and run application', name: 'deploy'
    }
    agent any
    tools {
        maven 'maven'
        jdk 'java8'
    }
    stages {
        stage('Git'){
            steps {
                git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
            }
        }

        stage ('Build') {
            steps {
                sh 'mvn clean install'
            }
            post {
                success {
                    archive 'target/*.jar'
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
        stage ('Deply') {
            when {
                expression { params.deploy }
            }
            steps {
                echo "Deploy application"
                sh "mkdir -p ${applicationDir}"
                sh "cp target/*.jar ${applicationDir}"
                sh "cp restCounter.sh ${applicationDir}"
            }
        }
    }
}
$ ll restCounter/
razem 17516
-rw-rw-r--. 1 jenkins jenkins 17928440 11-20 14:15 restCounter.jar
-rwxrwxr-x. 1 jenkins jenkins      556 11-20 14:15 restCounter.sh

Startujemy naszą aplikację

def applicationDir = '/opt/tools/restCounter'

pipeline {
    agent any
    parameters {
        booleanParam defaultValue: true, description: 'Deploy and run application', name: 'deploy'
    }
    tools {
        maven 'mvn'
        jdk 'jdk8'
    }
    stages {
        stage('Git'){
            steps {
                git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
            }
        }

        stage ('Build') {
            steps {
                sh 'mvn clean install'
            }
            post {
                success {
                    archive 'target/*.jar'
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
        
        stage ('Deply') {
            when {
                expression { params.deploy }
            }
            steps {
                echo "Deploy application"
                sh "mkdir -p ${applicationDir}"
                sh "cp target/*.jar ${applicationDir}"
                sh "cp restCounter.sh ${applicationDir}"
                dir("${applicationDir}") {
                    sh "${applicationDir}/restCounter.sh stop"
                    sh "${applicationDir}/restCounter.sh start &"
                    sleep 120
                }
            }
        }
    }
}

Sprawdzamy wynik http://192.168.0.178:8088/api/count/xyz
Niestety nasza aplikacja będzie działała tylko przez 2 minuty! ProcessTreeKiller Rozwiążmy ten problem i dodajmy sprawdzanie budowania

def applicationDir = '/opt/tools/restCounter'

pipeline {
    agent any
    
    parameters {
        booleanParam defaultValue: true, description: 'Deploy and run application', name: 'deploy'
    }
    
    tools {
        maven 'mvn'
        jdk 'jdk8'
    }
    
    triggers {
        pollSCM '*/5 * * * *', ignorePostCommitHooks: true
    }
    
    stages {
        stage('Git'){
            steps {
                git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
            }
        }

        stage ('Build') {
            steps {
                sh 'mvn clean install'
            }
            post {
                success {
                    archive 'target/*.jar'
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
        
        stage ('Deply') {
            when {
                expression { params.deploy }
            }
            steps {
                echo "Deploy application"
                sh "mkdir -p ${applicationDir}"
                sh "cp target/*.jar ${applicationDir}"
                sh "cp restCounter.sh ${applicationDir}"
                dir("${applicationDir}") {
                    //sh "${applicationDir}/restCounter.sh stop"
                    //sh "${applicationDir}/restCounter.sh start &"
                    //sleep 120
                    sh "export JENKINS_NODE_COOKIE=REST_COUNTER; ${applicationDir}/restCounter.sh start &"
                }
            }
        }
    }
}

Sprawdzamy wynik http://192.168.0.178:8088/api/count/xyz

16: Multibranch Pipeline

Jenkins -> Nowy Projekt -> Multibranch Pipeline (LearnMB)

17: Shared Library

Pozwalają wyodrębnić oraz współdzielić wspólne części pomiędzy wieloma potokami. Elementy takie mogą być zamknięte w bibliotece przechowywanej w repozytorium kodu.

Struktura

├── README.md
├── src                                 (Groovy source files)
│   └── pl
│       └── klimas7
│           └── jenkins
│               ├── Deployer.groovy
│               └── Rectangle.groovy
├── vars                                (Exposed variables)
│   ├── checkStatus.groovy
│   ├── deploy.groovy
│   ├── disableThrottling.groovy
│   ├── enableThrottling.groovy
│   └── printBuildinfo.groovy
└── resources                           (resource files)
    └── pl
        └── klimas7
            └── jenkins
                └── static_content.json
    

src Standardowy katalog znany z projektów javy. Jest dodawany do classpath w trakcie wykonania potoku.
vars Definicje zmiennych, które są dostępne w potoku.
Przykład jenkins-shared-lib.
Dodanie nowej biblioteki do Jenkinsa.
Jenkins -> Zarządzaj Jenkinsem -> Skonfiguruj system -> Global Pipeline Libraries Shared lib

Nowy projekt wykorzystujący przygotowaną bibliotekę.
Jenkins -> Nowy Projekt -> Pipeline (JenkinsSharedLib)
Proste wykorzystanie

@Library('jenkins-shared-lib')_

stage('Print Build Info') {
    printBuildinfo {
        name = "Sample Name"
    }
} stage('Disable throttling') {
    disableThrottling()
} stage('Deploy') {
    deploy()
} stage('Enable throtling') {
    enableThrottling()
} stage('Check Status') {
    checkStatus()
}

Hint! @Library('jenkins-shared-lib')_ _ nie jest pomyłką.
Przykład z parametrami

@Library('jenkins-shared-lib')_

pipeline {
    parameters {
        string defaultValue: 'One default', description: '', name: 'param1', trim: false
        string defaultValue: 'Two default', description: '', name: 'param2', trim: false
    }
    agent any
    
    stages {
        stage('Print Build Info') {
            steps {
                script {
                    printBuildinfo {
                        name = "Sample Name"
                    }
                }
            }
        }
        stage('Disable throttling') {
            steps {
                script {
                    disableThrottling()
                }
            }
        }
        stage('Deploy') {
            steps {
                script { 
                    deploy()
                }
            }
        }
        stage('Enable throtling') {
            steps {
                script {
                    enableThrottling()
                }
            }
        }
        stage('Check Status') {
            steps {
                script {
                    checkStatus()
                }
            }
        }
    }
}

17.1: Ćwiczenie

Wykorzystać przygotowaną bibliotekę w przykładowym potoku, prosty skrypt bez parametrów.

17.2: Ćwiczenie*

Wykorzystać przygotowaną bibliotekę w przykładowym potoku, pełen skrypt z użyciem konwencji potoku i parametrów.

18: Bitbucket integration

Należy zainstalować wtyczki Bitbucket Server Integration, Bitbucket Server Notifier

18.1 Hook oraz notyfikacja

Konfiguracja po stronie repozytorium

Bitbucket 1

Konfiguracja po stronie Jenkinsa oraz projektu

Dashboard -> Zarządzaj Jenkinsem -> Skonfiguruj system -> Bitbucket Server Notifier Bitbucket 2
Bitbucket 3

18.2 Bitbucket Server Integration

Konfiguracja po stronie Jenkinsa

Bitbucket 4
Bitbucket 5
Bitbucket 6

Koniec

Postaw mi kawę na buycoffee.to