Merge ~jdkandersson/jenkins-agent-charm:switch-to-agent-managed-jnlp-download into jenkins-agent-charm:master

Proposed by Johann David Krister Andersson
Status: Merged
Approved by: Haw Loeung
Approved revision: 013b75593fc69bfb5d7d5789b3b0a0d698650968
Merged at revision: 0f5d1b9c7dbaee2ecc2fec4093ca5c53b5b44b79
Proposed branch: ~jdkandersson/jenkins-agent-charm:switch-to-agent-managed-jnlp-download
Merge into: jenkins-agent-charm:master
Diff against target: 233 lines (+71/-25)
8 files modified
.gitignore (+2/-0)
charmcraft.yaml (+17/-0)
config.yaml (+9/-0)
files/download-slave.sh (+21/-9)
files/jenkins-slave-systemd-config (+1/-1)
reactive/jenkins_slave.py (+5/-5)
templates/jenkins-slave-default (+10/-4)
tests/unit/test_jenkins_slave.py (+6/-6)
Reviewer Review Type Date Requested Status
Haw Loeung +1 Approve
Arturo Enrique Seijas Fernández Approve
Canonical IS Reviewers Pending
Review via email: mp+429936@code.launchpad.net

Commit message

Use agent.jar to download jnlp file rather than download it separately

Description of the change

Newer versions of Jenkins no longer allow the downloading of the JNLP without authentication which is why the Jenkins agent machine charm fails to start up. This fix gets the agent.jar file to perform the download by passing an additional parameter to it.

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Arturo Enrique Seijas Fernández (arturo-seijas) wrote :

LGTM

review: Approve
Revision history for this message
Haw Loeung (hloeung) wrote :

Per Mattermost, is the restriction on the ability to download JNLP's documented somewhere on the Jenkins site?

Would this continue to work or break if we were to roll out the latest charm changes to an existing environment running an older Jenkins version?

Revision history for this message
Johann David Krister Andersson (jdkandersson) wrote :

> Per Mattermost, is the restriction on the ability to download JNLP's
> documented somewhere on the Jenkins site?
>
> Would this continue to work or break if we were to roll out the latest charm
> changes to an existing environment running an older Jenkins version?

I found some notes in a Jira issue: https://issues.jenkins.io/browse/JENKINS-16273?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&showAll=true

I have tested this against all versions of Jenkins running in production (back to 2.150)

Revision history for this message
Haw Loeung (hloeung) wrote :

Comments inline. It's a lengthy diff. Can we split out the various whitespace and formatting changes to a different merge proposal (and make it either a pre-req. of for this or this a pre-req. for that)?

Revision history for this message
Johann David Krister Andersson (jdkandersson) :
Revision history for this message
Haw Loeung (hloeung) wrote :

LGTM

review: Approve (+1)
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 0f5d1b9c7dbaee2ecc2fec4093ca5c53b5b44b79

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/.gitignore b/.gitignore
index ceaa0a2..7682ff5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ __pycache__/
9builds/9builds/
10deps/10deps/
11revision11revision
12.vscode
13*.charm
diff --git a/charmcraft.yaml b/charmcraft.yaml
12new file mode 10064414new file mode 100644
index 0000000..b5b7ae1
--- /dev/null
+++ b/charmcraft.yaml
@@ -0,0 +1,17 @@
1type: "charm"
2bases:
3 - build-on:
4 - name: "ubuntu"
5 channel: "20.04"
6 run-on:
7 - name: "ubuntu"
8 channel: "16.04"
9 - name: "ubuntu"
10 channel: "18.04"
11 - name: "ubuntu"
12 channel: "20.04"
13parts:
14 charm:
15 source: .
16 plugin: reactive
17 build-snaps: [charm]
diff --git a/config.yaml b/config.yaml
index 50380ce..de60423 100644
--- a/config.yaml
+++ b/config.yaml
@@ -26,3 +26,12 @@ options:
26 description: |26 description: |
27 A comma-separated list of nagios servicegroups.27 A comma-separated list of nagios servicegroups.
28 If left empty, the nagios_context will be used as the servicegroup28 If left empty, the nagios_context will be used as the servicegroup
29 download_jnlp_file:
30 default: false
31 type: boolean
32 description: |
33 When `true`, use the DEPRECATED agent startup method that downloads the
34 JNLP file before starting the agent. The JNLP endpoint requires
35 authentication in newer versions of Jenkins meaning that the JNLP
36 download will fail. This parameter is deprecated and will be removed in
37 future versions of this charm.
diff --git a/files/download-slave.sh b/files/download-slave.sh
index dfe1462..60097e2 100755
--- a/files/download-slave.sh
+++ b/files/download-slave.sh
@@ -7,7 +7,8 @@
7SLAVE_JAR=/var/run/jenkins/slave.jar7SLAVE_JAR=/var/run/jenkins/slave.jar
8JENKINS_URL=$18JENKINS_URL=$1
9JENKINS_HOSTNAME=$29JENKINS_HOSTNAME=$2
10JENKINS_JNLP=$310JENKINS_JNLP_FILE=$3
11DOWNLOAD_JNLP_FILE=$4
1112
12if [ -z "$JENKINS_URL" ]13if [ -z "$JENKINS_URL" ]
13then14then
@@ -15,20 +16,31 @@ then
15 exit 116 exit 1
16fi17fi
1718
19# Retrieve the JNLP file from Jenkins
20if [ "$DOWNLOAD_JNLP_FILE" = "True" ]
21then
22 echo "Downloading agent.jnlp from ${JENKINS_URL}..."
23 wget -q ${JENKINS_URL}/computer/${JENKINS_HOSTNAME}/jenkins-agent.jnlp -O ${JENKINS_JNLP_FILE}.new || wget -q ${JENKINS_URL}/computer/${JENKINS_HOSTNAME}/slave-agent.jnlp -O ${JENKINS_JNLP_FILE}.new
24 sed -i -r "s|(<argument>-url</argument>.*</argument>)|\1<argument>-url</argument><argument>${JENKINS_URL}</argument>|g" ${JENKINS_JNLP_FILE}.new
25
26 # Check to make sure that JNLP file was downloaded
27 if [ -s ${JENKINS_JNLP_FILE}.new ]
28 then
29 mv ${JENKINS_JNLP_FILE}.new ${JENKINS_JNLP_FILE}
30 else
31 echo "Unable to download the JNLP file"
32 exit 1
33 fi
34fi
35
18# Retrieve Slave JAR from Jenkins36# Retrieve Slave JAR from Jenkins
19echo "Downloading slave.jar from ${JENKINS_URL}..."37echo "Downloading slave.jar from ${JENKINS_URL}..."
20wget -q -O ${SLAVE_JAR}.new ${JENKINS_URL}/jnlpJars/slave.jar38wget -q -O ${SLAVE_JAR}.new ${JENKINS_URL}/jnlpJars/slave.jar
2139
22# Retrive JNLP file from Jenkins40# Check to make sure slave.jar was downloaded.
23echo "Downloading agent.jnlp from ${JENKINS_URL}..."41if [ -s ${SLAVE_JAR}.new ]
24wget -q ${JENKINS_URL}/computer/${JENKINS_HOSTNAME}/jenkins-agent.jnlp -O ${JENKINS_JNLP}.new || wget -q ${JENKINS_URL}/computer/${JENKINS_HOSTNAME}/slave-agent.jnlp -O ${JENKINS_JNLP}.new
25sed -i -r "s|(<argument>-url</argument>.*</argument>)|\1<argument>-url</argument><argument>${JENKINS_URL}</argument>|g" ${JENKINS_JNLP}.new
26
27# Check to make sure slave.jar and agent.jnlp were downloaded.
28if [ -s ${SLAVE_JAR}.new ] && [ -s ${JENKINS_JNLP}.new ]
29then42then
30 mv ${SLAVE_JAR}.new ${SLAVE_JAR}43 mv ${SLAVE_JAR}.new ${SLAVE_JAR}
31 mv ${JENKINS_JNLP}.new ${JENKINS_JNLP}
32 exit 044 exit 0
33else45else
34 exit 146 exit 1
diff --git a/files/jenkins-slave-systemd-config b/files/jenkins-slave-systemd-config
index 8148aaa..11eac18 100644
--- a/files/jenkins-slave-systemd-config
+++ b/files/jenkins-slave-systemd-config
@@ -11,7 +11,7 @@ Type=simple
11# Always be Jenkinsing!11# Always be Jenkinsing!
12Restart=always12Restart=always
13RestartSec=313RestartSec=3
14ExecStartPre=/bin/bash -c '[ -r /etc/default/jenkins-slave ] && . /etc/default/jenkins-slave ; [ -n "$JENKINS_URL" ] || { exit 1; }; mkdir $JENKINS_RUN > /dev/null 2>&1 || true ; chown -R $JENKINS_USER $JENKINS_RUN || true ; /usr/local/sbin/download-slave.sh $JENKINS_URL $JENKINS_HOSTNAME $JENKINS_JNLP; chown -R $JENKINS_USER $JENKINS_HOME || true'14ExecStartPre=/bin/bash -c '[ -r /etc/default/jenkins-slave ] && . /etc/default/jenkins-slave ; [ -n "$JENKINS_URL" ] || { exit 1; }; mkdir $JENKINS_RUN > /dev/null 2>&1 || true ; chown -R $JENKINS_USER $JENKINS_RUN || true ; /usr/local/sbin/download-slave.sh $JENKINS_URL $JENKINS_HOSTNAME $JENKINS_JNLP_FILE $DOWNLOAD_JNLP_FILE; chown -R $JENKINS_USER $JENKINS_HOME || true'
15ExecStart=/bin/bash -c '[ -r /etc/default/jenkins-slave ] && . /etc/default/jenkins-slave ; exec start-stop-daemon --start -c $JENKINS_USER --exec $JAVA --name jenkins-slave -- $JAVA_ARGS -jar $JENKINS_RUN/slave.jar $JENKINS_ARGS'15ExecStart=/bin/bash -c '[ -r /etc/default/jenkins-slave ] && . /etc/default/jenkins-slave ; exec start-stop-daemon --start -c $JENKINS_USER --exec $JAVA --name jenkins-slave -- $JAVA_ARGS -jar $JENKINS_RUN/slave.jar $JENKINS_ARGS'
1616
17[Install]17[Install]
diff --git a/reactive/jenkins_slave.py b/reactive/jenkins_slave.py
index dcb947d..91f281c 100644
--- a/reactive/jenkins_slave.py
+++ b/reactive/jenkins_slave.py
@@ -93,10 +93,10 @@ def configure_jenkins_slave():
9393
94 if config.get('master_url'):94 if config.get('master_url'):
95 status.maintenance("Using 'master_url' to configure the slave.")95 status.maintenance("Using 'master_url' to configure the slave.")
96 write_default_conf(config.get('master_url'))96 write_default_conf(config.get('master_url'), download_jnlp_file=config.get('download_jnlp_file'))
97 elif kv.get('url'):97 elif kv.get('url'):
98 status.maintenance("Using url from relation as 'master_url'")98 status.maintenance("Using url from relation as 'master_url'")
99 write_default_conf(kv.get('url'), secret=kv.get('secret'))99 write_default_conf(kv.get('url'), secret=kv.get('secret'), download_jnlp_file=config.get('download_jnlp_file'))
100 else:100 else:
101 status.maintenance("No 'master_url' set; not configuring slave at this time.")101 status.maintenance("No 'master_url' set; not configuring slave at this time.")
102 status.blocked("requires either slave relation or 'master_url'")102 status.blocked("requires either slave relation or 'master_url'")
@@ -191,7 +191,7 @@ def slave_relation():
191 secret = hookenv.relation_get('secret')191 secret = hookenv.relation_get('secret')
192 kv.set('url', url)192 kv.set('url', url)
193 kv.set('secret', secret)193 kv.set('secret', secret)
194 write_default_conf(url, secret=secret)194 write_default_conf(url, secret=secret, download_jnlp_file=config.get('download_jnlp_file'))
195 else:195 else:
196 hookenv.log("Master hasn't exported its url yet, exiting...")196 hookenv.log("Master hasn't exported its url yet, exiting...")
197 return197 return
@@ -233,14 +233,14 @@ def file_to_units(local_path, unit_path, perms=None, owner='root', group='root')
233233
234234
235def write_default_conf(235def write_default_conf(
236 master_url=None, owner='root', group='root', conf_path='/etc/default/jenkins-slave', secret=None236 master_url=None, owner='root', group='root', conf_path='/etc/default/jenkins-slave', secret=None, download_jnlp_file=False
237):237):
238 templates_dir = os.path.join(hookenv.charm_dir(), 'templates')238 templates_dir = os.path.join(hookenv.charm_dir(), 'templates')
239 slave_host = hookenv.local_unit().replace('/', '-')239 slave_host = hookenv.local_unit().replace('/', '-')
240 templating.render(240 templating.render(
241 'jenkins-slave-default',241 'jenkins-slave-default',
242 conf_path,242 conf_path,
243 {'master_url': master_url, 'slave_host': slave_host, 'secret': secret},243 {'master_url': master_url, 'slave_host': slave_host, 'secret': secret, 'download_jnlp_file': download_jnlp_file},
244 owner=owner,244 owner=owner,
245 group=group,245 group=group,
246 perms=0o444,246 perms=0o444,
diff --git a/templates/jenkins-slave-default b/templates/jenkins-slave-default
index b3b2148..0fa9937 100644
--- a/templates/jenkins-slave-default
+++ b/templates/jenkins-slave-default
@@ -60,14 +60,20 @@ JENKINS_SLAVE_LOG=/var/log/jenkins/$NAME.log
60# to change the OS limits setup.60# to change the OS limits setup.
61MAXOPENFILES=819261MAXOPENFILES=8192
6262
63# Jenkins jnlp file63# Jenkins jnlp URL
64JENKINS_JNLP="${JENKINS_HOME}/agent.jnlp"64DOWNLOAD_JNLP_FILE="{{ download_jnlp_file }}"
65JENKINS_JNLP_FILE="${JENKINS_HOME}/agent.jnlp"
66{% if download_jnlp_file %}
67JENKINS_JNLP_URL="file:${JENKINS_JNLP_FILE}"
68{% else %}
69JENKINS_JNLP_URL="${JENKINS_URL}/computer/${JENKINS_HOSTNAME}/slave-agent.jnlp"
70{% endif %}
6571
66# Arguments to pass to jenkins slave on startup72# Arguments to pass to jenkins slave on startup
67{% if secret %}73{% if secret %}
68JENKINS_SLAVE_SECRET="{{ secret }}"74JENKINS_SLAVE_SECRET="{{ secret }}"
6975
70JENKINS_ARGS="-jnlpUrl file:${JENKINS_JNLP} -secret ${JENKINS_SLAVE_SECRET}"76JENKINS_ARGS="-jnlpUrl ${JENKINS_JNLP_URL} -secret ${JENKINS_SLAVE_SECRET} -workDir ${JENKINS_HOME}"
71{% else %}77{% else %}
72JENKINS_ARGS="-jnlpUrl file:${JENKINS_JNLP}"78JENKINS_ARGS="-jnlpUrl ${JENKINS_JNLP_URL} -workDir ${JENKINS_HOME}"
73{% endif %}79{% endif %}
diff --git a/tests/unit/test_jenkins_slave.py b/tests/unit/test_jenkins_slave.py
index 1300c73..6d32481 100644
--- a/tests/unit/test_jenkins_slave.py
+++ b/tests/unit/test_jenkins_slave.py
@@ -201,10 +201,10 @@ class TestSetDefaultConf(unittest.TestCase):
201 def test_configure_jenkins_slave_master_url(201 def test_configure_jenkins_slave_master_url(
202 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag202 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag
203 ):203 ):
204 config.return_value = {'master_url': 'http://10.1.1.1:8080'}204 config.return_value = {'master_url': 'http://10.1.1.1:8080', 'download_jnlp_file': False}
205 unitdata_kv.return_value = {}205 unitdata_kv.return_value = {}
206 jenkins_slave.configure_jenkins_slave()206 jenkins_slave.configure_jenkins_slave()
207 self.assertFalse(write_default_conf.assert_called_once_with('http://10.1.1.1:8080'))207 self.assertFalse(write_default_conf.assert_called_once_with('http://10.1.1.1:8080', download_jnlp_file=False))
208 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))208 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))
209 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]209 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]
210 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))210 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))
@@ -218,11 +218,11 @@ class TestSetDefaultConf(unittest.TestCase):
218 def test_configure_jenkins_slave_relation_url(218 def test_configure_jenkins_slave_relation_url(
219 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag219 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag
220 ):220 ):
221 config.return_value = {}221 config.return_value = {'download_jnlp_file': False}
222 unitdata_kv.return_value = {'url': 'http://10.22.22.22:8080'}222 unitdata_kv.return_value = {'url': 'http://10.22.22.22:8080'}
223 jenkins_slave.configure_jenkins_slave()223 jenkins_slave.configure_jenkins_slave()
224 self.assertTrue(write_default_conf.called_once_with('http://10.22.22.22:8080'))224 self.assertTrue(write_default_conf.called_once_with('http://10.22.22.22:8080'))
225 self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080', secret=None)])225 self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080', secret=None, download_jnlp_file=False)])
226 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))226 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))
227 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]227 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]
228 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))228 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))
@@ -236,10 +236,10 @@ class TestSetDefaultConf(unittest.TestCase):
236 def test_configure_jenkins_slave_relation_url_with_secret(236 def test_configure_jenkins_slave_relation_url_with_secret(
237 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag237 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag
238 ):238 ):
239 config.return_value = {}239 config.return_value = {'download_jnlp_file': False}
240 unitdata_kv.return_value = {'url': 'http://10.22.22.22:8080', 'secret': 'sekrit'}240 unitdata_kv.return_value = {'url': 'http://10.22.22.22:8080', 'secret': 'sekrit'}
241 jenkins_slave.configure_jenkins_slave()241 jenkins_slave.configure_jenkins_slave()
242 self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080', secret='sekrit')])242 self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080', secret='sekrit', download_jnlp_file=False)])
243 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))243 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))
244 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]244 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]
245 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))245 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))

Subscribers

People subscribed via source and target branches