Abstract common cloud driver tests

We want to test node launch and image upload (if relevant) in the
same manner for every cloud driver, so abstract those tests into
a base class that is shared.

Change-Id: I9d000fc9e385286122d818bb5490d6aa07ecd800
This commit is contained in:
James E. Blair 2024-10-19 09:42:22 -07:00
parent f9a368b1c8
commit 08a3c43dd7
4 changed files with 148 additions and 195 deletions

View File

@ -26,16 +26,17 @@ from zuul.driver.aws.awsmodel import AwsProviderNode
from tests.fake_aws import FakeAws, FakeAwsProviderEndpoint
from tests.base import (
ZuulTestCase,
iterate_timeout,
simple_layout,
return_data,
)
from tests.unit.test_launcher import ImageMocksFixture
from tests.unit.test_cloud_driver import BaseCloudDriverTest
class TestAwsDriver(ZuulTestCase):
class TestAwsDriver(BaseCloudDriverTest):
config_file = 'zuul-connections-nodepool.conf'
cloud_test_image_format = 'raw'
mock_aws = mock_aws()
debian_return_data = {
'zuul': {
@ -146,13 +147,38 @@ class TestAwsDriver(ZuulTestCase):
super().tearDown()
@simple_layout('layouts/nodepool.yaml', enable_nodepool=True)
def test_aws_config(self):
aws_conn = self.scheds.first.sched.connections.connections['aws']
self.assertEqual('fake', aws_conn.access_key_id)
layout = self.scheds.first.sched.abide.tenants.get('tenant-one').layout
provider = layout.providers['aws-us-east-1-main']
endpoint = provider.getEndpoint()
self.assertEqual([], list(endpoint.listInstances()))
def test_aws_node_lifecycle(self):
self._test_node_lifecycle('debian-normal')
@simple_layout('layouts/aws/nodepool-image-snapshot.yaml',
enable_nodepool=True)
@return_data(
'build-debian-local-image',
'refs/heads/master',
debian_return_data,
)
def test_aws_diskimage_snapshot(self):
self._test_diskimage()
@simple_layout('layouts/aws/nodepool-image-image.yaml',
enable_nodepool=True)
@return_data(
'build-debian-local-image',
'refs/heads/master',
debian_return_data,
)
def test_aws_diskimage_image(self):
self._test_diskimage()
@simple_layout('layouts/aws/nodepool-image-ebs-direct.yaml',
enable_nodepool=True)
@return_data(
'build-debian-local-image',
'refs/heads/master',
debian_return_data,
)
def test_aws_diskimage_ebs_direct(self):
self._test_diskimage()
@simple_layout('layouts/nodepool.yaml', enable_nodepool=True)
def test_state_machines_instance(self):
@ -219,54 +245,3 @@ class TestAwsDriver(ZuulTestCase):
'zuul.driver.aws.awsendpoint.AwsProviderEndpoint.'
'_completeCreateInstance', return_value=None)):
yield
# Test the 3 image import methods:
def _test_aws_diskimage_import(self):
self.waitUntilSettled()
self.assertHistory([
dict(name='build-debian-local-image', result='SUCCESS'),
], ordered=False)
name = 'review.example.com%2Forg%2Fcommon-config/debian-local'
artifacts = self.launcher.image_build_registry.\
getArtifactsForImage(name)
self.assertEqual(1, len(artifacts))
self.assertEqual('raw', artifacts[0].format)
self.assertTrue(artifacts[0].validated)
uploads = self.launcher.image_upload_registry.getUploadsForImage(
name)
self.assertEqual(1, len(uploads))
self.assertEqual(artifacts[0].uuid, uploads[0].artifact_uuid)
self.assertIn('ami-', uploads[0].external_id)
self.assertTrue(uploads[0].validated)
@simple_layout('layouts/aws/nodepool-image-snapshot.yaml',
enable_nodepool=True)
@return_data(
'build-debian-local-image',
'refs/heads/master',
debian_return_data,
)
def test_aws_diskimage_snapshot(self):
self._test_aws_diskimage_import()
@simple_layout('layouts/aws/nodepool-image-image.yaml',
enable_nodepool=True)
@return_data(
'build-debian-local-image',
'refs/heads/master',
debian_return_data,
)
def test_aws_diskimage_image(self):
self._test_aws_diskimage_import()
@simple_layout('layouts/aws/nodepool-image-ebs-direct.yaml',
enable_nodepool=True)
@return_data(
'build-debian-local-image',
'refs/heads/master',
debian_return_data,
)
def test_aws_diskimage_ebs_direct(self):
self._test_aws_diskimage_import()

View File

@ -0,0 +1,102 @@
# Copyright 2024 Acme Gating, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import testtools
from kazoo.exceptions import NoNodeError
from zuul import model
from zuul.launcher.client import LauncherClient
from tests.base import (
ZuulTestCase,
iterate_timeout,
)
class BaseCloudDriverTest(ZuulTestCase):
cloud_test_connection_type = 'ssh'
cloud_test_image_format = ''
def _assertProviderNodeAttributes(self, pnode):
self.assertEqual(pnode.connection_type,
self.cloud_test_connection_type)
self.assertIsNotNone(pnode.interface_ip)
def _test_node_lifecycle(self, label):
# Call this in a test to run a node lifecycle
nodeset = model.NodeSet()
nodeset.addNode(model.Node("node", label))
ctx = self.createZKContext(None)
request = self.requestNodes([n.label for n in nodeset.getNodes()])
client = LauncherClient(self.zk_client, None)
request = client.getRequest(request.uuid)
self.assertEqual(request.state, model.NodesetRequest.State.FULFILLED)
self.assertEqual(len(request.nodes), 1)
client.acceptNodeset(request, nodeset)
self.waitUntilSettled()
with testtools.ExpectedException(NoNodeError):
# Request should be gone
request.refresh(ctx)
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertIsNotNone(pnode)
self.assertTrue(pnode.hasLock())
self._assertProviderNodeAttributes(pnode)
client.useNodeset(nodeset)
self.waitUntilSettled()
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertTrue(pnode.hasLock())
self.assertTrue(pnode.state, pnode.State.IN_USE)
client.returnNodeset(nodeset)
self.waitUntilSettled()
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertFalse(pnode.hasLock())
self.assertTrue(pnode.state, pnode.State.USED)
for _ in iterate_timeout(60, "node to be deleted"):
try:
pnode.refresh(ctx)
except NoNodeError:
break
def _test_diskimage(self):
self.waitUntilSettled()
self.assertHistory([
dict(name='build-debian-local-image', result='SUCCESS'),
], ordered=False)
name = 'review.example.com%2Forg%2Fcommon-config/debian-local'
artifacts = self.launcher.image_build_registry.\
getArtifactsForImage(name)
self.assertEqual(1, len(artifacts))
self.assertEqual(self.cloud_test_image_format, artifacts[0].format)
self.assertTrue(artifacts[0].validated)
uploads = self.launcher.image_upload_registry.getUploadsForImage(
name)
self.assertEqual(1, len(uploads))
self.assertEqual(artifacts[0].uuid, uploads[0].artifact_uuid)
self.assertIsNotNone(uploads[0].external_id)
self.assertTrue(uploads[0].validated)

View File

@ -464,55 +464,6 @@ class TestLauncher(ZuulTestCase):
request.delete(ctx)
self.waitUntilSettled()
@simple_layout('layouts/nodepool.yaml', enable_nodepool=True)
def test_node_lifecycle(self):
nodeset = model.NodeSet()
nodeset.addNode(model.Node("node", "debian-normal"))
ctx = self.createZKContext(None)
request = self.requestNodes([n.label for n in nodeset.getNodes()])
client = LauncherClient(self.zk_client, None)
request = client.getRequest(request.uuid)
self.assertEqual(request.state, model.NodesetRequest.State.FULFILLED)
self.assertEqual(len(request.nodes), 1)
client.acceptNodeset(request, nodeset)
self.waitUntilSettled()
with testtools.ExpectedException(NoNodeError):
# Request should be gone
request.refresh(ctx)
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertIsNotNone(pnode)
self.assertTrue(pnode.hasLock())
client.useNodeset(nodeset)
self.waitUntilSettled()
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertTrue(pnode.hasLock())
self.assertTrue(pnode.state, pnode.State.IN_USE)
self.assertEqual(pnode.connection_type, 'ssh')
client.returnNodeset(nodeset)
self.waitUntilSettled()
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertFalse(pnode.hasLock())
self.assertTrue(pnode.state, pnode.State.USED)
for _ in iterate_timeout(60, "node to be deleted"):
try:
pnode.refresh(ctx)
except NoNodeError:
break
@simple_layout('layouts/nodepool.yaml', enable_nodepool=True)
def test_lost_nodeset_request(self):
ctx = self.createZKContext(None)

View File

@ -15,11 +15,7 @@
import os
import fixtures
import testtools
from kazoo.exceptions import NoNodeError
from zuul import model
from zuul.launcher.client import LauncherClient
from zuul.driver.openstack import OpenstackDriver
from tests.fake_openstack import (
@ -29,14 +25,15 @@ from tests.fake_openstack import (
from tests.base import (
FIXTURE_DIR,
ZuulTestCase,
iterate_timeout,
simple_layout,
return_data,
)
from tests.unit.test_launcher import ImageMocksFixture
from tests.unit.test_cloud_driver import BaseCloudDriverTest
class BaseOpenstackDriverTest(ZuulTestCase):
cloud_test_image_format = 'qcow2'
config_file = 'zuul-connections-nodepool.conf'
debian_return_data = {
'zuul': {
@ -78,70 +75,12 @@ class BaseOpenstackDriverTest(ZuulTestCase):
def tearDown(self):
super().tearDown()
def _test_openstack_node_lifecycle(self, label):
nodeset = model.NodeSet()
nodeset.addNode(model.Node("node", label))
ctx = self.createZKContext(None)
request = self.requestNodes([n.label for n in nodeset.getNodes()])
client = LauncherClient(self.zk_client, None)
request = client.getRequest(request.uuid)
self.assertEqual(request.state, model.NodesetRequest.State.FULFILLED)
self.assertEqual(len(request.nodes), 1)
client.acceptNodeset(request, nodeset)
self.waitUntilSettled()
with testtools.ExpectedException(NoNodeError):
# Request should be gone
request.refresh(ctx)
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertIsNotNone(pnode)
self.assertTrue(pnode.hasLock())
client.useNodeset(nodeset)
self.waitUntilSettled()
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertTrue(pnode.hasLock())
self.assertTrue(pnode.state, pnode.State.IN_USE)
self.assertEqual(pnode.connection_type, 'ssh')
client.returnNodeset(nodeset)
self.waitUntilSettled()
for node in nodeset.getNodes():
pnode = node._provider_node
self.assertFalse(pnode.hasLock())
self.assertTrue(pnode.state, pnode.State.USED)
for _ in iterate_timeout(60, "node to be deleted"):
try:
pnode.refresh(ctx)
except NoNodeError:
break
class TestOpenstackDriver(BaseOpenstackDriverTest):
# TODO: make this a generic driver test
@simple_layout('layouts/openstack/nodepool.yaml', enable_nodepool=True)
def test_openstack_config(self):
layout = self.scheds.first.sched.abide.tenants.get('tenant-one').layout
provider = layout.providers['openstack-main']
endpoint = provider.getEndpoint()
self.assertEqual([], list(endpoint.listInstances()))
# TODO: make this a generic driver test
class TestOpenstackDriver(BaseOpenstackDriverTest, BaseCloudDriverTest):
@simple_layout('layouts/openstack/nodepool.yaml', enable_nodepool=True)
def test_openstack_node_lifecycle(self):
self._test_openstack_node_lifecycle('debian-normal')
self._test_node_lifecycle('debian-normal')
# TODO: make this a generic driver test
@simple_layout('layouts/openstack/nodepool-image.yaml',
enable_nodepool=True)
@return_data(
@ -150,27 +89,12 @@ class TestOpenstackDriver(BaseOpenstackDriverTest):
BaseOpenstackDriverTest.debian_return_data,
)
def test_openstack_diskimage(self):
self.waitUntilSettled()
self.assertHistory([
dict(name='build-debian-local-image', result='SUCCESS'),
], ordered=False)
name = 'review.example.com%2Forg%2Fcommon-config/debian-local'
artifacts = self.launcher.image_build_registry.\
getArtifactsForImage(name)
self.assertEqual(1, len(artifacts))
self.assertEqual('qcow2', artifacts[0].format)
self.assertTrue(artifacts[0].validated)
uploads = self.launcher.image_upload_registry.getUploadsForImage(
name)
self.assertEqual(1, len(uploads))
self.assertEqual(artifacts[0].uuid, uploads[0].artifact_uuid)
self.assertIsNotNone(uploads[0].external_id)
self.assertTrue(uploads[0].validated)
self._test_diskimage()
# Openstack-driver specific tests
class TestOpenstackDriverFloatingIp(BaseOpenstackDriverTest):
class TestOpenstackDriverFloatingIp(BaseOpenstackDriverTest,
BaseCloudDriverTest):
# This test is for nova-net clouds with floating ips that require
# manual attachment.
openstack_needs_floating_ip = True
@ -178,13 +102,14 @@ class TestOpenstackDriverFloatingIp(BaseOpenstackDriverTest):
@simple_layout('layouts/openstack/nodepool.yaml', enable_nodepool=True)
def test_openstack_fip(self):
self._test_openstack_node_lifecycle('debian-normal')
self._test_node_lifecycle('debian-normal')
class TestOpenstackDriverAutoAttachFloatingIp(BaseOpenstackDriverTest):
class TestOpenstackDriverAutoAttachFloatingIp(BaseOpenstackDriverTest,
BaseCloudDriverTest):
openstack_needs_floating_ip = True
openstack_auto_attach_floating_ip = True
@simple_layout('layouts/openstack/nodepool.yaml', enable_nodepool=True)
def test_openstack_auto_attach_fip(self):
self._test_openstack_node_lifecycle('debian-normal')
self._test_node_lifecycle('debian-normal')