Merge ~pwlars/testflinger-agent:run-test-phases into testflinger-agent:master

Proposed by Paul Larson
Status: Merged
Approved by: Maciej Kisielewski
Approved revision: 5d862227cd7fb49fd063f6027195d29b6ce0ae92
Merged at revision: 5d862227cd7fb49fd063f6027195d29b6ce0ae92
Proposed branch: ~pwlars/testflinger-agent:run-test-phases
Merge into: testflinger-agent:master
Diff against target: 142 lines (+100/-0)
2 files modified
testflinger_agent/client.py (+40/-0)
testflinger_agent/tests/test_client.py (+60/-0)
Reviewer Review Type Date Requested Status
Maciej Kisielewski (community) Approve
Review via email: mp+303191@code.launchpad.net

Description of the change

This is the part that does the main execution. Create the temporary execution directory, dump the json job data to testflinger.json, then run the setup, provision, and test phases if they are defined in the config.

To post a comment you must log in.
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

Everything works beautifully! +1

Turbo-minor nitpick, tests use uuid1, while TF uses uuid4 (this doesn't matter in those tests)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/testflinger_agent/client.py b/testflinger_agent/client.py
2index 3322ed3..c184d2e 100644
3--- a/testflinger_agent/client.py
4+++ b/testflinger_agent/client.py
5@@ -13,7 +13,11 @@
6 # along with this program. If not, see <http://www.gnu.org/licenses/>.
7
8 import logging
9+import json
10+import os
11 import requests
12+import subprocess
13+import sys
14 import time
15
16 from urllib.parse import urljoin
17@@ -25,10 +29,20 @@ logger = logging.getLogger()
18
19 def process_jobs():
20 """Coordinate checking for new jobs and handling them if they exists"""
21+ TEST_PHASES = ['setup', 'provision', 'test']
22 job_data = check_jobs()
23 if not job_data:
24 return
25 logger.info("Starting job %s", job_data.get('job_id'))
26+ rundir = os.path.join(testflinger_agent.config.get('execution_basedir'),
27+ job_data.get('job_id'))
28+ os.makedirs(rundir)
29+ # Dump the job data to testflinger.json in our execution directory
30+ with open(os.path.join(rundir, 'testflinger.json'), 'w') as f:
31+ json.dump(job_data, f)
32+
33+ for phase in TEST_PHASES:
34+ run_test_phase(phase, rundir)
35
36
37 def check_jobs():
38@@ -53,3 +67,29 @@ def check_jobs():
39 logger.exception(e)
40 # Wait a little extra before trying again
41 time.sleep(60)
42+
43+
44+def run_test_phase(phase, rundir):
45+ cmd = testflinger_agent.config.get(phase+'_command')
46+ if not cmd:
47+ return
48+ phase_log = os.path.join(rundir, phase+'.log')
49+ logger.info('Running %s_command: %s' % (phase, cmd))
50+ run_with_log(cmd, phase_log, rundir)
51+
52+
53+def run_with_log(cmd, logfile, cwd=None):
54+ with open(logfile, 'w') as f:
55+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
56+ stderr=subprocess.STDOUT,
57+ shell=True, cwd=cwd)
58+ while process.poll() is None:
59+ line = process.stdout.readline()
60+ if line:
61+ sys.stdout.write(line.decode())
62+ f.write(line.decode())
63+ f.flush()
64+ line = process.stdout.read()
65+ if line:
66+ sys.stdout.write(line.decode())
67+ f.write(line.decode())
68diff --git a/testflinger_agent/tests/test_client.py b/testflinger_agent/tests/test_client.py
69index 26caaaa..940fade 100644
70--- a/testflinger_agent/tests/test_client.py
71+++ b/testflinger_agent/tests/test_client.py
72@@ -14,6 +14,9 @@
73
74 import json
75 import requests
76+import tempfile
77+import os
78+import shutil
79 import uuid
80
81 import testflinger_agent
82@@ -38,3 +41,60 @@ class ClientTest(TestCase):
83 mock_requests_get.return_value = fake_response
84 job_data = testflinger_agent.client.check_jobs()
85 self.assertEqual(job_data, fake_job_data)
86+
87+
88+class ClientRunTests(TestCase):
89+ def setUp(self):
90+ self.tmpdir = tempfile.mkdtemp()
91+ testflinger_agent.config = {'agent_id': 'test01',
92+ 'polling_interval': '2',
93+ 'server_address': '127.0.0.1:8000',
94+ 'job_queues': ['test'],
95+ 'execution_basedir': self.tmpdir,
96+ 'logging_basedir': self.tmpdir,
97+ }
98+
99+ def tearDown(self):
100+ shutil.rmtree(self.tmpdir)
101+
102+ @patch('requests.get')
103+ def test_check_and_run_setup(self, mock_requests_get):
104+ testflinger_agent.config['setup_command'] = 'echo setup1'
105+ fake_job_data = {'job_id': str(uuid.uuid1()),
106+ 'job_queue': 'test'}
107+ fake_response = requests.Response()
108+ fake_response._content = json.dumps(fake_job_data).encode()
109+ mock_requests_get.return_value = fake_response
110+ testflinger_agent.client.process_jobs()
111+ setuplog = open(os.path.join(self.tmpdir,
112+ fake_job_data.get('job_id'),
113+ 'setup.log')).read()
114+ self.assertEqual('setup1', setuplog.strip())
115+
116+ @patch('requests.get')
117+ def test_check_and_run_provision(self, mock_requests_get):
118+ testflinger_agent.config['provision_command'] = 'echo provision1'
119+ fake_job_data = {'job_id': str(uuid.uuid1()),
120+ 'job_queue': 'test'}
121+ fake_response = requests.Response()
122+ fake_response._content = json.dumps(fake_job_data).encode()
123+ mock_requests_get.return_value = fake_response
124+ testflinger_agent.client.process_jobs()
125+ provisionlog = open(os.path.join(self.tmpdir,
126+ fake_job_data.get('job_id'),
127+ 'provision.log')).read()
128+ self.assertEqual('provision1', provisionlog.strip())
129+
130+ @patch('requests.get')
131+ def test_check_and_run_test(self, mock_requests_get):
132+ testflinger_agent.config['test_command'] = 'echo test1'
133+ fake_job_data = {'job_id': str(uuid.uuid1()),
134+ 'job_queue': 'test'}
135+ fake_response = requests.Response()
136+ fake_response._content = json.dumps(fake_job_data).encode()
137+ mock_requests_get.return_value = fake_response
138+ testflinger_agent.client.process_jobs()
139+ testlog = open(os.path.join(self.tmpdir,
140+ fake_job_data.get('job_id'),
141+ 'test.log')).read()
142+ self.assertEqual('test1', testlog.strip())

Subscribers

People subscribed via source and target branches