Понякога, работейки с променливи, особено съдържащи специални символи (като $), трябва да подхождаме по-внимателно.
ЗАБЕЛЕЖКА: Ако искате да тествате описаното по-долу, ще ви трябва машина с инсталирани Docker и Jenkins.
Нека да вземем за пример този код на процес в Jenkins:
pipeline {
agent any
environment
{
PASS='Pa$$word'
}
stages {
stage('Clean') {
steps {
sh 'docker container rm --force $(docker container ls -aq) || true'
}
}
stage('Escape Test #1 - NOK') {
steps {
sh 'docker container run -d --name db1 -e MYSQL_ROOT_PASSWORD=Pa$$word mariadb'
}
}
stage('Escape Test #2 - NOK') {
steps {
sh "docker container run -d --name db2 -e MYSQL_ROOT_PASSWORD=Pa\$\$word mariadb"
}
}
stage('Escape Test #3 - OK') {
steps {
sh "docker container run -d --name db3 -e MYSQL_ROOT_PASSWORD='Pa\$\$word' mariadb"
}
}
stage('Escape Test #4 - OK') {
steps {
sh 'docker container run -d --name db4 -e MYSQL_ROOT_PASSWORD=\'Pa\$\$word\' mariadb'
}
}
stage('Escape Test #5 - OK') {
steps {
sh '''docker container run -d --name db5 -e MYSQL_ROOT_PASSWORD='Pa$$word' mariadb'''
}
}
stage('Escape Test #6 - OK') {
steps {
sh 'docker container run -d --name db6 -e MYSQL_ROOT_PASSWORD=$PASS mariadb'
}
}
}
}
ЗАБЕЛЕЖКА: MYSQL_ROOT_PASSWORD в горния пример може да бъде заменено с MARIADB_ROOT_PASSWORD.
На всяка стъпка се стартира по един контейнер с MariaDB. Разликата е в това как се подава паролата за root потребителя. При всички положения се очаква, че се подава парола със съдържание Pa$$word.
При изпълнение на процеса, той ще приключи успешно при това без явна индикация за потенциален проблем, поне не на пръв поглед. Да ама не.
Ако проверим в терминална сесия дали успяваме да комуникираме с базата, използвайки паролата, която сме задали, ще установим, че нещата не са съвсем както очакваме.
Подход 1 - неработещ:
$ docker container exec db1 mariadb -u root -p'Pa$$word' -e 'SHOW DATABASES;'
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
$
Подход 2 - неработещ:
$ docker container exec db2 mariadb -u root -p'Pa$$word' -e 'SHOW DATABASES;'
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
$
Подход 3 - работещ:
$ docker container exec db3 mariadb -u root -p'Pa$$word' -e 'SHOW DATABASES;'
Database
information_schema
mysql
performance_schema
sys
$
Подходи 4, 5 и 6 са също работещи и резултатът е същия като при подход 3.
Разбира се, този резултат не е съвсем необясним. За да разберем причината, е достатъчно да погледнем журнала от изпълнението на процеса.
Ето извадка за подход 1:
...
[Pipeline] { (Escape Test #1 - NOK)
[Pipeline] sh
+ docker container run -d --name db1 -e MYSQL_ROOT_PASSWORD=Pa6768word mariadb
...
И за подход 2:
...
[Pipeline] { (Escape Test #2 - NOK)
[Pipeline] sh
+ docker container run -d --name db2 -e MYSQL_ROOT_PASSWORD=Pa6948word mariadb
...
Можем да видим, че настъпва заместване на двата последователни символа $$ със стойност, отговаряща на номера на текущия процес. Т.е. вместо очакваното Pa$$word, ние получаваме нещо различно - в единия случай Pa6768word, а в другия - Pa6948word. Разбира се, при следващо изпълнение стойностите ще са други.
Това е. Просто трябва да бъдем внимателни за това как точно ограждаме командите, които ще изпълняваме и как обработваме специалните символи в тях. :)