def test_job_graph_disallows_multiple_jobs_with_same_name(self): graph = model.JobGraph() job1 = model.Job('job') job2 = model.Job('job') graph.addJob(job1) with testtools.ExpectedException(Exception, "Job job already added"): graph.addJob(job2)
def test_node_request(self): # Test a simple node request nodeset = model.NodeSet() nodeset.addNode(model.Node(['controller', 'foo'], 'ubuntu-xenial')) nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial')) job = model.Job('testjob') job.nodeset = nodeset request = self.nodepool.requestNodes(None, job, 0) self.waitForRequests() self.assertEqual(len(self.provisioned_requests), 1) self.assertEqual(request.state, 'fulfilled') # Accept the nodes self.nodepool.acceptNodes(request, request.id) nodeset = request.nodeset for node in nodeset.getNodes(): self.assertIsNotNone(node.lock) self.assertEqual(node.state, 'ready') # Mark the nodes in use self.nodepool.useNodeSet(nodeset) for node in nodeset.getNodes(): self.assertEqual(node.state, 'in-use') # Return the nodes self.nodepool.returnNodeSet(nodeset) for node in nodeset.getNodes(): self.assertIsNone(node.lock) self.assertEqual(node.state, 'used')
def job(self): job = model.Job('job') job.skip_if_matcher = cm.MatchAll([ cm.ProjectMatcher('^project$'), cm.MatchAllFiles([cm.FileMatcher('^docs/.*$')]), ]) return job
def test_job_graph_allows_soft_dependencies(self): parent = model.Job('parent') child = model.Job('child') child.dependencies = frozenset( [model.JobDependency(parent.name, True)]) # With the parent graph = model.JobGraph() graph.addJob(parent) graph.addJob(child) self.assertEqual(graph.getParentJobsRecursively(child.name), [parent]) # Skip the parent graph = model.JobGraph() graph.addJob(child) self.assertEqual(graph.getParentJobsRecursively(child.name), [])
def test_node_request(self): # Test a simple node request nodeset = model.NodeSet() nodeset.addNode(model.Node(['controller'], 'fake-label')) job = model.Job('testjob') job.nodeset = nodeset request = self.nodepool.requestNodes(None, job, 0) self.waitForRequests() self.assertEqual(len(self.provisioned_requests), 1) self.assertEqual(request.state, model.STATE_FULFILLED) # Accept the nodes self.nodepool.acceptNodes(request, request.id) nodeset = request.nodeset for node in nodeset.getNodes(): self.assertIsNotNone(node.lock) self.assertEqual(node.state, model.STATE_READY) # Mark the nodes in use self.nodepool.useNodeSet(nodeset) for node in nodeset.getNodes(): self.assertEqual(node.state, model.STATE_IN_USE) # Return the nodes self.nodepool.returnNodeSet(nodeset) for node in nodeset.getNodes(): self.assertIsNone(node.lock) self.assertEqual(node.state, model.STATE_USED)
def test_job_variants(self): # This simulates freezing a job. secrets = ['foo'] py27_pre = model.PlaybookContext(self.context, 'py27-pre', [], secrets) py27_run = model.PlaybookContext(self.context, 'py27-run', [], secrets) py27_post = model.PlaybookContext(self.context, 'py27-post', [], secrets) py27 = model.Job('py27') py27.timeout = 30 py27.pre_run = [py27_pre] py27.run = [py27_run] py27.post_run = [py27_post] job = py27.copy() self.assertEqual(30, job.timeout) # Apply the diablo variant diablo = model.Job('py27') diablo.timeout = 40 job.applyVariant(diablo, self.layout) self.assertEqual(40, job.timeout) self.assertEqual(['py27-pre'], [x.path for x in job.pre_run]) self.assertEqual(['py27-run'], [x.path for x in job.run]) self.assertEqual(['py27-post'], [x.path for x in job.post_run]) self.assertEqual(secrets, job.pre_run[0].secrets) self.assertEqual(secrets, job.run[0].secrets) self.assertEqual(secrets, job.post_run[0].secrets) # Set the job to final for the following checks job.final = True self.assertTrue(job.voting) good_final = model.Job('py27') good_final.voting = False job.applyVariant(good_final, self.layout) self.assertFalse(job.voting) bad_final = model.Job('py27') bad_final.timeout = 600 with testtools.ExpectedException(Exception, "Unable to modify final job"): job.applyVariant(bad_final, self.layout)
def test_invalid_node_request(self): # Test requests with an invalid node type fail nodeset = model.NodeSet() nodeset.addNode(model.Node(['controller'], 'invalid-label')) job = model.Job('testjob') job.nodeset = nodeset request = self.nodepool.requestNodes(None, job, 0) self.waitForRequests() self.assertEqual(len(self.provisioned_requests), 1) self.assertEqual(request.state, model.STATE_FAILED)
def test_job_graph_allows_soft_dependencies4(self): # A more complex scenario with multiple parents at each level parents = [model.Job('parent%i' % i) for i in range(6)] child = model.Job('child') child.dependencies = frozenset([ model.JobDependency(parents[0].name, True), model.JobDependency(parents[1].name) ]) parents[0].dependencies = frozenset([ model.JobDependency(parents[2].name), model.JobDependency(parents[3].name, True) ]) parents[1].dependencies = frozenset([ model.JobDependency(parents[4].name), model.JobDependency(parents[5].name) ]) # Run them all graph = model.JobGraph() for j in parents: graph.addJob(j) graph.addJob(child) self.assertEqual(set(graph.getParentJobsRecursively(child.name)), set(parents)) # Skip first parent, therefore its recursive dependencies don't appear graph = model.JobGraph() for j in parents: if j is not parents[0]: graph.addJob(j) graph.addJob(child) self.assertEqual( set(graph.getParentJobsRecursively(child.name)), set(parents) - set([parents[0], parents[2], parents[3]])) # Skip a leaf node graph = model.JobGraph() for j in parents: if j is not parents[3]: graph.addJob(j) graph.addJob(child) self.assertEqual(set(graph.getParentJobsRecursively(child.name)), set(parents) - set([parents[3]]))
def test_job_graph_disallows_circular_dependencies(self): graph = model.JobGraph() jobs = [model.Job('job%d' % i) for i in range(0, 10)] prevjob = None for j in jobs[:3]: if prevjob: j.dependencies = frozenset([model.JobDependency(prevjob.name)]) graph.addJob(j) prevjob = j # 0 triggers 1 triggers 2 triggers 3... # Cannot depend on itself with testtools.ExpectedException( Exception, "Dependency cycle detected in job jobX"): j = model.Job('jobX') j.dependencies = frozenset([model.JobDependency(j.name)]) graph.addJob(j) # Disallow circular dependencies with testtools.ExpectedException( Exception, "Dependency cycle detected in job job3"): jobs[4].dependencies = frozenset( [model.JobDependency(jobs[3].name)]) graph.addJob(jobs[4]) jobs[3].dependencies = frozenset( [model.JobDependency(jobs[4].name)]) graph.addJob(jobs[3]) jobs[5].dependencies = frozenset([model.JobDependency(jobs[4].name)]) graph.addJob(jobs[5]) with testtools.ExpectedException( Exception, "Dependency cycle detected in job job3"): jobs[3].dependencies = frozenset( [model.JobDependency(jobs[5].name)]) graph.addJob(jobs[3]) jobs[3].dependencies = frozenset([model.JobDependency(jobs[2].name)]) graph.addJob(jobs[3]) jobs[6].dependencies = frozenset([model.JobDependency(jobs[2].name)]) graph.addJob(jobs[6])
def test_node_request_canceled(self): # Test that node requests can be canceled nodeset = model.NodeSet() nodeset.addNode(model.Node(['controller'], 'ubuntu-xenial')) nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial')) job = model.Job('testjob') job.nodeset = nodeset self.fake_nodepool.pause() request = self.nodepool.requestNodes(None, job, 0) self.nodepool.cancelRequest(request) self.waitForRequests() self.assertEqual(len(self.provisioned_requests), 0)
def test_node_request_disconnect(self): # Test that node requests are re-submitted after disconnect nodeset = model.NodeSet() nodeset.addNode(model.Node(['controller'], 'ubuntu-xenial')) nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial')) job = model.Job('testjob') job.nodeset = nodeset self.fake_nodepool.pause() request = self.nodepool.requestNodes(None, job, 0) self.zk.client.stop() self.zk.client.start() self.fake_nodepool.unpause() self.waitForRequests() self.assertEqual(len(self.provisioned_requests), 1) self.assertEqual(request.state, 'fulfilled')
def test_node_request_priority(self): # Test that requests are satisfied in priority order nodeset = model.NodeSet() nodeset.addNode(model.Node(['controller', 'foo'], 'ubuntu-xenial')) nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial')) job = model.Job('testjob') job.nodeset = nodeset self.fake_nodepool.pause() request1 = self.nodepool.requestNodes(None, job, 1) request2 = self.nodepool.requestNodes(None, job, 0) self.fake_nodepool.unpause() self.waitForRequests() self.assertEqual(len(self.provisioned_requests), 2) self.assertEqual(request1.state, 'fulfilled') self.assertEqual(request2.state, 'fulfilled') self.assertTrue(request2.state_time < request1.state_time)
def test_accept_nodes_resubmitted(self): # Test that a resubmitted request would not lock nodes nodeset = model.NodeSet() nodeset.addNode(model.Node(['controller'], 'ubuntu-xenial')) nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial')) job = model.Job('testjob') job.nodeset = nodeset request = self.nodepool.requestNodes(None, job, 0) self.waitForRequests() self.assertEqual(len(self.provisioned_requests), 1) self.assertEqual(request.state, 'fulfilled') # Accept the nodes, passing a different ID self.nodepool.acceptNodes(request, "invalid") nodeset = request.nodeset for node in nodeset.getNodes(): self.assertIsNone(node.lock) self.assertEqual(node.state, 'ready')
def test_accept_nodes_lost_request(self): # Test that a lost request would not lock nodes nodeset = model.NodeSet() nodeset.addNode(model.Node(['controller'], 'ubuntu-xenial')) nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial')) job = model.Job('testjob') job.nodeset = nodeset request = self.nodepool.requestNodes(None, job, 0) self.waitForRequests() self.assertEqual(len(self.provisioned_requests), 1) self.assertEqual(request.state, 'fulfilled') self.zk.deleteNodeRequest(request) # Accept the nodes self.nodepool.acceptNodes(request, request.id) nodeset = request.nodeset for node in nodeset.getNodes(): self.assertIsNone(node.lock) self.assertEqual(node.state, 'ready')
def test_job_sets_defaults_for_boolean_attributes(self): job = model.Job('job') self._assert_job_booleans_are_not_none(job)
def test_copy_retains_skip_if(self): job = model.Job('job') job.copy(self.job) self.assertTrue(job.skip_if_matcher)
def test_metajob_does_not_set_defaults_for_boolean_attributes(self): job = model.Job('^job') self.assertIsNone(job.voting) self.assertIsNone(job.hold_following_changes)
def test_metajob_copy_does_not_set_undefined_boolean_attributes(self): job = model.Job('job') metajob = model.Job('^job') job.copy(metajob) self._assert_job_booleans_are_not_none(job)