blob: be065a1f8cc45a44fd598c0ec0bd242f342a4929 [file] [log] [blame]
Victor Ryzhenkinef34a022018-06-22 19:36:13 +04001/**
2 * Update kuberentes cluster
3 *
4 * Expected parameters:
5 * SALT_MASTER_CREDENTIALS Credentials to the Salt API.
6 * SALT_MASTER_URL Full Salt API address [https://10.10.10.1:8000].
7 * KUBERNETES_HYPERKUBE_IMAGE Target kubernetes version. May be null in case of reclass-system rollout
8 * KUBERNETES_PAUSE_IMAGE Kubernetes pause image should have same version as hyperkube. May be null in case of reclass-system rollout
9 * TARGET_UPDATES Comma separated list of nodes to update (Valid values are ctl,cmp)
10 * CTL_TARGET Salt targeted kubernetes CTL nodes (ex. I@kubernetes:master). Kubernetes control plane
11 * CMP_TARGET Salt targeted compute nodes (ex. cmp* and 'I@kubernetes:pool') Kubernetes computes
12 * PER_NODE Target nodes will be managed one by one (bool)
Victor Ryzhenkin42e4b382018-09-11 17:57:56 +040013 * SIMPLE_UPGRADE Use previous version of upgrade without conron/drain abilities
14 * UPGRADE_DOCKER Upgrade docker component
Victor Ryzhenkinae22a5a2018-10-12 15:52:27 +040015 * CONFORMANCE_RUN_AFTER Run Kubernetes conformance tests after update
16 * CONFORMANCE_RUN_BEFORE Run Kubernetes conformance tests before update
17 * TEST_K8S_API_SERVER Kubernetes API server address for test execution
18 * ARTIFACTORY_URL Artifactory URL where docker images located. Needed to correctly fetch conformance images.
Victor Ryzhenkinef34a022018-06-22 19:36:13 +040019 *
20**/
21def common = new com.mirantis.mk.Common()
22def salt = new com.mirantis.mk.Salt()
23def python = new com.mirantis.mk.Python()
24
25def updates = TARGET_UPDATES.tokenize(",").collect{it -> it.trim()}
26def pepperEnv = "pepperEnv"
27
28def overrideKubernetesImage(pepperEnv) {
29 def salt = new com.mirantis.mk.Salt()
30
31 def k8sSaltOverrides = """
32 kubernetes_hyperkube_image: ${KUBERNETES_HYPERKUBE_IMAGE}
33 kubernetes_pause_image: ${KUBERNETES_PAUSE_IMAGE}
34 """
35 stage("Override kubernetes images to target version") {
36 salt.setSaltOverrides(pepperEnv, k8sSaltOverrides)
37 }
38}
39
40def performKubernetesComputeUpdate(pepperEnv, target) {
41 def salt = new com.mirantis.mk.Salt()
42
43 stage("Execute Kubernetes compute update on ${target}") {
44 salt.enforceState(pepperEnv, target, 'kubernetes.pool')
45 salt.runSaltProcessStep(pepperEnv, target, 'service.restart', ['kubelet'])
46 }
47}
48
49def performKubernetesControlUpdate(pepperEnv, target) {
50 def salt = new com.mirantis.mk.Salt()
51
52 stage("Execute Kubernetes control plane update on ${target}") {
53 salt.enforceStateWithExclude(pepperEnv, target, "kubernetes", "kubernetes.master.setup")
54 // Restart kubelet
55 salt.runSaltProcessStep(pepperEnv, target, 'service.restart', ['kubelet'])
56 }
57}
58
Victor Ryzhenkin42e4b382018-09-11 17:57:56 +040059def cordonNode(pepperEnv, target) {
60 def salt = new com.mirantis.mk.Salt()
61 def originalTarget = "I@kubernetes:master and not ${target}"
62
63 stage("Cordoning ${target} kubernetes node") {
64 def nodeShortName = target.tokenize(".")[0]
65 salt.cmdRun(pepperEnv, originalTarget, "kubectl cordon ${nodeShortName}", true, 1)
66 }
67}
68
69def uncordonNode(pepperEnv, target) {
70 def salt = new com.mirantis.mk.Salt()
71 def originalTarget = "I@kubernetes:master and not ${target}"
72
73 stage("Uncordoning ${target} kubernetes node") {
74 def nodeShortName = target.tokenize(".")[0]
75 salt.cmdRun(pepperEnv, originalTarget, "kubectl uncordon ${nodeShortName}", true, 1)
76 }
77}
78
79def drainNode(pepperEnv, target) {
80 def salt = new com.mirantis.mk.Salt()
81 def originalTarget = "I@kubernetes:master and not ${target}"
82
83 stage("Draining ${target} kubernetes node") {
84 def nodeShortName = target.tokenize(".")[0]
85 salt.cmdRun(pepperEnv, originalTarget, "kubectl drain --force --ignore-daemonsets --grace-period 100 --timeout 300s --delete-local-data ${nodeShortName}", true, 1)
86 }
87}
88
89def regenerateCerts(pepperEnv, target) {
90 def salt = new com.mirantis.mk.Salt()
91
92 stage("Regenerate certs for ${target}") {
93 salt.enforceState(pepperEnv, target, 'salt.minion.cert')
94 }
95}
96
Victor Ryzhenkinae909182018-10-02 17:49:18 +040097def updateAddons(pepperEnv, target) {
98 def salt = new com.mirantis.mk.Salt()
99
100 stage("Upgrading Addons at ${target}") {
Victor Ryzhenkin40625bc2018-10-04 16:15:27 +0400101 salt.enforceState(pepperEnv, target, "kubernetes.master.kube-addons")
Victor Ryzhenkinfd9677f2018-10-16 16:14:40 +0400102 }
103}
104
105def updateAddonManager(pepperEnv, target) {
106 def salt = new com.mirantis.mk.Salt()
107
108 stage("Upgrading AddonManager at ${target}") {
Victor Ryzhenkinae909182018-10-02 17:49:18 +0400109 salt.enforceState(pepperEnv, target, "kubernetes.master.setup")
110 }
111}
112
Victor Ryzhenkin42e4b382018-09-11 17:57:56 +0400113def upgradeDocker(pepperEnv, target) {
114 def salt = new com.mirantis.mk.Salt()
115
116 stage("Upgrading docker at ${target}") {
117 salt.enforceState(pepperEnv, target, 'docker.host')
118 }
119}
Victor Ryzhenkinef34a022018-06-22 19:36:13 +0400120
Victor Ryzhenkinae22a5a2018-10-12 15:52:27 +0400121def runConformance(pepperEnv, target, k8s_api, image) {
122 def salt = new com.mirantis.mk.Salt()
123 def containerName = 'conformance_tests'
124 output_file = image.replaceAll('/', '-') + '.output'
125 def output_file_full_path = "/tmp/" + image.replaceAll('/', '-') + '.output'
126 def artifacts_dir = '_artifacts/'
127 salt.cmdRun(pepperEnv, target, "docker rm -f ${containerName}", false)
128 salt.cmdRun(pepperEnv, target, "docker run -d --name ${containerName} --net=host -e API_SERVER=${k8s_api} ${image}")
129 sleep(10)
130
131 print("Waiting for tests to run...")
132 salt.runSaltProcessStep(pepperEnv, target, 'cmd.run', ["docker wait ${containerName}"], null, false)
133
134 print("Writing test results to output file...")
135 salt.runSaltProcessStep(pepperEnv, target, 'cmd.run', ["docker logs -t ${containerName} > ${output_file_full_path}"])
136 print("Conformance test output saved in " + output_file_full_path)
137
138 // collect output
139 sh "mkdir -p ${artifacts_dir}"
140 file_content = salt.getFileContent(pepperEnv, target, '/tmp/' + output_file)
141 writeFile file: "${artifacts_dir}${output_file}", text: file_content
142 sh "cat ${artifacts_dir}${output_file}"
143 try {
144 sh "cat ${artifacts_dir}${output_file} | grep 'Test Suite Failed' && exit 1 || exit 0"
145 } catch (Throwable e) {
146 print("Conformance tests failed. Please check output")
147 currentBuild.result = "FAILURE"
148 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
149 throw e
150 }
151}
152
153def buildImageURL(pepperEnv, target, mcp_repo) {
154 def salt = new com.mirantis.mk.Salt()
155 def raw_version = salt.cmdRun(pepperEnv, target, "kubectl version --short -o json")['return'][0].values()[0].replaceAll('Salt command execution success','')
156 print("Kubernetes version: " + raw_version)
157 def serialized_version = readJSON text: raw_version
158 def short_version = (serialized_version.serverVersion.gitVersion =~ /([v])(\d+\.)(\d+\.)(\d+\-)(\d+)/)[0][0]
159 print("Kubernetes short version: " + short_version)
160 def conformance_image = mcp_repo + "/mirantis/kubernetes/k8s-conformance:" + short_version
161 return conformance_image
162}
163
164def executeConformance(pepperEnv, target, k8s_api, mcp_repo) {
165 stage("Running conformance tests") {
166 def image = buildImageURL(pepperEnv, target, mcp_repo)
167 print("Using image: " + image)
168 runConformance(pepperEnv, target, k8s_api, image)
169 }
170}
171
172
Victor Ryzhenkinef34a022018-06-22 19:36:13 +0400173timeout(time: 12, unit: 'HOURS') {
174 node() {
175 try {
176
177 stage("Setup virtualenv for Pepper") {
178 python.setupPepperVirtualenv(pepperEnv, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
179 }
180
Victor Ryzhenkinae22a5a2018-10-12 15:52:27 +0400181 if (CONFORMANCE_RUN_BEFORE.toBoolean()) {
182 def target = CTL_TARGET
183 def mcp_repo = ARTIFACTORY_URL
184 def k8s_api = TEST_K8S_API_SERVER
185 firstTarget = salt.getFirstMinion(pepperEnv, target)
186 executeConformance(pepperEnv, firstTarget, k8s_api, mcp_repo)
187 }
188
Victor Ryzhenkinef34a022018-06-22 19:36:13 +0400189 if ((common.validInputParam('KUBERNETES_HYPERKUBE_IMAGE')) && (common.validInputParam('KUBERNETES_PAUSE_IMAGE'))) {
190 overrideKubernetesImage(pepperEnv)
191 }
192
193 /*
194 * Execute update
195 */
196 if (updates.contains("ctl")) {
197 def target = CTL_TARGET
198
199 if (PER_NODE.toBoolean()) {
200 def targetHosts = salt.getMinionsSorted(pepperEnv, target)
201
202 for (t in targetHosts) {
Victor Ryzhenkin42e4b382018-09-11 17:57:56 +0400203 if (SIMPLE_UPGRADE.toBoolean()) {
204 performKubernetesControlUpdate(pepperEnv, t)
205 } else {
206 cordonNode(pepperEnv, t)
207 drainNode(pepperEnv, t)
208 regenerateCerts(pepperEnv, t)
209 if (UPGRADE_DOCKER.toBoolean()) {
210 upgradeDocker(pepperEnv, t)
211 }
212 performKubernetesControlUpdate(pepperEnv, t)
Victor Ryzhenkinfd9677f2018-10-16 16:14:40 +0400213 updateAddonManager(pepperEnv, t)
Victor Ryzhenkin42e4b382018-09-11 17:57:56 +0400214 uncordonNode(pepperEnv, t)
215 }
Victor Ryzhenkinef34a022018-06-22 19:36:13 +0400216 }
217 } else {
218 performKubernetesControlUpdate(pepperEnv, target)
219 }
Victor Ryzhenkinfd9677f2018-10-16 16:14:40 +0400220 if (!SIMPLE_UPGRADE.toBoolean()) {
221 // Addons upgrade should be performed after all nodes will upgraded
222 updateAddons(pepperEnv, target)
223 // Wait for 90 sec for addons reconciling
224 sleep(90)
225 }
Victor Ryzhenkinef34a022018-06-22 19:36:13 +0400226 }
227
228 if (updates.contains("cmp")) {
229 def target = CMP_TARGET
230
231 if (PER_NODE.toBoolean()) {
232 def targetHosts = salt.getMinionsSorted(pepperEnv, target)
233
234 for (t in targetHosts) {
Victor Ryzhenkin42e4b382018-09-11 17:57:56 +0400235 if (SIMPLE_UPGRADE.toBoolean()) {
236 performKubernetesComputeUpdate(pepperEnv, t)
237 } else {
238 cordonNode(pepperEnv, t)
239 drainNode(pepperEnv, t)
240 regenerateCerts(pepperEnv, t)
241 if (UPGRADE_DOCKER.toBoolean()) {
242 upgradeDocker(pepperEnv, t)
243 }
244 performKubernetesComputeUpdate(pepperEnv, t)
245 uncordonNode(pepperEnv, t)
246 }
Victor Ryzhenkinef34a022018-06-22 19:36:13 +0400247 }
248 } else {
249 performKubernetesComputeUpdate(pepperEnv, target)
250 }
251 }
Victor Ryzhenkinae22a5a2018-10-12 15:52:27 +0400252
253 if (CONFORMANCE_RUN_AFTER.toBoolean()) {
254 def target = CTL_TARGET
255 def mcp_repo = ARTIFACTORY_URL
256 def k8s_api = TEST_K8S_API_SERVER
257 firstTarget = salt.getFirstMinion(pepperEnv, target)
258 executeConformance(pepperEnv, firstTarget, k8s_api, mcp_repo)
259 }
Victor Ryzhenkinef34a022018-06-22 19:36:13 +0400260 } catch (Throwable e) {
261 // If there was an error or exception thrown, the build failed
262 currentBuild.result = "FAILURE"
263 currentBuild.description = currentBuild.description ? e.message + " " + currentBuild.description : e.message
264 throw e
265 }
266 }
267}