Merge charm-k8s-jenkins-agent:manual-agent into charm-k8s-jenkins-agent:master
- Git
- lp:charm-k8s-jenkins-agent
- manual-agent
- Merge into master
Status: | Rejected |
---|---|
Rejected by: | Alexandre Gomes |
Proposed branch: | charm-k8s-jenkins-agent:manual-agent |
Merge into: | charm-k8s-jenkins-agent:master |
Diff against target: |
437 lines (+258/-39) 6 files modified
Makefile (+7/-0) dockerfile/Dockerfile (+2/-1) dockerfile/files/entrypoint.sh (+25/-3) metadata.yaml (+3/-0) src/charm.py (+155/-35) src/interface_jenkins_slave.py (+66/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexandre Gomes | Needs Resubmitting | ||
Tom Haddon | Needs Fixing | ||
Canonical IS Reviewers | Pending | ||
Review via email: mp+389041@code.launchpad.net |
Commit message
Description of the change
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.
Tom Haddon (mthaddon) wrote : | # |
We're missing a commit message for this MP, so it wouldn't let us merge yet.
I've included some comments inline, but also I had to make the following changes to get lint and tests to run (`make test`) https:/
Alexandre Gomes (alejdg) wrote : | # |
> We're missing a commit message for this MP, so it wouldn't let us merge yet.
>
> I've included some comments inline, but also I had to make the following
> changes to get lint and tests to run (`make test`)
> https:/
I addressed all the comments but two:
- The term "master" isn't going away as per their blog post[1], only "slave". So I think we should follow the same terms Jenkins devs are using.
- The interface is still called jenkins-slave, so we should keep that until we update the interface.
Alexandre Gomes (alejdg) wrote : | # |
All changes in this MP can be found in https:/
Alexandre Gomes (alejdg) wrote : | # |
I forgot to provide the link to the blog post:
https:/
Unmerged commits
- 9ba35a0... by Alexandre Gomes
-
Improve the charm so it can work properly with juju config and add unit
- 79d2ac2... by Alexandre Gomes
-
Fix redeployment issues and make the charm not scalable
- 0d8faa5... by Alexandre Gomes
-
Save agent's secret in a dictionary instead
- 1ca7e4b... by Alexandre Gomes
-
Address comments and make use of the secret provided by jenkins through relation
- 8e2d166... by Alexandre Gomes
-
Fix logging
- 4f4c6cb... by Alexandre Gomes
-
Add slave_relation_
joined - 0eea73b... by Alexandre Gomes
-
Create on_jenkins_
relation_ joined event - dcfe2f6... by Alexandre Gomes
-
Add initial jenkins-slave relation
Preview Diff
1 | diff --git a/Makefile b/Makefile | |||
2 | index 86bd8d3..8c8056a 100644 | |||
3 | --- a/Makefile | |||
4 | +++ b/Makefile | |||
5 | @@ -18,6 +18,13 @@ build-image: | $(dockerfile_dir) | |||
6 | 18 | --build-arg REVISION="$(revision)" \ | 18 | --build-arg REVISION="$(revision)" \ |
7 | 19 | -t $(JENKINS_IMAGE) $(dockerfile_dir) | 19 | -t $(JENKINS_IMAGE) $(dockerfile_dir) |
8 | 20 | 20 | ||
9 | 21 | build-image-no-cache: | $(dockerfile_dir) | ||
10 | 22 | docker build \ | ||
11 | 23 | --build-arg AUTHOR=$(author) \ | ||
12 | 24 | --build-arg REVISION="$(revision)" \ | ||
13 | 25 | --no-cache \ | ||
14 | 26 | -t $(JENKINS_IMAGE) $(dockerfile_dir) | ||
15 | 27 | |||
16 | 21 | build-release-image: | $(dockerfile_dir) | 28 | build-release-image: | $(dockerfile_dir) |
17 | 22 | docker build \ | 29 | docker build \ |
18 | 23 | --build-arg DATE_CREATED="$(date_created)" \ | 30 | --build-arg DATE_CREATED="$(date_created)" \ |
19 | diff --git a/dockerfile/Dockerfile b/dockerfile/Dockerfile | |||
20 | index 1abd7e6..e03339f 100644 | |||
21 | --- a/dockerfile/Dockerfile | |||
22 | +++ b/dockerfile/Dockerfile | |||
23 | @@ -47,7 +47,8 @@ RUN apt-get update -y \ | |||
24 | 47 | 47 | ||
25 | 48 | WORKDIR /var/lib/jenkins | 48 | WORKDIR /var/lib/jenkins |
26 | 49 | 49 | ||
28 | 50 | USER ${USER} | 50 | #USER ${USER} |
29 | 51 | USER root | ||
30 | 51 | 52 | ||
31 | 52 | COPY files/entrypoint.sh / | 53 | COPY files/entrypoint.sh / |
32 | 53 | 54 | ||
33 | diff --git a/dockerfile/files/entrypoint.sh b/dockerfile/files/entrypoint.sh | |||
34 | index c4e879c..c8ece01 100755 | |||
35 | --- a/dockerfile/files/entrypoint.sh | |||
36 | +++ b/dockerfile/files/entrypoint.sh | |||
37 | @@ -3,6 +3,7 @@ | |||
38 | 3 | set -eu -o pipefail | 3 | set -eu -o pipefail |
39 | 4 | 4 | ||
40 | 5 | export LC_ALL=C | 5 | export LC_ALL=C |
41 | 6 | export TERM=xterm | ||
42 | 6 | 7 | ||
43 | 7 | # defaults for jenkins-agent component of the jenkins continuous integration | 8 | # defaults for jenkins-agent component of the jenkins continuous integration |
44 | 8 | # system | 9 | # system |
45 | @@ -25,11 +26,15 @@ typeset JENKINS_URL="${JENKINS_URL:?"URL of a jenkins server must be provided"}" | |||
46 | 25 | # hostname of the server the agent is running on. | 26 | # hostname of the server the agent is running on. |
47 | 26 | typeset JENKINS_HOSTNAME="${JENKINS_HOSTNAME:-$(hostname)}" | 27 | typeset JENKINS_HOSTNAME="${JENKINS_HOSTNAME:-$(hostname)}" |
48 | 27 | 28 | ||
49 | 29 | |||
50 | 30 | typeset JENKINS_WORKDIR="/var/lib/jenkins" | ||
51 | 31 | |||
52 | 28 | # Arguments to pass to jenkins agent on startup | 32 | # Arguments to pass to jenkins agent on startup |
53 | 29 | typeset -a JENKINS_ARGS | 33 | typeset -a JENKINS_ARGS |
54 | 30 | 34 | ||
57 | 31 | JENKINS_ARGS+=(-jnlpUrl "${JENKINS_URL}"/computer/"${JENKINS_HOSTNAME}"/slave-agent.jnlp) | 35 | # JENKINS_ARGS+=(-jnlpUrl "${JENKINS_URL}"/computer/"${JENKINS_HOSTNAME}"/slave-agent.jnlp) |
58 | 32 | JENKINS_ARGS+=(-jnlpCredentials "${JENKINS_API_USER:?Please specify JENKINS_API_USER}:${JENKINS_API_TOKEN:?Please specify JENKINS_API_TOKEN}") | 36 | # JENKINS_ARGS+=(-jnlpCredentials "${JENKINS_API_USER:?Please specify JENKINS_API_USER}:${JENKINS_API_TOKEN:?Please specify JENKINS_API_TOKEN}") |
59 | 37 | # JENKINS_ARGS+=(-noReconect) | ||
60 | 33 | 38 | ||
61 | 34 | # Path of the agent.jar | 39 | # Path of the agent.jar |
62 | 35 | typeset AGENT_JAR=/var/lib/jenkins/agent.jar | 40 | typeset AGENT_JAR=/var/lib/jenkins/agent.jar |
63 | @@ -56,4 +61,21 @@ download_agent | |||
64 | 56 | touch /var/lib/jenkins/agents/.ready | 61 | touch /var/lib/jenkins/agents/.ready |
65 | 57 | 62 | ||
66 | 58 | #shellcheck disable=SC2086 | 63 | #shellcheck disable=SC2086 |
68 | 59 | "${JAVA}" ${JAVA_ARGS} -jar "${AGENT_JAR}" "${JENKINS_ARGS[@]}" | 64 | # "${JAVA}" ${JAVA_ARGS} -jar "${AGENT_JAR}" "${JENKINS_ARGS[@]}" |
69 | 65 | |||
70 | 66 | # Transform the env variables in arrays to iterate through it | ||
71 | 67 | IFS=':' read -r -a AGENTS <<< ${JENKINS_AGENTS} | ||
72 | 68 | IFS=':' read -r -a TOKENS <<< ${JENKINS_TOKENS} | ||
73 | 69 | |||
74 | 70 | echo ${!AGENTS[@]} | ||
75 | 71 | |||
76 | 72 | for index in ${!AGENTS[@]}; do | ||
77 | 73 | echo "agent : ${AGENTS[$index]}" | ||
78 | 74 | echo "value: ${TOKENS[$index]}" | ||
79 | 75 | echo "${JAVA}" "${JAVA_ARGS}" -jar "${AGENT_JAR}" -jnlpUrl "${JENKINS_URL}"/computer/"${AGENTS[$index]}"/slave-agent.jnlp -workDir "${JENKINS_WORKDIR}" -noReconnect -secret "${TOKENS[$index]}" | ||
80 | 76 | ${JAVA} ${JAVA_ARGS} -jar ${AGENT_JAR} -jnlpUrl ${JENKINS_URL}/computer/${AGENTS[$index]}/slave-agent.jnlp -workDir ${JENKINS_WORKDIR} -noReconnect -secret ${TOKENS[$index]} || echo "Invalid or already used credentials." || True | ||
81 | 77 | # ${JAVA} ${JAVA_ARGS} -jar ${AGENT_JAR} -jnlpUrl ${JENKINS_URL}/computer/${AGENTS[$index]}/slave-agent.jnlp -workDir ${JENKINS_WORKDIR} -noReconnect -secret ${TOKENS[$index]} || tail -f /dev/null | ||
82 | 78 | done | ||
83 | 79 | echo "Tail End" | ||
84 | 80 | tail -f /dev/null | ||
85 | 81 | echo "Tail After End" | ||
86 | 60 | \ No newline at end of file | 82 | \ No newline at end of file |
87 | diff --git a/metadata.yaml b/metadata.yaml | |||
88 | index b53a613..dd92ccb 100644 | |||
89 | --- a/metadata.yaml | |||
90 | +++ b/metadata.yaml | |||
91 | @@ -9,3 +9,6 @@ series: | |||
92 | 9 | - kubernetes | 9 | - kubernetes |
93 | 10 | deployment: | 10 | deployment: |
94 | 11 | service: omit | 11 | service: omit |
95 | 12 | provides: | ||
96 | 13 | slave: | ||
97 | 14 | interface: jenkins-slave | ||
98 | diff --git a/src/charm.py b/src/charm.py | |||
99 | index 4ba8782..ea6d681 100755 | |||
100 | --- a/src/charm.py | |||
101 | +++ b/src/charm.py | |||
102 | @@ -5,10 +5,14 @@ | |||
103 | 5 | 5 | ||
104 | 6 | import io | 6 | import io |
105 | 7 | import pprint | 7 | import pprint |
106 | 8 | import os | ||
107 | 9 | import sys | ||
108 | 8 | import logging | 10 | import logging |
109 | 9 | 11 | ||
110 | 12 | sys.path.append('lib') # noqa: E402 | ||
111 | 13 | |||
112 | 10 | from ops.charm import CharmBase | 14 | from ops.charm import CharmBase |
114 | 11 | from ops.framework import StoredState | 15 | from ops.framework import StoredState, EventSource, EventBase |
115 | 12 | from ops.main import main | 16 | from ops.main import main |
116 | 13 | from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus | 17 | from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus |
117 | 14 | 18 | ||
118 | @@ -16,32 +20,10 @@ from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus | |||
119 | 16 | logger = logging.getLogger() | 20 | logger = logging.getLogger() |
120 | 17 | 21 | ||
121 | 18 | 22 | ||
122 | 19 | def generate_pod_config(config, secured=True): | ||
123 | 20 | """Kubernetes pod config generator. | ||
124 | 21 | |||
125 | 22 | generate_pod_config generates Kubernetes deployment config. | ||
126 | 23 | If the secured keyword is set then it will return a sanitised copy | ||
127 | 24 | without exposing secrets. | ||
128 | 25 | """ | ||
129 | 26 | pod_config = {} | ||
130 | 27 | |||
131 | 28 | pod_config["JENKINS_API_USER"] = config["jenkins_user"] | ||
132 | 29 | if config.get("jenkins_master_url", None): | ||
133 | 30 | pod_config["JENKINS_URL"] = config["jenkins_master_url"] | ||
134 | 31 | if config.get("jenkins_agent_name", None): | ||
135 | 32 | pod_config["JENKINS_HOSTNAME"] = config["jenkins_agent_name"] | ||
136 | 33 | |||
137 | 34 | if secured: | ||
138 | 35 | return pod_config | ||
139 | 36 | |||
140 | 37 | # Add secrets from charm config | ||
141 | 38 | pod_config["JENKINS_API_TOKEN"] = config["jenkins_api_token"] | ||
142 | 39 | |||
143 | 40 | return pod_config | ||
144 | 41 | |||
145 | 42 | |||
146 | 43 | class JenkinsAgentCharm(CharmBase): | 23 | class JenkinsAgentCharm(CharmBase): |
148 | 44 | state = StoredState() | 24 | _stored = StoredState() |
149 | 25 | |||
150 | 26 | # on_slave_relation_configured = EventSource(SlaveRelationConfigureEvent) | ||
151 | 45 | 27 | ||
152 | 46 | def __init__(self, framework, parent): | 28 | def __init__(self, framework, parent): |
153 | 47 | super().__init__(framework, parent) | 29 | super().__init__(framework, parent) |
154 | @@ -49,8 +31,10 @@ class JenkinsAgentCharm(CharmBase): | |||
155 | 49 | framework.observe(self.on.start, self.configure_pod) | 31 | framework.observe(self.on.start, self.configure_pod) |
156 | 50 | framework.observe(self.on.config_changed, self.configure_pod) | 32 | framework.observe(self.on.config_changed, self.configure_pod) |
157 | 51 | framework.observe(self.on.upgrade_charm, self.configure_pod) | 33 | framework.observe(self.on.upgrade_charm, self.configure_pod) |
158 | 34 | framework.observe(self.on.slave_relation_joined, self.on_slave_relation_joined) | ||
159 | 35 | framework.observe(self.on.slave_relation_changed, self.on_slave_relation_changed) | ||
160 | 52 | 36 | ||
162 | 53 | self.state.set_default(_spec=None) | 37 | self._stored.set_default(_spec=None, jenkins_url=None, agent_tokens=None, agents=None) |
163 | 54 | 38 | ||
164 | 55 | def on_upgrade_charm(self, event): | 39 | def on_upgrade_charm(self, event): |
165 | 56 | pass | 40 | pass |
166 | @@ -58,14 +42,52 @@ class JenkinsAgentCharm(CharmBase): | |||
167 | 58 | def on_config_changed(self, event): | 42 | def on_config_changed(self, event): |
168 | 59 | pass | 43 | pass |
169 | 60 | 44 | ||
170 | 45 | def generate_pod_config(self, config, secured=True): | ||
171 | 46 | """Kubernetes pod config generator. | ||
172 | 47 | |||
173 | 48 | generate_pod_config generates Kubernetes deployment config. | ||
174 | 49 | If the secured keyword is set then it will return a sanitised copy | ||
175 | 50 | without exposing secrets. | ||
176 | 51 | """ | ||
177 | 52 | pod_config = {} | ||
178 | 53 | |||
179 | 54 | pod_config["JENKINS_API_USER"] = config["jenkins_user"] | ||
180 | 55 | |||
181 | 56 | if self._stored.jenkins_url: | ||
182 | 57 | pod_config["JENKINS_URL"] = self._stored.jenkins_url | ||
183 | 58 | elif config.get("jenkins_master_url", None): | ||
184 | 59 | pod_config["JENKINS_URL"] = config["jenkins_master_url"] | ||
185 | 60 | if config.get("jenkins_agent_name", None): | ||
186 | 61 | pod_config["JENKINS_HOSTNAME"] = config["jenkins_agent_name"] | ||
187 | 62 | |||
188 | 63 | if secured: | ||
189 | 64 | return pod_config | ||
190 | 65 | |||
191 | 66 | # pod_config["JENKINS_API_TOKEN"] = self._stored.agent_tokens or config["jenkins_api_token"] | ||
192 | 67 | if self._stored.agent_tokens and self._stored.agents: | ||
193 | 68 | # for agent in self._stored.agents: | ||
194 | 69 | # logger.info("ALEJDG - generate_pod_config - self._stored.agent: %s", agent) | ||
195 | 70 | logger.info("ALEJDG - generate_pod_config - self._stored.agent: %s", self._stored.agents) | ||
196 | 71 | # pod_config["JENKINS_AGENTS"] = ":".join(self._stored.agents) | ||
197 | 72 | pod_config["JENKINS_AGENTS"] = ":".join(self._stored.agents) | ||
198 | 73 | pod_config["JENKINS_TOKENS"] = ":".join(self._stored.agent_tokens) | ||
199 | 74 | else: | ||
200 | 75 | pod_config["JENKINS_TOKENS"] = config["jenkins_api_token"] | ||
201 | 76 | |||
202 | 77 | return pod_config | ||
203 | 78 | |||
204 | 61 | def configure_pod(self, event): | 79 | def configure_pod(self, event): |
205 | 62 | is_valid = self.is_valid_config() | 80 | is_valid = self.is_valid_config() |
206 | 63 | if not is_valid: | 81 | if not is_valid: |
207 | 64 | return | 82 | return |
208 | 65 | 83 | ||
209 | 84 | if not self.unit.is_leader(): | ||
210 | 85 | self.unit.status = ActiveStatus() | ||
211 | 86 | return | ||
212 | 87 | |||
213 | 66 | spec = self.make_pod_spec() | 88 | spec = self.make_pod_spec() |
216 | 67 | if spec != self.state._spec: | 89 | if spec != self._stored._spec: |
217 | 68 | self.state._spec = spec | 90 | self._stored._spec = spec |
218 | 69 | # only the leader can set_spec() | 91 | # only the leader can set_spec() |
219 | 70 | if self.model.unit.is_leader(): | 92 | if self.model.unit.is_leader(): |
220 | 71 | spec = self.make_pod_spec() | 93 | spec = self.make_pod_spec() |
221 | @@ -81,13 +103,15 @@ class JenkinsAgentCharm(CharmBase): | |||
222 | 81 | else: | 103 | else: |
223 | 82 | logger.info("Pod spec unchanged") | 104 | logger.info("Pod spec unchanged") |
224 | 83 | 105 | ||
226 | 84 | self.state.is_started = True | 106 | self._stored.is_started = True |
227 | 85 | self.model.unit.status = ActiveStatus() | 107 | self.model.unit.status = ActiveStatus() |
228 | 86 | 108 | ||
229 | 87 | def make_pod_spec(self): | 109 | def make_pod_spec(self): |
230 | 88 | config = self.model.config | 110 | config = self.model.config |
233 | 89 | full_pod_config = generate_pod_config(config, secured=False) | 111 | logger.info("ALEJDG - config type: %s - config data: %s", type(config), config) |
234 | 90 | secure_pod_config = generate_pod_config(config, secured=True) | 112 | |
235 | 113 | full_pod_config = self.generate_pod_config(config, secured=False) | ||
236 | 114 | secure_pod_config = self.generate_pod_config(config, secured=True) | ||
237 | 91 | 115 | ||
238 | 92 | spec = { | 116 | spec = { |
239 | 93 | "containers": [ | 117 | "containers": [ |
240 | @@ -101,18 +125,23 @@ class JenkinsAgentCharm(CharmBase): | |||
241 | 101 | } | 125 | } |
242 | 102 | 126 | ||
243 | 103 | out = io.StringIO() | 127 | out = io.StringIO() |
244 | 104 | pprint.pprint(spec, out) | ||
245 | 105 | logger.info("This is the Kubernetes Pod spec config (sans secrets) <<EOM\n{}\nEOM".format(out.getvalue())) | 128 | logger.info("This is the Kubernetes Pod spec config (sans secrets) <<EOM\n{}\nEOM".format(out.getvalue())) |
246 | 106 | 129 | ||
247 | 107 | secure_pod_config.update(full_pod_config) | 130 | secure_pod_config.update(full_pod_config) |
248 | 131 | pprint.pprint(spec, out) | ||
249 | 132 | logger.info("This is the Kubernetes Pod spec config (with secrets) <<EOM\n{}\nEOM".format(out.getvalue())) | ||
250 | 108 | 133 | ||
251 | 109 | return spec | 134 | return spec |
252 | 110 | 135 | ||
253 | 111 | def is_valid_config(self): | 136 | def is_valid_config(self): |
254 | 112 | is_valid = True | 137 | is_valid = True |
255 | 113 | config = self.model.config | ||
256 | 114 | 138 | ||
258 | 115 | want = ("image", "jenkins_user", "jenkins_api_token") | 139 | config = self.model.config |
259 | 140 | logger.info("ALEJDG SPEC: %s", self._stored._spec) | ||
260 | 141 | if self._stored.agent_tokens: | ||
261 | 142 | want = ("image", "jenkins_user") | ||
262 | 143 | else: | ||
263 | 144 | want = ("image", "jenkins_user", "jenkins_api_token") | ||
264 | 116 | missing = [k for k in want if config[k].rstrip() == ""] | 145 | missing = [k for k in want if config[k].rstrip() == ""] |
265 | 117 | if missing: | 146 | if missing: |
266 | 118 | message = "Missing required config: {}".format(" ".join(missing)) | 147 | message = "Missing required config: {}".format(" ".join(missing)) |
267 | @@ -122,6 +151,97 @@ class JenkinsAgentCharm(CharmBase): | |||
268 | 122 | 151 | ||
269 | 123 | return is_valid | 152 | return is_valid |
270 | 124 | 153 | ||
271 | 154 | def on_slave_relation_joined(self, event): | ||
272 | 155 | logger.info("Jenkins relation joined") | ||
273 | 156 | noexecutors = os.cpu_count() | ||
274 | 157 | config_labels = self.model.config.get('labels') | ||
275 | 158 | agent_name = "" | ||
276 | 159 | if self._stored.agents: | ||
277 | 160 | self._stored.agents[-1] | ||
278 | 161 | name, number = self._stored.agents[-1].rsplit('-', 1) | ||
279 | 162 | agent_name = "{}-{}".format(name, int(number) + 1) | ||
280 | 163 | else: | ||
281 | 164 | self._stored.agents = [] | ||
282 | 165 | agent_name = self.unit.name.replace('/', '-') | ||
283 | 166 | |||
284 | 167 | if config_labels: | ||
285 | 168 | labels = config_labels | ||
286 | 169 | else: | ||
287 | 170 | labels = os.uname()[4] | ||
288 | 171 | |||
289 | 172 | # slave_address = hookenv.unit_private_ip() | ||
290 | 173 | |||
291 | 174 | logger.info("noexecutors: %s - type: %s", noexecutors, type(noexecutors)) | ||
292 | 175 | logger.info("labels: %s - type: %s",labels, type(labels)) | ||
293 | 176 | event.relation.data[self.model.unit]["executors"] = str(noexecutors) | ||
294 | 177 | event.relation.data[self.model.unit]["labels"] = labels | ||
295 | 178 | event.relation.data[self.model.unit]["slavehost"] = agent_name | ||
296 | 179 | |||
297 | 180 | remote_data = event.relation.data[event.app] | ||
298 | 181 | logger.info("ALEJDG - remote_data_app: %s", remote_data) | ||
299 | 182 | for i in remote_data: | ||
300 | 183 | logger.info("ALEJDG - remote_data_app['%s']: %s", i, remote_data[i]) | ||
301 | 184 | |||
302 | 185 | if event.unit is not None: | ||
303 | 186 | remote_data = event.relation.data[event.unit] | ||
304 | 187 | logger.info("ALEJDG - os.environ: %s", os.environ) | ||
305 | 188 | |||
306 | 189 | logger.info("ALEJDG - remote_data_post_app: %s", remote_data) | ||
307 | 190 | for i in remote_data: | ||
308 | 191 | logger.info("ALEJDG - remote_data_post_app['%s']: %s", i, remote_data[i]) | ||
309 | 192 | |||
310 | 193 | def on_slave_relation_changed(self, event): | ||
311 | 194 | logger.info("Jenkins relation changed") | ||
312 | 195 | try: | ||
313 | 196 | logger.info("ALEJDG - event.relation.data[event.unit]['url']: %s", event.relation.data[event.unit]['url']) | ||
314 | 197 | self._stored.jenkins_url = event.relation.data[event.unit]['url'] | ||
315 | 198 | except KeyError: | ||
316 | 199 | pass | ||
317 | 200 | |||
318 | 201 | try: | ||
319 | 202 | logger.info("ALEJDG - event.relation.data[event.unit]['secret']: %s", event.relation.data[event.unit]['secret']) | ||
320 | 203 | logger.info("ALEJDG - event.unit.name: %s", event.unit.name) | ||
321 | 204 | logger.info("ALEJDG - self.unit.name.: %s", self.unit.name) | ||
322 | 205 | self._stored.agent_tokens = self._stored.agent_tokens or [] | ||
323 | 206 | self._stored.agent_tokens.append(event.relation.data[event.unit]['secret']) | ||
324 | 207 | agent_name = "" | ||
325 | 208 | if self._stored.agents: | ||
326 | 209 | logger.info("ALEJDG - self._stored.agents[-1]: %s", self._stored.agents[-1]) | ||
327 | 210 | self._stored.agents[-1] | ||
328 | 211 | name, number = self._stored.agents[-1].rsplit('-', 1) | ||
329 | 212 | agent_name = "{}-{}".format(name, int(number) + 1) | ||
330 | 213 | self._stored.agents.append(agent_name) | ||
331 | 214 | else: | ||
332 | 215 | self._stored.agents = [] | ||
333 | 216 | agent_name = self.unit.name.replace('/', '-') | ||
334 | 217 | self._stored.agents.append(agent_name) | ||
335 | 218 | except KeyError: | ||
336 | 219 | pass | ||
337 | 220 | |||
338 | 221 | self.configure_slave_through_relation(event) | ||
339 | 222 | |||
340 | 223 | def configure_slave_through_relation(self, event): | ||
341 | 224 | logger.info("Setting up jenkins via slave relation") | ||
342 | 225 | logger.info("ALEJDG - on_slave_relation_joined - self._stored.agents_setup: %s", self._stored.agents) | ||
343 | 226 | self.model.unit.status = MaintenanceStatus("Configuring jenkins agent") | ||
344 | 227 | |||
345 | 228 | if self.model.config.get("url"): | ||
346 | 229 | logger.info("Config option 'url' is set. Can't use agent relation.") | ||
347 | 230 | self.model.unit.status = ActiveStatus() | ||
348 | 231 | return | ||
349 | 232 | |||
350 | 233 | if self._stored.jenkins_url is None: | ||
351 | 234 | logger.info("Jenkins hasn't exported its url yet. Skipping setup for now.") | ||
352 | 235 | self.model.unit.status = ActiveStatus() | ||
353 | 236 | return | ||
354 | 237 | |||
355 | 238 | if self._stored.agent_tokens is None: | ||
356 | 239 | logger.info("Jenkins hasn't exported the agent secret yet. Skipping setup for now.") | ||
357 | 240 | self.model.unit.status = ActiveStatus() | ||
358 | 241 | return | ||
359 | 242 | |||
360 | 243 | self.configure_pod(event) | ||
361 | 244 | |||
362 | 125 | 245 | ||
363 | 126 | if __name__ == '__main__': | 246 | if __name__ == '__main__': |
364 | 127 | main(JenkinsAgentCharm) | 247 | main(JenkinsAgentCharm) |
365 | diff --git a/src/interface_jenkins_slave.py b/src/interface_jenkins_slave.py | |||
366 | 128 | new file mode 100644 | 248 | new file mode 100644 |
367 | index 0000000..dec278d | |||
368 | --- /dev/null | |||
369 | +++ b/src/interface_jenkins_slave.py | |||
370 | @@ -0,0 +1,66 @@ | |||
371 | 1 | import json | ||
372 | 2 | |||
373 | 3 | from ops.framework import EventBase, EventsBase, EventSource, Object, StoredState | ||
374 | 4 | |||
375 | 5 | |||
376 | 6 | class NewClient(EventBase): | ||
377 | 7 | def __init__(self, handle, client): | ||
378 | 8 | super().__init__(handle) | ||
379 | 9 | self.client = client | ||
380 | 10 | |||
381 | 11 | def snapshot(self): | ||
382 | 12 | return { | ||
383 | 13 | 'relation_name': self.client._relation.name, | ||
384 | 14 | 'relation_id': self.client._relation.id, | ||
385 | 15 | } | ||
386 | 16 | |||
387 | 17 | def restore(self, snapshot): | ||
388 | 18 | relation = self.model.get_relation(snapshot['relation_name'], snapshot['relation_id']) | ||
389 | 19 | self.client = HTTPInterfaceClient(relation, self.model.unit) | ||
390 | 20 | |||
391 | 21 | |||
392 | 22 | class HTTPServerEvents(EventsBase): | ||
393 | 23 | new_client = EventSource(NewClient) | ||
394 | 24 | |||
395 | 25 | |||
396 | 26 | class HTTPServer(Object): | ||
397 | 27 | on = HTTPServerEvents() | ||
398 | 28 | state = StoredState() | ||
399 | 29 | |||
400 | 30 | def __init__(self, charm, relation_name): | ||
401 | 31 | super().__init__(charm, relation_name) | ||
402 | 32 | self.relation_name = relation_name | ||
403 | 33 | self.framework.observe(charm.on.start, self.init_state) | ||
404 | 34 | self.framework.observe(charm.on[relation_name].relation_joined, self.on_joined) | ||
405 | 35 | self.framework.observe(charm.on[relation_name].relation_departed, self.on_departed) | ||
406 | 36 | |||
407 | 37 | def init_state(self, event): | ||
408 | 38 | self.state.apps = [] | ||
409 | 39 | |||
410 | 40 | @property | ||
411 | 41 | def _relations(self): | ||
412 | 42 | return self.model.relations[self.relation_name] | ||
413 | 43 | |||
414 | 44 | def on_joined(self, event): | ||
415 | 45 | if event.app not in self.state.apps: | ||
416 | 46 | self.state.apps.append(event.app) | ||
417 | 47 | self.on.new_client.emit(HTTPInterfaceClient(event.relation, self.model.unit)) | ||
418 | 48 | |||
419 | 49 | def on_departed(self, event): | ||
420 | 50 | self.state.apps = [app for app in self._relations] | ||
421 | 51 | |||
422 | 52 | def clients(self): | ||
423 | 53 | return [HTTPInterfaceClient(relation, self.model.unit) for relation in self._relations] | ||
424 | 54 | |||
425 | 55 | |||
426 | 56 | class HTTPInterfaceClient: | ||
427 | 57 | def __init__(self, relation, local_unit): | ||
428 | 58 | self._relation = relation | ||
429 | 59 | self._local_unit = local_unit | ||
430 | 60 | self.ingress_address = relation.data[local_unit]['ingress-address'] | ||
431 | 61 | |||
432 | 62 | def serve(self, hosts, port): | ||
433 | 63 | self._relation.data[self._local_unit]['extended_data'] = json.dumps([{ | ||
434 | 64 | 'hostname': host, | ||
435 | 65 | 'port': port, | ||
436 | 66 | } for host in hosts]) | ||
437 | 0 | \ No newline at end of file | 67 | \ No newline at end of file |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.