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
1diff --git a/.gitignore b/.gitignore
2index ceaa0a2..7682ff5 100644
3--- a/.gitignore
4+++ b/.gitignore
5@@ -9,3 +9,5 @@ __pycache__/
6 builds/
7 deps/
8 revision
9+.vscode
10+*.charm
11diff --git a/charmcraft.yaml b/charmcraft.yaml
12new file mode 100644
13index 0000000..b5b7ae1
14--- /dev/null
15+++ b/charmcraft.yaml
16@@ -0,0 +1,17 @@
17+type: "charm"
18+bases:
19+ - build-on:
20+ - name: "ubuntu"
21+ channel: "20.04"
22+ run-on:
23+ - name: "ubuntu"
24+ channel: "16.04"
25+ - name: "ubuntu"
26+ channel: "18.04"
27+ - name: "ubuntu"
28+ channel: "20.04"
29+parts:
30+ charm:
31+ source: .
32+ plugin: reactive
33+ build-snaps: [charm]
34diff --git a/config.yaml b/config.yaml
35index 50380ce..de60423 100644
36--- a/config.yaml
37+++ b/config.yaml
38@@ -26,3 +26,12 @@ options:
39 description: |
40 A comma-separated list of nagios servicegroups.
41 If left empty, the nagios_context will be used as the servicegroup
42+ download_jnlp_file:
43+ default: false
44+ type: boolean
45+ description: |
46+ When `true`, use the DEPRECATED agent startup method that downloads the
47+ JNLP file before starting the agent. The JNLP endpoint requires
48+ authentication in newer versions of Jenkins meaning that the JNLP
49+ download will fail. This parameter is deprecated and will be removed in
50+ future versions of this charm.
51diff --git a/files/download-slave.sh b/files/download-slave.sh
52index dfe1462..60097e2 100755
53--- a/files/download-slave.sh
54+++ b/files/download-slave.sh
55@@ -7,7 +7,8 @@
56 SLAVE_JAR=/var/run/jenkins/slave.jar
57 JENKINS_URL=$1
58 JENKINS_HOSTNAME=$2
59-JENKINS_JNLP=$3
60+JENKINS_JNLP_FILE=$3
61+DOWNLOAD_JNLP_FILE=$4
62
63 if [ -z "$JENKINS_URL" ]
64 then
65@@ -15,20 +16,31 @@ then
66 exit 1
67 fi
68
69+# Retrieve the JNLP file from Jenkins
70+if [ "$DOWNLOAD_JNLP_FILE" = "True" ]
71+then
72+ echo "Downloading agent.jnlp from ${JENKINS_URL}..."
73+ 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
74+ sed -i -r "s|(<argument>-url</argument>.*</argument>)|\1<argument>-url</argument><argument>${JENKINS_URL}</argument>|g" ${JENKINS_JNLP_FILE}.new
75+
76+ # Check to make sure that JNLP file was downloaded
77+ if [ -s ${JENKINS_JNLP_FILE}.new ]
78+ then
79+ mv ${JENKINS_JNLP_FILE}.new ${JENKINS_JNLP_FILE}
80+ else
81+ echo "Unable to download the JNLP file"
82+ exit 1
83+ fi
84+fi
85+
86 # Retrieve Slave JAR from Jenkins
87 echo "Downloading slave.jar from ${JENKINS_URL}..."
88 wget -q -O ${SLAVE_JAR}.new ${JENKINS_URL}/jnlpJars/slave.jar
89
90-# Retrive JNLP file from Jenkins
91-echo "Downloading agent.jnlp from ${JENKINS_URL}..."
92-wget -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
93-sed -i -r "s|(<argument>-url</argument>.*</argument>)|\1<argument>-url</argument><argument>${JENKINS_URL}</argument>|g" ${JENKINS_JNLP}.new
94-
95-# Check to make sure slave.jar and agent.jnlp were downloaded.
96-if [ -s ${SLAVE_JAR}.new ] && [ -s ${JENKINS_JNLP}.new ]
97+# Check to make sure slave.jar was downloaded.
98+if [ -s ${SLAVE_JAR}.new ]
99 then
100 mv ${SLAVE_JAR}.new ${SLAVE_JAR}
101- mv ${JENKINS_JNLP}.new ${JENKINS_JNLP}
102 exit 0
103 else
104 exit 1
105diff --git a/files/jenkins-slave-systemd-config b/files/jenkins-slave-systemd-config
106index 8148aaa..11eac18 100644
107--- a/files/jenkins-slave-systemd-config
108+++ b/files/jenkins-slave-systemd-config
109@@ -11,7 +11,7 @@ Type=simple
110 # Always be Jenkinsing!
111 Restart=always
112 RestartSec=3
113-ExecStartPre=/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'
114+ExecStartPre=/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'
115 ExecStart=/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'
116
117 [Install]
118diff --git a/reactive/jenkins_slave.py b/reactive/jenkins_slave.py
119index dcb947d..91f281c 100644
120--- a/reactive/jenkins_slave.py
121+++ b/reactive/jenkins_slave.py
122@@ -93,10 +93,10 @@ def configure_jenkins_slave():
123
124 if config.get('master_url'):
125 status.maintenance("Using 'master_url' to configure the slave.")
126- write_default_conf(config.get('master_url'))
127+ write_default_conf(config.get('master_url'), download_jnlp_file=config.get('download_jnlp_file'))
128 elif kv.get('url'):
129 status.maintenance("Using url from relation as 'master_url'")
130- write_default_conf(kv.get('url'), secret=kv.get('secret'))
131+ write_default_conf(kv.get('url'), secret=kv.get('secret'), download_jnlp_file=config.get('download_jnlp_file'))
132 else:
133 status.maintenance("No 'master_url' set; not configuring slave at this time.")
134 status.blocked("requires either slave relation or 'master_url'")
135@@ -191,7 +191,7 @@ def slave_relation():
136 secret = hookenv.relation_get('secret')
137 kv.set('url', url)
138 kv.set('secret', secret)
139- write_default_conf(url, secret=secret)
140+ write_default_conf(url, secret=secret, download_jnlp_file=config.get('download_jnlp_file'))
141 else:
142 hookenv.log("Master hasn't exported its url yet, exiting...")
143 return
144@@ -233,14 +233,14 @@ def file_to_units(local_path, unit_path, perms=None, owner='root', group='root')
145
146
147 def write_default_conf(
148- master_url=None, owner='root', group='root', conf_path='/etc/default/jenkins-slave', secret=None
149+ master_url=None, owner='root', group='root', conf_path='/etc/default/jenkins-slave', secret=None, download_jnlp_file=False
150 ):
151 templates_dir = os.path.join(hookenv.charm_dir(), 'templates')
152 slave_host = hookenv.local_unit().replace('/', '-')
153 templating.render(
154 'jenkins-slave-default',
155 conf_path,
156- {'master_url': master_url, 'slave_host': slave_host, 'secret': secret},
157+ {'master_url': master_url, 'slave_host': slave_host, 'secret': secret, 'download_jnlp_file': download_jnlp_file},
158 owner=owner,
159 group=group,
160 perms=0o444,
161diff --git a/templates/jenkins-slave-default b/templates/jenkins-slave-default
162index b3b2148..0fa9937 100644
163--- a/templates/jenkins-slave-default
164+++ b/templates/jenkins-slave-default
165@@ -60,14 +60,20 @@ JENKINS_SLAVE_LOG=/var/log/jenkins/$NAME.log
166 # to change the OS limits setup.
167 MAXOPENFILES=8192
168
169-# Jenkins jnlp file
170-JENKINS_JNLP="${JENKINS_HOME}/agent.jnlp"
171+# Jenkins jnlp URL
172+DOWNLOAD_JNLP_FILE="{{ download_jnlp_file }}"
173+JENKINS_JNLP_FILE="${JENKINS_HOME}/agent.jnlp"
174+{% if download_jnlp_file %}
175+JENKINS_JNLP_URL="file:${JENKINS_JNLP_FILE}"
176+{% else %}
177+JENKINS_JNLP_URL="${JENKINS_URL}/computer/${JENKINS_HOSTNAME}/slave-agent.jnlp"
178+{% endif %}
179
180 # Arguments to pass to jenkins slave on startup
181 {% if secret %}
182 JENKINS_SLAVE_SECRET="{{ secret }}"
183
184-JENKINS_ARGS="-jnlpUrl file:${JENKINS_JNLP} -secret ${JENKINS_SLAVE_SECRET}"
185+JENKINS_ARGS="-jnlpUrl ${JENKINS_JNLP_URL} -secret ${JENKINS_SLAVE_SECRET} -workDir ${JENKINS_HOME}"
186 {% else %}
187-JENKINS_ARGS="-jnlpUrl file:${JENKINS_JNLP}"
188+JENKINS_ARGS="-jnlpUrl ${JENKINS_JNLP_URL} -workDir ${JENKINS_HOME}"
189 {% endif %}
190diff --git a/tests/unit/test_jenkins_slave.py b/tests/unit/test_jenkins_slave.py
191index 1300c73..6d32481 100644
192--- a/tests/unit/test_jenkins_slave.py
193+++ b/tests/unit/test_jenkins_slave.py
194@@ -201,10 +201,10 @@ class TestSetDefaultConf(unittest.TestCase):
195 def test_configure_jenkins_slave_master_url(
196 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag
197 ):
198- config.return_value = {'master_url': 'http://10.1.1.1:8080'}
199+ config.return_value = {'master_url': 'http://10.1.1.1:8080', 'download_jnlp_file': False}
200 unitdata_kv.return_value = {}
201 jenkins_slave.configure_jenkins_slave()
202- self.assertFalse(write_default_conf.assert_called_once_with('http://10.1.1.1:8080'))
203+ self.assertFalse(write_default_conf.assert_called_once_with('http://10.1.1.1:8080', download_jnlp_file=False))
204 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))
205 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]
206 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))
207@@ -218,11 +218,11 @@ class TestSetDefaultConf(unittest.TestCase):
208 def test_configure_jenkins_slave_relation_url(
209 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag
210 ):
211- config.return_value = {}
212+ config.return_value = {'download_jnlp_file': False}
213 unitdata_kv.return_value = {'url': 'http://10.22.22.22:8080'}
214 jenkins_slave.configure_jenkins_slave()
215 self.assertTrue(write_default_conf.called_once_with('http://10.22.22.22:8080'))
216- self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080', secret=None)])
217+ self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080', secret=None, download_jnlp_file=False)])
218 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))
219 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]
220 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))
221@@ -236,10 +236,10 @@ class TestSetDefaultConf(unittest.TestCase):
222 def test_configure_jenkins_slave_relation_url_with_secret(
223 self, file_to_units, write_default_conf, unitdata_kv, config, set_flag, clear_flag
224 ):
225- config.return_value = {}
226+ config.return_value = {'download_jnlp_file': False}
227 unitdata_kv.return_value = {'url': 'http://10.22.22.22:8080', 'secret': 'sekrit'}
228 jenkins_slave.configure_jenkins_slave()
229- self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080', secret='sekrit')])
230+ self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080', secret='sekrit', download_jnlp_file=False)])
231 self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True))
232 expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')]
233 self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True))

Subscribers

People subscribed via source and target branches