Skip to content

Commit 382f268

Browse files
committed
feat: Jenkins Airgap Rancher and Airgap Harvester Pipeline Support
* brings in Jenkins pipeline * adjusts Debian based provisioning * adds additional credential * additional settings.yml.sample configuration * additional pipeline dependent plugin introduced Resolves: feat/air-gap-harvester-air-gap-rancher-cicd-pipelining-feat
1 parent 5082bc9 commit 382f268

5 files changed

Lines changed: 253 additions & 1 deletion

File tree

CI/harvester-installer/roles/jenkins/tasks/install_jenkins.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
- bootstrap5-api
4747
- configuration-as-code
4848
- credentials-binding
49+
- docker-workflow
4950
- email-ext
5051
- ghprb
5152
- git
@@ -88,6 +89,19 @@
8889
group: jenkins
8990
mode: 0755
9091

92+
# supports airgap rancher airgap harvester pipeline
93+
- name: jenkins | install_jenkins | Build Jenkins .ssh directory
94+
ansible.builtin.file:
95+
path: /var/lib/jenkins/.ssh
96+
state: directory
97+
owner: jenkins
98+
group: jenkins
99+
100+
- name: jenkins | install_jenkins | Build known_hosts file
101+
ansible.builtin.file:
102+
path: /var/lib/jenkins/.ssh/known_hosts
103+
state: present
104+
91105
- name: jenkins | install_jenkins | Create Jenkins config as code file
92106
template:
93107
src: config_jenkins_as_code.yaml.j2
@@ -105,6 +119,7 @@
105119
mode: 0755
106120
with_items:
107121
- harvester_vagrant_installation_test.groovy
122+
- airgap_rancher_airgap_harvester_pipelinejob.groovy
108123

109124
- name: jenkins | install_jenkins | Restart jenkins
110125
service:

CI/harvester-installer/roles/jenkins/tasks/install_jenkins_on_Debian.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Install prerequisite packages for Jenkins
44
apt:
55
name: [ca-certificates, openjdk-11-jdk, curl, apt-transport-https, gnupg,
6-
python3, python3-pip, figlet]
6+
python3, python3-pip, figlet, sshpass]
77
state: latest
88
update_cache: yes
99

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
2+
pipelineJob('airgap-harvester-airgap-rancher-vagrant-installation-test') {
3+
description('Test airgapped Rancher with airgapped Harvester')
4+
properties {
5+
githubProjectUrl('https://github.com/irishgordo/harvester-installer')
6+
}
7+
logRotator {
8+
numToKeep(-1)
9+
daysToKeep(10)
10+
}
11+
parameters {
12+
booleanParam('keep_environment', false, 'Keep the Vagrant environment around after the test had finish.')
13+
booleanParam('slack_notify', true, 'Send notifications to Slack')
14+
stringParam('WORKSPACE', '/var/lib/jenkins/workspace/airgap-harvester-airgap-rancher-vagrant-installation-test', 'Required: Provide A Workspace')
15+
}
16+
triggers {
17+
cron("H 8 * * *")
18+
}
19+
definition {
20+
cps {
21+
script(
22+
'''
23+
def AdjustBuildURL(String buildurl = 'default', String endpointinfo = 'default'){
24+
echo "this is AdjustBuildURL Groovy script function..."
25+
echo "this is buildurl: ${buildurl}"
26+
echo "this is endpointinfo: ${endpointinfo}"
27+
String localHost = "localhost"
28+
def result = ""
29+
if (buildurl.contains(localHost)) {
30+
echo "hits if conditional, buildurl does contain localhost"
31+
String actualbuildurl = buildurl.replaceAll(localHost, endpointinfo)
32+
result = actualbuildurl
33+
echo "this is result that will be returned: ${result}"
34+
return result
35+
} else {
36+
echo "hits else block, buildurl does not contain localhost"
37+
result = buildurl
38+
echo "this is result that will be returned: ${result}"
39+
return result
40+
}
41+
}
42+
boolean PROVISIONINGPASSED = false
43+
boolean DOCKERIZEDTESTREPOBUILDPASSED = false
44+
boolean DOCKERIZEDAPITESTSPASSED = false
45+
pipeline {
46+
agent any
47+
options{
48+
timeout(time: 12, unit: 'HOURS')
49+
disableConcurrentBuilds()
50+
}
51+
environment{
52+
SLACK_CREDENTIALS = credentials('jenkins-slack-token')
53+
JENKINS_ENDPOINT_IP = credentials('jenkins-endpoint-ip')
54+
}
55+
stages {
56+
stage('tell Slack we are starting the pipeline'){
57+
steps{
58+
script{
59+
if(params.slack_notify){
60+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
61+
echo 'sending message to slack, letting channel know build has been started...'
62+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\", \"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_starting_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , first-stage, running... \" } } ]}'"""
63+
} else {
64+
echo 'slack notifications currently turned off...'
65+
}
66+
}
67+
}
68+
}
69+
stage('checkout harvester-installer master branch (temporarily set to featurebranch)') {
70+
steps {
71+
dir('harvester-installer') {
72+
checkout([$class: 'GitSCM', branches: [[name: 'feat/airgap-harvester-airgap-rancher-install-test' ]], userRemoteConfigs: [[url: 'https://github.com/irishgordo/harvester-installer.git']]])
73+
}
74+
script{
75+
if(params.slack_notify){
76+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
77+
echo 'sending message to slack, letting channel know build has grabbed latest from master branch...'
78+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_started_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , grabbed latest master branch changes... \" } } ]}'"""
79+
} else {
80+
echo 'slack notifications currently turned off...'
81+
}
82+
}
83+
}
84+
}
85+
stage('checkout harvester/tests branch clone down (temporarily set to featurebranch)') {
86+
steps {
87+
script{
88+
echo 'cloning down tests repo (currently fork)...'
89+
dir("${params['WORKSPACE']}"){
90+
sh "git clone https://github.com/irishgordo/tests.git"
91+
echo 'cloned tests repo successfully (currently fork)...'
92+
sh "cd tests && git checkout feat/dockerize-backend-tests && make build-docker-backend-tests-image"
93+
DOCKERIZEDTESTREPOBUILDPASSED = true
94+
}
95+
if(params.slack_notify){
96+
echo 'sending message to slack, letting channel know jenkins job about dockerized tests repo...'
97+
if(DOCKERIZEDTESTREPOBUILDPASSED){
98+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
99+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_started_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , pulled harvester/tests and built harvester/tests docker image: *SUCCESS* \" } } ]}'"""
100+
} else {
101+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
102+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_started_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , pulled harvester/tests and built harvester/tests docker image: *FAIULRE* \" } } ]}'"""
103+
}
104+
} else {
105+
echo 'slack notifications currently turned off...'
106+
}
107+
}
108+
}
109+
}
110+
stage('temp: add upstream repo and pull changes'){
111+
steps{
112+
dir('harvester-installer'){
113+
script{
114+
echo 'temporarily pulling upstream harvester-installer - demoing test environment...'
115+
try {
116+
def addUpstream = sh(script: "git remote add upstream https://github.com/harvester/harvester-installer.git", returnStdout: true).trim()
117+
} catch (Exception e) {
118+
echo 'Exception occurred: ' + e.toString()
119+
}
120+
def fetchUpstream = sh(script: "git fetch upstream", returnStdout: true).trim()
121+
def fetchRemotes = sh(script: "git remote -v", returnStdout: true).trim()
122+
echo 'now we will grab harvester master branch, switch to the master branch on fork, pull down master branch from upstream harvester, then checkout the fork feature branch and merge the origin master that has been updated within it to keep the run up to date...'
123+
def pullUpstreamMaster = sh(script: "git checkout origin/master && git pull upstream master && git checkout feat/airgap-harvester-airgap-rancher-install-test && git merge origin/master", returnStdout: true).trim()
124+
}
125+
}
126+
}
127+
}
128+
stage('Provision an instance of Airgapped Rancher (k3s) with 4 Node Airgapped Harvester') {
129+
steps {
130+
script {
131+
if(params.slack_notify){
132+
echo 'sending message to slack, letting channel know jenkins job about starting provisioning...'
133+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
134+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_started_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , 6ish-hr long provisioning process for 4 Airgapped Harvester Nodes, 1 Airgapped Rancher (on k3s) with Docker-Registry has: *STARTED* \" } } ]}'"""
135+
} else {
136+
echo 'slack notifications currently turned off...'
137+
}
138+
ansiblePlaybook extras: "-e WORKSPACE=${env.WORKSPACE} -e harvester_installer_repo_name=irishgordo/harvester-installer", playbook: "${env.WORKSPACE}/harvester-installer/ci/run_airgap_rancher_and_airgap_harvester_install_test.yml"
139+
PROVISIONINGPASSED = true
140+
}
141+
}
142+
}
143+
stage('Run dockerized harvester/tests API tests against 4 Node Airgapped Harvester'){
144+
options {
145+
timeout(time: 2, unit: "HOURS")
146+
}
147+
steps {
148+
script {
149+
try {
150+
docker.image('harvester-tests-backend:latest').inside(){
151+
sh "ls -alh"
152+
sh "cd tests && pytest -k 'not verify_host_maintenance_mode and not host_mgmt_maintenance_mode and not host_reboot_maintenance_mode and not host_poweroff_state and not create_images_using_terraform and not create_keypairs_using_terraform and not create_edit_network and not create_network_using_terraform and not create_volume_using_terraform' --junitxml=result_harvester_api_latest.xml -m 'not delete_host' harvester_e2e_tests/apis --username admin --password testtesttest --endpoint https://192.168.2.131 --harvester_cluster_nodes 4"
153+
}
154+
DOCKERIZEDAPITESTSPASSED = true
155+
} catch (err) {
156+
echo "failed within docker image inside command on harvester testing api stage"
157+
echo err.getMessage()
158+
}
159+
}
160+
}
161+
}
162+
}
163+
post {
164+
always {
165+
dir("${env.WORKSPACE}"){
166+
script{
167+
if(PROVISIONINGPASSED){
168+
if(params.slack_notify){
169+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
170+
echo 'sending message to slack, letting channel know provisioning passed...'
171+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\",\"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [ { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_finished_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , provisioning airgap-rancher & airgap-harvester: *SUCCESS* \" } } ]}'"""
172+
} else {
173+
echo 'slack notifications currently turned off...'
174+
}
175+
} else {
176+
if(params.slack_notify){
177+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
178+
echo 'sending message to slack, letting channel know provisioning failed...'
179+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\",\"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [ { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_finished_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , provisioning airgap-rancher & airgap-harvester: *FAIULRE* \" } } ]}'"""
180+
} else {
181+
echo 'slack notifications currently turned off...'
182+
}
183+
}
184+
if(DOCKERIZEDAPITESTSPASSED){
185+
if(params.slack_notify){
186+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
187+
echo 'sending message to slack, letting channel know provisioning passed...'
188+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\",\"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [ { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_finished_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , provisioning airgap-rancher & airgap-harvester TEST API STAGE: *SUCCESS* \" } } ]}'"""
189+
} else {
190+
echo 'slack notifications currently turned off...'
191+
}
192+
} else {
193+
if(params.slack_notify){
194+
String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}")
195+
echo 'sending message to slack, letting channel know provisioning failed...'
196+
sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\",\"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [ { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_finished_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , provisioning airgap-rancher & airgap-harvester TEST API STAGE: *FAIULRE* \" } } ]}'"""
197+
} else {
198+
echo 'slack notifications currently turned off...'
199+
}
200+
}
201+
if(params.keep_environment){
202+
echo 'keeping environment...'
203+
} else {
204+
echo 'discarding environment...'
205+
sh "cd ipxe-examples/vagrant-pxe-airgap-harvester/ && ls -alh && vagrant destroy -f"
206+
}
207+
echo 'destroying docker image, so next run will re-make with fresh start...'
208+
sh "cd ${env.WORKSPACE}/tests && make destroy-docker-backend-tests-image"
209+
echo 'destroying tests folder, so next run will always get the latest...'
210+
sh "cd ${env.WORKSPACE} && rm -rf tests"
211+
}
212+
}
213+
}
214+
}
215+
}
216+
'''
217+
)
218+
sandbox()
219+
}
220+
}
221+
}

CI/harvester-installer/roles/jenkins/templates/config_jenkins_as_code.yaml.j2

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ credentials:
3030
username: "{{ GITHUB_CI_USERNAME }}"
3131
password: "{{ GITHUB_CI_PASSWORD }}"
3232
id: "for-harvester-ci"
33+
- usernamePassword:
34+
scope: "GLOBAL"
35+
username: "SLACK_WEBHOOK_USER"
36+
password: "{{ SLACK_WEBHOOK_TOKEN }}"
37+
id: "jenkins-slack-token"
38+
- usernamePassword:
39+
scope: "GLOBAL"
40+
username: "JENKINS_ENDPOINT_IP"
41+
password: "{{ JENKINS_ENDPOINT_IP }}"
42+
id: "jenkins-endpoint-ip"
3343

3444
security:
3545
queueItemAuthenticator:
@@ -39,6 +49,7 @@ security:
3949

4050
jobs:
4151
- file: /var/lib/jenkins/casc_configs/harvester_vagrant_installation_test.groovy
52+
- file: /var/lib/jenkins/casc_configs/airgap_rancher_airgap_harvester_pipelinejob.groovy
4253

4354
unclassified:
4455
location:

CI/harvester-installer/settings.yml.sample

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,8 @@ GITHUB_CI_PASSWORD: ghp_g9sDDSsehUePKAQBSzqpMZkVGDYkg0Rx6aJX
2828
# a commercial CA.
2929
JENKINS_USE_PROXY: True
3030
JENKINS_PROXY_ENABLE_SSL: True
31+
32+
33+
# Additional
34+
SLACK_WEBHOOK_TOKEN: change-me
35+
JENKINS_ENDPOINT_IP: "111.222.333.444"

0 commit comments

Comments
 (0)