Add ability to set GCE node labels via Salt Cloud (#62046)

* Add ability to set GCE node labels via salt cloud

* Add documentation for GCE labels

* Correct example dicts in documentation for labels

* Fix gce unit pytest

* Add ex_labels to config fixture

* Add versionadded tag

* saltstack/salt/#62046 metadata, label, tag, image fixtures

* saltstack/salt/#62046 location fixture

* saltstack/salt/#62046 size fixture

* saltstack/salt/#62046 network fixture

* saltstack/salt/#62046 subnetwork fixture

* saltstack/salt/#62046 update test data

* Revert ex_tags to tags

* saltstack#62046 correct expected keyword from ex_metadata to metadata

* saltstack#62046 revert defaulting logic for image and update tests

* saltstack#62046 revert defaulting logic

* saltstack#62046 revert failing tests

* Fix doc failure

Co-authored-by: Brandon Kucera <brandon.kucera@ubisoft.com>
Co-authored-by: Megan Wilhite <mwilhite@vmware.com>
This commit is contained in:
Brandon Kucera 2022-09-30 15:35:18 -04:00 committed by GitHub
parent d93c400805
commit 58a2bcc253
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 175 additions and 39 deletions

1
changelog/61245.added Normal file
View file

@ -0,0 +1 @@
Added node label support for GCE

View file

@ -147,6 +147,7 @@ Set up an initial profile at ``/etc/salt/cloud.profiles`` or
location: europe-west1-b
network: default
subnetwork: default
labels: '{"name": "myinstance"}'
tags: '["one", "two", "three"]'
metadata: '{"one": "1", "2": "two"}'
use_persistent_disk: True
@ -192,6 +193,7 @@ Set up an initial profile at ``/etc/salt/cloud.profiles`` or
location: europe-west1-b
network: default
subnetwork: default
labels: '{"name": "myinstance"}'
tags: '["one", "two", "three"]'
metadata: '{"one": "1", "2": "two"}'
use_persistent_disk: True
@ -235,6 +237,15 @@ Additionally, the subnetwork your instance is created under is associated with t
.. versionadded:: 2017.7.0
labels
------
This setting allows you to set labels on your GCE instances. It
should be a dictionary and must be parse-able by the python
ast.literal_eval() function to convert it to a python dictionary.
.. versionadded:: 3006
tags
----
@ -322,6 +333,7 @@ key in your cloud profile. The following example enables the bigquery scope.
location: us-central1-a
network: default
subnetwork: default
labels: '{"name": "myinstance"}'
tags: '["one", "two", "three"]'
metadata: '{"one": "1", "2": "two",
"sshKeys": ""}'

View file

@ -393,6 +393,24 @@ def __get_size(conn, vm_):
return conn.ex_get_size(size, __get_location(conn, vm_))
def __get_labels(vm_):
"""
Get configured labels.
"""
l = config.get_cloud_config_value(
"ex_labels", vm_, __opts__, default="{}", search_global=False
)
# Consider warning the user that the labels in the cloud profile
# could not be interpreted, bad formatting?
try:
labels = literal_eval(l)
except Exception: # pylint: disable=W0703
labels = None
if not labels or not isinstance(labels, dict):
labels = None
return labels
def __get_tags(vm_):
"""
Get configured tags.
@ -2320,6 +2338,7 @@ def request_instance(vm_):
"size": __get_size(conn, vm_),
"image": __get_image(conn, vm_),
"location": __get_location(conn, vm_),
"ex_labels": __get_labels(vm_),
"ex_network": __get_network(conn, vm_),
"ex_subnetwork": __get_subnetwork(vm_),
"ex_tags": __get_tags(vm_),

View file

@ -49,26 +49,153 @@ def configure_loader_modules():
}
@pytest.fixture(scope="module")
def location():
return collections.namedtuple("Location", "name")("chicago")
@pytest.fixture(
params=[
{"expected": "", "image": ""},
{"expected": None, "image": None},
{"expected": "debian-10", "image": "debian-10"},
]
)
def config_image(request):
return request.param["expected"], request.param["image"]
@pytest.fixture(
params=[
{"expected": None, "label": "{}"},
{"expected": {"mylabel": "myvalue"}, "label": "{'mylabel': 'myvalue'}"},
]
)
def config_labels(request):
return request.param["expected"], request.param["label"]
@pytest.fixture(
params=[
{
"expected": collections.namedtuple("Location", "name")("chicago"),
"location": collections.namedtuple("Location", "name")("chicago"),
},
]
)
def config_location(request):
return request.param["expected"], request.param["location"]
@pytest.fixture(
params=[
{
"expected": {"items": [{"key": "salt-cloud-profile", "value": None}]},
"metadata": {},
},
{
"expected": {
"items": [
{"key": "mykey", "value": "myvalue"},
{"key": "salt-cloud-profile", "value": None},
]
},
"metadata": "{'mykey': 'myvalue'}",
},
]
)
def config_metadata(request):
return request.param["expected"], request.param["metadata"]
@pytest.fixture(
params=[
{"expected": "mynetwork", "network": "mynetwork"},
]
)
def config_network(request):
return request.param["expected"], request.param["network"]
@pytest.fixture(
params=[
{"expected": "e2-standard-2", "size": "e2-standard-2"},
]
)
def config_size(request):
return request.param["expected"], request.param["size"]
@pytest.fixture(
params=[
{"expected": "mysubnetwork", "subnetwork": "mysubnetwork"},
]
)
def config_subnetwork(request):
return request.param["expected"], request.param["subnetwork"]
@pytest.fixture(
params=[
{"expected": None, "tag": "{}"},
{"expected": ["mytag", "myvalue"], "tag": "['mytag', 'myvalue']"},
]
)
def config_tags(request):
return request.param["expected"], request.param["tag"]
@pytest.fixture
def config(location):
return {
def config(
config_image,
config_labels,
config_location,
config_metadata,
config_network,
config_size,
config_subnetwork,
config_tags,
):
expected_image, image = config_image
expected_labels, labels = config_labels
expected_location, location = config_location
expected_metadata, metadata = config_metadata
expected_network, network = config_network
expected_size, size = config_size
expected_subnetwork, subnetwork = config_subnetwork
expected_tags, tags = config_tags
expected_call_kwargs = {
"ex_disk_type": "pd-standard",
"ex_metadata": expected_metadata,
"ex_accelerator_count": 42,
"name": "new",
"ex_service_accounts": None,
"external_ip": "ephemeral",
"ex_accelerator_type": "foo",
"ex_tags": expected_tags,
"ex_labels": expected_labels,
"ex_disk_auto_delete": True,
"ex_network": expected_network,
"ex_disks_gce_struct": None,
"ex_preemptible": False,
"ex_can_ip_forward": False,
"ex_on_host_maintenance": "TERMINATE",
"location": expected_location,
"ex_subnetwork": expected_subnetwork,
"image": expected_image,
"size": expected_size,
}
config = {
"name": "new",
"driver": "gce",
"profile": None,
"size": 1234,
"image": "myimage",
"size": size,
"image": image,
"location": location,
"ex_network": "mynetwork",
"ex_subnetwork": "mysubnetwork",
"ex_tags": "mytags",
"ex_metadata": "metadata",
"ex_accelerator_type": "foo",
"ex_accelerator_count": 42,
"network": network,
"subnetwork": subnetwork,
"ex_labels": labels,
"tags": tags,
"metadata": metadata,
}
return expected_call_kwargs, config
@pytest.fixture
@ -195,36 +322,13 @@ def test_get_configured_provider_should_return_expected_result(fake_conf_provide
assert actual_result is expected_result
def test_request_instance_with_accelerator(config, location, conn, fake_libcloud_2_5_0):
def test_request_instance_with_accelerator(config, conn):
"""
Test requesting an instance with GCE accelerators
"""
config.update({"ex_accelerator_type": "foo", "ex_accelerator_count": 42})
call_kwargs = {
"ex_disk_type": "pd-standard",
"ex_metadata": {"items": [{"value": None, "key": "salt-cloud-profile"}]},
"ex_accelerator_count": 42,
"name": "new",
"ex_service_accounts": None,
"external_ip": "ephemeral",
"ex_accelerator_type": "foo",
"ex_tags": None,
"ex_disk_auto_delete": True,
"ex_network": "default",
"ex_disks_gce_struct": None,
"ex_preemptible": False,
"ex_can_ip_forward": False,
"ex_on_host_maintenance": "TERMINATE",
"location": location,
"ex_subnetwork": None,
"image": "myimage",
"size": 1234,
}
gce.request_instance(config)
conn.create_node.assert_called_once_with(**call_kwargs)
expected_call_kwargs, vm_config = config
gce.request_instance(vm_config)
conn.create_node.assert_called_once_with(**expected_call_kwargs)
def test_create_address_should_fire_creating_and_created_events_with_expected_args(