From 8ec6f227f56019eabcb57575d1f3d4fb3b8e4406 Mon Sep 17 00:00:00 2001 From: Ricardo Mestre Date: Mon, 24 Feb 2025 21:28:49 +0000 Subject: [PATCH] Rework ProxyConfigurationApplyAction --- .../action/ProxyConfigurationApplyAction.java | 32 ++++-- .../ProxyConfigurationApplyActionTest.java | 98 ------------------- .../services/SaltServerActionService.java | 22 +---- .../ProxyConfigUpdateApplySaltState.java | 73 +++++++------- .../salt/proxy/apply_proxy_config.sls | 10 +- 5 files changed, 70 insertions(+), 165 deletions(-) delete mode 100644 java/code/src/com/redhat/rhn/domain/action/test/ProxyConfigurationApplyActionTest.java diff --git a/java/code/src/com/redhat/rhn/domain/action/ProxyConfigurationApplyAction.java b/java/code/src/com/redhat/rhn/domain/action/ProxyConfigurationApplyAction.java index 668492b8b62b..48e31675c479 100644 --- a/java/code/src/com/redhat/rhn/domain/action/ProxyConfigurationApplyAction.java +++ b/java/code/src/com/redhat/rhn/domain/action/ProxyConfigurationApplyAction.java @@ -17,17 +17,17 @@ import com.redhat.rhn.domain.org.Org; -import com.redhat.rhn.domain.server.MinionSummary; import com.redhat.rhn.domain.server.Pillar; -import com.suse.manager.webui.services.SaltServerActionService; import com.suse.proxy.ProxyConfigUtils; import com.suse.salt.netapi.calls.LocalCall; import com.suse.salt.netapi.calls.modules.State; +import com.suse.salt.netapi.utils.Xor; + +import com.google.gson.reflect.TypeToken; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -36,6 +36,7 @@ */ public class ProxyConfigurationApplyAction extends Action { + private static final String APPLY_PROXY_CONFIG = "proxy.apply_proxy_config"; private final Pillar pillar; private final Map proxyConfigFiles; @@ -47,6 +48,7 @@ public class ProxyConfigurationApplyAction extends Action { */ public ProxyConfigurationApplyAction(Pillar pillarIn, Map proxyConfigFilesIn, Org orgIn) { this.setActionType(ActionFactory.TYPE_PROXY_CONFIGURATION_APPLY); + this.setId(ActionFactory.TYPE_PROXY_CONFIGURATION_APPLY.getId().longValue()); this.pillar = pillarIn; this.proxyConfigFiles = proxyConfigFilesIn; this.setOrg(orgIn); @@ -61,18 +63,30 @@ public Map getProxyConfigFiles() { } /** - * Get the apply_proxy_config local call - * @param minions the minions + * Builds the LocalCall for the apply_proxy_config state apply with the pillar and config files data * @return the apply_proxy_config local call */ - public Map, List> getApplyProxyConfigAction(List minions) { + public LocalCall>> getApplyProxyConfigCall() { + Map data = new HashMap<>(); + data.putAll(ProxyConfigUtils.applyProxyConfigDataFromPillar(getPillar())); + data.putAll(getProxyConfigFiles()); + + return State.apply( + Collections.singletonList(APPLY_PROXY_CONFIG), + Optional.of(data), + Optional.of(false), Optional.of(false), + new TypeToken>>() { } + ); + } + + public LocalCall> getApplyProxyConfigCallSimple() { Map data = new HashMap<>(); data.putAll(ProxyConfigUtils.applyProxyConfigDataFromPillar(getPillar())); data.putAll(getProxyConfigFiles()); - return Map.of( - State.apply(Collections.singletonList(SaltServerActionService.APPLY_PROXY_CONFIG), Optional.of(data)), - minions + return State.apply( + Collections.singletonList(APPLY_PROXY_CONFIG), + Optional.of(data) ); } } diff --git a/java/code/src/com/redhat/rhn/domain/action/test/ProxyConfigurationApplyActionTest.java b/java/code/src/com/redhat/rhn/domain/action/test/ProxyConfigurationApplyActionTest.java deleted file mode 100644 index accb63a8e3a4..000000000000 --- a/java/code/src/com/redhat/rhn/domain/action/test/ProxyConfigurationApplyActionTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2025 SUSE LLC - * - * This software is licensed to you under the GNU General Public License, - * version 2 (GPLv2). There is NO WARRANTY for this software, express or - * implied, including the implied warranties of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 - * along with this software; if not, see - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * Red Hat trademarks are not licensed under GPLv2. No permission is - * granted to use or replicate Red Hat trademarks that are incorporated - * in this software or its documentation. - */ - -package com.redhat.rhn.domain.action.test; - -import static com.redhat.rhn.domain.action.ActionFactory.TYPE_PROXY_CONFIGURATION_APPLY; -import static com.suse.proxy.ProxyConfigUtils.EMAIL_FIELD; -import static com.suse.proxy.ProxyConfigUtils.MAX_CACHE_FIELD; -import static com.suse.proxy.ProxyConfigUtils.PARENT_FQDN_FIELD; -import static com.suse.proxy.ProxyConfigUtils.PROXY_PILLAR_CATEGORY; -import static com.suse.proxy.ProxyConfigUtils.PROXY_PORT_FIELD; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import com.redhat.rhn.domain.action.ProxyConfigurationApplyAction; -import com.redhat.rhn.domain.server.MinionServer; -import com.redhat.rhn.domain.server.MinionSummary; -import com.redhat.rhn.domain.server.Pillar; -import com.redhat.rhn.domain.server.test.MinionServerFactoryTest; -import com.redhat.rhn.domain.user.User; -import com.redhat.rhn.testing.RhnBaseTestCase; -import com.redhat.rhn.testing.UserTestUtils; - -import com.suse.salt.netapi.calls.LocalCall; - -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; - -public class ProxyConfigurationApplyActionTest extends RhnBaseTestCase { - - @Test - public void testGetApplyProxyConfigAction() { - final Map pillarMap = Map.of( - PROXY_PORT_FIELD, "3128", - MAX_CACHE_FIELD, "456", - PARENT_FQDN_FIELD, "parent.suse.com", - EMAIL_FIELD, "admin@suse.com" - ); - final Map proxyConfigFilesMap = Map.of( - "some", "more", - "additional", "configs" - ); - final User user = UserTestUtils.createUser("testUser", - UserTestUtils.createOrg("testOrg" + this.getClass().getSimpleName())); - final Pillar pillar = new Pillar(PROXY_PILLAR_CATEGORY, pillarMap); - final ProxyConfigurationApplyAction action = - new ProxyConfigurationApplyAction(pillar, proxyConfigFilesMap, user.getOrg()); - - // - assertEquals(TYPE_PROXY_CONFIGURATION_APPLY, action.getActionType()); - assertEquals(proxyConfigFilesMap, action.getProxyConfigFiles()); - - Pillar actionPillar = action.getPillar(); - assertNotNull(actionPillar); - assertEquals(pillarMap, actionPillar.getPillar()); - assertEquals(PROXY_PILLAR_CATEGORY, actionPillar.getCategory()); - - // - MinionServer testMinionServer = MinionServerFactoryTest.createTestMinionServer(user); - MinionSummary testMinionSummary = new MinionSummary(testMinionServer); - - Map, List> result = action.getApplyProxyConfigAction(List.of(testMinionSummary)); - assertNotNull(result); - assertEquals(1, result.size()); - assertEquals(testMinionSummary, result.values().iterator().next().get(0)); - - // check pillar result (from call) contains all the values provided in pillarMap and proxyConfigFilesMap - LocalCall call = result.keySet().iterator().next(); - assertNotNull(call); - - Object kwarg = call.getPayload().get("kwarg"); - assertNotNull(kwarg); - assertInstanceOf(Map.class, kwarg); - - Object pillarObj = ((Map) kwarg).get("pillar"); - assertNotNull(pillarObj); - assertInstanceOf(Map.class, pillarObj); - - Map pillarFromCall = (Map) pillarObj; - pillarMap.forEach((key, value) -> assertEquals(value, pillarFromCall.get(key))); - proxyConfigFilesMap.forEach((key, value) -> assertEquals(value, pillarFromCall.get(key))); - } -} diff --git a/java/code/src/com/suse/manager/webui/services/SaltServerActionService.java b/java/code/src/com/suse/manager/webui/services/SaltServerActionService.java index 554332f60ae6..df29e6316e65 100644 --- a/java/code/src/com/suse/manager/webui/services/SaltServerActionService.java +++ b/java/code/src/com/suse/manager/webui/services/SaltServerActionService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016--2025 SUSE LLC + * Copyright (c) 2016 SUSE LLC * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or @@ -44,7 +44,6 @@ import com.redhat.rhn.domain.action.ActionFactory; import com.redhat.rhn.domain.action.ActionStatus; import com.redhat.rhn.domain.action.ActionType; -import com.redhat.rhn.domain.action.ProxyConfigurationApplyAction; import com.redhat.rhn.domain.action.appstream.AppStreamAction; import com.redhat.rhn.domain.action.appstream.AppStreamActionDetails; import com.redhat.rhn.domain.action.channel.SubscribeChannelsAction; @@ -221,7 +220,6 @@ public class SaltServerActionService { public static final String APPSTREAMS_CONFIGURE = "appstreams.configure"; public static final String PARAM_APPSTREAMS_ENABLE = "param_appstreams_enable"; public static final String PARAM_APPSTREAMS_DISABLE = "param_appstreams_disable"; - public static final String APPLY_PROXY_CONFIG = "proxy.apply_proxy_config"; /** SLS pillar parameter name for the list of update stack patch names. */ public static final String PARAM_UPDATE_STACK_PATCHES = "param_update_stack_patches"; @@ -363,9 +361,6 @@ else if (ActionFactory.TYPE_COCO_ATTESTATION.equals(actionType)) { else if (ActionFactory.TYPE_APPSTREAM_CONFIGURE.equals(actionType)) { return appStreamAction(minions, (AppStreamAction) actionIn); } - else if (ActionFactory.TYPE_PROXY_CONFIGURATION_APPLY.equals(actionType)) { - return ((ProxyConfigurationApplyAction) actionIn).getApplyProxyConfigAction(minions); - } else { if (LOG.isDebugEnabled()) { LOG.debug("Action type {} is not supported with Salt", actionType != null ? actionType.getName() : ""); @@ -1895,10 +1890,9 @@ private Map> callAsyncActionChainStart( * * @param action the action to be executed * @param minion minion on which the action will be executed - * @return a map with the results for all calls */ - public Map, Optional> executeSSHAction(Action action, MinionServer minion) { - return executeSSHAction(action, minion, false); + public void executeSSHAction(Action action, MinionServer minion) { + executeSSHAction(action, minion, false); } /** @@ -1907,13 +1901,8 @@ public Map, Optional> executeSSHAction(Action action, * @param action the action to be executed * @param minion minion on which the action will be executed * @param forcePkgRefresh set to true if a package list refresh should be scheduled at the end - * @return a map with the results for all calls */ - public Map, Optional> executeSSHAction( - Action action, MinionServer minion, boolean forcePkgRefresh - ) { - Map, Optional> results = new HashMap<>(); - + public void executeSSHAction(Action action, MinionServer minion, boolean forcePkgRefresh) { Optional serverAction = action.getServerActions().stream() .filter(sa -> sa.getServerId().equals(minion.getId())) .findFirst(); @@ -2004,7 +1993,6 @@ public Map, Optional> executeSSHAction( sa.setCompletionTime(new Date()); } }, jsonResult -> { - results.put(call, Optional.of(jsonResult)); String function = (String) call.getPayload().get("fun"); /* bsc#1197591 ssh push reboot has an answer that is not a failure but the action needs to stay @@ -2042,11 +2030,9 @@ else if (sa.getStatus().equals(ActionFactory.STATUS_QUEUED)) { sa.setStatus(STATUS_FAILED); sa.setResultMsg("Minion is down or could not be contacted."); sa.setCompletionTime(new Date()); - results.put(call, Optional.empty()); }); } }); - return results; } /** diff --git a/java/code/src/com/suse/proxy/update/ProxyConfigUpdateApplySaltState.java b/java/code/src/com/suse/proxy/update/ProxyConfigUpdateApplySaltState.java index f757c18753ea..5f6af7f6aa57 100644 --- a/java/code/src/com/suse/proxy/update/ProxyConfigUpdateApplySaltState.java +++ b/java/code/src/com/suse/proxy/update/ProxyConfigUpdateApplySaltState.java @@ -15,20 +15,14 @@ package com.suse.proxy.update; -import static com.suse.utils.Predicates.isAbsent; - -import com.redhat.rhn.GlobalInstanceHolder; import com.redhat.rhn.domain.action.ProxyConfigurationApplyAction; -import com.redhat.rhn.manager.action.ActionManager; - -import com.suse.salt.netapi.calls.LocalCall; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; +import com.suse.salt.netapi.calls.modules.State; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -38,45 +32,52 @@ public class ProxyConfigUpdateApplySaltState implements ProxyConfigUpdateContextHandler { private static final Logger LOG = LogManager.getLogger(ProxyConfigUpdateApplySaltState.class); - public static final String FAIL_APPLY_MESSAGE = "Failed to apply proxy configuration salt state."; + private static final String FAIL_APPLY_MESSAGE = "Failed to apply proxy configuration salt state."; @Override public void handle(ProxyConfigUpdateContext context) { + ProxyConfigurationApplyAction action = new ProxyConfigurationApplyAction( context.getPillar(), context.getProxyConfigFiles(), context.getUser().getOrg() ); - action.setName("Apply proxy configuration: " + context.getProxyMinion().getMinionId()); - ActionManager.addServerToAction(context.getProxyMinion(), action); - Map, Optional> applySaltStateResponse = - GlobalInstanceHolder.SALT_SERVER_ACTION_SERVICE.executeSSHAction(action, context.getProxyMinion()); - if (isAbsent(applySaltStateResponse)) { - context.getErrorReport().register(FAIL_APPLY_MESSAGE); - LOG.error(FAIL_APPLY_MESSAGE + " No response."); - return; - } - else if (applySaltStateResponse.size() != 1) { - context.getErrorReport().register(FAIL_APPLY_MESSAGE); - LOG.error(FAIL_APPLY_MESSAGE + " Unexpected response size. {}", applySaltStateResponse); - return; - } + Optional> stringApplyResultMap = context.getSaltApi().callSync( + action.getApplyProxyConfigCall(), + context.getProxyMinion().getMinionId()) + .map( + result -> result.fold( + error -> { + context.getErrorReport().register(error); + return null; + }, + applyResults -> { + if (applyResults.isEmpty()) { + context.getErrorReport().register(FAIL_APPLY_MESSAGE); + LOG.error(FAIL_APPLY_MESSAGE + " Unexpected response size. {}", applyResults.size()); + return null; + } + + List failedStates = applyResults.entrySet().stream() + .filter(p -> !p.getValue().isResult()) + .map(e -> + String.format( + "name: %s, comment: %s", + e.getKey(), + e.getValue().getComment() + )) + .toList(); + if (!failedStates.isEmpty()) { + context.getErrorReport().register(FAIL_APPLY_MESSAGE); + LOG.debug(FAIL_APPLY_MESSAGE + " Fail applying: {}", failedStates); + } + return applyResults; + })); - JsonElement jsonElement = applySaltStateResponse.values().iterator().next().orElse(null); - if (jsonElement == null || !jsonElement.isJsonObject()) { + if (stringApplyResultMap.isEmpty()) { context.getErrorReport().register(FAIL_APPLY_MESSAGE); - LOG.error(FAIL_APPLY_MESSAGE + " Unexpected response format. {}", jsonElement); - return; + LOG.error(FAIL_APPLY_MESSAGE + " No apply results."); } - JsonObject jsonObject = jsonElement.getAsJsonObject(); - for (String key : jsonObject.keySet()) { - JsonElement jsonElementChild = jsonObject.get(key); - if (!jsonElementChild.getAsJsonObject().get("result").getAsBoolean()) { - context.getErrorReport().register(FAIL_APPLY_MESSAGE); - LOG.error(FAIL_APPLY_MESSAGE + " Failing entry: {}", jsonElementChild); - } - } - } } diff --git a/susemanager-utils/susemanager-sls/salt/proxy/apply_proxy_config.sls b/susemanager-utils/susemanager-sls/salt/proxy/apply_proxy_config.sls index 710ccd7358de..8eb2e706f864 100644 --- a/susemanager-utils/susemanager-sls/salt/proxy/apply_proxy_config.sls +++ b/susemanager-utils/susemanager-sls/salt/proxy/apply_proxy_config.sls @@ -113,6 +113,12 @@ mgrpxy_installed: [Install] WantedBy=multi-user.target + - require: + - file: /etc/uyuni/proxy/config.yaml + - file: /etc/uyuni/proxy/httpd.yaml + - file: /etc/uyuni/proxy/ssh.yaml + - pkg: podman_installed + - pkg: mgrpxy_installed # The system will run this service to enable apply_proxy_config.service after reboot enable_apply_proxy_config_service: @@ -121,10 +127,6 @@ enable_apply_proxy_config_service: - enable: True - require: - file: /etc/systemd/system/apply_proxy_config.service - - file: /etc/uyuni/proxy/config.yaml - - file: /etc/uyuni/proxy/httpd.yaml - - file: /etc/uyuni/proxy/ssh.yaml - {% else %}