blob: 776f7c8037f8a91a60e4d641d5f3e2741843a36f [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):
118 '''Create a test user 'DT_test_user' in openldap,
119 add the user to admin group, login using the user to Jenkins.
120 Add the user to devops group in Gerrit and then login to Gerrit,
121 using test_user credentials. Finally, delete the user from admin
122 group and openldap
123 '''
124 ldap_password = get_password(local_salt_client,'openldap:client')
125 #Check that ldap_password is exists, otherwise skip test
126 if not ldap_password:
127 pytest.skip("Openldap service or openldap:client pillar \
128 are not found on this environment.")
129 ldap_port = local_salt_client.cmd(
130 'I@openldap:client and not I@salt:master',
131 'pillar.get',
132 ['_param:haproxy_openldap_bind_port'],
133 expr_form='compound').values()[0]
134 ldap_address = local_salt_client.cmd(
135 'I@openldap:client and not I@salt:master',
136 'pillar.get',
137 ['_param:haproxy_openldap_bind_host'],
138 expr_form='compound').values()[0]
139 ldap_dc = local_salt_client.cmd(
140 'openldap:client',
141 'pillar.get',
142 ['_param:openldap_dn'],
143 expr_form='pillar').values()[0]
144 ldap_con_admin = local_salt_client.cmd(
145 'openldap:client',
146 'pillar.get',
147 ['openldap:client:server:auth:user'],
148 expr_form='pillar').values()[0]
149 ldap_url = 'ldap://{0}:{1}'.format(ldap_address,ldap_port)
150 ldap_error = ''
151 ldap_result = ''
152 gerrit_result = ''
153 gerrit_error = ''
154 jenkins_error = ''
155 #Test user's CN
156 test_user_name = 'DT_test_user'
157 test_user = 'cn={0},ou=people,{1}'.format(test_user_name,ldap_dc)
158 #Admins group CN
159 admin_gr_dn = 'cn=admins,ou=groups,{0}'.format(ldap_dc)
160 #List of attributes for test user
161 attrs = {}
162 attrs['objectclass'] = ['organizationalRole','simpleSecurityObject','shadowAccount']
163 attrs['cn'] = test_user_name
164 attrs['uid'] = test_user_name
165 attrs['userPassword'] = 'aSecretPassw'
166 attrs['description'] = 'Test user for CVP DT test'
167 searchFilter = 'cn={0}'.format(test_user_name)
168 #Get a test job name from config
169 config = utils.get_configuration()
mkraynov058ee122018-11-30 13:15:49 +0400170 jenkins_cvp_job = config['jenkins_cvp_job']
mkraynov360c30d2018-09-27 17:02:45 +0400171 #Open connection to ldap and creating test user in admins group
172 try:
173 ldap_server = ldap.initialize(ldap_url)
174 ldap_server.simple_bind_s(ldap_con_admin,ldap_password)
175 ldif = modlist.addModlist(attrs)
176 ldap_server.add_s(test_user,ldif)
177 ldap_server.modify_s(admin_gr_dn,[(ldap.MOD_ADD, 'memberUid', [test_user_name],)],)
178 #Check search test user in LDAP
179 searchScope = ldap.SCOPE_SUBTREE
180 ldap_result = ldap_server.search_s(ldap_dc, searchScope, searchFilter)
181 except ldap.LDAPError, e:
182 ldap_error = e
183 try:
184 #Check connection between Jenkins and LDAP
185 jenkins_server = join_to_jenkins(local_salt_client,test_user_name,'aSecretPassw')
mkraynov058ee122018-11-30 13:15:49 +0400186 jenkins_version = jenkins_server.get_job_name(jenkins_cvp_job)
mkraynov360c30d2018-09-27 17:02:45 +0400187 #Check connection between Gerrit and LDAP
188 gerrit_server = join_to_gerrit(local_salt_client,'admin',ldap_password)
189 gerrit_check = gerrit_server.get("/changes/?q=owner:self%20status:open")
190 #Add test user to devops-contrib group in Gerrit and check login
191 _link = "/groups/devops-contrib/members/{0}".format(test_user_name)
192 gerrit_add_user = gerrit_server.put(_link)
193 gerrit_server = join_to_gerrit(local_salt_client,test_user_name,'aSecretPassw')
194 gerrit_result = gerrit_server.get("/changes/?q=owner:self%20status:open")
195 except HTTPError, e:
196 gerrit_error = e
197 except jenkins.JenkinsException, e:
198 jenkins_error = e
199 finally:
200 ldap_server.modify_s(admin_gr_dn,[(ldap.MOD_DELETE, 'memberUid', [test_user_name],)],)
201 ldap_server.delete_s(test_user)
202 ldap_server.unbind_s()
203 assert ldap_error == '', \
204 '''Something is wrong with connection to LDAP:
205 {0}'''.format(e)
206 assert jenkins_error == '', \
207 '''Connection to Jenkins was not established:
208 {0}'''.format(e)
209 assert gerrit_error == '', \
210 '''Connection to Gerrit was not established:
211 {0}'''.format(e)
212 assert ldap_result !=[], \
213 '''Test user was not found'''
Mikhail Kraynov351e8412018-10-04 18:27:44 +0400214
Hanna Arhipovae792be52019-02-13 13:28:11 +0200215
Mikhail Kraynov351e8412018-10-04 18:27:44 +0400216def test_drivetrain_jenkins_job(local_salt_client):
217 jenkins_password = get_password(local_salt_client,'jenkins:client')
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400218 server = join_to_jenkins(local_salt_client,'admin',jenkins_password)
219 #Getting Jenkins test job name from configuration
220 config = utils.get_configuration()
221 jenkins_test_job = config['jenkins_test_job']
mkraynov058ee122018-11-30 13:15:49 +0400222 if not server.get_job_name(jenkins_test_job):
223 server.create_job(jenkins_test_job, jenkins.EMPTY_CONFIG_XML)
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400224 if server.get_job_name(jenkins_test_job):
225 next_build_num = server.get_job_info(jenkins_test_job)['nextBuildNumber']
226 #If this is first build number skip building check
227 if next_build_num != 1:
228 #Check that test job is not running at this moment,
229 #Otherwise skip the test
230 last_build_num = server.get_job_info(jenkins_test_job)['lastBuild'].get('number')
231 last_build_status = server.get_build_info(jenkins_test_job,last_build_num)['building']
232 if last_build_status:
233 pytest.skip("Test job {0} is already running").format(jenkins_test_job)
mkraynov058ee122018-11-30 13:15:49 +0400234 server.build_job(jenkins_test_job)
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400235 timeout = 0
236 #Use job status True by default to exclude timeout between build job and start job.
237 job_status = True
238 while job_status and ( timeout < 180 ):
Mikhail Kraynov351e8412018-10-04 18:27:44 +0400239 time.sleep(10)
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400240 timeout += 10
241 job_status = server.get_build_info(jenkins_test_job,next_build_num)['building']
242 job_result = server.get_build_info(jenkins_test_job,next_build_num)['result']
243 else:
244 pytest.skip("The job {0} was not found").format(test_job_name)
245 assert job_result == 'SUCCESS', \
246 '''Test job '{0}' build was not successfull or timeout is too small
247 '''.format(jenkins_test_job)
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500248
Hanna Arhipovaf2660bd2019-02-08 17:25:39 +0200249
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500250def test_drivetrain_services_replicas(local_salt_client):
Hanna Arhipovaf2660bd2019-02-08 17:25:39 +0200251 # TODO: replace with rerunfalures plugin
252 for _ in range(4):
253 salt_output = local_salt_client.cmd(
254 'I@gerrit:client',
255 'cmd.run',
256 ['docker service ls'],
257 expr_form='compound')
258 wrong_items = []
259 for line in salt_output[salt_output.keys()[0]].split('\n'):
260 if line[line.find('/') - 1] != line[line.find('/') + 1] \
261 and 'replicated' in line:
262 wrong_items.append(line)
263 if len(wrong_items) == 0:
264 break
265 else:
266 print('''Some DriveTrain services doesn't have expected number of replicas:
267 {}\n'''.format(json.dumps(wrong_items, indent=4)))
268 time.sleep(5)
269 assert len(wrong_items) == 0
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500270
271
272def test_drivetrain_components_and_versions(local_salt_client):
273 config = utils.get_configuration()
Oleksii Zhurba7b705372019-01-15 18:40:29 -0600274 if not config['drivetrain_version']:
Hanna Arhipovae792be52019-02-13 13:28:11 +0200275 expected_version = \
Oleksii Zhurba7b705372019-01-15 18:40:29 -0600276 local_salt_client.cmd(
277 'I@salt:master',
278 'pillar.get',
279 ['_param:mcp_version'],
280 expr_form='compound').values()[0] or \
281 local_salt_client.cmd(
282 'I@salt:master',
283 'pillar.get',
284 ['_param:apt_mk_version'],
285 expr_form='compound').values()[0]
Hanna Arhipovae792be52019-02-13 13:28:11 +0200286 if not expected_version:
Oleksii Zhurba7b705372019-01-15 18:40:29 -0600287 pytest.skip("drivetrain_version is not defined. Skipping")
288 else:
Hanna Arhipovae792be52019-02-13 13:28:11 +0200289 expected_version = config['drivetrain_version']
290 table_with_docker_services = local_salt_client.cmd('I@gerrit:client',
291 'cmd.run',
292 ['docker service ls --format "{{.Image}}"'],
293 expr_form='compound')
294 table_from_pillar = local_salt_client.cmd('I@gerrit:client',
295 'pillar.get',
296 ['docker:client:images'],
297 expr_form='compound')
298
299 expected_images = table_from_pillar[table_from_pillar.keys()[0]]
300 actual_images = table_with_docker_services[table_with_docker_services.keys()[0]].split('\n')
301
302 # ---------------- Check that all docker services are found regarding the 'pillar.get docker:client:images' ----
303 not_found_services = list(set(expected_images) - set(actual_images))
304 assert not_found_services.__len__() == 0, \
305 ''' Some DriveTrain components are not found: {}'''.format(json.dumps(not_found_services, indent=4))
306
307 # ---------- Check that all docker services has label that equals to mcp_version (except of external images) ----
308 version_mismatch = [
309 "{image}: expected version - {expected_version}, actual - {version}".format(version=image.split(":")[-1], **locals())
310 for image in actual_images
311 if image.split(":")[-1] != expected_version and "mirantis/external" not in image]
312
313 assert version_mismatch.__len__() == 0, \
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500314 '''Version mismatch found:
315 {}'''.format(json.dumps(version_mismatch, indent=4))
316
317
318def test_jenkins_jobs_branch(local_salt_client):
Oleksii Zhurbad52b5fe2019-03-28 11:11:35 -0500319 """ This test compares Jenkins jobs versions
320 collected from the cloud vs collected from pillars.
321 """
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200322 excludes = ['upgrade-mcp-release', 'deploy-update-salt']
323
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500324 config = utils.get_configuration()
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200325 drivetrain_version = config.get('drivetrain_version', '')
326 if not drivetrain_version:
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500327 pytest.skip("drivetrain_version is not defined. Skipping")
Hanna Arhipovae792be52019-02-13 13:28:11 +0200328 jenkins_password = get_password(local_salt_client, 'jenkins:client')
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500329 version_mismatch = []
Hanna Arhipovae792be52019-02-13 13:28:11 +0200330 server = join_to_jenkins(local_salt_client, 'admin', jenkins_password)
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400331 for job_instance in server.get_jobs():
332 job_name = job_instance.get('name')
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200333 if job_name in excludes:
334 continue
335
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400336 job_config = server.get_job_config(job_name)
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500337 xml_data = minidom.parseString(job_config)
338 BranchSpec = xml_data.getElementsByTagName('hudson.plugins.git.BranchSpec')
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200339
340 # We use master branch for pipeline-library in case of 'testing,stable,nighlty' versions
341 # Leave proposed version as is
342 # in other cases we get release/{drivetrain_version} (e.g release/2019.2.0)
343 if drivetrain_version in ['testing','nightly','stable']:
Mikhail Kraynove5cc81b2018-10-03 13:01:06 +0400344 expected_version = 'master'
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200345 else:
Oleksii Zhurbad52b5fe2019-03-28 11:11:35 -0500346 expected_version = local_salt_client.cmd(
347 'I@gerrit:client',
348 'pillar.get',
349 ['jenkins:client:job:{}:scm:branch'.format(job_name)],
350 expr_form='compound').values()[0]
Hanna Arhipova6f34fbb2019-02-08 11:19:41 +0200351
352 if not BranchSpec:
353 print("No BranchSpec has found for {} job".format(job_name))
354 continue
355
356 actual_version = BranchSpec[0].getElementsByTagName('name')[0].childNodes[0].data
357 if (actual_version not in [expected_version, "release/{}".format(drivetrain_version)]):
358 version_mismatch.append("Job {0} has {1} branch."
359 "Expected {2}".format(job_name,
360 actual_version,
361 expected_version))
Oleksii Zhurbaa25984b2018-06-15 15:30:41 -0500362 assert len(version_mismatch) == 0, \
363 '''Some DriveTrain jobs have version/branch mismatch:
364 {}'''.format(json.dumps(version_mismatch, indent=4))