From 2bbc729de919ac623820de422b418d9225eb1e8e Mon Sep 17 00:00:00 2001 From: "Urvashi Jain (urvajain)" Date: Fri, 20 Oct 2023 17:00:41 -0700 Subject: [PATCH] feat: add step and platform commands for device network --- .../testing/features/GreengrassCliSteps.java | 30 ++++++- .../testing/features/NetworkUtilsSteps.java | 34 +++++++- .../testing/platform/NetworkUtils.java | 5 ++ .../platform/linux/LinuxNetworkUtils.java | 29 ++++--- .../platform/macos/MacosNetworkUtils.java | 80 +++++++++++++++++++ .../testing/platform/macos/MacosPlatform.java | 2 +- pom.xml | 2 +- 7 files changed, 164 insertions(+), 18 deletions(-) diff --git a/aws-greengrass-testing-features/aws-greengrass-testing-features-api/src/main/java/com/aws/greengrass/testing/features/GreengrassCliSteps.java b/aws-greengrass-testing-features/aws-greengrass-testing-features-api/src/main/java/com/aws/greengrass/testing/features/GreengrassCliSteps.java index a3cf5466..a08d1b60 100644 --- a/aws-greengrass-testing-features/aws-greengrass-testing-features-api/src/main/java/com/aws/greengrass/testing/features/GreengrassCliSteps.java +++ b/aws-greengrass-testing-features/aws-greengrass-testing-features-api/src/main/java/com/aws/greengrass/testing/features/GreengrassCliSteps.java @@ -14,6 +14,7 @@ import com.google.common.annotations.VisibleForTesting; import io.cucumber.guice.ScenarioScoped; import io.cucumber.java.en.And; +import io.cucumber.java.en.When; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -74,10 +75,15 @@ public void verifyCliInstallation() { * @param componentName name of the component * @param status {RUNNING, BROKEN, FINISHED} * @throws InterruptedException {@link InterruptedException} + * @throws IllegalStateException {@link IllegalStateException} */ @And("I verify the {word} component is {word} using the greengrass-cli") public void verifyComponentIsRunning(String componentName, String status) throws InterruptedException { - waitSteps.untilTrue(() -> this.isComponentInState(componentName, status), 30, TimeUnit.SECONDS); + if (!waitSteps.untilTrue(() -> this.isComponentInState(componentName, status), + 60, + TimeUnit.SECONDS)) { + throw new IllegalStateException("Component status is not in " + status + " status"); + } } /** @@ -97,6 +103,28 @@ public void verifyLocalDeployment(String status, int value, String unit) throws terminalStatuses::contains, value, timeUnit); } + /** + * Use greengrass-cli to start or stop the component. + * + * @param componentName Name of the component + * @param action Action to take: stop or restart + * @throws UnsupportedOperationException if action word is other than stop or restart + */ + @When("I use greengrass-cli to {word} the component {word}") + public void restartOrStopComponent(String action, String componentName) { + if (action.matches("stop|restart")) { + platform.commands().executeToString(CommandInput.builder() + .line(testContext.installRoot().resolve("bin").resolve("greengrass-cli").toString()) + .addAllArgs(Arrays.asList("component", action, "--names", componentName)) + .build()); + LOGGER.debug("Performing {} on component {}", action, componentName); + } else { + throw new UnsupportedOperationException("Invalid action: " + + action + ". Please use restart or stop action words."); + } + + } + @VisibleForTesting String getLocalDeploymentStatus() { try { diff --git a/aws-greengrass-testing-features/aws-greengrass-testing-features-api/src/main/java/com/aws/greengrass/testing/features/NetworkUtilsSteps.java b/aws-greengrass-testing-features/aws-greengrass-testing-features-api/src/main/java/com/aws/greengrass/testing/features/NetworkUtilsSteps.java index 78784319..98b49c0c 100644 --- a/aws-greengrass-testing-features/aws-greengrass-testing-features-api/src/main/java/com/aws/greengrass/testing/features/NetworkUtilsSteps.java +++ b/aws-greengrass-testing-features/aws-greengrass-testing-features-api/src/main/java/com/aws/greengrass/testing/features/NetworkUtilsSteps.java @@ -26,6 +26,7 @@ public class NetworkUtilsSteps { private final Platform platform; private final AtomicBoolean mqttConnectivity = new AtomicBoolean(true); + private final AtomicBoolean networkConnectivity = new AtomicBoolean(true); private final Set addedIPs = new HashSet<>(); @Inject @@ -61,7 +62,7 @@ public boolean connectivityValue(String status) { } /** - * Disables or dnables device MQTT connectivity to IoT Core by blocking traffic on ports 8883 and 443. + * Disables or enables device MQTT connectivity to IoT Core by blocking traffic on ports 8883. * * @param connectivity the value of connectivity to set * @throws IOException on IO errors @@ -109,6 +110,27 @@ public void removeLoopBackAddress(final String address) throws IOException, Inte addedIPs.remove(address); } + /** + * Disables or enables device internet connectivity by blocking traffic on ports 443. + * + * @param connectivity the value of connectivity to set + * @throws IOException on IO errors + * @throws InterruptedException when thread has been interrupted + */ + @When("I set device network connectivity to {connectivityValue}") + public void setDeviceNetwork(final boolean connectivity) throws IOException, InterruptedException { + boolean changed = networkConnectivity.compareAndSet(!connectivity, connectivity); + if (changed) { + if (connectivity) { + LOGGER.info("Restoring Network connection"); + platform.networkUtils().recoverNetwork(); + } else { + LOGGER.info("Disconnecting Network connection"); + platform.networkUtils().disconnectNetwork(); + } + } + } + /** * Restore settings to defaults. * @@ -118,12 +140,18 @@ public void removeLoopBackAddress(final String address) throws IOException, Inte @After(order = Integer.MAX_VALUE) public void restoreDefaultSettings() throws IOException, InterruptedException { // rollback firewall changes - boolean changed = mqttConnectivity.compareAndSet(false, true); - if (changed) { + boolean mqttChange = mqttConnectivity.compareAndSet(false, true); + if (mqttChange) { LOGGER.info("Automatically unblocking blocked MQTT connections"); platform.networkUtils().recoverMqtt(); } + boolean networkChange = networkConnectivity.compareAndSet(false, true); + if (networkChange) { + LOGGER.info("Automatically unblocking blocked Network connection"); + platform.networkUtils().recoverNetwork(); + } + // rollback lo interface changes Set ipsToRemove = new HashSet<>(addedIPs); for (String ip : ipsToRemove) { diff --git a/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/NetworkUtils.java b/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/NetworkUtils.java index d75834d6..5a610604 100644 --- a/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/NetworkUtils.java +++ b/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/NetworkUtils.java @@ -9,6 +9,7 @@ public abstract class NetworkUtils { protected static final String[] MQTT_PORTS = {"8883"}; + protected static final String[] NETWORK_PORTS = {"443"}; /** * Disables incoming and outgoing MQTT connections by apply firewall rules. @@ -29,4 +30,8 @@ public abstract class NetworkUtils { public abstract void addLoopbackAddress(String address) throws IOException, InterruptedException; public abstract void deleteLoopbackAddress(String address) throws IOException, InterruptedException; + + public abstract void disconnectNetwork() throws InterruptedException, IOException; + + public abstract void recoverNetwork() throws InterruptedException, IOException; } diff --git a/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/linux/LinuxNetworkUtils.java b/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/linux/LinuxNetworkUtils.java index f8ed9f20..60bc0dea 100644 --- a/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/linux/LinuxNetworkUtils.java +++ b/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/linux/LinuxNetworkUtils.java @@ -12,7 +12,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -20,7 +19,6 @@ public class LinuxNetworkUtils extends NetworkUtils { private static final Logger LOGGER = LogManager.getLogger(LinuxNetworkUtils.class); private static final long TIMEOUT_IN_SECONDS = 2L; - private static final String DISABLE_OPTION = "--delete"; private static final String APPEND_OPTION = "-A"; private static final String IPTABLES = "iptables"; @@ -31,11 +29,8 @@ public class LinuxNetworkUtils extends NetworkUtils { "OUTPUT -p tcp -d localhost --dport %s -j ACCEPT", "OUTPUT -p tcp --dport %s -j DROP" }; - private static final String ADD_LOOPBACK_ADDRESS_TEMPLATE = "addr add %s/32 dev lo"; private static final String DELETE_LOOPBACK_ADDRESS_TEMPLATE = "addr delete %s/32 dev lo"; - private static final String COMMAND_FAILED_TO_RUN = "Command (%s) failed to run."; - private final LinuxCommands commands; LinuxNetworkUtils(final Device device, final PillboxContext pillboxContext) { @@ -47,13 +42,13 @@ public class LinuxNetworkUtils extends NetworkUtils { * On Linux connections to/from 127.0.0.1 will still be allowed. */ @Override - public void disconnectMqtt() throws InterruptedException, IOException { - modifyMqttConnection(APPEND_OPTION); + public void disconnectMqtt() { + modifyConnection(APPEND_OPTION, MQTT_PORTS); } @Override - public void recoverMqtt() throws InterruptedException, IOException { - modifyMqttConnection(DISABLE_OPTION); + public void recoverMqtt() { + modifyConnection(DISABLE_OPTION, MQTT_PORTS); } @Override @@ -81,13 +76,13 @@ public void deleteLoopbackAddress(String address) { commands.execute(commandInput); } - private void modifyMqttConnection(String action) throws IOException, InterruptedException { + private void modifyConnection(String action, String[] ports) { for (String template : TEMPLATES) { - for (String port : MQTT_PORTS) { + for (String port : ports) { List arguments = new ArrayList<>(); arguments.add(action); String cmd = String.format(template, port); - LOGGER.info("Running {} command: {}", IPTABLES, cmd); + LOGGER.debug("Running {} command: {}", IPTABLES, cmd); arguments.addAll(Arrays.asList(cmd.split(" "))); CommandInput commandInput = CommandInput.builder() @@ -100,4 +95,14 @@ private void modifyMqttConnection(String action) throws IOException, Interrupted } } } + + @Override + public void disconnectNetwork() { + modifyConnection(APPEND_OPTION, NETWORK_PORTS); + } + + @Override + public void recoverNetwork() { + modifyConnection(DISABLE_OPTION, NETWORK_PORTS); + } } diff --git a/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/macos/MacosNetworkUtils.java b/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/macos/MacosNetworkUtils.java index 0da2d9f9..1bbfe038 100644 --- a/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/macos/MacosNetworkUtils.java +++ b/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/macos/MacosNetworkUtils.java @@ -5,11 +5,34 @@ package com.aws.greengrass.testing.platform.macos; +import com.aws.greengrass.testing.api.device.Device; +import com.aws.greengrass.testing.api.device.model.CommandInput; +import com.aws.greengrass.testing.api.model.PillboxContext; import com.aws.greengrass.testing.platform.NetworkUtils; +import org.apache.logging.log4j.LogManager; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; public class MacosNetworkUtils extends NetworkUtils { + private static final org.apache.logging.log4j.Logger LOGGER = LogManager.getLogger(MacosNetworkUtils.class); + private static final String NETWORK_SETUP_COMMAND = "networksetup"; + private static final String DOWN_OPERATION = "off"; + private static final String UP_OPERATION = "on"; + private static final String ACTIVE_SERVICE = "Active"; + private static final String INACTIVE_SERVICE = "Inactive"; + private static final long TIMEOUT_IN_SECONDS = 2L; + private static final AtomicBoolean networkDown = new AtomicBoolean(false); + private final MacosCommands commands; + + MacosNetworkUtils(final Device device, final PillboxContext pillboxContext) { + this.commands = new MacosCommands(device, pillboxContext); + } + @Override public void disconnectMqtt() throws InterruptedException, IOException { throw new UnsupportedOperationException("Operation not supported"); @@ -29,4 +52,61 @@ public void addLoopbackAddress(String address) { public void deleteLoopbackAddress(String address) { throw new UnsupportedOperationException("Operation not supported"); } + + @Override + public void disconnectNetwork() { + List activeNetworkServices = networkServicesList().get(ACTIVE_SERVICE); + LOGGER.debug("Active network service {}", activeNetworkServices); + if (!activeNetworkServices.isEmpty()) { + for (String networkService : activeNetworkServices) { + commands.execute(CommandInput.builder() + .line(NETWORK_SETUP_COMMAND) + .addArgs("-setnetworkserviceenabled", networkService, DOWN_OPERATION) + .timeout(TIMEOUT_IN_SECONDS) + .build()); + } + networkDown.set(true); + } + } + + @Override + public void recoverNetwork() { + if (networkDown.get()) { + List inactiveNetworkServices = networkServicesList().get(INACTIVE_SERVICE); + LOGGER.debug("Inactive network service {}", inactiveNetworkServices); + if (!inactiveNetworkServices.isEmpty()) { + for (String networkService : inactiveNetworkServices) { + commands.execute(CommandInput.builder() + .line(NETWORK_SETUP_COMMAND) + .addArgs("-setnetworkserviceenabled", networkService, UP_OPERATION) + .timeout(TIMEOUT_IN_SECONDS) + .build()); + } + } + } + } + + /** + * Gets list of all network services and creates a map with Active and Inactive status. + * @return Map of list of active and inactive network services + */ + private Map> networkServicesList() { + Map> networkList = new HashMap<>(); + String response = commands.executeToString(CommandInput.builder() + .line(NETWORK_SETUP_COMMAND) + .addArgs("-listallnetworkservices") + .build()); + String[] networkServiceList = response.split("\\r?\\n|\\r"); + for (int i = 1; i < networkServiceList.length; i++) { + String netService = networkServiceList[i]; + if (netService.startsWith("*")) { + netService = "\"".concat(netService.substring(1, netService.length())).concat("\""); + networkList.computeIfAbsent(INACTIVE_SERVICE, k -> new ArrayList<>()).add(netService); + } else { + netService = "\"".concat(netService).concat("\""); + networkList.computeIfAbsent(ACTIVE_SERVICE, k -> new ArrayList<>()).add(netService); + } + } + return networkList; + } } diff --git a/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/macos/MacosPlatform.java b/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/macos/MacosPlatform.java index 969d0c91..2141c934 100644 --- a/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/macos/MacosPlatform.java +++ b/aws-greengrass-testing-platform/aws-greengrass-testing-platform-api/src/main/java/com/aws/greengrass/testing/platform/macos/MacosPlatform.java @@ -23,6 +23,6 @@ public MacosCommands commands() { @Override public NetworkUtils networkUtils() { - return new MacosNetworkUtils(); + return new MacosNetworkUtils(device, pillboxContext); } } diff --git a/pom.xml b/pom.xml index 3307df6f..6882f705 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 5.7.0 2.8.2 2.17.1 - 2.20.157 + 2.21.5 1.0 3.0.2 5.0.1