From be4f2b8bbdacbbbe0b70b6aba0f66d419d040b19 Mon Sep 17 00:00:00 2001 From: Bilgetz Date: Thu, 9 Feb 2023 09:53:01 +0100 Subject: [PATCH] Sort network for have it in dclared order --- .../compute/JCloudsSlaveTemplate.java | 27 ++++++++ .../openstack/compute/internal/Openstack.java | 15 ++++- .../compute/JCloudsSlaveTemplateTest.java | 63 +++++++++++++++++++ .../compute/internal/OpenstackTest.java | 57 +++++++++++++++++ 4 files changed, 161 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/jenkins/plugins/openstack/compute/JCloudsSlaveTemplate.java b/plugin/src/main/java/jenkins/plugins/openstack/compute/JCloudsSlaveTemplate.java index 234c006a..1eab2849 100644 --- a/plugin/src/main/java/jenkins/plugins/openstack/compute/JCloudsSlaveTemplate.java +++ b/plugin/src/main/java/jenkins/plugins/openstack/compute/JCloudsSlaveTemplate.java @@ -57,6 +57,8 @@ public class JCloudsSlaveTemplate implements Describable, public static final String OPENSTACK_CLOUD_NAME_KEY = "jenkins-cloud-name"; // To be attached on all servers provisioned from configured templates public static final String OPENSTACK_TEMPLATE_NAME_KEY = "jenkins-template-name"; + // To be attached on all servers provisioned from configured templates + public static final String OPENSTACK_NETWORK_ORDER = "jenkins-network-order"; private static final Logger LOGGER = Logger.getLogger(JCloudsSlaveTemplate.class.getName()); @@ -304,6 +306,7 @@ public boolean canProvision(final Label label) { List networks = selectNetworkIds(openstack, nid); LOGGER.fine("Setting networks to " + networks); builder.networks(networks); + builder.addMetadataItem(OPENSTACK_NETWORK_ORDER, String.join(",", selectNetworkOrder(openstack, nid))); } String securityGroups = opts.getSecurityGroups(); @@ -477,6 +480,30 @@ private String getServerName() { return ret.stream().map(Network::getId).collect(Collectors.toList()); } + @VisibleForTesting + /*package*/ static @Nonnull List selectNetworkOrder(@Nonnull Openstack openstack, @Nonnull String spec) { + if (spec == null || spec.isEmpty()) throw new IllegalArgumentException(); + + List> declared = TokenGroup.from(spec, ',', '|'); + + List allDeclaredNetworks = declared.stream().flatMap(Collection::stream).collect(Collectors.toList()); + + if (declared.isEmpty() || declared.contains(Collections.emptyList()) || allDeclaredNetworks.contains("")) { + throw new IllegalArgumentException("Networks declaration contains blank '" + declared + "'"); + } + + Map osNetworksById = openstack.getNetworks(allDeclaredNetworks); + Map osNetworksByName = osNetworksById.values().stream().collect(Collectors.toMap(Network::getName, n -> n)); + + final Function RESOLVE_IDS_TO_NAME = n -> { + if (osNetworksByName.containsKey(n)) return n; + Network network = osNetworksById.getOrDefault(n, null); + if (network != null) return network.getName(); + throw new IllegalArgumentException("No network name '" + n + "' found for " + spec); + }; + + return allDeclaredNetworks.stream().map(RESOLVE_IDS_TO_NAME).distinct().collect(Collectors.toList()); + } /*package for testing*/ @CheckForNull String getUserData() { return UserDataConfig.resolve(getEffectiveSlaveOptions().getUserDataId()); diff --git a/plugin/src/main/java/jenkins/plugins/openstack/compute/internal/Openstack.java b/plugin/src/main/java/jenkins/plugins/openstack/compute/internal/Openstack.java index 5be2fff7..7064640e 100644 --- a/plugin/src/main/java/jenkins/plugins/openstack/compute/internal/Openstack.java +++ b/plugin/src/main/java/jenkins/plugins/openstack/compute/internal/Openstack.java @@ -34,6 +34,7 @@ import hudson.Util; import hudson.util.FormValidation; import jenkins.model.Jenkins; +import jenkins.plugins.openstack.compute.JCloudsSlaveTemplate; import jenkins.plugins.openstack.compute.auth.OpenstackCredential; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; @@ -72,6 +73,7 @@ import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -735,7 +737,18 @@ public static Address getAccessIpAddressObject(@Nonnull Server server) { Address fixedIPv4 = null; Address fixedIPv6 = null; Address floatingIPv6 = null; - Collection> addressMap = server.getAddresses().getAddresses().values(); + String order= server.getMetadata().get(JCloudsSlaveTemplate.OPENSTACK_NETWORK_ORDER); + Collection> addressMap; + if (order != null && !order.isEmpty()) { + final List listOrder = Arrays.asList(order.split(",")); + + TreeMap> sortedAddress = new TreeMap<>(Comparator.comparingInt(listOrder::indexOf)); + sortedAddress.putAll(server.getAddresses().getAddresses()); + addressMap = sortedAddress.values(); + } else { + addressMap = server.getAddresses().getAddresses().values(); + } + for (List addresses: addressMap) { for (Address addr: addresses) { String type = addr.getType(); diff --git a/plugin/src/test/java/jenkins/plugins/openstack/compute/JCloudsSlaveTemplateTest.java b/plugin/src/test/java/jenkins/plugins/openstack/compute/JCloudsSlaveTemplateTest.java index d392ef73..b3865354 100644 --- a/plugin/src/test/java/jenkins/plugins/openstack/compute/JCloudsSlaveTemplateTest.java +++ b/plugin/src/test/java/jenkins/plugins/openstack/compute/JCloudsSlaveTemplateTest.java @@ -36,6 +36,7 @@ import static jenkins.plugins.openstack.compute.JCloudsSlaveTemplate.parseSecurityGroups; import static jenkins.plugins.openstack.compute.JCloudsSlaveTemplate.selectNetworkIds; +import static jenkins.plugins.openstack.compute.JCloudsSlaveTemplate.selectNetworkOrder; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; @@ -515,4 +516,66 @@ public void selectNetworksWhenUtilizationApiDisabled() { // If we have not utilization info, result will likely be suboptimal assertEquals(Arrays.asList("uuid-empty", "uuid-full", "uuid-loaded", "uuid-empty"), selectNetworkIds(os, "empty|full,full,loaded|empty,empty")); } + + @Test + public void selectNetworkOrderTest() { + JCloudsSlaveTemplate t = j.dummySlaveTemplate("foo"); + JCloudsCloud c = j.dummyCloud(t); + j.configureSlaveProvisioning(c, Collections.emptyList()); + + Network fullNet = mockNetwork("full"); + Network emptyNet = mockNetwork("empty"); + Network loadedNet = mockNetwork("loaded"); + Map networkMap = new HashMap<>(); + Stream.of(fullNet, emptyNet, loadedNet).forEach(n -> { + networkMap.put(n.getId(), n ); + networkMap.put(n.getName(), n); + }); + + Openstack os = c.getOpenstack(); + doAnswer(i -> { + Map ret = new HashMap<>(); + + for (String netSpec : ((List) i.getArguments()[0])) { + Network n = networkMap.get(netSpec); + ret.put(n.getId(), n); + } + return ret; + }).when(os).getNetworks(any()); + doAnswer(i -> { + Map requestedNets = (Map) i.getArguments()[0]; + + Map ret = new HashMap<>(); + if (requestedNets.containsKey(fullNet.getId())) { + ret.put(fullNet, 0); + } + if (requestedNets.containsKey(emptyNet.getId())) { + ret.put(emptyNet, 10); + } + if (requestedNets.containsKey(loadedNet.getId())) { + ret.put(loadedNet, 5); + } + return ret; + }).when(os).getNetworksCapacity(any()); + + assertEquals(singletonList("empty"), selectNetworkOrder(os, "empty")); + assertEquals(singletonList("loaded"), selectNetworkOrder(os, "loaded")); + assertEquals(singletonList("full"), selectNetworkOrder(os, "full")); + assertEquals(singletonList("empty"), selectNetworkOrder(os, "uuid-empty")); + assertEquals(singletonList("loaded"), selectNetworkOrder(os, "uuid-loaded")); + assertEquals(singletonList("full"), selectNetworkOrder(os, "uuid-full")); + assertEquals(Arrays.asList("empty", "full", "loaded"), selectNetworkOrder(os, "empty,uuid-full,loaded")); + assertEquals(Arrays.asList("empty"), selectNetworkOrder(os, "empty,uuid-empty,empty")); + + assertEquals(Arrays.asList("empty","full"), selectNetworkOrder(os, "empty|full")); + assertEquals(Arrays.asList("empty","loaded"), selectNetworkOrder(os, "empty|loaded")); + assertEquals(Arrays.asList("loaded","full"), selectNetworkOrder(os, "loaded|full")); + assertEquals(Arrays.asList("empty", "loaded", "full"), selectNetworkOrder(os, "empty|loaded|full")); + assertEquals(Arrays.asList("full", "loaded", "empty"), selectNetworkOrder(os, "full|loaded|empty")); + + assertEquals(Arrays.asList("empty", "full", "loaded"), selectNetworkOrder(os, "empty|full,full,loaded|empty,empty")); + assertEquals(Arrays.asList("empty", "loaded"), selectNetworkOrder(os, "empty|empty,loaded|loaded,empty|empty")); + } + + } diff --git a/plugin/src/test/java/jenkins/plugins/openstack/compute/internal/OpenstackTest.java b/plugin/src/test/java/jenkins/plugins/openstack/compute/internal/OpenstackTest.java index f65fca78..a06af9ac 100644 --- a/plugin/src/test/java/jenkins/plugins/openstack/compute/internal/OpenstackTest.java +++ b/plugin/src/test/java/jenkins/plugins/openstack/compute/internal/OpenstackTest.java @@ -9,6 +9,7 @@ import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.*; +import jenkins.plugins.openstack.compute.JCloudsSlaveTemplate; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; @@ -22,6 +23,8 @@ import org.openstack4j.api.networking.ext.NetworkIPAvailabilityService; import org.openstack4j.api.storage.BlockVolumeSnapshotService; import org.openstack4j.model.common.ActionResponse; +import org.openstack4j.model.compute.Address; +import org.openstack4j.model.compute.Addresses; import org.openstack4j.model.compute.Fault; import org.openstack4j.model.compute.Server; import org.openstack4j.model.compute.builder.ServerCreateBuilder; @@ -488,6 +491,60 @@ public void deleteFloatingIpsWhenDeletingMachine() { verify(fips, never()).delete("keep-me"); } + + @Test + public void getAccessIpAddress() { + getAccesIpAdressTest(getAddressesOneAddress(), Collections.singletonList("default_network"),"DEFAULT_IP"); + + getAccesIpAdressTest(getAddressesTwoAddress(), Arrays.asList("first_network", "second_network"),"FIRST_IP"); + getAccesIpAdressTest(getAddressesTwoAddress(), Arrays.asList("second_network", "first_network"),"SECOND_IP"); + } + + private Addresses getAddressesOneAddress() { + Address address = getAddress("DEFAULT_IP", "fixed", 4); + Addresses addresses = mock(Addresses.class); + Map> mapAdresses = new HashMap>() {{ + put("default_network", Collections.singletonList(address)); + }}; + when(addresses.getAddresses()).thenReturn(mapAdresses); + when(addresses.getAddresses(any())) + .thenAnswer((Answer>) invocationOnMock -> mapAdresses.get(invocationOnMock.getArguments()[0])); + return addresses; + } + private Addresses getAddressesTwoAddress() { + Address firstIp = getAddress("FIRST_IP", "fixed", 4); + Address secondIp = getAddress("SECOND_IP", "fixed", 4); + Addresses addresses = mock(Addresses.class); + Map> mapAdresses = new HashMap>() {{ + put("first_network", Collections.singletonList(firstIp)); + put("second_network", Collections.singletonList(secondIp)); + }}; + when(addresses.getAddresses()).thenReturn(mapAdresses); + when(addresses.getAddresses(any())) + .thenAnswer((Answer>) invocationOnMock -> mapAdresses.get(invocationOnMock.getArguments()[0])); + return addresses; + } + + private Address getAddress(String ip, String type, int version) { + Address address = mock(Address.class); + when(address.getAddr()).thenReturn(ip); + when(address.getVersion()).thenReturn(version); + when(address.getType()).thenReturn(type); + return address; + } + + private void getAccesIpAdressTest(Addresses addresses,List networkOrder, String result) { + Server server = mock(Server.class); + when(server.getAddresses()).thenReturn(addresses); + Map metadata = new HashMap<>(); + metadata.put(JCloudsSlaveTemplate.OPENSTACK_NETWORK_ORDER, String.join(",", networkOrder)); + when(server.getMetadata()).thenReturn(metadata); + + String ipAdresse = Openstack.getAccessIpAddress(server); + + assertThat(ipAdresse, equalTo(result)); + } + /** * Track the state of the openstack to be manifested by different client calls; */