blob: 640d6a7971ee27cd962220639218c8603a6ce044 [file] [log] [blame]
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +04001import jenkins
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -05002from xml.dom import minidom
Hanna Arhipova16e93fb2019-01-23 19:03:01 +02003import utils
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -05004import json
5import pytest
Mikhail Kraynov351e8412018-10-04 18:27:44 +04006import time
7import os
8from pygerrit2 import GerritRestAPI, HTTPBasicAuth
9from requests import HTTPError
10import git
mkraynov360c30d2018-09-27 17:02:45 +040011import ldap
12import ldap.modlist as modlist
Mikhail Kraynov351e8412018-10-04 18:27:44 +040013
Hanna Arhipovae792be52019-02-13 13:28:11 +020014
Mikhail Kraynov351e8412018-10-04 18:27:44 +040015def join_to_gerrit(local_salt_client, gerrit_user, gerrit_password):
16 gerrit_port = local_salt_client.cmd(
17 'I@gerrit:client and not I@salt:master',
18 'pillar.get',
19 ['_param:haproxy_gerrit_bind_port'],
20 expr_form='compound').values()[0]
21 gerrit_address = local_salt_client.cmd(
22 'I@gerrit:client and not I@salt:master',
23 'pillar.get',
24 ['_param:haproxy_gerrit_bind_host'],
25 expr_form='compound').values()[0]
26 url = 'http://{0}:{1}'.format(gerrit_address,gerrit_port)
27 auth = HTTPBasicAuth(gerrit_user, gerrit_password)
28 rest = GerritRestAPI(url=url, auth=auth)
29 return rest
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -050030
Hanna Arhipovae792be52019-02-13 13:28:11 +020031
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +040032def join_to_jenkins(local_salt_client, jenkins_user, jenkins_password):
33 jenkins_port = local_salt_client.cmd(
34 'I@jenkins:client and not I@salt:master',
35 'pillar.get',
36 ['_param:haproxy_jenkins_bind_port'],
37 expr_form='compound').values()[0]
38 jenkins_address = local_salt_client.cmd(
39 'I@jenkins:client and not I@salt:master',
40 'pillar.get',
41 ['_param:haproxy_jenkins_bind_host'],
42 expr_form='compound').values()[0]
43 jenkins_url = 'http://{0}:{1}'.format(jenkins_address,jenkins_port)
44 server = jenkins.Jenkins(jenkins_url, username=jenkins_user, password=jenkins_password)
45 return server
46
Hanna Arhipovae792be52019-02-13 13:28:11 +020047
Mikhail Kraynov351e8412018-10-04 18:27:44 +040048def get_password(local_salt_client,service):
49 password = local_salt_client.cmd(
50 service,
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +040051 'pillar.get',
52 ['_param:openldap_admin_password'],
53 expr_form='pillar').values()[0]
Mikhail Kraynov351e8412018-10-04 18:27:44 +040054 return password
55
Hanna Arhipovae792be52019-02-13 13:28:11 +020056
Mikhail Kraynov351e8412018-10-04 18:27:44 +040057def test_drivetrain_gerrit(local_salt_client):
58 gerrit_password = get_password(local_salt_client,'gerrit:client')
59 gerrit_error = ''
60 current_date = time.strftime("%Y%m%d-%H.%M.%S", time.localtime())
61 test_proj_name = "test-dt-{0}".format(current_date)
62 gerrit_port = local_salt_client.cmd(
63 'I@gerrit:client and not I@salt:master',
64 'pillar.get',
65 ['_param:haproxy_gerrit_bind_port'],
66 expr_form='compound').values()[0]
67 gerrit_address = local_salt_client.cmd(
68 'I@gerrit:client and not I@salt:master',
69 'pillar.get',
70 ['_param:haproxy_gerrit_bind_host'],
71 expr_form='compound').values()[0]
72 try:
73 #Connecting to gerrit and check connection
74 server = join_to_gerrit(local_salt_client,'admin',gerrit_password)
75 gerrit_check = server.get("/changes/?q=owner:self%20status:open")
76 #Check deleteproject plugin and skip test if the plugin is not installed
77 gerrit_plugins = server.get("/plugins/?all")
78 if 'deleteproject' not in gerrit_plugins:
79 pytest.skip("Delete-project plugin is not installed")
80 #Create test project and add description
81 server.put("/projects/"+test_proj_name)
82 server.put("/projects/"+test_proj_name+"/description",json={"description":"Test DriveTrain project","commit_message": "Update the project description"})
83 except HTTPError, e:
84 gerrit_error = e
85 try:
86 #Create test folder and init git
87 repo_dir = os.path.join(os.getcwd(),test_proj_name)
88 file_name = os.path.join(repo_dir, current_date)
89 repo = git.Repo.init(repo_dir)
90 #Add remote url for this git repo
91 origin = repo.create_remote('origin', 'http://admin:{1}@{2}:{3}/{0}.git'.format(test_proj_name,gerrit_password,gerrit_address,gerrit_port))
92 #Add commit-msg hook to automatically add Change-Id to our commit
93 os.system("curl -Lo {0}/.git/hooks/commit-msg 'http://admin:{1}@{2}:{3}/tools/hooks/commit-msg' > /dev/null 2>&1".format(repo_dir,gerrit_password,gerrit_address,gerrit_port))
94 os.system("chmod u+x {0}/.git/hooks/commit-msg".format(repo_dir))
95 #Create a test file
96 f = open(file_name, 'w+')
97 f.write("This is a test file for DriveTrain test")
98 f.close()
99 #Add file to git and commit it to Gerrit for review
100 repo.index.add([file_name])
101 repo.index.commit("This is a test commit for DriveTrain test")
102 repo.git.push("origin", "HEAD:refs/for/master")
103 #Get change id from Gerrit. Set Code-Review +2 and submit this change
104 changes = server.get("/changes/?q=project:{0}".format(test_proj_name))
105 last_change = changes[0].get('change_id')
106 server.post("/changes/{0}/revisions/1/review".format(last_change),json={"message":"All is good","labels":{"Code-Review":"+2"}})
107 server.post("/changes/{0}/submit".format(last_change))
108 except HTTPError, e:
109 gerrit_error = e
110 finally:
111 #Delete test project
112 server.post("/projects/"+test_proj_name+"/deleteproject~delete")
113 assert gerrit_error == '',\
114 'Something is wrong with Gerrit'.format(gerrit_error)
115
Hanna Arhipovae792be52019-02-13 13:28:11 +0200116
mkraynov360c30d2018-09-27 17:02:45 +0400117def test_drivetrain_openldap(local_salt_client):
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300118 """
119 1. Create a test user 'DT_test_user' in openldap
120 2. Add the user to admin group
121 3. Login using the user to Jenkins
122 4. Check that no error occurred
123 5. Add the user to devops group in Gerrit and then login to Gerrit
124 using test_user credentials.
125 6 Start job in jenkins from this user
126 7. Get info from gerrit from this user
127 6. Finally, delete the user from admin
128 group and openldap
129 """
130
131 # TODO split to several test cases. One check - per one test method. Make the login process in fixture
mkraynov360c30d2018-09-27 17:02:45 +0400132 ldap_password = get_password(local_salt_client,'openldap:client')
133 #Check that ldap_password is exists, otherwise skip test
134 if not ldap_password:
135 pytest.skip("Openldap service or openldap:client pillar \
136 are not found on this environment.")
137 ldap_port = local_salt_client.cmd(
138 'I@openldap:client and not I@salt:master',
139 'pillar.get',
140 ['_param:haproxy_openldap_bind_port'],
141 expr_form='compound').values()[0]
142 ldap_address = local_salt_client.cmd(
143 'I@openldap:client and not I@salt:master',
144 'pillar.get',
145 ['_param:haproxy_openldap_bind_host'],
146 expr_form='compound').values()[0]
147 ldap_dc = local_salt_client.cmd(
148 'openldap:client',
149 'pillar.get',
150 ['_param:openldap_dn'],
151 expr_form='pillar').values()[0]
152 ldap_con_admin = local_salt_client.cmd(
153 'openldap:client',
154 'pillar.get',
155 ['openldap:client:server:auth:user'],
156 expr_form='pillar').values()[0]
157 ldap_url = 'ldap://{0}:{1}'.format(ldap_address,ldap_port)
158 ldap_error = ''
159 ldap_result = ''
160 gerrit_result = ''
161 gerrit_error = ''
162 jenkins_error = ''
163 #Test user's CN
164 test_user_name = 'DT_test_user'
165 test_user = 'cn={0},ou=people,{1}'.format(test_user_name,ldap_dc)
166 #Admins group CN
167 admin_gr_dn = 'cn=admins,ou=groups,{0}'.format(ldap_dc)
168 #List of attributes for test user
169 attrs = {}
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300170 attrs['objectclass'] = ['organizationalRole', 'simpleSecurityObject', 'shadowAccount']
mkraynov360c30d2018-09-27 17:02:45 +0400171 attrs['cn'] = test_user_name
172 attrs['uid'] = test_user_name
173 attrs['userPassword'] = 'aSecretPassw'
174 attrs['description'] = 'Test user for CVP DT test'
175 searchFilter = 'cn={0}'.format(test_user_name)
176 #Get a test job name from config
177 config = utils.get_configuration()
mkraynov058ee122018-11-30 13:15:49 +0400178 jenkins_cvp_job = config['jenkins_cvp_job']
mkraynov360c30d2018-09-27 17:02:45 +0400179 #Open connection to ldap and creating test user in admins group
180 try:
181 ldap_server = ldap.initialize(ldap_url)
182 ldap_server.simple_bind_s(ldap_con_admin,ldap_password)
183 ldif = modlist.addModlist(attrs)
184 ldap_server.add_s(test_user,ldif)
185 ldap_server.modify_s(admin_gr_dn,[(ldap.MOD_ADD, 'memberUid', [test_user_name],)],)
186 #Check search test user in LDAP
187 searchScope = ldap.SCOPE_SUBTREE
188 ldap_result = ldap_server.search_s(ldap_dc, searchScope, searchFilter)
189 except ldap.LDAPError, e:
190 ldap_error = e
191 try:
192 #Check connection between Jenkins and LDAP
193 jenkins_server = join_to_jenkins(local_salt_client,test_user_name,'aSecretPassw')
mkraynov058ee122018-11-30 13:15:49 +0400194 jenkins_version = jenkins_server.get_job_name(jenkins_cvp_job)
mkraynov360c30d2018-09-27 17:02:45 +0400195 #Check connection between Gerrit and LDAP
196 gerrit_server = join_to_gerrit(local_salt_client,'admin',ldap_password)
197 gerrit_check = gerrit_server.get("/changes/?q=owner:self%20status:open")
198 #Add test user to devops-contrib group in Gerrit and check login
199 _link = "/groups/devops-contrib/members/{0}".format(test_user_name)
200 gerrit_add_user = gerrit_server.put(_link)
201 gerrit_server = join_to_gerrit(local_salt_client,test_user_name,'aSecretPassw')
202 gerrit_result = gerrit_server.get("/changes/?q=owner:self%20status:open")
203 except HTTPError, e:
204 gerrit_error = e
205 except jenkins.JenkinsException, e:
206 jenkins_error = e
207 finally:
208 ldap_server.modify_s(admin_gr_dn,[(ldap.MOD_DELETE, 'memberUid', [test_user_name],)],)
209 ldap_server.delete_s(test_user)
210 ldap_server.unbind_s()
211 assert ldap_error == '', \
212 '''Something is wrong with connection to LDAP:
213 {0}'''.format(e)
214 assert jenkins_error == '', \
215 '''Connection to Jenkins was not established:
216 {0}'''.format(e)
217 assert gerrit_error == '', \
218 '''Connection to Gerrit was not established:
219 {0}'''.format(e)
220 assert ldap_result !=[], \
221 '''Test user was not found'''
Mikhail Kraynov351e8412018-10-04 18:27:44 +0400222
Hanna Arhipovae792be52019-02-13 13:28:11 +0200223
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500224def test_drivetrain_services_replicas(local_salt_client):
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300225 """
226 # Execute ` salt -C 'I@gerrit:client' cmd.run 'docker service ls'` command to get info for each docker service like that:
227 "x5nzktxsdlm6 jenkins_slave02 replicated 0/1 docker-prod-local.artifactory.mirantis.com/mirantis/cicd/jnlp-slave:2019.2.0 "
228 # Check that each service has all replicas
229 """
Hanna Arhipovaf2660bd2019-02-08 17:25:39 +0200230 # TODO: replace with rerunfalures plugin
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300231 wrong_items = []
Hanna Arhipovaf2660bd2019-02-08 17:25:39 +0200232 for _ in range(4):
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300233 docker_services_by_nodes = local_salt_client.cmd(
Hanna Arhipovaf2660bd2019-02-08 17:25:39 +0200234 'I@gerrit:client',
235 'cmd.run',
236 ['docker service ls'],
237 expr_form='compound')
238 wrong_items = []
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300239 for line in docker_services_by_nodes[docker_services_by_nodes.keys()[0]].split('\n'):
Hanna Arhipovaf2660bd2019-02-08 17:25:39 +0200240 if line[line.find('/') - 1] != line[line.find('/') + 1] \
241 and 'replicated' in line:
242 wrong_items.append(line)
243 if len(wrong_items) == 0:
244 break
245 else:
246 print('''Some DriveTrain services doesn't have expected number of replicas:
247 {}\n'''.format(json.dumps(wrong_items, indent=4)))
248 time.sleep(5)
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300249 assert len(wrong_items) == 0
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500250
251
252def test_drivetrain_components_and_versions(local_salt_client):
Oleksii Zhurbab91c3142019-03-26 16:49:44 -0500253 """
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300254 1. Execute command `docker service ls --format "{{.Image}}"'` on the 'I@gerrit:client' target
255 2. Execute ` salt -C 'I@gerrit:client' pillar.get docker:client:images`
256 3. Check that list of images from step 1 is the same as a list from the step2
257 4. Check that all docker services has label that equals to mcp_version
258
259 """
260 config = utils.get_configuration()
261 if not config['drivetrain_version']:
262 expected_version = \
263 local_salt_client.cmd(
264 'I@salt:master',
265 'pillar.get',
266 ['_param:mcp_version'],
267 expr_form='compound').values()[0] or \
268 local_salt_client.cmd(
269 'I@salt:master',
270 'pillar.get',
271 ['_param:apt_mk_version'],
272 expr_form='compound').values()[0]
273 if not expected_version:
274 pytest.skip("drivetrain_version is not defined. Skipping")
275 else:
276 expected_version = config['drivetrain_version']
Hanna Arhipovae792be52019-02-13 13:28:11 +0200277 table_with_docker_services = local_salt_client.cmd('I@gerrit:client',
278 'cmd.run',
279 ['docker service ls --format "{{.Image}}"'],
280 expr_form='compound')
281 table_from_pillar = local_salt_client.cmd('I@gerrit:client',
282 'pillar.get',
283 ['docker:client:images'],
284 expr_form='compound')
285
Oleksii Zhurbab91c3142019-03-26 16:49:44 -0500286 mismatch = {}
287 actual_images = {}
288 for image in set(table_with_docker_services[table_with_docker_services.keys()[0]].split('\n')):
289 actual_images[image.split(":")[0]] = image.split(":")[-1]
290 for image in set(table_from_pillar[table_from_pillar.keys()[0]]):
291 im_name = image.split(":")[0]
292 if im_name not in actual_images:
293 mismatch[im_name] = 'not found on env'
294 elif image.split(":")[-1] != actual_images[im_name]:
295 mismatch[im_name] = 'has {actual} version instead of {expected}'.format(
296 actual=actual_images[im_name], expected=image.split(":")[-1])
297 assert len(mismatch) == 0, \
298 '''Some DriveTrain components do not have expected versions:
299 {}'''.format(json.dumps(mismatch, indent=4))
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500300
301
302def test_jenkins_jobs_branch(local_salt_client):
Oleksii Zhurbad52b5fe2019-03-28 11:11:35 -0500303 """ This test compares Jenkins jobs versions
304 collected from the cloud vs collected from pillars.
305 """
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200306 excludes = ['upgrade-mcp-release', 'deploy-update-salt']
307
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500308 config = utils.get_configuration()
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200309 drivetrain_version = config.get('drivetrain_version', '')
310 if not drivetrain_version:
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500311 pytest.skip("drivetrain_version is not defined. Skipping")
Hanna Arhipovae792be52019-02-13 13:28:11 +0200312 jenkins_password = get_password(local_salt_client, 'jenkins:client')
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500313 version_mismatch = []
Hanna Arhipovae792be52019-02-13 13:28:11 +0200314 server = join_to_jenkins(local_salt_client, 'admin', jenkins_password)
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400315 for job_instance in server.get_jobs():
316 job_name = job_instance.get('name')
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200317 if job_name in excludes:
318 continue
319
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400320 job_config = server.get_job_config(job_name)
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500321 xml_data = minidom.parseString(job_config)
322 BranchSpec = xml_data.getElementsByTagName('hudson.plugins.git.BranchSpec')
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200323
324 # We use master branch for pipeline-library in case of 'testing,stable,nighlty' versions
325 # Leave proposed version as is
326 # in other cases we get release/{drivetrain_version} (e.g release/2019.2.0)
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300327 if drivetrain_version in ['testing', 'nightly', 'stable']:
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400328 expected_version = 'master'
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200329 else:
Oleksii Zhurbad52b5fe2019-03-28 11:11:35 -0500330 expected_version = local_salt_client.cmd(
331 'I@gerrit:client',
332 'pillar.get',
333 ['jenkins:client:job:{}:scm:branch'.format(job_name)],
334 expr_form='compound').values()[0]
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200335
336 if not BranchSpec:
337 print("No BranchSpec has found for {} job".format(job_name))
338 continue
339
340 actual_version = BranchSpec[0].getElementsByTagName('name')[0].childNodes[0].data
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300341 if actual_version not in [expected_version, "release/{}".format(drivetrain_version)]:
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200342 version_mismatch.append("Job {0} has {1} branch."
343 "Expected {2}".format(job_name,
344 actual_version,
345 expected_version))
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500346 assert len(version_mismatch) == 0, \
347 '''Some DriveTrain jobs have version/branch mismatch:
348 {}'''.format(json.dumps(version_mismatch, indent=4))
Hanna Arhipova16a8f412019-04-08 17:10:38 +0300349
350
351def test_drivetrain_jenkins_job(local_salt_client):
352 """
353 # Login to Jenkins on jenkins:client
354 # Read the name of jobs from configuration 'jenkins_test_job'
355 # Start job
356 # Wait till the job completed
357 # Check that job has completed with "SUCCESS" result
358 """
359 job_result = None
360
361 jenkins_password = get_password(local_salt_client, 'jenkins:client')
362 server = join_to_jenkins(local_salt_client, 'admin', jenkins_password)
363 # Getting Jenkins test job name from configuration
364 config = utils.get_configuration()
365 jenkins_test_job = config['jenkins_test_job']
366 if not server.get_job_name(jenkins_test_job):
367 server.create_job(jenkins_test_job, jenkins.EMPTY_CONFIG_XML)
368 if server.get_job_name(jenkins_test_job):
369 next_build_num = server.get_job_info(jenkins_test_job)['nextBuildNumber']
370 # If this is first build number skip building check
371 if next_build_num != 1:
372 # Check that test job is not running at this moment,
373 # Otherwise skip the test
374 last_build_num = server.get_job_info(jenkins_test_job)['lastBuild'].get('number')
375 last_build_status = server.get_build_info(jenkins_test_job, last_build_num)['building']
376 if last_build_status:
377 pytest.skip("Test job {0} is already running").format(jenkins_test_job)
378 server.build_job(jenkins_test_job)
379 timeout = 0
380 # Use job status True by default to exclude timeout between build job and start job.
381 job_status = True
382 while job_status and (timeout < 180):
383 time.sleep(10)
384 timeout += 10
385 job_status = server.get_build_info(jenkins_test_job, next_build_num)['building']
386 job_result = server.get_build_info(jenkins_test_job, next_build_num)['result']
387 else:
388 pytest.skip("The job {0} was not found").format(jenkins_test_job)
389 assert job_result == 'SUCCESS', \
390 '''Test job '{0}' build was not successful or timeout is too small
391 '''.format(jenkins_test_job)