From d50f71b22a4a96cc17fbafc1cdbe28e7cc536b29 Mon Sep 17 00:00:00 2001 From: mbussolotto Date: Tue, 27 Sep 2022 15:30:49 +0200 Subject: [PATCH 1/8] Refactoring --- .../rhn/common/NoWheelResultsException.java | 47 + .../reactor/messaging/RegistrationUtils.java | 2 +- .../test/MinionActionCleanupTest.java | 12 +- .../test/RegisterMinionActionTest.java | 10 +- .../manager/webui/controllers/StatesAPI.java | 2 +- .../bootstrap/AbstractMinionBootstrapper.java | 2 +- .../bootstrap/RegularMinionBootstrapper.java | 2 +- .../AbstractMinionBootstrapperTestBase.java | 16 +- .../test/RegularMinionBootstrapperTest.java | 4 +- .../manager/webui/services/iface/SaltApi.java | 1079 +++++++++--- .../webui/services/iface/SystemQuery.java | 375 ++++- .../webui/services/impl/SaltService.java | 1439 ----------------- .../services/impl/test/SaltServiceTest.java | 16 +- .../webui/services/test/TestSaltApi.java | 8 +- .../webui/services/test/TestSystemQuery.java | 2 +- .../webui/utils/MinionActionUtils.java | 2 +- .../webui/websocket/RemoteMinionCommands.java | 4 +- java/spacewalk-java.changes | 2 + 18 files changed, 1294 insertions(+), 1730 deletions(-) create mode 100644 java/code/src/com/redhat/rhn/common/NoWheelResultsException.java delete mode 100644 java/code/src/com/suse/manager/webui/services/impl/SaltService.java diff --git a/java/code/src/com/redhat/rhn/common/NoWheelResultsException.java b/java/code/src/com/redhat/rhn/common/NoWheelResultsException.java new file mode 100644 index 000000000000..c6fbb0224c76 --- /dev/null +++ b/java/code/src/com/redhat/rhn/common/NoWheelResultsException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 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.common; + +/** + * NoWheelResultsException + */ +public class NoWheelResultsException extends RhnRuntimeException { + + /** + * Default Constructor + */ + public NoWheelResultsException() { + super(); + } + + /** + * Constructor + * @param errorMessage + */ + public NoWheelResultsException(String errorMessage) { + super(errorMessage); + } + /** + * Constructor + * @param errorMessage + * @param err + */ + public NoWheelResultsException(String errorMessage, Throwable err) { + super(errorMessage, err); + } + + + +} diff --git a/java/code/src/com/suse/manager/reactor/messaging/RegistrationUtils.java b/java/code/src/com/suse/manager/reactor/messaging/RegistrationUtils.java index 58f7602b8ff5..844480e8632d 100644 --- a/java/code/src/com/suse/manager/reactor/messaging/RegistrationUtils.java +++ b/java/code/src/com/suse/manager/reactor/messaging/RegistrationUtils.java @@ -363,7 +363,7 @@ else if ("redhat".equalsIgnoreCase(grains.getValueAsString(OS)) || "amazon".equalsIgnoreCase(grains.getValueAsString(OS)) || "rocky".equalsIgnoreCase(grains.getValueAsString(OS))) { - Optional redhatProductInfo = systemQuery.redhatProductInfo(server.getMinionId()); + Optional redhatProductInfo = systemQuery.getRedhatProductInfo(server.getMinionId()); Optional rhelProduct = redhatProductInfo.flatMap(x -> RhelUtils.detectRhelProduct( diff --git a/java/code/src/com/suse/manager/reactor/messaging/test/MinionActionCleanupTest.java b/java/code/src/com/suse/manager/reactor/messaging/test/MinionActionCleanupTest.java index ebfbd042f5c8..51b693539fe8 100644 --- a/java/code/src/com/suse/manager/reactor/messaging/test/MinionActionCleanupTest.java +++ b/java/code/src/com/suse/manager/reactor/messaging/test/MinionActionCleanupTest.java @@ -113,10 +113,10 @@ public void testMinionActionCleanup() throws Exception { SaltService saltServiceMock = mock(SaltService.class); context().checking(new Expectations() { { - allowing(saltServiceMock).running(with(any(MinionList.class))); + allowing(saltServiceMock).getRunning(with(any(MinionList.class))); will(returnValue(running)); - never(saltServiceMock).jobsByMetadata(with(any(Object.class))); - never(saltServiceMock).listJob(with(any(String.class))); + never(saltServiceMock).getJobsByMetadata(with(any(Object.class))); + never(saltServiceMock).getListJob(with(any(String.class))); } }); SaltUtils saltUtils = new SaltUtils(saltServiceMock, saltServiceMock); @@ -227,7 +227,7 @@ public void testMinionActionChainCleanupAllCompleted() throws Exception { allowing(taskomaticMock).scheduleActionExecution(with(any(Action.class))); allowing(taskomaticMock).scheduleActionChainExecution(with(any(ActionChain.class))); - allowing(saltServiceMock).jobsByMetadata( + allowing(saltServiceMock).getJobsByMetadata( with(any(Object.class)), with(any(LocalDateTime.class)), with(any(LocalDateTime.class))); will(returnValue(Optional.of(jobsByMetadata("jobs.list_jobs.actionchains.json", 0)))); @@ -238,8 +238,8 @@ public void testMinionActionChainCleanupAllCompleted() throws Exception { } private void mockListJob(String jid) { - never(saltServiceMock).jobsByMetadata(with(any(Object.class))); - never(saltServiceMock).listJob(with(any(String.class))); + never(saltServiceMock).getJobsByMetadata(with(any(Object.class))); + never(saltServiceMock).getListJob(with(any(String.class))); } }); diff --git a/java/code/src/com/suse/manager/reactor/test/RegisterMinionActionTest.java b/java/code/src/com/suse/manager/reactor/test/RegisterMinionActionTest.java index dbda1bed94ed..0524038980bd 100644 --- a/java/code/src/com/suse/manager/reactor/test/RegisterMinionActionTest.java +++ b/java/code/src/com/suse/manager/reactor/test/RegisterMinionActionTest.java @@ -1049,7 +1049,7 @@ public void testRegisterRHELMinionWithoutActivationKey() throws Exception { "redhat-release-server(x86-64),system-release,system-release(releasever),\n" + "PROVIDEVERSION=7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7,7Server,\n"))))); - allowing(saltServiceMock).redhatProductInfo(MINION_ID); + allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)"), @@ -1109,7 +1109,7 @@ public void testRegisterRHELMinionWithRESActivationKeyOneBaseChannel() throws Ex "redhat-release-server(x86-64),system-release,system-release(releasever),\n" + "PROVIDEVERSION=7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7,7Server,\n"))))); - allowing(saltServiceMock).redhatProductInfo(MINION_ID); + allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)"), @@ -1184,7 +1184,7 @@ public void testRegisterRHELMinionWithRESActivationKeyTwoBaseChannels() throws E "PROVIDEVERSION=7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7," + "7.2-9.el7,7Server,\n"))))); - allowing(saltServiceMock).redhatProductInfo(MINION_ID); + allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)"), @@ -1264,7 +1264,7 @@ public void testRegisterRESMinionWithoutActivationKey() throws Exception { "PROVIDEVERSION=,7.2-9.el7.2.1,7.2-9.el7.2.1,7.2-9.el7.2.1,7.2-9.el7.2.1," + "7.2-9.el7.2.1,7.2-9.el7.2.1,7Server,\n"))))); - allowing(saltServiceMock).redhatProductInfo(MINION_ID); + allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)\n" + @@ -1352,7 +1352,7 @@ public void testRegisterRHELMinionWithMultipleReleasePackages() throws Exception "PROVIDEVERSION=,7.3-7.el7,7.3-7.el7,7.3-7.el7,7.3-7.el7,7.3-7.el7,7.3-7.el7," + "7Server,\n"))))); - allowing(saltServiceMock).redhatProductInfo(MINION_ID); + allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)"), diff --git a/java/code/src/com/suse/manager/webui/controllers/StatesAPI.java b/java/code/src/com/suse/manager/webui/controllers/StatesAPI.java index 8af774b77937..9d695439ec35 100644 --- a/java/code/src/com/suse/manager/webui/controllers/StatesAPI.java +++ b/java/code/src/com/suse/manager/webui/controllers/StatesAPI.java @@ -720,7 +720,7 @@ public String showHighstate(Request request, Response response) { // sync to minion before showing highstate saltApi.syncAll(minions); - Map> result = saltApi.showHighstate(minionId); + Map> result = saltApi.getShowHighstate(minionId); return Optional.ofNullable(result.get(minionId)) .map(r -> r.fold( diff --git a/java/code/src/com/suse/manager/webui/controllers/bootstrap/AbstractMinionBootstrapper.java b/java/code/src/com/suse/manager/webui/controllers/bootstrap/AbstractMinionBootstrapper.java index 5b34f3ab8402..a9d73f6cd75a 100644 --- a/java/code/src/com/suse/manager/webui/controllers/bootstrap/AbstractMinionBootstrapper.java +++ b/java/code/src/com/suse/manager/webui/controllers/bootstrap/AbstractMinionBootstrapper.java @@ -362,7 +362,7 @@ private List validateBootstrap(BootstrapParameters params) { return Collections.singletonList(reactivationKeyError.get()); } - if (saltApi.keyExists(params.getHost(), KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED)) { + if (saltApi.isKeyExists(params.getHost(), KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED)) { return Collections.singletonList("A salt key for this" + " host (" + params.getHost() + ") seems to already exist, please check!"); diff --git a/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java b/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java index a07f73d96550..be8e3cd376dc 100644 --- a/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java +++ b/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java @@ -97,7 +97,7 @@ protected BootstrapResult bootstrapInternal(BootstrapParameters input, User user // If a key is pending for this minion, temporarily reject it boolean weRejectedIt = false; - if (saltApi.keyExists(minionId, KeyStatus.UNACCEPTED)) { + if (saltApi.isKeyExists(minionId, KeyStatus.UNACCEPTED)) { LOG.info("Pending key exists for {}, rejecting...", minionId); saltApi.rejectKey(minionId); weRejectedIt = true; diff --git a/java/code/src/com/suse/manager/webui/controllers/utils/test/AbstractMinionBootstrapperTestBase.java b/java/code/src/com/suse/manager/webui/controllers/utils/test/AbstractMinionBootstrapperTestBase.java index bca6b57f48e8..35524c29bac1 100644 --- a/java/code/src/com/suse/manager/webui/controllers/utils/test/AbstractMinionBootstrapperTestBase.java +++ b/java/code/src/com/suse/manager/webui/controllers/utils/test/AbstractMinionBootstrapperTestBase.java @@ -79,7 +79,7 @@ public void testBootstrapFailsWhenKeysExist() throws Exception { setEmptyActivationKeys(input); context().checking(new Expectations() {{ - allowing(saltServiceMock).keyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(true)); }}); @@ -142,7 +142,7 @@ public void testBootstrapFailsWhenMinionExists() setEmptyActivationKeys(input); context().checking(new Expectations() {{ - allowing(saltServiceMock).keyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); }}); @@ -162,9 +162,9 @@ public void testBootstrapSuccess() throws Exception { Key.Pair keyPair = mockKeyPair(); context().checking(new Expectations() {{ - allowing(saltServiceMock).keyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); - allowing(saltServiceMock).keyExists("myhost", KeyStatus.UNACCEPTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); will(returnValue(false)); allowing(saltServiceMock).generateKeysAndAccept("myhost", false); @@ -230,9 +230,9 @@ protected void testCompatibleActivationKeysBase(ActivationKey key) throws Except allowing(input).maybeGetReactivationKey(); will(returnValue(empty())); - allowing(saltServiceMock).keyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); - allowing(saltServiceMock).keyExists("myhost", KeyStatus.UNACCEPTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); will(returnValue(false)); Key.Pair keyPair = mockKeyPair(); @@ -272,9 +272,9 @@ protected void testCompatibleActivationKeysBase(ActivationKey key, ActivationKey allowing(input).maybeGetReactivationKey(); will(returnValue(Optional.of(reactKey.getKey()))); - allowing(saltServiceMock).keyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); - allowing(saltServiceMock).keyExists("myhost", KeyStatus.UNACCEPTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); will(returnValue(false)); Key.Pair keyPair = mockKeyPair(); diff --git a/java/code/src/com/suse/manager/webui/controllers/utils/test/RegularMinionBootstrapperTest.java b/java/code/src/com/suse/manager/webui/controllers/utils/test/RegularMinionBootstrapperTest.java index 5ffc2f69fda6..d7314adb8c5f 100644 --- a/java/code/src/com/suse/manager/webui/controllers/utils/test/RegularMinionBootstrapperTest.java +++ b/java/code/src/com/suse/manager/webui/controllers/utils/test/RegularMinionBootstrapperTest.java @@ -76,9 +76,9 @@ public void testKeysDeletedAfterFailure() throws Exception { Key.Pair keyPair = mockKeyPair(); context().checking(new Expectations() {{ - allowing(saltServiceMock).keyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); - allowing(saltServiceMock).keyExists("myhost", KeyStatus.UNACCEPTED); + allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); will(returnValue(false)); allowing(saltServiceMock).generateKeysAndAccept("myhost", false); diff --git a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java index b3f3aa2966ef..dc73d8323f0d 100644 --- a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java +++ b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java @@ -14,77 +14,221 @@ */ package com.suse.manager.webui.services.iface; +import com.redhat.rhn.common.NoWheelResultsException; +import com.redhat.rhn.common.RhnRuntimeException; +import com.redhat.rhn.common.conf.ConfigDefaults; +import com.redhat.rhn.common.messaging.JavaMailException; import com.redhat.rhn.domain.server.MinionServer; +import com.redhat.rhn.domain.server.MinionServerFactory; +import com.redhat.rhn.manager.audit.scap.file.ScapFileManager; +import com.suse.manager.reactor.PGEventStream; +import com.suse.manager.reactor.messaging.ApplyStatesEventMessage; import com.suse.manager.ssl.SSLCertPair; -import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService; -import com.suse.manager.webui.services.impl.runner.MgrK8sRunner; +import com.suse.manager.utils.MailHelper; +import com.suse.manager.utils.MinionServerUtils; +import com.suse.manager.webui.services.impl.MinionPendingRegistrationService; +import com.suse.manager.webui.services.impl.runner.MgrKiwiImageRunner; +import com.suse.manager.webui.services.impl.runner.MgrRunner; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.manager.webui.utils.ElementCallJson; -import com.suse.manager.webui.utils.gson.BootstrapParameters; import com.suse.manager.webui.utils.salt.custom.ScheduleMetadata; +import com.suse.salt.netapi.AuthModule; +import com.suse.salt.netapi.calls.AbstractCall; import com.suse.salt.netapi.calls.LocalAsyncResult; import com.suse.salt.netapi.calls.LocalCall; +import com.suse.salt.netapi.calls.RunnerCall; +import com.suse.salt.netapi.calls.WheelCall; +import com.suse.salt.netapi.calls.WheelResult; +import com.suse.salt.netapi.calls.modules.Cmd; import com.suse.salt.netapi.calls.modules.SaltUtil; import com.suse.salt.netapi.calls.modules.State; -import com.suse.salt.netapi.calls.runner.Jobs; +import com.suse.salt.netapi.calls.modules.State.ApplyResult; +import com.suse.salt.netapi.calls.modules.Test; import com.suse.salt.netapi.calls.wheel.Key; +import com.suse.salt.netapi.client.SaltClient; +import com.suse.salt.netapi.client.impl.HttpAsyncClientImpl; +import com.suse.salt.netapi.datatypes.AuthMethod; +import com.suse.salt.netapi.datatypes.Batch; +import com.suse.salt.netapi.datatypes.PasswordAuth; import com.suse.salt.netapi.datatypes.target.MinionList; import com.suse.salt.netapi.datatypes.target.Target; import com.suse.salt.netapi.errors.GenericError; +import com.suse.salt.netapi.errors.SaltError; +import com.suse.salt.netapi.event.EventListener; import com.suse.salt.netapi.event.EventStream; import com.suse.salt.netapi.exception.SaltException; import com.suse.salt.netapi.results.Result; -import com.suse.salt.netapi.results.SSHResult; +import com.suse.salt.netapi.results.StateApplyResult; +import com.suse.utils.Opt; import com.google.gson.JsonElement; import com.google.gson.reflect.TypeToken; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.impl.nio.client.HttpAsyncClients; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; -import java.time.LocalDateTime; +import java.nio.file.Paths; +import java.nio.file.attribute.GroupPrincipal; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.logging.LogManager; +import java.util.stream.Collectors; /** * Interface for interacting with salt. */ -public interface SaltApi { +public class SaltApi { + + enum Messages { + + CLEANUP_MINION_SALT_STATE("cleanup_minion"), GENERIC_RUNNER_ERROR( + "Generic Salt error for runner call %s: %s"), SSH_RUNNER_ERROR( + "SaltSSH error for runner call %s: %s"), PAYLOAD_CALL_TEMPLATE( + "%s with payload [%s]"), MINION_UNREACHABLE_ERROR( + "minion_unreachable"); + + private final String text; + + Messages(final String textIn) { + this.text = textIn; + } + + @Override + public String toString() { + return text; + } + + } + + private static final Logger LOG = LogManager.getLogger(SaltApi.class); + + // Reconnecting time (in seconds) to Salt event bus + private static final int DELAY_TIME_SECONDS = 5; + public static final int SSH_DEFAULT_PORT = 22; + + private static final Predicate SALT_MINION_PREDICATE = + mid -> MinionPendingRegistrationService.containsSSHMinion(mid) || + MinionServerFactory.findByMinionId(mid) + .filter(MinionServerUtils::isSshPushMinion).isPresent(); + + private static final String SALT_USER = "admin"; + private static final String SALT_PASSWORD = + com.redhat.rhn.common.conf.Config.get().getString("server.secret_key"); + private static final AuthModule AUTH_MODULE = AuthModule.FILE; + static final AuthMethod PW_AUTH = + new AuthMethod(new PasswordAuth(SALT_USER, SALT_PASSWORD, AUTH_MODULE)); + + private static final URI SALT_MASTER_URI = URI.create("https://" + + com.redhat.rhn.common.conf.Config.get().getString(ConfigDefaults.SALT_API_HOST, + "localhost") + + ":" + com.redhat.rhn.common.conf.Config.get() + .getString(ConfigDefaults.SALT_API_PORT, "9080")); + + private final Batch defaultBatch; + protected final SaltClient saltClient; + private final CloseableHttpAsyncClient asyncHttpClient; + private EventStream eventStream; /** - * Sync the channels of a list of minions - * @param minionIds of the targets. - * @throws SaltException if anything goes wrong. + * Default constructor */ - void deployChannels(List minionIds) throws SaltException; + public SaltApi() { + RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(0) + .setSocketTimeout(0).setConnectionRequestTimeout(0) + .setCookieSpec(CookieSpecs.STANDARD).build(); + HttpAsyncClientBuilder httpClientBuilder = HttpAsyncClients.custom(); + httpClientBuilder.setDefaultRequestConfig(requestConfig); + + asyncHttpClient = + httpClientBuilder.setMaxConnPerRoute(20).setMaxConnTotal(20).build(); + asyncHttpClient.start(); + + defaultBatch = Batch.custom() + .withBatchAsAmount(ConfigDefaults.get().getSaltBatchSize()) + .withDelay(ConfigDefaults.get().getSaltBatchDelay()) + .withPresencePingTimeout(ConfigDefaults.get().getSaltPresencePingTimeout()) + .withPresencePingGatherJobTimeout( + ConfigDefaults.get().getSaltPresencePingGatherJobTimeout()) + .build(); + saltClient = + new SaltClient(SALT_MASTER_URI, new HttpAsyncClientImpl(asyncHttpClient)); + } /** - * Get information about all containers running in a Kubernetes cluster. - * @param kubeconfig path to the kubeconfig file - * @param context kubeconfig context to use - * @return a list of containers + * Constructor to use for unit testing + * + * @param client Salt client */ - Optional> getAllContainers(String kubeconfig, String context); + public SaltApi(SaltClient client) { + defaultBatch = Batch.custom() + .withBatchAsAmount(ConfigDefaults.get().getSaltBatchSize()) + .withDelay(ConfigDefaults.get().getSaltBatchDelay()) + .withPresencePingTimeout(ConfigDefaults.get().getSaltPresencePingTimeout()) + .withPresencePingGatherJobTimeout( + ConfigDefaults.get().getSaltPresencePingGatherJobTimeout()) + .build(); + } /** - * Using a RunnerCall, store given contents to given path and set the mode, so that SSH likes it - * (read-write for owner, nothing for others). + * Sync the channels of a list of minions + * @param minionIds of the targets. + * @throws SaltException if anything goes wrong. + */ + public void deployChannels(List minionIds) throws SaltException { + callSync(com.suse.salt.netapi.calls.modules.State + .apply(ApplyStatesEventMessage.CHANNELS), new MinionList(minionIds)); + } + + /** + * Using a RunnerCall, store given contents to given path and set the mode, + * so that SSH likes it (read-write for owner, nothing for others). * * @param path the path where key will be stored * @param contents the contents of the key (PEM format) - * @throws IllegalStateException if something goes wrong during the operation, or if given path is not absolute + * @throws IllegalStateException if something goes wrong during the + * operation, or if given path is not absolute */ - void storeSshKeyFile(Path path, String contents); + public void storeSshKeyFile(Path path, String contents) { + ensureAbsolutePath(path); - /** - * Match minions synchronously using a compound matcher. - * @param target compound matcher - * @return list of minion ids - */ - List matchCompoundSync(String target); + String absolutePath = path.toAbsolutePath().toString(); + RunnerCall createFile = MgrRunner.writeTextFile(absolutePath, contents); + if (callSync(createFile).isEmpty()) { + throw new IllegalStateException("Can't create SSH priv key file " + path); + } + + // this might not be needed, the file is created with sane perms already + RunnerCall setMode = MgrRunner.setFileMode(absolutePath, "0600"); + if (callSync(setMode).isEmpty()) { + throw new IllegalStateException("Can't set mode for SSH priv key file " + path); + } + } /** * Remove given file using RunnerCall @@ -93,7 +237,12 @@ public interface SaltApi { * @throws IllegalStateException if the given path is not absolute * @return Optional with true if the file deletion succeeded. */ - Optional removeFile(Path path); + public Optional removeFile(Path path) { + ensureAbsolutePath(path); + String absolutePath = path.toAbsolutePath().toString(); + RunnerCall createFile = MgrRunner.removeFile(absolutePath); + return callSync(createFile); + } /** * Copy given file using RunnerCall @@ -103,7 +252,19 @@ public interface SaltApi { * @throws IllegalStateException if the given path is not absolute * @return Optional with true if the file deletion succeeded. */ - Optional copyFile(Path src, Path dst); + public Optional copyFile(Path src, Path dst) { + ensureAbsolutePath(src); + ensureAbsolutePath(dst); + RunnerCall call = MgrRunner.copyFile(src.toAbsolutePath().toString(), + dst.toAbsolutePath().toString(), false, false); + return callSync(call); + } + + private void ensureAbsolutePath(Path path) { + if (!path.isAbsolute()) { + throw new IllegalStateException("Given path is not absolute: " + path); + } + } /** * Performs an test.echo on a target set of minions for checkIn purpose. @@ -111,13 +272,34 @@ public interface SaltApi { * @return the LocalAsyncResult of the test.echo call * @throws SaltException if we get a failure from Salt */ - Optional> checkIn(MinionList targetIn) throws SaltException; + public Optional> checkIn(MinionList targetIn) + throws SaltException { + try { + LocalCall call = Test.echo("checkIn"); + return callAsync(call, targetIn); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } /** * Apply util.systeminfo state on the specified minion list * @param minionTarget minion list */ - void updateSystemInfo(MinionList minionTarget); + public void updateSystemInfo(MinionList minionTarget) { + try { + callAsync( + State.apply( + Collections.singletonList(ApplyStatesEventMessage.SYSTEM_INFO), + Optional.empty()), + minionTarget, + Optional.of(ScheduleMetadata.getDefaultMetadata().withMinionStartup())); + } + catch (SaltException ex) { + LOG.debug("Error while executing util.systeminfo state: {}", ex.getMessage()); + } + } /** * Store the files uploaded by a minion to the SCAP storage directory. @@ -128,48 +310,90 @@ public interface SaltApi { * {@code false} -> err message * */ - Map storeMinionScapFiles(MinionServer minion, String uploadDir, Long actionId); + public Map storeMinionScapFiles(MinionServer minion, String uploadDir, + Long actionId) { + String actionPath = ScapFileManager.getActionPath(minion.getOrg().getId(), + minion.getId(), actionId); + Path mountPoint = Paths.get(com.redhat.rhn.common.conf.Config.get() + .getString(ConfigDefaults.MOUNT_POINT)); + try { + // create dirs + Path actionDir = Files.createDirectories(mountPoint.resolve(actionPath)); - /** - * Return show highstate result. - * @param minionId of the target minion. - * @return show highstate result. - * @throws SaltException if anything goes wrong. - */ - Map> showHighstate(String minionId) throws SaltException; + UserPrincipalLookupService lookupService = + FileSystems.getDefault().getUserPrincipalLookupService(); + GroupPrincipal susemanagerGroup = + lookupService.lookupPrincipalByGroupName("susemanager"); + GroupPrincipal wwwGroup = lookupService.lookupPrincipalByGroupName("www"); + // systems///actions/ + changeGroupAndPerms(actionDir, susemanagerGroup); + // systems///actions + actionDir = actionDir.getParent(); + while (!actionDir.equals(mountPoint)) { + changeGroupAndPerms(actionDir, wwwGroup); + actionDir = actionDir.getParent(); + } + + } + catch (IOException e) { + LOG.error("Error creating dir {}", mountPoint.resolve(actionPath), e); + } + + RunnerCall> call = MgrUtilRunner.moveMinionUploadedFiles( + minion.getMinionId(), uploadDir, com.redhat.rhn.common.conf.Config.get() + .getString(ConfigDefaults.MOUNT_POINT), + actionPath); + Optional> result = callSync(call, err -> err.fold(e -> { + LOG.error(String.format("Function [%s] not available for runner call %s.", + e.getFunctionName(), callToString(call))); + return Optional.of(Collections.singletonMap(false, + String.format("Function [%s] not available", e.getFunctionName()))); + }, e -> { + LOG.error(String.format("Module [%s] not supported for runner call %s.", + e.getModuleName(), callToString(call))); + return Optional.of(Collections.singletonMap(false, + String.format("Module [%s] not supported", e.getModuleName()))); + }, e -> { + LOG.error(String.format("Error parsing json response from runner call %s: %s", + callToString(call), e.getJson())); + return Optional.of(Collections.singletonMap(false, + "Error parsing json response: " + e.getJson())); + }, e -> { + LOG.error(String.format(Messages.GENERIC_RUNNER_ERROR.toString(), + callToString(call), e.getMessage())); + return Optional.of(Collections.singletonMap(false, + "Generic Salt error: " + e.getMessage())); + }, e -> { + LOG.error(String.format(Messages.SSH_RUNNER_ERROR.toString(), + callToString(call), e.getMessage())); + return Optional.of( + Collections.singletonMap(false, "SaltSSH error: " + e.getMessage())); + })); + return result.orElseGet(() -> Collections.singletonMap(false, + "Error moving scap result files." + " Please check the logs.")); + } /** - * Call the custom mgrutil.ssh_keygen runner if the key files are not present. + * Call the custom mgrutil.ssh_keygen runner if the key files are not + * present. * * @param path of the key files * @return the result of the runner call as a map */ - Optional generateSSHKey(String path); - - /** - * Chain ssh calls over one or more hops to run a command on the last host in the chain. - * This calls the mgrutil.chain_ssh_command runner. - * - * @param hosts a list of hosts, where the last one is where - * the command will be executed - * @param clientKey the ssh key to use to connect to the first host - * @param proxyKey the ssh key path to use for the rest of the hosts - * @param user the user - * @param options ssh options - * @param command the command to execute - * @param outputfile the file to which to dump the command stdout - * @return the execution result - */ - Optional chainSSHCommand(List hosts, String clientKey, String proxyKey, - String user, Map options, String command, - String outputfile); + public Optional generateSSHKey(String path) { + RunnerCall call = MgrUtilRunner.generateSSHKey(path); + return callSync(call); + } /** * Removes a hostname from the Salt ~/.ssh/known_hosts file. * @param hostname the hostname to remote * @return the result of the runner call */ - Optional removeSaltSSHKnownHost(String hostname); + public Optional removeSaltSSHKnownHost( + String hostname) { + return removeSaltSSHKnownHost(hostname, SSH_DEFAULT_PORT); + } /** * Removes a hostname from the Salt ~/.ssh/known_hosts file. @@ -177,90 +401,99 @@ Optional chainSSHCommand(List hosts, String cl * @param port the port of the host to remove * @return the result of the runner call */ - Optional removeSaltSSHKnownHost(String hostname, int port); + public Optional removeSaltSSHKnownHost( + String hostname, int port) { + RunnerCall call = + MgrUtilRunner.removeSSHKnowHost("salt", hostname, port); + return callSync(call); + } /** * Call 'saltutil.sync_grains' to sync the grains to the target minion(s). * @param minionList minion list */ - void syncGrains(MinionList minionList); + public void syncGrains(MinionList minionList) { + try { + LocalCall> call = + SaltUtil.syncGrains(Optional.empty(), Optional.empty()); + callSync(call, minionList); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } /** * Call 'saltutil.sync_modules' to sync the grains to the target minion(s). * @param minionList minion list */ - void syncModules(MinionList minionList); + public void syncModules(MinionList minionList) { + try { + LocalCall> call = + SaltUtil.syncModules(Optional.empty(), Optional.empty()); + callSync(call, minionList); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } /** * Call 'saltutil.sync_all' to sync everything to the target minion(s). * @param minionList minion list */ - void syncAll(MinionList minionList); + public void syncAll(MinionList minionList) { + try { + LocalCall> call = + SaltUtil.syncAll(Optional.empty(), Optional.empty()); + callSync(call, minionList); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } /** * call salt test.ping * @param minionId id of the target minion * @return true */ - Optional ping(String minionId); - - /** - * Return the stream of events happening in salt. - * - * @return the event stream - */ - EventStream getEventStream(); + public Optional ping(String minionId) { + return callSync(Test.ping(), minionId); + } /** * Delete a given minion's key. * * @param minionId id of the minion */ - void deleteKey(String minionId); + public void deleteKey(String minionId) { + if (callSync(Key.delete(minionId)).isEmpty()) { + throw new NoWheelResultsException(); + } + } /** * Accept all keys matching the given pattern * * @param match a pattern for minion ids */ - void acceptKey(String match); + public void acceptKey(String match) { + if (callSync(Key.accept(match)).isEmpty()) { + throw new NoWheelResultsException(); + } + } /** * Reject a given minion's key. * * @param minionId id of the minion */ - void rejectKey(String minionId); - - /** - * Get the specified grains for a given minion. - * @deprecated this function is too general and should be replaced by more specific functionality. - * @param minionId id of the target minion - * @param type class type, result should be parsed into - * @param grainNames list of grains names - * @param Type result should be parsed into - * @return Optional containing the grains parsed into specified type - */ - @Deprecated - Optional getGrains(String minionId, TypeToken type, String... grainNames); - - /** - * Get the grains for a given minion. - * - * @deprecated this function is too general and should be replaced by more specific functionality. - * @param minionId id of the target minion - * @return map containing the grains - */ - @Deprecated - Optional> getGrains(String minionId); - - /** - * Gets a minion's master hostname. - * - * @param minionId the minion id - * @return the master hostname - */ - Optional getMasterHostname(String minionId); + public void rejectKey(String minionId) { + if (callSync(Key.reject(minionId)).isEmpty()) { + throw new NoWheelResultsException(); + } + } /** * Run a remote command on a given minion. @@ -269,22 +502,27 @@ Optional chainSSHCommand(List hosts, String cl * @param cmd the command * @return the output of the command */ - Map> runRemoteCommand(MinionList target, String cmd); + public Map> runRemoteCommand(MinionList target, String cmd) { + try { + return callSync(Cmd.run(cmd), target); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } /** - * Delete a Salt key from the "Rejected Keys" category using the mgrutil runner. + * Delete a Salt key from the "Rejected Keys" category using the mgrutil + * runner. * * @param minionId the minionId to look for in "Rejected Keys" * @return the result of the runner call as a map */ - Optional deleteRejectedKey(String minionId); - - /** - * Get the minion keys from salt with their respective status. - * - * @return the keys with their respective status as returned from salt - */ - Key.Names getKeys(); + public Optional deleteRejectedKey(String minionId) { + RunnerCall call = + MgrUtilRunner.deleteRejectedKey(minionId); + return callSync(call); + } /** * Generate a key pair for the given id and accept the public key. @@ -293,128 +531,170 @@ Optional chainSSHCommand(List hosts, String cl * @param force set true to overwrite an already existing key * @return the generated key pair */ - Key.Pair generateKeysAndAccept(String id, boolean force); + public Key.Pair generateKeysAndAccept(String id, boolean force) { + if (callSync(Key.genAccept(id, Optional.of(force))).isEmpty()) { + throw new NoWheelResultsException("no wheel results"); + } + } - /** - * For a given minion id check if there is a key in any of the given status. If no status is given as parameter, - * all the available status are considered. - * - * @param id the id to check for - * @param statusIn array of key status to consider - * @return true if there is a key with the given id, false otherwise - */ - boolean keyExists(String id, SaltService.KeyStatus... statusIn); + private synchronized void eventStreamClosed() { + eventStream = null; + } - /** - * Get the minion keys from salt with their respective status and fingerprint. - * - * @return the keys with their respective status and fingerprint as returned from salt - */ - Key.Fingerprints getFingerprints(); + private EventStream createEventStream() throws SaltException { + return new PGEventStream(); + } /** * Remove SUSE Manager specific configuration from a Salt minion. * * @param minion the minion. - * @param timeout operation timeout * @return list of error messages or empty if no error */ - Optional> cleanupMinion(MinionServer minion, int timeout); + public Optional> cleanupMinion(MinionServer minion) { + Optional> response = applyState(minion.getMinionId(), + Messages.CLEANUP_MINION_SALT_STATE.toString()); + + // response is empty in case the minion is down + if (response.isPresent()) { + return response.get().values().stream().filter(value -> !value.isResult()) + .map(StateApplyResult::getComment) + .collect(Collectors.collectingAndThen(Collectors.toList(), + list -> list.isEmpty() ? Optional.empty() : Optional.of(list))); + } + return Optional.of( + Collections.singletonList(Messages.MINION_UNREACHABLE_ERROR.toString())); + } /** - * Bootstrap a system using salt-ssh. + * Partitions minion ids according to the contact method of corresponding + * minions (salt-ssh minions in one partition, regular minions in the + * other). * - * @param parameters - bootstrap parameters - * @param bootstrapMods - state modules to be applied during the bootstrap - * @param pillarData - pillar data used salt-ssh call - * @throws SaltException if something goes wrong during command execution or - * during manipulation the salt-ssh roster - * @return the result of the underlying ssh call for given host + * @param minionIds minion ids + * @return map with partitioning */ - Result>> bootstrapMinion( - BootstrapParameters parameters, List bootstrapMods, - Map pillarData) throws SaltException; - + public static Map> partitionMinionsByContactMethod( + Collection minionIds) { + return minionIds.stream().collect(Collectors.partitioningBy(SALT_MINION_PREDICATE)); + } /** - * Synchronously executes a salt function on a single minion. - * If a SaltException is thrown, re-throw a RuntimeException. + * Synchronously executes a salt function on a single minion. If a + * SaltException is thrown, re-throw a RuntimeException. * * @param call salt function to call * @param minionId minion id to target * @param result type of the salt function - * @return Optional holding the result of the function - * or empty if the minion did not respond. + * @return Optional holding the result of the function or empty if the + * minion did not respond. */ - Optional callSync(LocalCall call, String minionId); + Map> callSync(LocalCall callIn, MinionList target) + throws SaltException { - /** - * Run a remote command on a given minion asynchronously. - * @param target the target - * @param cmd the command to execute - * @param cancel a future used to cancel waiting on return events - * @return a map holding a {@link CompletionStage}s for each minion - */ - Map>> runRemoteCommandAsync( - MinionList target, String cmd, CompletableFuture cancel); + List minionIds = getMinions(target); - /** - * Returns the currently running jobs on the target - * - * @param target the target - * @return list of running jobs - */ - Map>> running(MinionList target); + Map> results = new HashMap<>(); + + ScheduleMetadata metadata = ScheduleMetadata.getDefaultMetadata().withBatchMode(); + LOG.debug("Local callSync: {}", SaltApi.localCallToString(callIn)); + List>> callResult = + adaptException(callIn.withMetadata(metadata).callSync(saltClient, + new MinionList(minionIds), PW_AUTH, defaultBatch)); + results.putAll(callResult.stream().flatMap(map -> map.entrySet().stream()) + .collect(Collectors.toMap(Entry>::getKey, + Entry>::getValue))); + + return results; + } /** - * Return the result for a jobId + * Synchronously executes a salt function on a single minion. If a + * SaltException is thrown, re-throw a RuntimeException. * - * @param jid the job id - * @return map from minion to result + * @param call salt function to call + * @param target + * @param result type of the salt function + * @return Optional holding the result of the function or empty if the + * minion did not respond. */ - Optional listJob(String jid); - /** - * Match the given target expression asynchronously. - * @param target the target expression - * @param cancel a future used to cancel waiting on return events - * @return a map holding a {@link CompletionStage}s for each minion - */ - Map>> matchAsync( - String target, CompletableFuture cancel); + Map> callSync(LocalCall callIn, Target target) + throws SaltException { + + ScheduleMetadata metadata = ScheduleMetadata.getDefaultMetadata().withBatchMode(); + LOG.debug("Local callSync: {}", SaltApi.localCallToString(callIn)); + List>> callResult = + adaptException(callIn.withMetadata(metadata).callSync(saltClient, target, + PW_AUTH, defaultBatch)); + return callResult.stream().flatMap(map -> map.entrySet().stream()) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + } /** - * Return the jobcache filtered by metadata + * Synchronously executes a salt function on a single minion. If a + * SaltException is thrown, re-throw a RuntimeException. * - * @param metadata search metadata - * @return list of running jobs + * @param call salt function to call + * @param minionId minion id to target + * @param result type of the salt function + * @return Optional holding the result of the function or empty if the + * minion did not respond. */ - Optional> jobsByMetadata(Object metadata); + public Optional callSync(LocalCall call, String minionId) { + return callSyncResult(call, minionId).flatMap(r -> r.fold(error -> { + LOG.warn(error.toString()); + return Optional.empty(); + }, Optional::of)); + } /** - * Return the jobcache filtered by metadata and start and end time. - * - * @param metadata search metadata - * @param startTime jobs start time - * @param endTime jobs end time - * @return list of running jobs + * Return a minion list + * @param target + * @return Minion list */ - Optional> jobsByMetadata(Object metadata, LocalDateTime startTime, - LocalDateTime endTime); + public List getMinions(MinionList target) { + + HashSet uniqueMinionIds = new HashSet<>(target.getTarget()); + Map> minionPartitions = + partitionMinionsByContactMethod(uniqueMinionIds); + + return minionPartitions.get(false); + } /** - * Executes match.glob in another thread and returns a {@link CompletionStage}. - * @param target the target to pass to match.glob - * @param cancel a future used to cancel waiting - * @return a future or Optional.empty if there's no ssh-push minion in the db + * Run a remote command on a given minion asynchronously. + * @param target the target + * @param cmd the command to execute + * @param cancel a future used to cancel waiting on return events + * @return a map holding a {@link CompletionStage}s for each minion */ - Optional>>> matchAsyncSSH( - String target, CompletableFuture cancel); + public Map>> runRemoteCommandAsync( + MinionList target, String cmd, CompletableFuture cancel) { + + List minionIds = getMinions(target); + + Map>> results = new HashMap<>(); + LocalCall call = Cmd.run(cmd); + + if (!minionIds.isEmpty()) { + try { + results.putAll(completableAsyncCall(call, target, getEventStream(), cancel) + .orElseGet(Collections::emptyMap)); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } + + return results; + } /** * Execute a LocalCall asynchronously on the default Salt client. * - * @deprecated this function is too general and should be replaced by more specific functionality. + * @deprecated this function is too general and should be replaced by more + * specific functionality. * @param the return type of the call * @param callIn the call to execute * @param target minions targeted by the call @@ -423,17 +703,16 @@ Optional>>> matchAsyncSSH( * @throws SaltException in case of an error executing the job with Salt */ @Deprecated - Optional> callAsync(LocalCall callIn, Target target, - Optional metadataIn) throws SaltException; - - - /** - * Get pending resume information. - * @param minionIds to target. - * @return pending resume information. - * @throws SaltException if anything goes wrong. - */ - Map>> getPendingResume(List minionIds) throws SaltException; + public Optional> callAsync(LocalCall callIn, + Target target, Optional metadataIn) + throws SaltException { + ScheduleMetadata metadata = Opt + .fold(metadataIn, ScheduleMetadata::getDefaultMetadata, Function.identity()) + .withBatchMode(); + LOG.debug("Local callAsync: {}", SaltApi.localCallToString(callIn)); + return adaptException(callIn.withMetadata(metadata).callAsync(saltClient, target, + PW_AUTH, defaultBatch)); + } /** * Execute generic salt call. @@ -441,7 +720,9 @@ Optional> callAsync(LocalCall callIn, Target targe * @param minionId of the target minion. * @return raw salt call result in json format. */ - Optional> rawJsonCall(LocalCall call, String minionId); + public Optional> rawJsonCall(LocalCall call, String minionId) { + return callSyncResult(new ElementCallJson(call), minionId); + } /** * Execute generic salt call. @@ -451,33 +732,387 @@ Optional> callAsync(LocalCall callIn, Target targe * @deprecated this method should not be used for new code */ @Deprecated - default Optional rawJsonCallOld(LocalCall call, String minionId) { + Optional rawJsonCallOld(LocalCall call, String minionId) { return callSync(new ElementCallJson(call), minionId); } /** - * @deprecated this function is too general and should be replaced by more specific functionality. - * @return saltSSHService to get - */ - @Deprecated - SaltSSHService getSaltSSHService(); - - /** - * Call 'saltutil.refresh_pillar' to sync the grains to the target minion(s). + * Call 'saltutil.refresh_pillar' to sync the grains to the target + * minion(s). * @param minionList minion list */ - void refreshPillar(MinionList minionList); + public void refreshPillar(MinionList minionList) { + try { + LocalCall call = + SaltUtil.refreshPillar(Optional.empty(), Optional.empty()); + callAsync(call, minionList); + + // Salt pillar refresh doesn't reload the modules with the new + // pillar + LocalCall modulesRefreshCall = + new LocalCall<>("saltutil.refresh_modules", Optional.empty(), + Optional.empty(), new TypeToken<>() { + }); + callAsync(modulesRefreshCall, minionList); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } /** * Check SSL certificates before deploying them. * * @param rootCA root CA used to sign the SSL certificate in PEM format - * @param intermediateCAs intermediate CAs used to sign the SSL certificate in PEM format + * @param intermediateCAs intermediate CAs used to sign the SSL certificate + * in PEM format * @param serverCertKey server CRT an Key pair * @return the certificate to deploy * - * @throws IllegalArgumentException if the cert check fails due to erroneous certificates + * @throws IllegalArgumentException if the cert check fails due to erroneous + * certificates + */ + public String checkSSLCert(String rootCA, SSLCertPair serverCertKey, + List intermediateCAs) + throws IllegalArgumentException { + RunnerCall> call = + MgrUtilRunner.checkSSLCert(rootCA, serverCertKey, intermediateCAs); + Map result = + callSync(call).orElseThrow(() -> new IllegalArgumentException( + "Unknown error while checking certificates")); + String error = result.getOrDefault("error", null); + if (error != null) { + throw new IllegalArgumentException(error); + } + return result.get("cert"); + } + + /** + * Upload built Kiwi image to SUSE Manager + * + * @param minion the minion + * @param filepath the filepath of the image to upload, in the build host + * @param imageStore the image store location + * @return the execution result + */ + public Optional collectKiwiImage(MinionServer minion, + String filepath, String imageStore) { + RunnerCall call = MgrKiwiImageRunner.collectImage( + minion.getMinionId(), minion.getIpAddress(), filepath, imageStore); + return callSync(call); + } + + private String runnerCallToString(RunnerCall call) { + return String.format(Messages.PAYLOAD_CALL_TEMPLATE.toString(), call, + call.getPayload()); + } + + /** + * Executes a salt wheel module function. + * + * @param call wheel call + * @param result type of the wheel call + * @return the result of the call or empty on error + */ + public Optional callSync(WheelCall call) { + return callSync(call, p -> p.fold(e -> { + LOG.error(String.format("Function [%s] not available for wheel call %s", + e.getFunctionName(), wheelCallToString(call))); + return Optional.empty(); + }, e -> { + LOG.error(String.format("Module [%s] not supported for wheel call %s", + e.getModuleName(), wheelCallToString(call))); + return Optional.empty(); + }, e -> { + LOG.error("Error parsing json response from wheel call {}: {}", + wheelCallToString(call), e.getJson()); + return Optional.empty(); + }, e -> { + LOG.error("Generic Salt error for wheel call {}: {}", wheelCallToString(call), + e.getMessage()); + return Optional.empty(); + }, e -> { + LOG.error("SaltSSH error for wheel call {}: {}", wheelCallToString(call), + e.getMessage()); + return Optional.empty(); + })); + } + + /** + * Executes a salt wheel module function. On error it invokes the + * {@code errorHandler} passed as parameter. + * + * @param call wheel call + * @param errorHandler function that handles errors + * @param result type of the wheel call + * @return the result of the call or empty on error + */ + public Optional callSync(WheelCall call, + Function> errorHandler) { + try { + LOG.debug("Wheel callSync: {}", wheelCallToString(call)); + WheelResult> result = + adaptException(call.callSync(saltClient, PW_AUTH)); + return result.getData().getResult().fold(errorHandler, Optional::of); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } + + /** + * Executes a salt runner module function. On error it logs the error and + * returns an empty result. + * + * @param call salt function to call + * @param result type of the salt function + * @return the result of the call or empty on error */ - String checkSSLCert(String rootCA, SSLCertPair serverCertKey, List intermediateCAs) - throws IllegalArgumentException; + public Optional callSync(RunnerCall call) { + return callSync(call, p -> p.fold(e -> { + LOG.error(String.format("Function [%s] not available for runner call %s", + e.getFunctionName(), runnerCallToString(call))); + return Optional.empty(); + }, e -> { + LOG.error(String.format("Module [%s] not supported for runner call %s", + e.getModuleName(), runnerCallToString(call))); + return Optional.empty(); + }, e -> { + LOG.error("Error parsing json response from runner call {}: {}", + runnerCallToString(call), e.getJson()); + return Optional.empty(); + }, e -> { + LOG.error(String.format(Messages.GENERIC_RUNNER_ERROR.toString(), + runnerCallToString(call), e.getMessage())); + return Optional.empty(); + }, e -> { + LOG.error(String.format(Messages.SSH_RUNNER_ERROR.toString(), + runnerCallToString(call), e.getMessage())); + return Optional.empty(); + })); + } + + /** + * Executes a salt runner module function. On error it invokes the + * {@code errorHandler} passed as parameter. + * + * @param call salt function to call + * @param errorHandler function that handles errors + * @param result type of the salt function + * @return the result of the call or empty on error + */ + public Optional callSync(RunnerCall call, + Function> errorHandler) { + try { + LOG.debug("Runner callSync: {}", runnerCallToString(call)); + Result result = adaptException(call.callSync(saltClient, PW_AUTH)); + return result.fold(errorHandler, Optional::of); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } + + /** + * This is a helper for keeping the old exception behaviour until all code + * makes proper use of the async api. + * @param fn function to execute and adapt. + * @param result of fn + * @return the result of fn + * @throws SaltException if an exception gets thrown + */ + public static T adaptException(CompletionStage fn) throws SaltException { + try { + return fn.toCompletableFuture().join(); + } + catch (CompletionException e) { + Throwable cause = e.getCause(); + if (cause instanceof SaltException) { + throw (SaltException) cause; + } + else { + throw new SaltException(cause); + } + } + } + + /** + * Execute a LocalCall asynchronously on the default Salt client, without + * passing any metadata on the call. + * + * @param the return type of the call + * @param call the call to execute + * @param target minions targeted by the call + * @return the LocalAsyncResult of the call + * @throws SaltException in case of an error executing the job with Salt + */ + Optional> callAsync(LocalCall call, Target target) + throws SaltException { + return callAsync(call, target, Optional.empty()); + } + + private void changeGroupAndPerms(Path dir, GroupPrincipal group) { + PosixFileAttributeView posixAttrs = Files.getFileAttributeView(dir, + PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS); + try { + Set wantedPers = + PosixFilePermissions.fromString("rwxrwxr-x"); + if (!posixAttrs.readAttributes().permissions().equals(wantedPers)) { + posixAttrs.setPermissions(wantedPers); + } + } + catch (IOException e) { + LOG.warn(String.format("Could not set 'rwxrwxr-x' permissions on %s: %s", dir, + e.getMessage())); + } + try { + if (!posixAttrs.readAttributes().group().equals(group)) { + posixAttrs.setGroup(group); + } + } + catch (IOException e) { + LOG.warn(String.format("Could not set group on %s to %s: %s", dir, group, + e.getMessage())); + } + } + + String callToString(AbstractCall call) { + return String.format("[%s.%s]", call.getModuleName(), call.getFunctionName()); + } + + /** + * Call 'saltutil.sync_beacons' to sync the beacons to the target minion(s). + * @param minionList minionList + */ + public void syncBeacons(MinionList minionList) { + try { + LocalCall> call = + SaltUtil.syncBeacons(Optional.of(true), Optional.empty()); + + callSync(call, minionList); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } + + /** + * Return local call options as a string (for debugging) + * + * @param call the local call + * @return string representation + */ + public static String localCallToString(LocalCall call) { + return String.format(Messages.PAYLOAD_CALL_TEMPLATE.toString(), call, + call.getPayload()); + } + + private String wheelCallToString(WheelCall call) { + return String.format(Messages.PAYLOAD_CALL_TEMPLATE.toString(), call, + call.getPayload()); + } + + private Optional> applyState(String minionId, String state) { + return callSync(State.apply(Collections.singletonList(state), Optional.empty()), + minionId); + } + + /** + * Synchronously executes a salt function on a single minion and returns the + * result. + * + * @param call the salt function + * @param minionId the minion server id + * @param type of result + * @return an optional containing the result or empty if no result was + * retrieved from the minion + * @throws RuntimeException when a {@link SaltException} is thrown + */ + public Optional> callSyncResult(LocalCall call, String minionId) { + try { + Map> stringRMap = callSync(call, new MinionList(minionId)); + + return Opt.fold(Optional.ofNullable(stringRMap.get(minionId)), () -> { + LOG.warn( + "Got no result for {} on minion {} (minion did not respond in time)", + call.getPayload().get("fun"), minionId); + return Optional.empty(); + }, Optional::of); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } + + /** + * Return the stream of events happening in salt. + * + * @return the event stream + */ + public EventStream getEventStream() { + + int retries = 0; + + while (eventStream == null || eventStream.isEventStreamClosed()) { + retries++; + try { + eventStream = createEventStream(); + eventStream.addEventListener(new EventListener() { + + @Override + public void notify(com.suse.salt.netapi.datatypes.Event event) { + // Only listening for close + } + + @Override + public void eventStreamClosed(int code, String phrase) { + SaltApi.this.eventStreamClosed(); + } + }); + if (eventStream.isEventStreamClosed()) { + eventStream = null; + } + + if (retries > 1) { + LOG.warn( + "Successfully connected to the Salt event bus after {} retries.", + retries - 1); + } + else { + LOG.info("Successfully connected to the Salt event bus"); + } + } + catch (SaltException e) { + try { + LOG.error("Unable to connect: {}, retrying in " + DELAY_TIME_SECONDS + + " seconds.", e); + Thread.sleep(1000 * DELAY_TIME_SECONDS); + if (retries == 1) { + MailHelper.withSmtp().sendAdminEmail( + "Cannot connect to salt event bus", + "salt-api daemon is not responding. Check the status of " + + "salt-api daemon and (re)-start it if needed\n\n" + + "This is the only notification you will receive."); + } + } + catch (JavaMailException javaMailException) { + LOG.error("Error sending email: {}", javaMailException.getMessage()); + } + catch (InterruptedException e1) { + LOG.error("Interrupted during sleep: {}", e1); + } + } + } + return eventStream; + } + + private Optional>>> completableAsyncCall( + LocalCall callIn, Target target, EventStream events, + CompletableFuture cancel) + throws SaltException { + LocalCall call = + callIn.withMetadata(ScheduleMetadata.getDefaultMetadata().withBatchMode()); + return SaltApi.adaptException(call.callAsync(saltClient, target, + SaltApi.PW_AUTH, events, cancel, defaultBatch)); + } } diff --git a/java/code/src/com/suse/manager/webui/services/iface/SystemQuery.java b/java/code/src/com/suse/manager/webui/services/iface/SystemQuery.java index 9470456438de..76effc0965db 100644 --- a/java/code/src/com/suse/manager/webui/services/iface/SystemQuery.java +++ b/java/code/src/com/suse/manager/webui/services/iface/SystemQuery.java @@ -14,29 +14,82 @@ */ package com.suse.manager.webui.services.iface; +import com.redhat.rhn.common.NoWheelResultsException; +import com.redhat.rhn.common.RhnRuntimeException; +import com.redhat.rhn.common.client.ClientCertificate; import com.redhat.rhn.domain.server.MinionServer; +import com.redhat.rhn.manager.system.SystemManager; -import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; -import com.suse.salt.netapi.calls.LocalCall; +import com.suse.manager.webui.services.impl.runner.MgrK8sRunner; +import com.suse.manager.webui.utils.salt.custom.MgrActionChains; +import com.suse.manager.webui.utils.salt.custom.PkgProfileUpdateSlsResult; +import com.suse.salt.netapi.calls.RunnerCall; +import com.suse.salt.netapi.calls.modules.Config; +import com.suse.salt.netapi.calls.modules.Event; +import com.suse.salt.netapi.calls.modules.Grains; +import com.suse.salt.netapi.calls.modules.Match; +import com.suse.salt.netapi.calls.modules.SaltUtil; +import com.suse.salt.netapi.calls.modules.State; import com.suse.salt.netapi.calls.modules.Zypper; +import com.suse.salt.netapi.calls.runner.Jobs; +import com.suse.salt.netapi.calls.wheel.Key; +import com.suse.salt.netapi.datatypes.target.Glob; +import com.suse.salt.netapi.datatypes.target.MinionList; +import com.suse.salt.netapi.errors.GenericError; import com.suse.salt.netapi.exception.SaltException; +import com.suse.salt.netapi.results.CmdResult; +import com.suse.salt.netapi.results.Result; +import com.google.gson.reflect.TypeToken; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; /** * Interface containing methods for directly interacting and getting information from a system. * Note: This interface should be split up further at some point. */ -public interface SystemQuery { +public class SystemQuery { /** - * Get the machine id for a given minion. - * - * @param minionId id of the target minion - * @return the machine id as a string + * Enum of all the available status for Salt keys. + */ + public enum KeyStatus { + ACCEPTED, DENIED, UNACCEPTED, REJECTED + } + + private static final int DELAY_TIME_SECONDS = 5; + private SaltApi saltApi; + + /** + * Constructor + * @param saltApiIn */ - Optional getMachineId(String minionId); + public SystemQuery(SaltApi saltApiIn) { + this.saltApi = saltApiIn; + } + + /** + * Return show highstate result. + * @param minionId of the target minion. + * @return show highstate result. + * @throws SaltException if anything goes wrong. + */ + public Map> getShowHighstate(String minionId) throws SaltException { + return saltApi.callSync(com.suse.salt.netapi.calls.modules.State.showHighstate(), new MinionList(minionId)); + } /** * Send notification about a system id to be generated. @@ -44,43 +97,309 @@ public interface SystemQuery { * @throws InstantiationException if signature generation fails * @throws SaltException if anything goes wrong. */ - void notifySystemIdGenerated(MinionServer minion) throws InstantiationException, SaltException; + public void notifySystemIdGenerated(MinionServer minion) throws InstantiationException, SaltException { + ClientCertificate cert = SystemManager.createClientCertificate(minion); + Map data = new HashMap<>(); + data.put("data", cert.toString()); + saltApi.callAsync( + Event.fire(data, "suse/systemid/generated"), + new MinionList(minion.getMinionId()) + ); + } /** * Query product information. * @param minionId of the target minion. * @return product information */ - Optional> getProducts(String minionId); + public Optional> getProducts(String minionId) { + return saltApi.callSync(Zypper.listProducts(false), minionId); + } /** - * Synchronously executes a salt function on a single minion. - * If a SaltException is thrown, re-throw a RuntimeException. + * Get redhat product information + * @param minionId id of the target minion + * @return redhat product information + */ + public Optional getRedhatProductInfo(String minionId) { + return saltApi.callSync(State.apply(Collections.singletonList("packages.redhatproductinfo"), + Optional.empty()), minionId) + .map(result -> { + if (result.isEmpty()) { + return new RedhatProductInfo(); + } + Optional oracleReleaseContent = Optional + .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_ORACLE_RELEASE) + .getChanges(CmdResult.class).getStdout()); + Optional centosReleaseContent = Optional + .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_CENTOS_RELEASE) + .getChanges(CmdResult.class).getStdout()); + Optional rhelReleaseContent = Optional + .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_REDHAT_RELEASE) + .getChanges(CmdResult.class).getStdout()); + Optional alibabaReleaseContent = Optional + .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_ALIBABA_RELEASE) + .getChanges(CmdResult.class).getStdout()); + Optional almaReleaseContent = Optional + .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_ALMA_RELEASE) + .getChanges(CmdResult.class).getStdout()); + Optional amazonReleaseContent = Optional + .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_AMAZON_RELEASE) + .getChanges(CmdResult.class).getStdout()); + Optional rockyReleaseContent = Optional + .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_ROCKY_RELEASE) + .getChanges(CmdResult.class).getStdout()); + Optional whatProvidesRes = Optional + .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_WHATPROVIDES_SLES_RELEASE) + .getChanges(CmdResult.class).getStdout()); + + return new RedhatProductInfo(centosReleaseContent, rhelReleaseContent, + oracleReleaseContent, alibabaReleaseContent, almaReleaseContent, + amazonReleaseContent, rockyReleaseContent, whatProvidesRes); + }); + } + /** + * Get information about all containers running in a Kubernetes cluster. + * @param kubeconfig path to the kubeconfig file + * @param context kubeconfig context to use + * @return a list of containers + */ + public Optional> getAllContainers(String kubeconfig, String context) { + RunnerCall call = + MgrK8sRunner.getAllContainers(kubeconfig, context); + return saltApi.callSync(call, + err -> err.fold( + e -> { + LOG.error(String.format("Function [%s] not available for runner call %s.", + e.getFunctionName(), saltApi.callToString(call))); + throw new NoSuchElementException(); + }, + e -> { + LOG.error(String.format("Module [%s] not supported for runner call %s", + e.getModuleName(), saltApi.callToString(call))); + throw new NoSuchElementException(); + }, + e -> { + LOG.error(String.format("Error parsing json response from runner call %s: %s", + saltApi.callToString(call), e.getJson())); + throw new NoSuchElementException(); + }, + e -> { + LOG.error(String.format(SaltApi.Messages.GENERIC_RUNNER_ERROR.toString(), + saltApi.callToString(call), e.getMessage())); + throw new NoSuchElementException(); + }, + e -> { + LOG.error(String.format(SaltApi.Messages.SSH_RUNNER_ERROR.toString(), + saltApi.callToString(call), e.getMessage())); + throw new NoSuchElementException(); + } + ) + ).map(MgrK8sRunner.ContainersList::getContainers); + } + + /** + * Get the specified grains for a given minion. + * @deprecated this function is too general and should be replaced by more specific functionality. + * @param minionId id of the target minion + * @param type class type, result should be parsed into + * @param grainNames list of grains names + * @param Type result should be parsed into + * @return Optional containing the grains parsed into specified type + */ + @Deprecated + public Optional getGrains(String minionId, TypeToken type, String... grainNames) { + return saltApi.callSync(Grains.item(false, type, grainNames), minionId); + } + + /** + * Get the grains for a given minion. + * + * @deprecated this function is too general and should be replaced by more specific functionality. + * @param minionId id of the target minion + * @return map containing the grains + */ + @Deprecated + public Optional> getGrains(String minionId) { + return saltApi.callSync(Grains.items(false), minionId); + } + + /** + * Gets a minion's master hostname. + * + * @param minionId the minion id + * @return the master hostname + */ + public Optional getMasterHostname(String minionId) { + return saltApi.callSync(Config.get(Config.MASTER), minionId); + } + /** + * Get the minion keys from salt with their respective status. * - * @param call salt function to call - * @param minionId minion id to target - * @param result type of the salt function - * @return Optional holding the result of the function - * or empty if the minion did not respond. + * @return the keys with their respective status as returned from salt */ - Optional callSync(LocalCall call, String minionId); + public Key.Names getKeys() { + return saltApi.callSync(Key.listAll()).orElseThrow(NoWheelResultsException::new); + } /** - * Upload built Kiwi image to SUSE Manager + * Get the minion keys from salt with their respective status and fingerprint. * - * @param minion the minion - * @param filepath the filepath of the image to upload, in the build host - * @param imageStore the image store location - * @return the execution result + * @return the keys with their respective status and fingerprint as returned from salt */ - Optional collectKiwiImage(MinionServer minion, String filepath, - String imageStore); + public Key.Fingerprints getFingerprints() { + return saltApi.callSync(Key.finger("*")).orElseThrow(NoWheelResultsException::new); + } /** - * Get redhat product information + * Return the result for a jobId + * + * @param jid the job id + * @return map from minion to result + */ + public Optional getListJob(String jid) { + return saltApi.callSync(Jobs.listJob(jid)); + } + + /** + * Return the jobcache filtered by metadata + * + * @param metadata search metadata + * @return list of running jobs + */ + public Optional> getJobsByMetadata(Object metadata) { + return saltApi.callSync(Jobs.listJobs(metadata)); + } + + /** + * Return the jobcache filtered by metadata and start and end time. + * + * @param metadata search metadata + * @param startTime jobs start time + * @param endTime jobs end time + * @return list of running jobs + */ + public Optional> getJobsByMetadata(Object metadata, + LocalDateTime startTime, LocalDateTime endTime) { + return saltApi.callSync(Jobs.listJobs(metadata, startTime, endTime)); + } + + + /** + * Get pending resume information. + * @param minionIds to target. + * @return pending resume information. + * @throws SaltException if anything goes wrong. + */ + public Map>> getPendingResume(List minionIds) throws SaltException { + return saltApi.callSync( + MgrActionChains.getPendingResume(), + new MinionList(minionIds)); + } + + /** + * Returns the currently running jobs on the target + * + * @param target the target + * @return list of running jobs + */ + public Map>> getRunning(MinionList target) { + try { + return saltApi.callSync(SaltUtil.running(), target); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } + + /** + * Match minions synchronously using a compound matcher. + * @param target compound matcher + * @return list of minion ids + */ + public List getMatchCompoundSync(String target) { + try { + Map> result = + saltApi.callSync(Match.compound(target, Optional.empty()), new Glob("*")); + return result.entrySet().stream() + .filter(e -> e.getValue().result().isPresent() && Boolean.TRUE.equals(e.getValue().result().get())) + .map(Entry::getKey) + .collect(Collectors.toList()); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } + + + /** + * For a given minion id check if there is a key in any of the given status. If no status is given as parameter, + * all the available status are considered. + * + * @param id the id to check for + * @param statusIn array of key status to consider + * @return true if there is a key with the given id, false otherwise + */ + public boolean isKeyExists(String id, KeyStatus... statusIn) { + final Set status = new HashSet<>(Arrays.asList(statusIn)); + if (status.isEmpty()) { + status.addAll(Arrays.asList(KeyStatus.values())); + } + + Key.Names keys = getKeys(); + return status.contains(KeyStatus.ACCEPTED) && keys.getMinions().contains(id) || + status.contains(KeyStatus.DENIED) && keys.getDeniedMinions().contains(id) || + status.contains(KeyStatus.UNACCEPTED) && keys.getUnacceptedMinions().contains(id) || + status.contains(KeyStatus.REJECTED) && keys.getRejectedMinions().contains(id); + } + + + /** + * Match the given target expression asynchronously. + * @param target the target expression + * @param cancel a future used to cancel waiting on return events + * @return a map holding a {@link CompletionStage}s for each minion + */ + public Map>> getMatchAsync( + String target, CompletableFuture cancel) { + try { + return saltApi.completableAsyncCall(Match.glob(target), new Glob(target), + saltApi.getEventStream(), cancel).orElseGet(Collections::emptyMap); + } + catch (SaltException e) { + throw new RhnRuntimeException(e); + } + } + + /** + * getMachineId + * @param minionId + * @return machine ID + */ + public Optional getMachineId(String minionId) { + return getGrain(minionId, "machine_id").flatMap(grain -> { + if (grain instanceof String) { + return Optional.of((String) grain); + } + else { + LOG.warn("Minion {} returned non string: {} as minion_id", minionId, grain); + return Optional.empty(); + } + }); + } + + /** + * Get a given grain's value from a given minion. + * * @param minionId id of the target minion - * @return redhat product information + * @param grain name of the grain + * @return the grain value */ - Optional redhatProductInfo(String minionId); + private Optional getGrain(String minionId, String grain) { + return saltApi.callSync(Grains.item(true, grain), minionId).flatMap(grains -> + Optional.ofNullable(grains.get(grain)) + ); + } + } diff --git a/java/code/src/com/suse/manager/webui/services/impl/SaltService.java b/java/code/src/com/suse/manager/webui/services/impl/SaltService.java deleted file mode 100644 index f331254df489..000000000000 --- a/java/code/src/com/suse/manager/webui/services/impl/SaltService.java +++ /dev/null @@ -1,1439 +0,0 @@ -/* - * Copyright (c) 2015--2021 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.suse.manager.webui.services.impl; - -import com.redhat.rhn.common.RhnRuntimeException; -import com.redhat.rhn.common.client.ClientCertificate; -import com.redhat.rhn.common.conf.ConfigDefaults; -import com.redhat.rhn.common.messaging.JavaMailException; -import com.redhat.rhn.domain.server.MinionServer; -import com.redhat.rhn.domain.server.MinionServerFactory; -import com.redhat.rhn.domain.server.ServerFactory; -import com.redhat.rhn.manager.audit.scap.file.ScapFileManager; -import com.redhat.rhn.manager.system.SystemManager; - -import com.suse.manager.reactor.PGEventStream; -import com.suse.manager.reactor.messaging.ApplyStatesEventMessage; -import com.suse.manager.ssl.SSLCertPair; -import com.suse.manager.utils.MailHelper; -import com.suse.manager.utils.MinionServerUtils; -import com.suse.manager.webui.controllers.utils.ContactMethodUtil; -import com.suse.manager.webui.services.SaltActionChainGeneratorService; -import com.suse.manager.webui.services.iface.RedhatProductInfo; -import com.suse.manager.webui.services.iface.SaltApi; -import com.suse.manager.webui.services.iface.SystemQuery; -import com.suse.manager.webui.services.impl.runner.MgrK8sRunner; -import com.suse.manager.webui.services.impl.runner.MgrKiwiImageRunner; -import com.suse.manager.webui.services.impl.runner.MgrRunner; -import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; -import com.suse.manager.webui.utils.ElementCallJson; -import com.suse.manager.webui.utils.gson.BootstrapParameters; -import com.suse.manager.webui.utils.salt.custom.MgrActionChains; -import com.suse.manager.webui.utils.salt.custom.PkgProfileUpdateSlsResult; -import com.suse.manager.webui.utils.salt.custom.ScheduleMetadata; -import com.suse.salt.netapi.AuthModule; -import com.suse.salt.netapi.calls.AbstractCall; -import com.suse.salt.netapi.calls.LocalAsyncResult; -import com.suse.salt.netapi.calls.LocalCall; -import com.suse.salt.netapi.calls.RunnerCall; -import com.suse.salt.netapi.calls.WheelCall; -import com.suse.salt.netapi.calls.WheelResult; -import com.suse.salt.netapi.calls.modules.Cmd; -import com.suse.salt.netapi.calls.modules.Config; -import com.suse.salt.netapi.calls.modules.Event; -import com.suse.salt.netapi.calls.modules.Grains; -import com.suse.salt.netapi.calls.modules.Match; -import com.suse.salt.netapi.calls.modules.SaltUtil; -import com.suse.salt.netapi.calls.modules.State; -import com.suse.salt.netapi.calls.modules.State.ApplyResult; -import com.suse.salt.netapi.calls.modules.Status; -import com.suse.salt.netapi.calls.modules.Test; -import com.suse.salt.netapi.calls.modules.Zypper; -import com.suse.salt.netapi.calls.runner.Jobs; -import com.suse.salt.netapi.calls.wheel.Key; -import com.suse.salt.netapi.client.SaltClient; -import com.suse.salt.netapi.client.impl.HttpAsyncClientImpl; -import com.suse.salt.netapi.datatypes.AuthMethod; -import com.suse.salt.netapi.datatypes.Batch; -import com.suse.salt.netapi.datatypes.PasswordAuth; -import com.suse.salt.netapi.datatypes.target.Glob; -import com.suse.salt.netapi.datatypes.target.MinionList; -import com.suse.salt.netapi.datatypes.target.Target; -import com.suse.salt.netapi.errors.GenericError; -import com.suse.salt.netapi.errors.SaltError; -import com.suse.salt.netapi.event.EventListener; -import com.suse.salt.netapi.event.EventStream; -import com.suse.salt.netapi.exception.SaltException; -import com.suse.salt.netapi.results.CmdResult; -import com.suse.salt.netapi.results.Result; -import com.suse.salt.netapi.results.SSHResult; -import com.suse.salt.netapi.results.StateApplyResult; -import com.suse.utils.Opt; - -import com.google.gson.JsonElement; -import com.google.gson.reflect.TypeToken; - -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; -import org.apache.http.impl.nio.client.HttpAsyncClients; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.GroupPrincipal; -import java.nio.file.attribute.PosixFileAttributeView; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.PosixFilePermissions; -import java.nio.file.attribute.UserPrincipalLookupService; -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Singleton class acting as a service layer for accessing the salt API. - */ -public class SaltService implements SystemQuery, SaltApi { - - private final Batch defaultBatch; - - // Logger - private static final Logger LOG = LogManager.getLogger(SaltService.class); - - // Salt properties - private static final URI SALT_MASTER_URI = URI.create("https://" + - com.redhat.rhn.common.conf.Config.get() - .getString(ConfigDefaults.SALT_API_HOST, "localhost") + - ":" + com.redhat.rhn.common.conf.Config.get() - .getString(ConfigDefaults.SALT_API_PORT, "9080")); - private static final String SALT_USER = "admin"; - private static final String SALT_PASSWORD = com.redhat.rhn.common.conf.Config.get().getString("server.secret_key"); - private static final AuthModule AUTH_MODULE = AuthModule.FILE; - - // Shared salt client instance - private final SaltClient saltClient; - private final CloseableHttpAsyncClient asyncHttpClient; - - // executing salt-ssh calls - private final SaltSSHService saltSSHService; - private static final AuthMethod PW_AUTH = new AuthMethod(new PasswordAuth(SALT_USER, SALT_PASSWORD, AUTH_MODULE)); - - private static final Predicate SALT_MINION_PREDICATE = mid -> - MinionPendingRegistrationService.containsSSHMinion(mid) || - MinionServerFactory - .findByMinionId(mid) - .filter(MinionServerUtils::isSshPushMinion) - .isPresent(); - - private static final String CLEANUP_MINION_SALT_STATE = "cleanup_minion"; - protected static final String MINION_UNREACHABLE_ERROR = "minion_unreachable"; - - private static final String GENERIC_RUNNER_ERROR = "Generic Salt error for runner call %s: %s"; - private static final String SSH_RUNNER_ERROR = "SaltSSH error for runner call %s: %s"; - private static final String PAYLOAD_CALL_TEMPLATE = "%s with payload [%s]"; - - /** - * Enum of all the available status for Salt keys. - */ - public enum KeyStatus { - ACCEPTED, DENIED, UNACCEPTED, REJECTED - } - - /** - * Default constructor - */ - public SaltService() { - RequestConfig requestConfig = RequestConfig.custom() - .setConnectTimeout(0) - .setSocketTimeout(0) - .setConnectionRequestTimeout(0) - .setCookieSpec(CookieSpecs.STANDARD) - .build(); - HttpAsyncClientBuilder httpClientBuilder = HttpAsyncClients.custom(); - httpClientBuilder.setDefaultRequestConfig(requestConfig); - - asyncHttpClient = httpClientBuilder - .setMaxConnPerRoute(20) - .setMaxConnTotal(20) - .build(); - asyncHttpClient.start(); - - saltClient = new SaltClient(SALT_MASTER_URI, new HttpAsyncClientImpl(asyncHttpClient)); - saltSSHService = new SaltSSHService(saltClient, SaltActionChainGeneratorService.INSTANCE); - defaultBatch = Batch.custom().withBatchAsAmount(ConfigDefaults.get().getSaltBatchSize()) - .withDelay(ConfigDefaults.get().getSaltBatchDelay()) - .withPresencePingTimeout(ConfigDefaults.get().getSaltPresencePingTimeout()) - .withPresencePingGatherJobTimeout(ConfigDefaults.get().getSaltPresencePingGatherJobTimeout()) - .build(); - } - - /** - * Constructor to use for unit testing - * - * @param client Salt client - */ - public SaltService(SaltClient client) { - asyncHttpClient = null; - saltClient = client; - saltSSHService = new SaltSSHService(saltClient, SaltActionChainGeneratorService.INSTANCE); - defaultBatch = Batch.custom().withBatchAsAmount(ConfigDefaults.get().getSaltBatchSize()) - .withDelay(ConfigDefaults.get().getSaltBatchDelay()) - .withPresencePingTimeout(ConfigDefaults.get().getSaltPresencePingTimeout()) - .withPresencePingGatherJobTimeout(ConfigDefaults.get().getSaltPresencePingGatherJobTimeout()) - .build(); - } - - /** - * Close the opened resources when the service is no longer needed - */ - public void close() { - if (asyncHttpClient == null) { - return; - } - - try { - asyncHttpClient.close(); - } - catch (IOException eIn) { - LOG.warn("Failed to close HTTP client", eIn); - } - } - - /** - * Synchronously executes a salt function on a single minion and returns the result. - * - * @param call the salt function - * @param minionId the minion server id - * @param type of result - * @return an optional containing the result or empty if no result was retrieved from the minion - * @throws RuntimeException when a {@link SaltException} is thrown - */ - public Optional> callSyncResult(LocalCall call, String minionId) { - try { - Map> stringRMap = callSync(call, new MinionList(minionId)); - - return Opt.fold(Optional.ofNullable(stringRMap.get(minionId)), () -> { - LOG.warn("Got no result for {} on minion {} (minion did not respond in time)", - call.getPayload().get("fun"), minionId); - return Optional.empty(); - }, Optional::of); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Optional callSync(LocalCall call, String minionId) { - return callSyncResult(call, minionId).flatMap(r -> - r.fold(error -> { - LOG.warn(error.toString()); - return Optional.empty(); - }, Optional::of) - ); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional ping(String minionId) { - return callSync(Test.ping(), minionId); - } - - /** - * Executes a salt runner module function. On error it - * logs the error and returns an empty result. - * - * @param call salt function to call - * @param result type of the salt function - * @return the result of the call or empty on error - */ - public Optional callSync(RunnerCall call) { - return callSync(call, p -> - p.fold( - e -> { - LOG.error(String.format("Function [%s] not available for runner call %s", - e.getFunctionName(), runnerCallToString(call))); - return Optional.empty(); - }, - e -> { - LOG.error(String.format("Module [%s] not supported for runner call %s", - e.getModuleName(), runnerCallToString(call))); - return Optional.empty(); - }, - e -> { - LOG.error("Error parsing json response from runner call {}: {}", - runnerCallToString(call), e.getJson()); - return Optional.empty(); - }, - e -> { - LOG.error(String.format(GENERIC_RUNNER_ERROR, runnerCallToString(call), e.getMessage())); - return Optional.empty(); - }, - e -> { - LOG.error(String.format(SSH_RUNNER_ERROR, runnerCallToString(call), e.getMessage())); - return Optional.empty(); - } - )); - } - - private String callToString(AbstractCall call) { - return String.format("[%s.%s]", call.getModuleName(), call.getFunctionName()); - } - - private String runnerCallToString(RunnerCall call) { - return String.format(PAYLOAD_CALL_TEMPLATE, call, call.getPayload()); - } - - /** - * Executes a salt runner module function. On error it - * invokes the {@code errorHandler} passed as parameter. - * - * @param call salt function to call - * @param errorHandler function that handles errors - * @param result type of the salt function - * @return the result of the call or empty on error - */ - public Optional callSync(RunnerCall call, - Function> errorHandler) { - try { - LOG.debug("Runner callSync: {}", runnerCallToString(call)); - Result result = adaptException(call.callSync(saltClient, PW_AUTH)); - return result.fold(errorHandler, Optional::of); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - private RuntimeException noWheelResult() { - return new RhnRuntimeException("no wheel results"); - } - - /** - * {@inheritDoc} - */ - @Override - public Key.Names getKeys() { - return callSync(Key.listAll()).orElseThrow(this::noWheelResult); - } - - /** - * Executes a salt wheel module function. - * - * @param call wheel call - * @param result type of the wheel call - * @return the result of the call or empty on error - */ - public Optional callSync(WheelCall call) { - return callSync(call, p -> - p.fold( - e -> { - LOG.error(String.format("Function [%s] not available for wheel call %s", - e.getFunctionName(), wheelCallToString(call))); - return Optional.empty(); - }, - e -> { - LOG.error(String.format("Module [%s] not supported for wheel call %s", - e.getModuleName(), wheelCallToString(call))); - return Optional.empty(); - }, - e -> { - LOG.error("Error parsing json response from wheel call {}: {}", - wheelCallToString(call), e.getJson()); - return Optional.empty(); - }, - e -> { - LOG.error("Generic Salt error for wheel call {}: {}", wheelCallToString(call), e.getMessage()); - return Optional.empty(); - }, - e -> { - LOG.error("SaltSSH error for wheel call {}: {}", wheelCallToString(call), e.getMessage()); - return Optional.empty(); - } - )); - } - - /** - * Executes a salt wheel module function. On error it - * invokes the {@code errorHandler} passed as parameter. - * - * @param call wheel call - * @param errorHandler function that handles errors - * @param result type of the wheel call - * @return the result of the call or empty on error - */ - public Optional callSync(WheelCall call, - Function> errorHandler) { - try { - LOG.debug("Wheel callSync: {}", wheelCallToString(call)); - WheelResult> result = adaptException(call.callSync(saltClient, PW_AUTH)); - return result.getData().getResult().fold(errorHandler, Optional::of); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - private String wheelCallToString(WheelCall call) { - return String.format(PAYLOAD_CALL_TEMPLATE, call, call.getPayload()); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean keyExists(String id, KeyStatus... statusIn) { - final Set status = new HashSet<>(Arrays.asList(statusIn)); - if (status.isEmpty()) { - status.addAll(Arrays.asList(KeyStatus.values())); - } - - Key.Names keys = getKeys(); - return status.contains(KeyStatus.ACCEPTED) && keys.getMinions().contains(id) || - status.contains(KeyStatus.DENIED) && keys.getDeniedMinions().contains(id) || - status.contains(KeyStatus.UNACCEPTED) && keys.getUnacceptedMinions().contains(id) || - status.contains(KeyStatus.REJECTED) && keys.getRejectedMinions().contains(id); - } - - /** - * {@inheritDoc} - */ - @Override - public Key.Fingerprints getFingerprints() { - return callSync(Key.finger("*")).orElseThrow(this::noWheelResult); - } - - /** - * {@inheritDoc} - */ - @Override - public Key.Pair generateKeysAndAccept(String id, - boolean force) { - return callSync(Key.genAccept(id, Optional.of(force))).orElseThrow(this::noWheelResult); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional getGrains(String minionId, TypeToken type, String... grainNames) { - return callSync(Grains.item(false, type, grainNames), minionId); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional> getGrains(String minionId) { - return callSync(Grains.items(false), minionId); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional getMachineId(String minionId) { - return getGrain(minionId, "machine_id").flatMap(grain -> { - if (grain instanceof String) { - return Optional.of((String) grain); - } - else { - LOG.warn("Minion {} returned non string: {} as minion_id", minionId, grain); - return Optional.empty(); - } - }); - } - - /** - * {@inheritDoc} - */ - @Override - public void acceptKey(String match) { - if (callSync(Key.accept(match)).isEmpty()) { - throw noWheelResult(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void deleteKey(String minionId) { - if (callSync(Key.delete(minionId)).isEmpty()) { - throw noWheelResult(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void rejectKey(String minionId) { - if (callSync(Key.reject(minionId)).isEmpty()) { - throw noWheelResult(); - } - } - - // Reconnecting time (in seconds) to Salt event bus - private static final int DELAY_TIME_SECONDS = 5; - - private EventStream eventStream; - - private synchronized void eventStreamClosed() { - eventStream = null; - } - - private synchronized EventStream createOrGetEventStream() { - - int retries = 0; - - while (eventStream == null || eventStream.isEventStreamClosed()) { - retries++; - try { - eventStream = createEventStream(); - eventStream.addEventListener(new EventListener() { - @Override - public void notify(com.suse.salt.netapi.datatypes.Event event) { - // Only listening for close - } - - @Override - public void eventStreamClosed(int code, String phrase) { - SaltService.this.eventStreamClosed(); - } - }); - if (eventStream.isEventStreamClosed()) { - eventStream = null; - } - - if (retries > 1) { - LOG.warn("Successfully connected to the Salt event bus after {} retries.", retries - 1); - } - else { - LOG.info("Successfully connected to the Salt event bus"); - } - } - catch (SaltException e) { - try { - LOG.error("Unable to connect: {}, retrying in " + DELAY_TIME_SECONDS + " seconds.", e); - Thread.sleep(1000 * DELAY_TIME_SECONDS); - if (retries == 1) { - MailHelper.withSmtp().sendAdminEmail("Cannot connect to salt event bus", - "salt-api daemon is not responding. Check the status of " + - "salt-api daemon and (re)-start it if needed\n\n" + - "This is the only notification you will receive."); - } - } - catch (JavaMailException javaMailException) { - LOG.error("Error sending email: {}", javaMailException.getMessage()); - } - catch (InterruptedException e1) { - LOG.error("Interrupted during sleep: {}", e1); - } - } - } - return eventStream; - } - - private EventStream createEventStream() throws SaltException { - return new PGEventStream(); - } - - /** - * {@inheritDoc} - */ - @Override - public EventStream getEventStream() { - return createOrGetEventStream(); - } - - /** - * Get a given grain's value from a given minion. - * - * @param minionId id of the target minion - * @param grain name of the grain - * @return the grain value - */ - private Optional getGrain(String minionId, String grain) { - return callSync(Grains.item(true, grain), minionId).flatMap(grains -> - Optional.ofNullable(grains.get(grain)) - ); - } - - /** - * {@inheritDoc} - */ - @Override - public Map> runRemoteCommand(MinionList target, String cmd) { - try { - return callSync(Cmd.run(cmd), target); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - private Optional>>> completableAsyncCall( - LocalCall callIn, Target target, EventStream events, - CompletableFuture cancel) throws SaltException { - LocalCall call = callIn.withMetadata(ScheduleMetadata.getDefaultMetadata().withBatchMode()); - return adaptException(call.callAsync(saltClient, target, PW_AUTH, events, cancel, defaultBatch)); - } - - /** - * {@inheritDoc} - */ - @Override - public Map>> runRemoteCommandAsync( - MinionList target, String cmd, CompletableFuture cancel) { - - HashSet uniqueMinionIds = new HashSet<>(target.getTarget()); - Map> minionPartitions = - partitionMinionsByContactMethod(uniqueMinionIds); - - List sshMinionIds = minionPartitions.get(true); - List regularMinionIds = minionPartitions.get(false); - Map>> results = - new HashMap<>(); - LocalCall call = Cmd.run(cmd); - if (!sshMinionIds.isEmpty()) { - results.putAll( - saltSSHService.callAsyncSSH( - call, - new MinionList(sshMinionIds), - cancel)); - } - - if (!regularMinionIds.isEmpty()) { - try { - results.putAll( - completableAsyncCall(call, target, - getEventStream(), cancel).orElseGet(Collections::emptyMap)); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - return results; - } - - /** - * {@inheritDoc} - */ - @Override - public Map>> running(MinionList target) { - try { - return callSync(SaltUtil.running(), target); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - - /** - * {@inheritDoc} - */ - @Override - public Optional> rawJsonCall(LocalCall call, String minionId) { - return callSyncResult(new ElementCallJson(call), minionId); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional> jobsByMetadata(Object metadata) { - return callSync(Jobs.listJobs(metadata)); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional> jobsByMetadata(Object metadata, - LocalDateTime startTime, LocalDateTime endTime) { - return callSync(Jobs.listJobs(metadata, startTime, endTime)); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional listJob(String jid) { - return callSync(Jobs.listJob(jid)); - } - - /** - * {@inheritDoc} - */ - @Override - public Map>> matchAsync( - String target, CompletableFuture cancel) { - try { - return completableAsyncCall(Match.glob(target), new Glob(target), - getEventStream(), cancel).orElseGet(Collections::emptyMap); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public List matchCompoundSync(String target) { - try { - Map> result = - callSync(Match.compound(target, Optional.empty()), new Glob("*")); - return result.entrySet().stream() - .filter(e -> e.getValue().result().isPresent() && Boolean.TRUE.equals(e.getValue().result().get())) - .map(Entry::getKey) - .collect(Collectors.toList()); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Optional>>> matchAsyncSSH( - String target, CompletableFuture cancel) { - return saltSSHService.matchAsyncSSH(target, cancel); - } - - /** - * Call 'saltutil.sync_beacons' to sync the beacons to the target minion(s). - * @param minionList minionList - */ - public void syncBeacons(MinionList minionList) { - try { - LocalCall> call = SaltUtil.syncBeacons( - Optional.of(true), - Optional.empty()); - - callSync(call, minionList); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void refreshPillar(MinionList minionList) { - try { - LocalCall call = SaltUtil.refreshPillar(Optional.empty(), - Optional.empty()); - callAsync(call, minionList); - - // Salt pillar refresh doesn't reload the modules with the new pillar - LocalCall modulesRefreshCall = new LocalCall<>("saltutil.refresh_modules", - Optional.empty(), Optional.empty(), new TypeToken<>() { - }); - callAsync(modulesRefreshCall, minionList); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void syncGrains(MinionList minionList) { - try { - LocalCall> call = SaltUtil.syncGrains(Optional.empty(), - Optional.empty()); - callSync(call, minionList); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void syncModules(MinionList minionList) { - try { - LocalCall> call = SaltUtil.syncModules(Optional.empty(), - Optional.empty()); - callSync(call, minionList); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Optional> getProducts(String minionId) { - return callSync(Zypper.listProducts(false), minionId); - } - - /** - * {@inheritDoc} - */ - @Override - public void syncAll(MinionList minionList) { - try { - LocalCall> call = SaltUtil.syncAll(Optional.empty(), - Optional.empty()); - callSync(call, minionList); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * Execute a LocalCall synchronously on the default Salt client. - * Note that salt-ssh systems are also called by this method. - * - * @param the return type of the call - * @param callIn the call to execute - * @param target minions targeted by the call - * @return the result of the call - * @throws SaltException in case of an error executing the job with Salt - */ - private Map> callSync(LocalCall callIn, MinionList target) - throws SaltException { - HashSet uniqueMinionIds = new HashSet<>(target.getTarget()); - Map> minionPartitions = - partitionMinionsByContactMethod(uniqueMinionIds); - - List sshMinionIds = minionPartitions.get(true); - List regularMinionIds = minionPartitions.get(false); - - Map> results = new HashMap<>(); - - if (!sshMinionIds.isEmpty()) { - results.putAll(saltSSHService.callSyncSSH( - callIn, - new MinionList(sshMinionIds))); - } - - if (!regularMinionIds.isEmpty()) { - ScheduleMetadata metadata = ScheduleMetadata.getDefaultMetadata().withBatchMode(); - LOG.debug("Local callSync: {}", SaltService.localCallToString(callIn)); - List>> callResult = - adaptException(callIn.withMetadata(metadata).callSync(saltClient, - new MinionList(regularMinionIds), PW_AUTH, defaultBatch)); - results.putAll( - callResult.stream().flatMap(map -> map.entrySet().stream()) - .collect(Collectors.toMap(Entry>::getKey, - Entry>::getValue)) - ); - } - - return results; - } - - private Map> callSync(LocalCall callIn, Target target) - throws SaltException { - - ScheduleMetadata metadata = ScheduleMetadata.getDefaultMetadata().withBatchMode(); - LOG.debug("Local callSync: {}", SaltService.localCallToString(callIn)); - List>> callResult = - adaptException(callIn.withMetadata(metadata).callSync(saltClient, - target, PW_AUTH, defaultBatch)); - return callResult.stream().flatMap(map -> map.entrySet().stream()) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - } - - /** - * {@inheritDoc} - */ - @Override - public Map> showHighstate(String minionId) throws SaltException { - return callSync(com.suse.salt.netapi.calls.modules.State.showHighstate(), new MinionList(minionId)); - } - - /** - * {@inheritDoc} - */ - @Override - public Map>> getPendingResume(List minionIds) throws SaltException { - return callSync( - MgrActionChains.getPendingResume(), - new MinionList(minionIds)); - } - - /** - * Return local call options as a string (for debugging) - * - * @param call the local call - * @return string representation - */ - public static String localCallToString(LocalCall call) { - return String.format(PAYLOAD_CALL_TEMPLATE, call, call.getPayload()); - } - - /** - * Partitions minion ids according to the contact method of corresponding minions - * (salt-ssh minions in one partition, regular minions in the other). - * - * @param minionIds minion ids - * @return map with partitioning - */ - public static Map> partitionMinionsByContactMethod( - Collection minionIds) { - return minionIds.stream() - .collect(Collectors.partitioningBy(SALT_MINION_PREDICATE)); - } - - /** - * Execute a LocalCall asynchronously on the default Salt client, - * without passing any metadata on the call. - * - * @param the return type of the call - * @param call the call to execute - * @param target minions targeted by the call - * @return the LocalAsyncResult of the call - * @throws SaltException in case of an error executing the job with Salt - */ - private Optional> callAsync(LocalCall call, Target target) - throws SaltException { - return callAsync(call, target, Optional.empty()); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional> callAsync(LocalCall callIn, Target target, - Optional metadataIn) throws SaltException { - ScheduleMetadata metadata = - Opt.fold(metadataIn, ScheduleMetadata::getDefaultMetadata, Function.identity()).withBatchMode(); - LOG.debug("Local callAsync: {}", SaltService.localCallToString(callIn)); - return adaptException(callIn.withMetadata(metadata).callAsync(saltClient, target, PW_AUTH, defaultBatch)); - } - - /** - * {@inheritDoc} - */ - @Override - public void deployChannels(List minionIds) throws SaltException { - callSync( - com.suse.salt.netapi.calls.modules.State.apply(ApplyStatesEventMessage.CHANNELS), - new MinionList(minionIds)); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional> checkIn(MinionList targetIn) throws SaltException { - try { - LocalCall call = Test.echo("checkIn"); - return callAsync(call, targetIn); - } - catch (SaltException e) { - throw new RhnRuntimeException(e); - } - } - - /** - * This is a helper for keeping the old exception behaviour until - * all code makes proper use of the async api. - * @param fn function to execute and adapt. - * @param result of fn - * @return the result of fn - * @throws SaltException if an exception gets thrown - */ - public static T adaptException(CompletionStage fn) throws SaltException { - try { - return fn.toCompletableFuture().join(); - } - catch (CompletionException e) { - Throwable cause = e.getCause(); - if (cause instanceof SaltException) { - throw (SaltException) cause; - } - else { - throw new SaltException(cause); - } - } - } - - /** - * Retrieves the uptime of the minion (in seconds). - * - * @param minion the minion - * @return Optional with the uptime in seconds or empty on error or if salt returned - * no value. - */ - public Optional getUptimeForMinion(MinionServer minion) { - Optional uptime = Optional.empty(); - try { - uptime = callSync( - Status.uptime(), - minion.getMinionId()) - .map(r -> ((Number) r.get("seconds")).longValue()); - - if (uptime.isEmpty()) { - LOG.error("Can't get uptime for {}", minion.getMinionId()); - } - } - catch (RuntimeException e) { - LOG.error(e); - } - return uptime; - } - - /** - * {@inheritDoc} - */ - @Override - public void updateSystemInfo(MinionList minionTarget) { - try { - callAsync(State.apply(Collections.singletonList(ApplyStatesEventMessage.SYSTEM_INFO), - Optional.empty()), minionTarget, - Optional.of(ScheduleMetadata.getDefaultMetadata().withMinionStartup())); - } - catch (SaltException ex) { - LOG.debug("Error while executing util.systeminfo state: {}", ex.getMessage()); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Optional getMasterHostname(String minionId) { - return callSync(Config.get(Config.MASTER), minionId); - } - - /** - * {@inheritDoc} - */ - private Optional> applyState(String minionId, String state) { - return callSync(State.apply(Collections.singletonList(state), Optional.empty()), minionId); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional redhatProductInfo(String minionId) { - return callSync(State.apply(Collections.singletonList("packages.redhatproductinfo"), - Optional.empty()), minionId) - .map(result -> { - if (result.isEmpty()) { - return new RedhatProductInfo(); - } - Optional oracleReleaseContent = Optional - .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_ORACLE_RELEASE) - .getChanges(CmdResult.class).getStdout()); - Optional centosReleaseContent = Optional - .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_CENTOS_RELEASE) - .getChanges(CmdResult.class).getStdout()); - Optional rhelReleaseContent = Optional - .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_REDHAT_RELEASE) - .getChanges(CmdResult.class).getStdout()); - Optional alibabaReleaseContent = Optional - .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_ALIBABA_RELEASE) - .getChanges(CmdResult.class).getStdout()); - Optional almaReleaseContent = Optional - .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_ALMA_RELEASE) - .getChanges(CmdResult.class).getStdout()); - Optional amazonReleaseContent = Optional - .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_AMAZON_RELEASE) - .getChanges(CmdResult.class).getStdout()); - Optional rockyReleaseContent = Optional - .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_ROCKY_RELEASE) - .getChanges(CmdResult.class).getStdout()); - Optional whatProvidesRes = Optional - .ofNullable(result.get(PkgProfileUpdateSlsResult.PKG_PROFILE_WHATPROVIDES_SLES_RELEASE) - .getChanges(CmdResult.class).getStdout()); - - return new RedhatProductInfo(centosReleaseContent, rhelReleaseContent, - oracleReleaseContent, alibabaReleaseContent, almaReleaseContent, - amazonReleaseContent, rockyReleaseContent, whatProvidesRes); - }); - } - - /** - * {@inheritDoc} - */ - @Override - public Result>> bootstrapMinion( - BootstrapParameters parameters, List bootstrapMods, - Map pillarData) throws SaltException { - return saltSSHService.bootstrapMinion(parameters, bootstrapMods, pillarData); - } - - /** - * {@inheritDoc} - */ - @Override - public Map storeMinionScapFiles( - MinionServer minion, String uploadDir, Long actionId) { - String actionPath = ScapFileManager - .getActionPath(minion.getOrg().getId(), - minion.getId(), actionId); - Path mountPoint = Paths.get(com.redhat.rhn.common.conf.Config.get() - .getString(ConfigDefaults.MOUNT_POINT)); - try { - // create dirs - Path actionDir = Files.createDirectories(mountPoint.resolve(actionPath)); - - UserPrincipalLookupService lookupService = FileSystems.getDefault() - .getUserPrincipalLookupService(); - GroupPrincipal susemanagerGroup = lookupService - .lookupPrincipalByGroupName("susemanager"); - GroupPrincipal wwwGroup = lookupService - .lookupPrincipalByGroupName("www"); - // systems///actions/ - changeGroupAndPerms(actionDir, susemanagerGroup); - // systems///actions - actionDir = actionDir.getParent(); - while (!actionDir.equals(mountPoint)) { - changeGroupAndPerms(actionDir, wwwGroup); - actionDir = actionDir.getParent(); - } - - } - catch (IOException e) { - LOG.error("Error creating dir {}", mountPoint.resolve(actionPath), e); - } - - RunnerCall> call = MgrUtilRunner.moveMinionUploadedFiles( - minion.getMinionId(), - uploadDir, - com.redhat.rhn.common.conf.Config.get() - .getString(ConfigDefaults.MOUNT_POINT), - actionPath); - Optional> result = callSync(call, - err -> err.fold( - e -> { - LOG.error(String.format("Function [%s] not available for runner call %s.", - e.getFunctionName(), callToString(call))); - return Optional.of(Collections.singletonMap(false, - String.format("Function [%s] not available", e.getFunctionName()))); - }, - e -> { - LOG.error(String.format("Module [%s] not supported for runner call %s.", - e.getModuleName(), callToString(call))); - return Optional.of(Collections.singletonMap(false, - String.format("Module [%s] not supported", e.getModuleName()))); - }, - e -> { - LOG.error(String.format("Error parsing json response from runner call %s: %s", - callToString(call), e.getJson())); - return Optional.of(Collections.singletonMap(false, - "Error parsing json response: " + e.getJson())); - }, - e -> { - LOG.error(String.format(GENERIC_RUNNER_ERROR, callToString(call), e.getMessage())); - return Optional.of(Collections.singletonMap(false, - "Generic Salt error: " + e.getMessage())); - }, - e -> { - LOG.error(String.format(SSH_RUNNER_ERROR, callToString(call), e.getMessage())); - return Optional.of(Collections.singletonMap(false, - "SaltSSH error: " + e.getMessage())); - } - ) - ); - return result.orElseGet(() -> - Collections.singletonMap(false, "Error moving scap result files." + - " Please check the logs.") - ); - } - - private void changeGroupAndPerms(Path dir, GroupPrincipal group) { - PosixFileAttributeView posixAttrs = Files - .getFileAttributeView(dir, - PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS); - try { - Set wantedPers = PosixFilePermissions.fromString("rwxrwxr-x"); - if (!posixAttrs.readAttributes().permissions().equals(wantedPers)) { - posixAttrs.setPermissions(wantedPers); - } - } - catch (IOException e) { - LOG.warn(String.format("Could not set 'rwxrwxr-x' permissions on %s: %s", - dir, e.getMessage())); - } - try { - if (!posixAttrs.readAttributes().group().equals(group)) { - posixAttrs.setGroup(group); - } - } - catch (IOException e) { - LOG.warn(String.format("Could not set group on %s to %s: %s", - dir, group, e.getMessage())); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Optional generateSSHKey(String path) { - RunnerCall call = MgrUtilRunner.generateSSHKey(path); - return callSync(call); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional removeSaltSSHKnownHost(String hostname) { - return removeSaltSSHKnownHost(hostname, SaltSSHService.SSH_DEFAULT_PORT); - } - - @Override - public Optional removeSaltSSHKnownHost(String hostname, int port) { - RunnerCall call = MgrUtilRunner.removeSSHKnowHost("salt", hostname, port); - return callSync(call); - } - - - /** - * {@inheritDoc} - */ - @Override - public Optional deleteRejectedKey(String minionId) { - RunnerCall call = MgrUtilRunner.deleteRejectedKey(minionId); - return callSync(call); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional chainSSHCommand(List hosts, - String clientKey, - String proxyKey, - String user, - Map options, - String command, - String outputfile) { - RunnerCall call = - MgrUtilRunner.chainSSHCommand( - hosts, clientKey, proxyKey, user, options, command, outputfile); - return callSync(call); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional> getAllContainers(String kubeconfig, - String context) { - RunnerCall call = - MgrK8sRunner.getAllContainers(kubeconfig, context); - return callSync(call, - err -> err.fold( - e -> { - LOG.error(String.format("Function [%s] not available for runner call %s.", - e.getFunctionName(), callToString(call))); - throw new NoSuchElementException(); - }, - e -> { - LOG.error(String.format("Module [%s] not supported for runner call %s", - e.getModuleName(), callToString(call))); - throw new NoSuchElementException(); - }, - e -> { - LOG.error(String.format("Error parsing json response from runner call %s: %s", - callToString(call), e.getJson())); - throw new NoSuchElementException(); - }, - e -> { - LOG.error(String.format(GENERIC_RUNNER_ERROR, callToString(call), e.getMessage())); - throw new NoSuchElementException(); - }, - e -> { - LOG.error(String.format(SSH_RUNNER_ERROR, callToString(call), e.getMessage())); - throw new NoSuchElementException(); - } - ) - ).map(MgrK8sRunner.ContainersList::getContainers); - } - - /** - * {@inheritDoc} - */ - @Override - public void notifySystemIdGenerated(MinionServer minion) throws InstantiationException, SaltException { - ClientCertificate cert = SystemManager.createClientCertificate(minion); - Map data = new HashMap<>(); - data.put("data", cert.toString()); - callAsync( - Event.fire(data, "suse/systemid/generated"), - new MinionList(minion.getMinionId()) - ); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional> cleanupMinion(MinionServer minion, - int timeout) { - boolean sshPush = Stream.of( - ServerFactory.findContactMethodByLabel(ContactMethodUtil.SSH_PUSH), - ServerFactory.findContactMethodByLabel(ContactMethodUtil.SSH_PUSH_TUNNEL) - ).anyMatch(cm -> minion.getContactMethod().equals(cm)); - - if (sshPush) { - return saltSSHService.cleanupSSHMinion(minion, timeout); - } - return this.cleanupRegularMinion(minion); - } - - /** - * {@inheritDoc} - */ - @Override - public void storeSshKeyFile(Path path, String contents) { - ensureAbsolutePath(path); - - String absolutePath = path.toAbsolutePath().toString(); - RunnerCall createFile = MgrRunner.writeTextFile(absolutePath, contents); - if (callSync(createFile).isEmpty()) { - throw new IllegalStateException("Can't create SSH priv key file " + path); - } - - // this might not be needed, the file is created with sane perms already - RunnerCall setMode = MgrRunner.setFileMode(absolutePath, "0600"); - if (callSync(setMode).isEmpty()) { - throw new IllegalStateException("Can't set mode for SSH priv key file " + path); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Optional removeFile(Path path) { - ensureAbsolutePath(path); - String absolutePath = path.toAbsolutePath().toString(); - RunnerCall createFile = MgrRunner.removeFile(absolutePath); - return callSync(createFile); - } - - /** - * {@inheritDoc} - */ - @Override - public Optional copyFile(Path src, Path dst) { - ensureAbsolutePath(src); - ensureAbsolutePath(dst); - RunnerCall call = MgrRunner.copyFile(src.toAbsolutePath().toString(), - dst.toAbsolutePath().toString(), false, false); - return callSync(call); - } - - private void ensureAbsolutePath(Path path) { - if (!path.isAbsolute()) { - throw new IllegalStateException("Given path is not absolute: " + path); - } - } - - /** - * Remove SUSE Manager specific configuration from a Salt regular minion. - * - * @param minion the minion. - * @return list of error messages or empty if no error - */ - private Optional> cleanupRegularMinion(MinionServer minion) { - Optional> response = applyState(minion.getMinionId(), CLEANUP_MINION_SALT_STATE); - - //response is empty in case the minion is down - if (response.isPresent()) { - return response.get().values().stream().filter(value -> !value.isResult()) - .map(StateApplyResult::getComment) - .collect(Collectors.collectingAndThen(Collectors.toList(), - list -> list.isEmpty() ? Optional.empty() : Optional.of(list))); - } - return Optional.of(Collections.singletonList(SaltService.MINION_UNREACHABLE_ERROR)); - } - - /** - * {@inheritDoc} - */ - @Override - public SaltSSHService getSaltSSHService() { - return saltSSHService; - } - - /** - * {@inheritDoc} - */ - @Override - public Optional collectKiwiImage(MinionServer minion, String filepath, - String imageStore) { - RunnerCall call = - MgrKiwiImageRunner.collectImage(minion.getMinionId(), minion.getIpAddress(), filepath, imageStore); - return callSync(call); - } - - @Override - public String checkSSLCert(String rootCA, SSLCertPair serverCertKey, List intermediateCAs) - throws IllegalArgumentException { - RunnerCall> call = MgrUtilRunner.checkSSLCert(rootCA, serverCertKey, intermediateCAs); - Map result = callSync(call) - .orElseThrow(() -> new IllegalArgumentException("Unknown error while checking certificates")); - String error = result.getOrDefault("error", null); - if (error != null) { - throw new IllegalArgumentException(error); - } - return result.get("cert"); - } -} diff --git a/java/code/src/com/suse/manager/webui/services/impl/test/SaltServiceTest.java b/java/code/src/com/suse/manager/webui/services/impl/test/SaltServiceTest.java index c4b7b4958f40..e05e31e407a5 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/test/SaltServiceTest.java +++ b/java/code/src/com/suse/manager/webui/services/impl/test/SaltServiceTest.java @@ -24,8 +24,9 @@ import com.suse.manager.reactor.messaging.test.SaltTestUtils; import com.suse.manager.webui.controllers.utils.ContactMethodUtil; +import com.suse.manager.webui.services.iface.SaltApi; +import com.suse.manager.webui.services.iface.SaltSSHApi; import com.suse.manager.webui.services.impl.MinionPendingRegistrationService; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.salt.netapi.calls.Client; import com.suse.salt.netapi.client.SaltClient; @@ -71,7 +72,7 @@ public void testfilterSSHMinionIdsNoSSHMinions() { minionIds.add("m2"); assertEquals( Collections.emptyList(), - SaltService.partitionMinionsByContactMethod(minionIds).get(true)); + SaltSSHApi.partitionMinionsByContactMethod(minionIds).get(true)); } @Test @@ -84,7 +85,7 @@ public void testfilterSSHMinionIdsBootstrap() { minionIds.add("m3"); assertEquals( Collections.singletonList("m1"), - SaltService.partitionMinionsByContactMethod(minionIds).get(true)); + SaltSSHApi.partitionMinionsByContactMethod(minionIds).get(true)); MinionPendingRegistrationService.removeMinion("m1"); MinionPendingRegistrationService.removeMinion("m2"); } @@ -99,7 +100,7 @@ public void testfilterSSHMinionIds() throws Exception { minionIds.add("m2"); assertEquals( Collections.singletonList(sshMinion.getMinionId()), - SaltService.partitionMinionsByContactMethod(minionIds).get(true)); + SaltSSHApi.partitionMinionsByContactMethod(minionIds).get(true)); } @Test @@ -114,7 +115,7 @@ public void testfilterSSHMinionIdsMixedMinions() throws Exception { minionIds.add(minion.getMinionId()); assertEquals( Collections.singletonList(sshMinion.getMinionId()), - SaltService.partitionMinionsByContactMethod(minionIds).get(true)); + SaltSSHApi.partitionMinionsByContactMethod(minionIds).get(true)); } @Test @@ -131,12 +132,11 @@ public void testGenerateSSHKeyExists() throws IOException { new TypeToken() { }.getType()))); }}); - SaltService systemQuery = new SaltService(saltClient); - Optional res = systemQuery + SaltApi saltApi = new SaltApi(); + Optional res = saltApi .generateSSHKey(keyPath.substring(0, keyPath.length() - 4)); assertTrue(res.isPresent()); assertEquals(0, res.orElseThrow().getReturnCode()); - systemQuery.close(); } @Override diff --git a/java/code/src/com/suse/manager/webui/services/test/TestSaltApi.java b/java/code/src/com/suse/manager/webui/services/test/TestSaltApi.java index dc2909f3ffab..6ad26b1b96ea 100644 --- a/java/code/src/com/suse/manager/webui/services/test/TestSaltApi.java +++ b/java/code/src/com/suse/manager/webui/services/test/TestSaltApi.java @@ -97,7 +97,7 @@ public Map storeMinionScapFiles(MinionServer minion, String upl } @Override - public Map> showHighstate(String minionId) throws SaltException { + public Map> getShowHighstate(String minionId) throws SaltException { throw new UnsupportedOperationException(); } @@ -199,7 +199,7 @@ public Key.Pair generateKeysAndAccept(String id, boolean force) { } @Override - public boolean keyExists(String id, SaltService.KeyStatus... statusIn) { + public boolean isKeyExists(String id, SaltService.KeyStatus... statusIn) { throw new UnsupportedOperationException(); } @@ -243,7 +243,7 @@ public Optional listJob(String jid) { } @Override - public Map>> matchAsync(String target, + public Map>> getMatchAsync(String target, CompletableFuture cancel) { throw new UnsupportedOperationException(); } @@ -260,7 +260,7 @@ public Optional> jobsByMetadata(Object metadata, } @Override - public Optional>>> matchAsyncSSH( + public Optional>>> getMatchAsyncSSH( String target, CompletableFuture cancel) { throw new UnsupportedOperationException(); } diff --git a/java/code/src/com/suse/manager/webui/services/test/TestSystemQuery.java b/java/code/src/com/suse/manager/webui/services/test/TestSystemQuery.java index c4ab0283812c..09dc11087b6a 100644 --- a/java/code/src/com/suse/manager/webui/services/test/TestSystemQuery.java +++ b/java/code/src/com/suse/manager/webui/services/test/TestSystemQuery.java @@ -55,7 +55,7 @@ public Optional collectKiwiImage( } @Override - public Optional redhatProductInfo(String minionId) { + public Optional getRedhatProductInfo(String minionId) { throw new UnsupportedOperationException(); } } diff --git a/java/code/src/com/suse/manager/webui/utils/MinionActionUtils.java b/java/code/src/com/suse/manager/webui/utils/MinionActionUtils.java index b1183e583bd4..db99fdc72783 100644 --- a/java/code/src/com/suse/manager/webui/utils/MinionActionUtils.java +++ b/java/code/src/com/suse/manager/webui/utils/MinionActionUtils.java @@ -163,7 +163,7 @@ public void cleanupMinionActions() { ).collect(Collectors.toList()); Map>> running = - saltApi.running(new MinionList(minionIds)); + saltApi.getRunning(new MinionList(minionIds)); serverActions.forEach(serverAction -> serverAction.getServer().asMinionServer().map(minion -> running.get(minion.getMinionId())) diff --git a/java/code/src/com/suse/manager/webui/websocket/RemoteMinionCommands.java b/java/code/src/com/suse/manager/webui/websocket/RemoteMinionCommands.java index 2bdbe38e9bbb..82c968e3a5e8 100644 --- a/java/code/src/com/suse/manager/webui/websocket/RemoteMinionCommands.java +++ b/java/code/src/com/suse/manager/webui/websocket/RemoteMinionCommands.java @@ -154,11 +154,11 @@ public void onMessage(Session session, String messageBody) { String target = StringUtils.trim(msg.getTarget()); Optional>>> resSSH = - saltApi.matchAsyncSSH(target, failAfter); + saltApi.getMatchAsyncSSH(target, failAfter); Map>> res = new HashMap<>(); - res = saltApi.matchAsync(target, failAfter); + res = saltApi.getMatchAsync(target, failAfter); if (res.isEmpty() && !resSSH.isPresent()) { // just return, no need to wait for salt-ssh results sendMessage(session, new ActionErrorEventDto(null, diff --git a/java/spacewalk-java.changes b/java/spacewalk-java.changes index e61e3711b6b6..f97f702a65ef 100644 --- a/java/spacewalk-java.changes +++ b/java/spacewalk-java.changes @@ -1,3 +1,5 @@ +- Refactoring of SaltApi, SystemQuery and SaltService + ------------------------------------------------------------------- Wed Sep 28 11:15:58 CEST 2022 - jgonzalez@suse.com From 14b1e97b573964d407a0f008a204e4a4c1762ebb Mon Sep 17 00:00:00 2001 From: mbussolotto Date: Wed, 28 Sep 2022 09:54:50 +0200 Subject: [PATCH 2/8] fix --- .../com/redhat/rhn/GlobalInstanceHolder.java | 5 +- .../manager/kubernetes/KubernetesManager.java | 1 - .../bootstrap/RegularMinionBootstrapper.java | 4 +- .../manager/webui/services/iface/SaltApi.java | 147 +++++++++++++++--- .../webui/services/impl/SaltSSHService.java | 121 +++----------- .../impl/test/SaltSSHServiceTest.java | 10 +- 6 files changed, 153 insertions(+), 135 deletions(-) diff --git a/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java b/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java index b9e730d989b3..4c7db9aa36ac 100644 --- a/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java +++ b/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java @@ -58,9 +58,8 @@ public class GlobalInstanceHolder { private GlobalInstanceHolder() { } - private static final SaltService SALT_SERVICE = new SaltService(); - public static final SystemQuery SYSTEM_QUERY = SALT_SERVICE; - public static final SaltApi SALT_API = SALT_SERVICE; + public static final SaltApi SALT_API = new SaltApi(); + public static final SystemQuery SYSTEM_QUERY = new SystemQuery(SALT_API); public static final ServerGroupManager SERVER_GROUP_MANAGER = new ServerGroupManager(SALT_API); public static final FormulaManager FORMULA_MANAGER = new FormulaManager(SALT_API); public static final SaltUtils SALT_UTILS = new SaltUtils(SYSTEM_QUERY, SALT_API); diff --git a/java/code/src/com/suse/manager/kubernetes/KubernetesManager.java b/java/code/src/com/suse/manager/kubernetes/KubernetesManager.java index 6247a297321b..78de21b02903 100644 --- a/java/code/src/com/suse/manager/kubernetes/KubernetesManager.java +++ b/java/code/src/com/suse/manager/kubernetes/KubernetesManager.java @@ -26,7 +26,6 @@ import com.suse.manager.model.kubernetes.ContainerInfo; import com.suse.manager.model.kubernetes.ImageUsage; import com.suse.manager.webui.services.iface.SaltApi; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.services.impl.runner.MgrK8sRunner; import org.apache.commons.lang3.StringUtils; diff --git a/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java b/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java index be8e3cd376dc..64abe2a25cbf 100644 --- a/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java +++ b/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java @@ -23,7 +23,7 @@ import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.SystemQuery; import com.suse.manager.webui.services.impl.MinionPendingRegistrationService; -import com.suse.manager.webui.services.impl.SaltService.KeyStatus; +import com.suse.manager.webui.services.iface.SystemQuery.KeyStatus; import com.suse.manager.webui.utils.InputValidator; import com.suse.manager.webui.utils.gson.BootstrapParameters; import com.suse.salt.netapi.calls.wheel.Key; @@ -97,7 +97,7 @@ protected BootstrapResult bootstrapInternal(BootstrapParameters input, User user // If a key is pending for this minion, temporarily reject it boolean weRejectedIt = false; - if (saltApi.isKeyExists(minionId, KeyStatus.UNACCEPTED)) { + if (systemQuery.isKeyExists(minionId, KeyStatus.UNACCEPTED)) { LOG.info("Pending key exists for {}, rejecting...", minionId); saltApi.rejectKey(minionId); weRejectedIt = true; diff --git a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java index dc73d8323f0d..9cecf6d826ba 100644 --- a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java +++ b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java @@ -14,12 +14,15 @@ */ package com.suse.manager.webui.services.iface; +import com.redhat.rhn.GlobalInstanceHolder; import com.redhat.rhn.common.NoWheelResultsException; import com.redhat.rhn.common.RhnRuntimeException; import com.redhat.rhn.common.conf.ConfigDefaults; import com.redhat.rhn.common.messaging.JavaMailException; import com.redhat.rhn.domain.server.MinionServer; import com.redhat.rhn.domain.server.MinionServerFactory; +import com.redhat.rhn.domain.server.ServerFactory; +import com.redhat.rhn.domain.token.ActivationKeyFactory; import com.redhat.rhn.manager.audit.scap.file.ScapFileManager; import com.suse.manager.reactor.PGEventStream; @@ -27,11 +30,15 @@ import com.suse.manager.ssl.SSLCertPair; import com.suse.manager.utils.MailHelper; import com.suse.manager.utils.MinionServerUtils; +import com.suse.manager.webui.controllers.utils.ContactMethodUtil; import com.suse.manager.webui.services.impl.MinionPendingRegistrationService; +import com.suse.manager.webui.services.impl.SaltSSHService; import com.suse.manager.webui.services.impl.runner.MgrKiwiImageRunner; import com.suse.manager.webui.services.impl.runner.MgrRunner; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.manager.webui.utils.ElementCallJson; +import com.suse.manager.webui.utils.SaltRoster; +import com.suse.manager.webui.utils.gson.BootstrapParameters; import com.suse.manager.webui.utils.salt.custom.ScheduleMetadata; import com.suse.salt.netapi.AuthModule; import com.suse.salt.netapi.calls.AbstractCall; @@ -59,6 +66,7 @@ import com.suse.salt.netapi.event.EventStream; import com.suse.salt.netapi.exception.SaltException; import com.suse.salt.netapi.results.Result; +import com.suse.salt.netapi.results.SSHResult; import com.suse.salt.netapi.results.StateApplyResult; import com.suse.utils.Opt; @@ -83,6 +91,7 @@ import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.nio.file.attribute.UserPrincipalLookupService; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -537,6 +546,122 @@ public Key.Pair generateKeysAndAccept(String id, boolean force) { } } + /** + * Bootstrap a system using salt-ssh. + * + * The call internally uses ssh identity key/cert on a hardcoded path. + * If the key/cert doesn't exist, it's created by salt and copied to the target host. + * Copying is implemented via a salt state (mgr_ssh_identity), as ssh_key_deploy + * is ignored by the api.) + * + * @param parameters - bootstrap parameters + * @param bootstrapMods - state modules to be applied during the bootstrap + * @param pillarData - pillar data used in the salt-ssh call + * @throws SaltException if something goes wrong during command execution or + * during manipulation the salt-ssh roster + * @return the result of the underlying ssh call for given host + */ + public Result>> bootstrapMinion( + BootstrapParameters parameters, List bootstrapMods, + Map pillarData) throws SaltException { + LOG.info("Bootstrapping host: {}", parameters.getHost()); + LocalCall> call = State.apply(bootstrapMods, Optional.of(pillarData)); + + List bootstrapProxyPath; + if (parameters.getProxyId().isPresent()) { + bootstrapProxyPath = parameters.getProxyId() + .map(ServerFactory::lookupById) + .map(SaltSSHService::proxyPathToHostnames) + .orElseThrow(() -> new SaltException( + "Proxy not found for id: " + parameters.getProxyId().get())); + } + else { + bootstrapProxyPath = Collections.emptyList(); + } + + String contactMethod = parameters.getFirstActivationKey() + .map(ActivationKeyFactory::lookupByKey) + .map(key -> key.getContactMethod().getLabel()).orElse(""); + + Optional portForwarding = SaltSSHService.remotePortForwarding(bootstrapProxyPath, contactMethod); + + // private key handling just for bootstrap + Optional tmpKeyFileAbsolutePath = parameters.getPrivateKey().map(key -> SaltSSHService.createTempKeyFilePath()); + + try { + tmpKeyFileAbsolutePath.ifPresent(p -> parameters.getPrivateKey().ifPresent(k -> + GlobalInstanceHolder.SALT_API.storeSshKeyFile(p, k))); + SaltRoster roster = new SaltRoster(); + roster.addHost(parameters.getHost(), + parameters.getUser(), + parameters.getPassword(), + tmpKeyFileAbsolutePath.map(Path::toString), + parameters.getPrivateKeyPassphrase(), + parameters.getPort(), + portForwarding, + SaltSSHService.getSSHProxyCommandOption(bootstrapProxyPath, + contactMethod, + parameters.getHost(), + parameters.getPort().orElse(SaltSSHService.SSH_PUSH_PORT)), + SaltSSHService.getSshPushTimeout(), + SaltSSHService.getMinionOpts(parameters.getHost(), contactMethod), + Optional.ofNullable(SaltSSHService.getSaltSSHPreflightScriptPath()), + Optional.of(Arrays.asList( + bootstrapProxyPath.isEmpty() ? + ConfigDefaults.get().getCobblerHost() : + bootstrapProxyPath.get(bootstrapProxyPath.size() - 1).split(":")[0], + ContactMethodUtil.SSH_PUSH_TUNNEL.equals(contactMethod) ? + SaltSSHService.getSshPushRemotePort() : SaltSSHService.SSL_PORT, + SaltSSHService.getSSHUseSaltThin() ? 1 : 0, + 1 + )) + ); + + Map>>> result = + callSyncSSHInternal(call, + new MinionList(parameters.getHost()), + Optional.of(roster), + parameters.isIgnoreHostKeys(), + SaltSSHService.isSudoUser(parameters.getUser())); + return result.get(parameters.getHost()); + } + finally { + tmpKeyFileAbsolutePath.ifPresent(this::cleanUpTempKeyFile); + } + } + + public Optional chainSSHCommand(List hosts, String clientKey, + String proxyKey, String user, Map options, String command, String outputfile) { + + RunnerCall call = MgrUtilRunner.chainSSHCommand(hosts, clientKey, + proxyKey, user, options, command, outputfile); + return callSync(call); + } + + /** + * This is a helper for keeping the old exception behaviour until + * all code makes proper use of the async api. + * @param fn function to execute and adapt. + * @param result of fn + * @return the result of fn + * @throws SaltException if an exception gets thrown + */ + public static T adaptException(CompletionStage fn) throws SaltException { + try { + return fn.toCompletableFuture().join(); + } + catch (CompletionException e) { + Throwable cause = e.getCause(); + if (cause instanceof SaltException) { + throw (SaltException) cause; + } + else { + throw new SaltException(cause); + } + } + } + + private synchronized void eventStreamClosed() { eventStream = null; } @@ -913,28 +1038,6 @@ public Optional callSync(RunnerCall call, } } - /** - * This is a helper for keeping the old exception behaviour until all code - * makes proper use of the async api. - * @param fn function to execute and adapt. - * @param result of fn - * @return the result of fn - * @throws SaltException if an exception gets thrown - */ - public static T adaptException(CompletionStage fn) throws SaltException { - try { - return fn.toCompletableFuture().join(); - } - catch (CompletionException e) { - Throwable cause = e.getCause(); - if (cause instanceof SaltException) { - throw (SaltException) cause; - } - else { - throw new SaltException(cause); - } - } - } /** * Execute a LocalCall asynchronously on the default Salt client, without diff --git a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java index 0ac05b82adbd..efb28d6f303a 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java +++ b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java @@ -36,6 +36,7 @@ import com.suse.manager.webui.services.FutureUtils; import com.suse.manager.webui.services.SaltActionChainGeneratorService; import com.suse.manager.webui.services.SaltConstants; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.manager.webui.utils.ActionSaltState; import com.suse.manager.webui.utils.SaltModuleRun; @@ -113,7 +114,8 @@ public class SaltSSHService { private static final String PROXY_SSH_PUSH_KEY = "/var/lib/spacewalk/" + PROXY_SSH_PUSH_USER + "/.ssh/id_susemanager_ssh_push"; - private static final int SSL_PORT = 443; + protected static final String MINION_UNREACHABLE_ERROR = "minion_unreachable"; + public static final int SSL_PORT = 443; public static final int SSH_DEFAULT_PORT = 22; public static final int SSH_PUSH_PORT = SSH_DEFAULT_PORT; @@ -256,12 +258,12 @@ private Optional prepareSaltRoster(MinionList target, Optional prepareSaltRoster(MinionList target, Optional LOG.error("Minion id='{}' not found in the database", mid)); } @@ -421,7 +423,7 @@ private static List sortServerPaths(Set serverPaths) { * @return the ProxyCommand string used by salt-ssh to connect * to the minion. */ - public static Optional> sshProxyCommandOption(List proxyPath, + public static Optional> getSSHProxyCommandOption(List proxyPath, String contactMethod, String minionHostname, int sshPushPort) { @@ -465,102 +467,18 @@ private boolean addSaltSSHMinionsFromDb(SaltRoster roster) { Optional.empty(), Opt.wrapFirstNonNull(minion.getSSHPushPort(), SSH_PUSH_PORT), remotePortForwarding(proxyPath, minion.getContactMethod().getLabel()), - sshProxyCommandOption(proxyPath, + getSSHProxyCommandOption(proxyPath, minion.getContactMethod().getLabel(), minion.getMinionId(), Optional.ofNullable(minion.getSSHPushPort()).orElse(SSH_PUSH_PORT)), getSshPushTimeout(), - minionOpts(minion.getMinionId(), minion.getContactMethod().getLabel())); + getMinionOpts(minion.getMinionId(), minion.getContactMethod().getLabel())); }); return !minions.isEmpty(); } - /** - * Bootstrap a system using salt-ssh. - * - * The call internally uses ssh identity key/cert on a hardcoded path. - * If the key/cert doesn't exist, it's created by salt and copied to the target host. - * Copying is implemented via a salt state (mgr_ssh_identity), as ssh_key_deploy - * is ignored by the api.) - * - * @param parameters - bootstrap parameters - * @param bootstrapMods - state modules to be applied during the bootstrap - * @param pillarData - pillar data used in the salt-ssh call - * @throws SaltException if something goes wrong during command execution or - * during manipulation the salt-ssh roster - * @return the result of the underlying ssh call for given host - */ - public Result>> bootstrapMinion( - BootstrapParameters parameters, List bootstrapMods, - Map pillarData) throws SaltException { - LOG.info("Bootstrapping host: {}", parameters.getHost()); - LocalCall> call = State.apply(bootstrapMods, Optional.of(pillarData)); - - List bootstrapProxyPath; - if (parameters.getProxyId().isPresent()) { - bootstrapProxyPath = parameters.getProxyId() - .map(ServerFactory::lookupById) - .map(SaltSSHService::proxyPathToHostnames) - .orElseThrow(() -> new SaltException( - "Proxy not found for id: " + parameters.getProxyId().get())); - } - else { - bootstrapProxyPath = Collections.emptyList(); - } - - String contactMethod = parameters.getFirstActivationKey() - .map(ActivationKeyFactory::lookupByKey) - .map(key -> key.getContactMethod().getLabel()).orElse(""); - - Optional portForwarding = remotePortForwarding(bootstrapProxyPath, contactMethod); - - // private key handling just for bootstrap - Optional tmpKeyFileAbsolutePath = parameters.getPrivateKey().map(key -> createTempKeyFilePath()); - - try { - tmpKeyFileAbsolutePath.ifPresent(p -> parameters.getPrivateKey().ifPresent(k -> - GlobalInstanceHolder.SALT_API.storeSshKeyFile(p, k))); - SaltRoster roster = new SaltRoster(); - roster.addHost(parameters.getHost(), - parameters.getUser(), - parameters.getPassword(), - tmpKeyFileAbsolutePath.map(Path::toString), - parameters.getPrivateKeyPassphrase(), - parameters.getPort(), - portForwarding, - sshProxyCommandOption(bootstrapProxyPath, - contactMethod, - parameters.getHost(), - parameters.getPort().orElse(SSH_PUSH_PORT)), - getSshPushTimeout(), - minionOpts(parameters.getHost(), contactMethod), - Optional.ofNullable(getSaltSSHPreflightScriptPath()), - Optional.of(Arrays.asList( - bootstrapProxyPath.isEmpty() ? - ConfigDefaults.get().getCobblerHost() : - bootstrapProxyPath.get(bootstrapProxyPath.size() - 1).split(":")[0], - ContactMethodUtil.SSH_PUSH_TUNNEL.equals(contactMethod) ? - getSshPushRemotePort() : SSL_PORT, - getSSHUseSaltThin() ? 1 : 0, - 1 - )) - ); - - Map>>> result = - callSyncSSHInternal(call, - new MinionList(parameters.getHost()), - Optional.of(roster), - parameters.isIgnoreHostKeys(), - isSudoUser(parameters.getUser())); - return result.get(parameters.getHost()); - } - finally { - tmpKeyFileAbsolutePath.ifPresent(this::cleanUpTempKeyFile); - } - } - // create temp key absolute path - private Path createTempKeyFilePath() { + public static Path createTempKeyFilePath() { String fileName = "boostrapKeyTmp-" + UUID.randomUUID(); return Path.of(SSH_TEMP_BOOTSTRAP_KEY_DIR).resolve(fileName).toAbsolutePath(); } @@ -571,7 +489,7 @@ private void cleanUpTempKeyFile(Path path) { .orElseThrow(() -> new IllegalStateException("Can't remove file " + path)); } - private Optional> minionOpts(String minionId, + public static Optional> getMinionOpts(String minionId, String sshContactMethod) { if (ContactMethodUtil.SSH_PUSH_TUNNEL.equals(sshContactMethod)) { Map options = new LinkedHashMap<>(); @@ -581,7 +499,7 @@ private Optional> minionOpts(String minionId, return Optional.empty(); } - private Optional remotePortForwarding(List proxyPath, + public static Optional remotePortForwarding(List proxyPath, String sshContactMethod) { if (ContactMethodUtil.SSH_PUSH_TUNNEL.equals(sshContactMethod)) { return Optional.of(getSshPushRemotePort() + ":" + @@ -590,15 +508,15 @@ private Optional remotePortForwarding(List proxyPath, return Optional.empty(); } - private static Integer getSshPushRemotePort() { + public static Integer getSshPushRemotePort() { return Config.get().getInt("ssh_push_port_https"); } - private boolean isSudoUser(String user) { + public static boolean isSudoUser(String user) { return !CommonConstants.ROOT.equals(user); } - private static Optional getSshPushTimeout() { + public static Optional getSshPushTimeout() { return Optional.ofNullable(ConfigDefaults.get().getSaltSSHConnectTimeout()); } @@ -688,8 +606,8 @@ private Map>> callSyncSSHInternal(LocalCall c extraFilerefs.ifPresent(sshConfigBuilder::extraFilerefs); SaltSSHConfig sshConfig = sshConfigBuilder.build(); - LOG.debug("Local callSyncSSH: {}", SaltService.localCallToString(call)); - return SaltService.adaptException(call.callSyncSSH(saltClient, target, sshConfig, PW_AUTH) + LOG.debug("Local callSyncSSH: {}", SaltApi.localCallToString(call)); + return SaltApi.adaptException(call.callSyncSSH(saltClient, target, sshConfig, PW_AUTH) .whenComplete((r, e) -> { if (roster.isPresent()) { try { @@ -843,13 +761,12 @@ public Optional> cleanupSSHMinion(MinionServer minion, int timeout) return result.isEmpty() ? Optional.>empty() : Optional.of(result); } else if (err instanceof TimeoutException) { - return Optional.of(singletonList(SaltService.MINION_UNREACHABLE_ERROR)); + return Optional.of(singletonList(SaltSSHService.MINION_UNREACHABLE_ERROR)); } else { return Optional.of(singletonList(err.getMessage())); } }).toCompletableFuture().get(); - } catch (InterruptedException | ExecutionException e) { LOG.error("Error applying state ssh_cleanup", e); diff --git a/java/code/src/com/suse/manager/webui/services/impl/test/SaltSSHServiceTest.java b/java/code/src/com/suse/manager/webui/services/impl/test/SaltSSHServiceTest.java index 038e1be93fc2..34c3318a398a 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/test/SaltSSHServiceTest.java +++ b/java/code/src/com/suse/manager/webui/services/impl/test/SaltSSHServiceTest.java @@ -56,14 +56,14 @@ public void setUp() throws Exception { @Test public void testProxyCommandNoProxy() { - Optional> res = SaltSSHService.sshProxyCommandOption( + Optional> res = SaltSSHService.getSSHProxyCommandOption( Collections.emptyList(), "ssh-push", "minion", 22); assertFalse(res.isPresent()); } @Test public void testProxyCommandSSHPush1Proxy() { - Optional> res = SaltSSHService.sshProxyCommandOption( + Optional> res = SaltSSHService.getSSHProxyCommandOption( List.of("proxy1"), "ssh-push", "minion", 22); assertTrue(res.isPresent()); assertEquals(List.of( @@ -76,7 +76,7 @@ public void testProxyCommandSSHPush1Proxy() { @Test public void testProxyCommandSSHPushTunnel1Proxy() { - Optional> res = SaltSSHService.sshProxyCommandOption( + Optional> res = SaltSSHService.getSSHProxyCommandOption( List.of("proxy1:24"), "ssh-push-tunnel", "minion", 22); assertTrue(res.isPresent()); assertEquals(List.of( @@ -89,7 +89,7 @@ public void testProxyCommandSSHPushTunnel1Proxy() { @Test public void testProxyCommandSSHPush2Proxies() { - Optional> res = SaltSSHService.sshProxyCommandOption( + Optional> res = SaltSSHService.getSSHProxyCommandOption( Arrays.asList("proxy1:23", "proxy2"), "ssh-push", "minion", 22); assertTrue(res.isPresent()); assertEquals(List.of( @@ -104,7 +104,7 @@ public void testProxyCommandSSHPush2Proxies() { @Test public void testProxyCommandSSHPushTunnel2Proxies() { - Optional> res = SaltSSHService.sshProxyCommandOption( + Optional> res = SaltSSHService.getSSHProxyCommandOption( Arrays.asList("proxy1", "proxy2"), "ssh-push-tunnel", "minion", 22); assertTrue(res.isPresent()); assertEquals(List.of( From d618befd6b3ff9643136e62e08788c75a348e0ec Mon Sep 17 00:00:00 2001 From: mbussolotto Date: Wed, 28 Sep 2022 10:06:28 +0200 Subject: [PATCH 3/8] fix --- .../src/com/redhat/rhn/GlobalInstanceHolder.java | 1 - .../bootstrap/RegularMinionBootstrapper.java | 2 +- .../suse/manager/webui/services/iface/SaltApi.java | 14 ++++++++++++++ .../webui/services/impl/SaltSSHService.java | 2 -- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java b/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java index 4c7db9aa36ac..3e6c4c99e66e 100644 --- a/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java +++ b/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java @@ -41,7 +41,6 @@ import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.SystemQuery; import com.suse.manager.webui.services.iface.VirtManager; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.utils.MinionActionUtils; import com.suse.manager.webui.utils.UserPreferenceUtils; import com.suse.manager.webui.utils.ViewHelper; diff --git a/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java b/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java index 64abe2a25cbf..958cad97abd1 100644 --- a/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java +++ b/java/code/src/com/suse/manager/webui/controllers/bootstrap/RegularMinionBootstrapper.java @@ -22,8 +22,8 @@ import com.suse.manager.reactor.messaging.ApplyStatesEventMessage; import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.SystemQuery; -import com.suse.manager.webui.services.impl.MinionPendingRegistrationService; import com.suse.manager.webui.services.iface.SystemQuery.KeyStatus; +import com.suse.manager.webui.services.impl.MinionPendingRegistrationService; import com.suse.manager.webui.utils.InputValidator; import com.suse.manager.webui.utils.gson.BootstrapParameters; import com.suse.salt.netapi.calls.wheel.Key; diff --git a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java index 9cecf6d826ba..8e66ad3bfc96 100644 --- a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java +++ b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java @@ -630,6 +630,20 @@ public Result>> bootstrapMinion( } } + /** + * Chain ssh calls over one or more hops to run a command on the last host in the chain. + * This calls the mgrutil.chain_ssh_command runner. + * + * @param hosts a list of hosts, where the last one is where + * the command will be executed + * @param clientKey the ssh key to use to connect to the first host + * @param proxyKey the ssh key path to use for the rest of the hosts + * @param user the user + * @param options ssh options + * @param command the command to execute + * @param outputfile the file to which to dump the command stdout + * @return the execution result + */ public Optional chainSSHCommand(List hosts, String clientKey, String proxyKey, String user, Map options, String command, String outputfile) { diff --git a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java index efb28d6f303a..dbb707c82b3a 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java +++ b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java @@ -28,7 +28,6 @@ import com.redhat.rhn.domain.server.Server; import com.redhat.rhn.domain.server.ServerFactory; import com.redhat.rhn.domain.server.ServerPath; -import com.redhat.rhn.domain.token.ActivationKeyFactory; import com.suse.manager.utils.SaltUtils; import com.suse.manager.webui.controllers.StatesAPI; @@ -44,7 +43,6 @@ import com.suse.manager.webui.utils.SaltState; import com.suse.manager.webui.utils.SaltTop; import com.suse.manager.webui.utils.gson.BootstrapParameters; -import com.suse.manager.webui.utils.salt.custom.MgrActionChains; import com.suse.salt.netapi.AuthModule; import com.suse.salt.netapi.calls.LocalCall; import com.suse.salt.netapi.calls.SaltSSHConfig; From 16d199ddfcae7c050826ff7c4decc8e306ae27ac Mon Sep 17 00:00:00 2001 From: mbussolotto Date: Wed, 28 Sep 2022 10:08:22 +0200 Subject: [PATCH 4/8] fix --- .../com/suse/manager/webui/services/impl/SaltSSHService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java index dbb707c82b3a..2582e4b91e4d 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java +++ b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java @@ -877,6 +877,11 @@ public Map> findApplyHighstateActionsPerMinion(Map Date: Wed, 28 Sep 2022 10:18:29 +0200 Subject: [PATCH 5/8] checkstyle --- .../com/redhat/rhn/GlobalInstanceHolder.java | 8 +- .../SystemEntitlementsSetupActionTest.java | 18 +- .../image/test/ImageInfoHandlerTest.java | 24 +- .../formula/test/FormulaManagerTest.java | 26 +- .../test/SystemEntitlementManagerTest.java | 18 +- .../system/test/SystemManagerMockTest.java | 10 +- .../system/test/SystemManagerTest.java | 25 +- .../task/test/MinionCheckinTest.java | 8 +- .../test/KubernetesManagerTest.java | 10 +- .../ImageDeployedEventMessageActionTest.java | 3 - .../test/JobReturnEventMessageActionTest.java | 72 +-- .../test/MinionActionCleanupTest.java | 34 +- .../reactor/test/MinionStartupActionTest.java | 10 +- .../test/RegisterMinionActionTest.java | 477 +++++++++--------- .../src/com/suse/manager/utils/SaltUtils.java | 4 +- .../bootstrap/AbstractMinionBootstrapper.java | 2 +- .../AbstractMinionBootstrapperTestBase.java | 42 +- .../test/RegularMinionBootstrapperTest.java | 2 +- .../services/SaltServerActionService.java | 16 +- .../manager/webui/services/iface/SaltApi.java | 7 +- .../webui/services/iface/SystemQuery.java | 5 + .../webui/services/impl/SaltSSHService.java | 27 +- .../services/impl/test/SaltServiceTest.java | 1 - .../test/SaltServerActionServiceTest.java | 21 +- .../webui/services/test/TestSaltApi.java | 102 +--- .../webui/services/test/TestSystemQuery.java | 18 +- .../webui/utils/MinionActionUtils.java | 7 +- 27 files changed, 460 insertions(+), 537 deletions(-) diff --git a/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java b/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java index 3e6c4c99e66e..8ece5ee651b7 100644 --- a/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java +++ b/java/code/src/com/redhat/rhn/GlobalInstanceHolder.java @@ -61,18 +61,16 @@ private GlobalInstanceHolder() { public static final SystemQuery SYSTEM_QUERY = new SystemQuery(SALT_API); public static final ServerGroupManager SERVER_GROUP_MANAGER = new ServerGroupManager(SALT_API); public static final FormulaManager FORMULA_MANAGER = new FormulaManager(SALT_API); - public static final SaltUtils SALT_UTILS = new SaltUtils(SYSTEM_QUERY, SALT_API); + public static final SaltUtils SALT_UTILS = new SaltUtils(SALT_API); public static final SaltKeyUtils SALT_KEY_UTILS = new SaltKeyUtils(SALT_API); - public static final SaltServerActionService SALT_SERVER_ACTION_SERVICE = new SaltServerActionService( - SALT_API, SALT_UTILS, SALT_KEY_UTILS); + public static final SaltServerActionService SALT_SERVER_ACTION_SERVICE = new SaltServerActionService(SALT_API); public static final Access ACCESS = new Access(); public static final AclFactory ACL_FACTORY = new AclFactory(ACCESS); // Referenced from JSP public static final MenuTree MENU_TREE = new MenuTree(ACL_FACTORY); public static final UserPreferenceUtils USER_PREFERENCE_UTILS = new UserPreferenceUtils(ACL_FACTORY); public static final RenderUtils RENDER_UTILS = new RenderUtils(ACL_FACTORY); - public static final MinionActionUtils MINION_ACTION_UTILS = new MinionActionUtils( - SALT_SERVER_ACTION_SERVICE, SALT_API, SALT_UTILS); + public static final MinionActionUtils MINION_ACTION_UTILS = new MinionActionUtils(SALT_SERVER_ACTION_SERVICE); public static final KubernetesManager KUBERNETES_MANAGER = new KubernetesManager(SALT_API); public static final VirtManager VIRT_MANAGER = new VirtManagerSalt(SALT_API); public static final RegularMinionBootstrapper REGULAR_MINION_BOOTSTRAPPER = diff --git a/java/code/src/com/redhat/rhn/frontend/action/systems/entitlements/test/SystemEntitlementsSetupActionTest.java b/java/code/src/com/redhat/rhn/frontend/action/systems/entitlements/test/SystemEntitlementsSetupActionTest.java index f2837b0f4361..3d482ba7f194 100644 --- a/java/code/src/com/redhat/rhn/frontend/action/systems/entitlements/test/SystemEntitlementsSetupActionTest.java +++ b/java/code/src/com/redhat/rhn/frontend/action/systems/entitlements/test/SystemEntitlementsSetupActionTest.java @@ -48,9 +48,9 @@ import com.suse.manager.virtualization.VirtManagerSalt; import com.suse.manager.webui.services.iface.MonitoringManager; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.VirtManager; import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.salt.netapi.datatypes.target.MinionList; import org.jmock.Expectations; @@ -70,7 +70,7 @@ public class SystemEntitlementsSetupActionTest extends RhnMockStrutsTestCase { */ private Mockery context = new Mockery(); - private SaltService saltServiceMock; + private SaltApi saltApiMock; private SystemEntitlementManager systemEntitlementManager; @Override @@ -79,16 +79,16 @@ public void setUp() throws Exception { super.setUp(); Config.get().setBoolean(ConfigDefaults.KIWI_OS_IMAGE_BUILDING_ENABLED, "true"); context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE); - saltServiceMock = context.mock(SaltService.class); - ServerGroupManager serverGroupManager = new ServerGroupManager(saltServiceMock); - VirtManager virtManager = new VirtManagerSalt(saltServiceMock); - MonitoringManager monitoringManager = new FormulaMonitoringManager(saltServiceMock); + saltApiMock = context.mock(SaltApi.class); + ServerGroupManager serverGroupManager = new ServerGroupManager(saltApiMock); + VirtManager virtManager = new VirtManagerSalt(saltApiMock); + MonitoringManager monitoringManager = new FormulaMonitoringManager(saltApiMock); systemEntitlementManager = new SystemEntitlementManager( new SystemUnentitler(virtManager, monitoringManager, serverGroupManager), - new SystemEntitler(saltServiceMock, virtManager, monitoringManager, serverGroupManager) + new SystemEntitler(saltApiMock, virtManager, monitoringManager, serverGroupManager) ); context.checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); setRequestPathInfo("/systems/SystemEntitlements"); UserTestUtils.addManagement(user.getOrg()); @@ -165,7 +165,7 @@ public void testContainerBuildHostType() throws Exception { @Test public void testOSImageBuildHostType() throws Exception { context.checking(new Expectations() {{ - allowing(saltServiceMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); + allowing(saltApiMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); }}); Server server = MinionServerFactoryTest.createTestMinionServer(user); diff --git a/java/code/src/com/redhat/rhn/frontend/xmlrpc/image/test/ImageInfoHandlerTest.java b/java/code/src/com/redhat/rhn/frontend/xmlrpc/image/test/ImageInfoHandlerTest.java index 490ff8866e72..8f47ee3acd05 100644 --- a/java/code/src/com/redhat/rhn/frontend/xmlrpc/image/test/ImageInfoHandlerTest.java +++ b/java/code/src/com/redhat/rhn/frontend/xmlrpc/image/test/ImageInfoHandlerTest.java @@ -69,9 +69,9 @@ import com.suse.manager.virtualization.VirtManagerSalt; import com.suse.manager.webui.services.iface.MonitoringManager; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.VirtManager; import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.salt.netapi.datatypes.target.MinionList; @@ -103,7 +103,7 @@ public class ImageInfoHandlerTest extends BaseHandlerTestCase { }}; private static TaskomaticApi taskomaticApi; - private SaltService saltServiceMock; + private SaltApi saltApiMock; private SystemEntitlementManager systemEntitlementManager; @Override @@ -112,18 +112,18 @@ public void setUp() throws Exception { super.setUp(); context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE); Config.get().setBoolean(ConfigDefaults.KIWI_OS_IMAGE_BUILDING_ENABLED, "true"); - saltServiceMock = context.mock(SaltService.class); - ServerGroupManager serverGroupManager = new ServerGroupManager(saltServiceMock); - VirtManager virtManager = new VirtManagerSalt(saltServiceMock); - MonitoringManager monitoringManager = new FormulaMonitoringManager(saltServiceMock); + saltApiMock = context.mock(SaltApi.class); + ServerGroupManager serverGroupManager = new ServerGroupManager(saltApiMock); + VirtManager virtManager = new VirtManagerSalt(saltApiMock); + MonitoringManager monitoringManager = new FormulaMonitoringManager(saltApiMock); systemEntitlementManager = new SystemEntitlementManager( new SystemUnentitler(virtManager, monitoringManager, serverGroupManager), - new SystemEntitler(saltServiceMock, virtManager, monitoringManager, serverGroupManager) + new SystemEntitler(saltApiMock, virtManager, monitoringManager, serverGroupManager) ); - handler = new ImageInfoHandler(saltServiceMock); + handler = new ImageInfoHandler(saltApiMock); context.checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); - allowing(saltServiceMock).removeFile( + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).removeFile( with(equal(Paths.get(String.format("/srv/www/os-images/%d/testimg.tgz", admin.getOrg().getId()))))); will(returnValue(Optional.of(true))); }}); @@ -161,7 +161,7 @@ public final void testimportContainerImage() throws Exception { assertEquals("Image already exists.", e.getMessage()); } - ImageInfoFactory.delete(info.get(), saltServiceMock); + ImageInfoFactory.delete(info.get(), saltApiMock); ret = handler.importContainerImage(admin, "my-external-image", "1.0", server.getId().intValue(), store.getLabel(), "", getNow()); assertTrue(ret > 0); @@ -194,7 +194,7 @@ public final void testScheduleOSImageBuild() throws Exception { ImageInfoFactory.setTaskomaticApi(getTaskomaticApi()); MgrUtilRunner.ExecResult mockResult = new MgrUtilRunner.ExecResult(); context.checking(new Expectations() {{ - allowing(saltServiceMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); + allowing(saltApiMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); will(returnValue(Optional.of(mockResult))); }}); diff --git a/java/code/src/com/redhat/rhn/manager/formula/test/FormulaManagerTest.java b/java/code/src/com/redhat/rhn/manager/formula/test/FormulaManagerTest.java index fb8881c90f89..0becd64f9f39 100644 --- a/java/code/src/com/redhat/rhn/manager/formula/test/FormulaManagerTest.java +++ b/java/code/src/com/redhat/rhn/manager/formula/test/FormulaManagerTest.java @@ -44,7 +44,7 @@ import com.redhat.rhn.testing.TestUtils; import com.redhat.rhn.testing.UserTestUtils; -import com.suse.manager.webui.services.impl.SaltService; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.salt.netapi.datatypes.target.MinionList; import com.suse.utils.Json; @@ -78,7 +78,7 @@ public class FormulaManagerTest extends JMockBaseTestCaseWithUser { static final String TEMP_PATH = "formulas/"; static final String FORMULA_NAME = "dhcpd"; - private SaltService saltServiceMock; + private SaltApi saltApiMock; private FormulaManager manager; private Path metadataDir; @@ -90,9 +90,9 @@ public void setUp() throws Exception { super.setUp(); setImposteriser(ByteBuddyClassImposteriser.INSTANCE); MockConnection.clear(); - saltServiceMock = mock(SaltService.class); + saltApiMock = mock(SaltApi.class); metadataDir = Files.createTempDirectory("metadata"); - manager = new FormulaManager(saltServiceMock); + manager = new FormulaManager(saltApiMock); FormulaFactory.setDataDir(tmpSaltRoot.toString()); FormulaFactory.setMetadataDirOfficial(metadataDir.toString()); createMetadataFiles(); @@ -119,7 +119,7 @@ public void testValidContents() throws Exception { Map contents = Json.GSON.fromJson(contentsData, Map.class); Map layout = Json.GSON.fromJson(layoutData, Map.class); - FormulaManager formulaManager = new FormulaManager(saltServiceMock); + FormulaManager formulaManager = new FormulaManager(saltApiMock); formulaManager.validateContents(contents, layout); } @@ -138,7 +138,7 @@ public void testInValidContents() throws Exception { contents.put("test", "dummy"); // add a random field - FormulaManager formulaManager = new FormulaManager(saltServiceMock); + FormulaManager formulaManager = new FormulaManager(saltApiMock); try { formulaManager.validateContents(contents, layout); fail("Exception expected but didn't throw"); @@ -160,7 +160,7 @@ public void testSaveGroupFormulaData() throws Exception { FormulaFactory.setDataDir(tmpSaltRoot.resolve(TEMP_PATH).toString()); context().checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); manager.saveGroupFormulaData(user, managed.getId(), FORMULA_NAME, contents); Map savedFormulaData = @@ -182,7 +182,7 @@ public void testEnableFormula() throws Exception { FormulaFactory.setMetadataDirOfficial(metadataDir.toString()); context().checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); manager.enableFormula(minion, FORMULA_NAME); List enabledFormulas = FormulaFactory.getFormulasByMinion(minion); @@ -202,7 +202,7 @@ public void testSaveServerFormulaData() throws Exception { MinionServer minion = MinionServerFactoryTest.createTestMinionServer(user); FormulaFactory.setDataDir(tmpSaltRoot.resolve(TEMP_PATH).toString()); context().checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); manager.saveServerFormulaData(user, minion.getId(), FORMULA_NAME, contents); Map savedFormulaData = @@ -223,7 +223,7 @@ public void testSaveServerFormulaDataForUnAuthorized() throws Exception { Map contents = Json.GSON.fromJson(contentsData, Map.class); MinionServer minion = MinionServerFactoryTest.createTestMinionServer(user); FormulaFactory.setDataDir(tmpSaltRoot.resolve(TEMP_PATH).toString()); - FormulaManager formulaManager = new FormulaManager(saltServiceMock); + FormulaManager formulaManager = new FormulaManager(saltApiMock); User testUser = UserTestUtils.createUser("test-user", user.getOrg().getId()); try { formulaManager.saveServerFormulaData(testUser, minion.getId(), FORMULA_NAME, contents); @@ -256,10 +256,10 @@ public void testGetCombinedFormulaDataForSystems() throws Exception { // Server should have a monitoring entitlement after being added to the group context().checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); SystemManager systemManager = new SystemManager(ServerFactory.SINGLETON, ServerGroupFactory.SINGLETON, - saltServiceMock); + saltApiMock); systemManager.addServerToServerGroup(minion, group); assertTrue(SystemManager.hasEntitlement(minion.getId(), EntitlementManager.MONITORING)); @@ -308,7 +308,7 @@ public void testListEndpoints() throws Exception { Map formulaValuesMap = Json.GSON.fromJson(formulaValues, Map.class); FormulaFactory.setDataDir(tmpSaltRoot.resolve(TEMP_PATH).toString()); context().checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); FormulaFactory.saveServerFormulas(minion, Collections.singletonList(PROMETHEUS_EXPORTERS)); manager.saveServerFormulaData(user, minion.getId(), PROMETHEUS_EXPORTERS, formulaValuesMap); diff --git a/java/code/src/com/redhat/rhn/manager/system/entitling/test/SystemEntitlementManagerTest.java b/java/code/src/com/redhat/rhn/manager/system/entitling/test/SystemEntitlementManagerTest.java index 439937786bfe..bfa06a28798c 100644 --- a/java/code/src/com/redhat/rhn/manager/system/entitling/test/SystemEntitlementManagerTest.java +++ b/java/code/src/com/redhat/rhn/manager/system/entitling/test/SystemEntitlementManagerTest.java @@ -41,9 +41,9 @@ import com.suse.manager.virtualization.VirtManagerSalt; import com.suse.manager.webui.services.iface.MonitoringManager; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.VirtManager; import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.salt.netapi.datatypes.target.MinionList; import org.jmock.Expectations; @@ -53,7 +53,7 @@ public class SystemEntitlementManagerTest extends JMockBaseTestCaseWithUser { - private SaltService saltServiceMock; + private SaltApi saltApiMock; private SystemEntitlementManager systemEntitlementManager; @Override @@ -61,16 +61,16 @@ public class SystemEntitlementManagerTest extends JMockBaseTestCaseWithUser { public void setUp() throws Exception { super.setUp(); setImposteriser(ByteBuddyClassImposteriser.INSTANCE); - saltServiceMock = mock(SaltService.class); - ServerGroupManager serverGroupManager = new ServerGroupManager(saltServiceMock); - VirtManager virtManager = new VirtManagerSalt(saltServiceMock); - MonitoringManager monitoringManager = new FormulaMonitoringManager(saltServiceMock); + saltApiMock = mock(SaltApi.class); + ServerGroupManager serverGroupManager = new ServerGroupManager(saltApiMock); + VirtManager virtManager = new VirtManagerSalt(saltApiMock); + MonitoringManager monitoringManager = new FormulaMonitoringManager(saltApiMock); systemEntitlementManager = new SystemEntitlementManager( new SystemUnentitler(virtManager, monitoringManager, serverGroupManager), - new SystemEntitler(saltServiceMock, virtManager, monitoringManager, serverGroupManager) + new SystemEntitler(saltApiMock, virtManager, monitoringManager, serverGroupManager) ); context().checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); } @@ -117,7 +117,7 @@ public void testEntitleServer() throws Exception { //Test OS Image Build Host context().checking(new Expectations() {{ - allowing(saltServiceMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); + allowing(saltApiMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); }}); minion.setServerArch(ServerFactory.lookupServerArchByLabel("x86_64-redhat-linux")); diff --git a/java/code/src/com/redhat/rhn/manager/system/test/SystemManagerMockTest.java b/java/code/src/com/redhat/rhn/manager/system/test/SystemManagerMockTest.java index 1868ba4fd6a7..cfdd415796b6 100644 --- a/java/code/src/com/redhat/rhn/manager/system/test/SystemManagerMockTest.java +++ b/java/code/src/com/redhat/rhn/manager/system/test/SystemManagerMockTest.java @@ -34,7 +34,7 @@ import com.redhat.rhn.testing.TestUtils; import com.redhat.rhn.testing.UserTestUtils; -import com.suse.manager.webui.services.impl.SaltService; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import org.apache.commons.codec.digest.DigestUtils; @@ -88,16 +88,16 @@ public void testRemovingServerInvalidatesTokens() throws Exception { MinionServer server = TestUtils.saveAndReload(testMinionServer); - SaltService saltServiceMock = mock(SaltService.class); + SaltApi saltApiMock = mock(SaltApi.class); context().checking(new Expectations() {{ - allowing(saltServiceMock).deleteKey(testMinionServer.getMinionId()); - allowing(saltServiceMock).removeSaltSSHKnownHost(testMinionServer.getHostname()); + allowing(saltApiMock).deleteKey(testMinionServer.getMinionId()); + allowing(saltApiMock).removeSaltSSHKnownHost(testMinionServer.getHostname()); will(returnValue(Optional.of(new MgrUtilRunner.RemoveKnowHostResult("removed", "")))); }}); SystemManager systemManager = new SystemManager(ServerFactory.SINGLETON, ServerGroupFactory.SINGLETON, - saltServiceMock); + saltApiMock); systemManager.deleteServer(server.getOrg().getActiveOrgAdmins().get(0), server.getId()); assertFalse(tokenBase.getValid()); diff --git a/java/code/src/com/redhat/rhn/manager/system/test/SystemManagerTest.java b/java/code/src/com/redhat/rhn/manager/system/test/SystemManagerTest.java index 19298841a6c2..d41ce19e55e5 100644 --- a/java/code/src/com/redhat/rhn/manager/system/test/SystemManagerTest.java +++ b/java/code/src/com/redhat/rhn/manager/system/test/SystemManagerTest.java @@ -135,7 +135,6 @@ import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.VirtManager; import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.manager.webui.services.test.TestSaltApi; import com.suse.manager.xmlrpc.dto.SystemEventDetailsDto; @@ -186,7 +185,7 @@ public class SystemManagerTest extends JMockBaseTestCaseWithUser { public static final int HOST_RAM_MB = 2048; public static final int HOST_SWAP_MB = 1024; - private SaltService saltServiceMock; + private SaltApi saltApiMock; protected Path tmpPillarRoot; protected Path tmpSaltRoot; protected Path metadataDirOfficial; @@ -203,7 +202,7 @@ public void setUp() throws Exception { setImposteriser(ByteBuddyClassImposteriser.INSTANCE); TaskomaticApi taskomaticMock = mock(TaskomaticApi.class); ActionManager.setTaskomaticApi(taskomaticMock); - saltServiceMock = mock(SaltService.class); + saltApiMock = mock(SaltApi.class); tmpSaltRoot = Files.createTempDirectory("salt"); metadataDirOfficial = Files.createTempDirectory("meta"); FormulaFactory.setDataDir(tmpSaltRoot.toString()); @@ -212,7 +211,7 @@ public void setUp() throws Exception { { allowing(taskomaticMock) .scheduleActionExecution(with(any(Action.class))); - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); } }); SaltApi saltApi = new TestSaltApi(); @@ -223,7 +222,7 @@ public void setUp() throws Exception { new SystemUnentitler(virtManager, monitoringManager, serverGroupManager), new SystemEntitler(saltApi, virtManager, monitoringManager, serverGroupManager) ); - this.systemManager = new SystemManager(ServerFactory.SINGLETON, ServerGroupFactory.SINGLETON, saltServiceMock); + this.systemManager = new SystemManager(ServerFactory.SINGLETON, ServerGroupFactory.SINGLETON, saltApiMock); createMetadataFiles(); } @@ -332,8 +331,8 @@ public void testFormulaDataCleanUp() throws Exception { minion.getPillarByCategory(FormulaFactory.PREFIX + formulaName).orElseThrow().getPillar()); context().checking(new Expectations() {{ - allowing(saltServiceMock).deleteKey(minionId); - allowing(saltServiceMock).removeSaltSSHKnownHost(minion.getHostname()); + allowing(saltApiMock).deleteKey(minionId); + allowing(saltApiMock).removeSaltSSHKnownHost(minion.getHostname()); will(returnValue(Optional.of(new MgrUtilRunner.RemoveKnowHostResult("removed", "")))); }}); systemManager.deleteServer(user, minion.getId()); @@ -354,8 +353,8 @@ public void testEmptyFormulaDataCleanUp() throws Exception { FormulaFactory.saveServerFormulas(minion, singletonList(formulaName)); context().checking(new Expectations() {{ - allowing(saltServiceMock).deleteKey(minionId); - allowing(saltServiceMock).removeSaltSSHKnownHost(minion.getHostname()); + allowing(saltApiMock).deleteKey(minionId); + allowing(saltApiMock).removeSaltSSHKnownHost(minion.getHostname()); will(returnValue(Optional.of(new MgrUtilRunner.RemoveKnowHostResult("removed", "")))); }}); systemManager.deleteServer(user, minion.getId()); @@ -1960,11 +1959,11 @@ public void testCreateProxyContainerConfig() throws InstantiationException, IOEx String sshPushPubKey = "DummySshPushPubKey"; context().checking(new Expectations() {{ - allowing(saltServiceMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); + allowing(saltApiMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); will(returnValue(Optional.of(new MgrUtilRunner.SshKeygenResult(sshKey, sshPubKey)))); - allowing(saltServiceMock).generateSSHKey(with(aNull(String.class))); + allowing(saltApiMock).generateSSHKey(with(aNull(String.class))); will(returnValue(Optional.of(new MgrUtilRunner.SshKeygenResult(sshPushKey, sshPushPubKey)))); - allowing(saltServiceMock) + allowing(saltApiMock) .checkSSLCert(with(equal(rootCA)), with(equal(new SSLCertPair(cert, key))), with(equal(otherCAs))); will(returnValue(apacheCert)); }}); @@ -2001,7 +2000,7 @@ public void testCreateProxyContainerConfigExisting() throws InstantiationExcepti String proxyName = "pxy.mgr.lab"; createTestProxy(proxyName); context().checking(new Expectations() {{ - oneOf(saltServiceMock).removeSaltSSHKnownHost(with(proxyName), with(8022)); + oneOf(saltApiMock).removeSaltSSHKnownHost(with(proxyName), with(8022)); will(returnValue(Optional.of(new MgrUtilRunner.RemoveKnowHostResult("removed", "")))); }}); testCreateProxyContainerConfig(); diff --git a/java/code/src/com/redhat/rhn/taskomatic/task/test/MinionCheckinTest.java b/java/code/src/com/redhat/rhn/taskomatic/task/test/MinionCheckinTest.java index 17746d69aa32..0d787232cf56 100644 --- a/java/code/src/com/redhat/rhn/taskomatic/task/test/MinionCheckinTest.java +++ b/java/code/src/com/redhat/rhn/taskomatic/task/test/MinionCheckinTest.java @@ -27,7 +27,7 @@ import com.redhat.rhn.testing.JMockBaseTestCaseWithUser; import com.redhat.rhn.testing.TestUtils; -import com.suse.manager.webui.services.impl.SaltService; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.salt.netapi.datatypes.target.MinionList; import org.apache.commons.lang3.time.DateUtils; @@ -75,14 +75,14 @@ public void testExecuteOnActiveMinions() throws Exception { minion1.setServerInfo(serverInfo); TestUtils.saveAndFlush(minion1); - SaltService saltServiceMock = mock(SaltService.class); + SaltApi saltApiMock = mock(SaltApi.class); context().checking(new Expectations() { { - never(saltServiceMock).checkIn(with(any(MinionList.class))); + never(saltApiMock).checkIn(with(any(MinionList.class))); } }); MinionCheckin minionCheckinJob = new MinionCheckin(); - minionCheckinJob.setSaltApi(saltServiceMock); + minionCheckinJob.setSaltApi(saltApiMock); minionCheckinJob.execute(null); } diff --git a/java/code/src/com/suse/manager/kubernetes/test/KubernetesManagerTest.java b/java/code/src/com/suse/manager/kubernetes/test/KubernetesManagerTest.java index e2b12669ccd1..845aab7ff4e6 100644 --- a/java/code/src/com/suse/manager/kubernetes/test/KubernetesManagerTest.java +++ b/java/code/src/com/suse/manager/kubernetes/test/KubernetesManagerTest.java @@ -30,7 +30,7 @@ import com.suse.manager.kubernetes.KubernetesManager; import com.suse.manager.model.kubernetes.ContainerInfo; import com.suse.manager.model.kubernetes.ImageUsage; -import com.suse.manager.webui.services.impl.SaltService; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.impl.runner.MgrK8sRunner; import com.suse.salt.netapi.parser.JsonParser; @@ -51,7 +51,7 @@ */ public class KubernetesManagerTest extends JMockBaseTestCaseWithUser { - private SaltService saltServiceMock; + private SaltApi saltApiMock; private KubernetesManager manager; @Override @@ -60,8 +60,8 @@ public void setUp() throws Exception { super.setUp(); setImposteriser(ByteBuddyClassImposteriser.INSTANCE); - saltServiceMock = mock(SaltService.class); - manager = new KubernetesManager(saltServiceMock); + saltApiMock = mock(SaltApi.class); + manager = new KubernetesManager(saltApiMock); for (VirtualHostManager virtHostMgr : VirtualHostManagerFactory.getInstance().listVirtualHostManagers()) { VirtualHostManagerFactory.getInstance().delete(virtHostMgr); @@ -237,7 +237,7 @@ public void testGetContainersUsageInactiveContainers() throws Exception { private void expectGetAllContainers(String kubeconfig, String context, String file) throws IOException, ClassNotFoundException { context().checking(new Expectations() { { - allowing(saltServiceMock).getAllContainers(with(kubeconfig), with(context)); + allowing(saltApiMock).getAllContainers(with(kubeconfig), with(context)); will(returnValue(Optional.of(new JsonParser<>(MgrK8sRunner.getAllContainers("", "").getReturnType()).parse( TestUtils.readRelativeFile(this, file)).getContainers()))); } }); diff --git a/java/code/src/com/suse/manager/reactor/messaging/test/ImageDeployedEventMessageActionTest.java b/java/code/src/com/suse/manager/reactor/messaging/test/ImageDeployedEventMessageActionTest.java index cf103b1db621..e3387023015e 100644 --- a/java/code/src/com/suse/manager/reactor/messaging/test/ImageDeployedEventMessageActionTest.java +++ b/java/code/src/com/suse/manager/reactor/messaging/test/ImageDeployedEventMessageActionTest.java @@ -46,7 +46,6 @@ import com.suse.manager.reactor.messaging.ImageDeployedEventMessageAction; import com.suse.manager.reactor.utils.ValueMap; import com.suse.manager.webui.services.iface.SystemQuery; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.services.test.TestSystemQuery; import com.suse.manager.webui.utils.salt.custom.ImageDeployedEvent; import com.suse.salt.netapi.calls.modules.Grains; @@ -78,7 +77,6 @@ public class ImageDeployedEventMessageActionTest extends JMockBaseTestCaseWithUs private Map grains; // Mocks - private SaltService saltMock; private SystemQuery systemQuery; private TaskomaticApi taskomaticMock; @@ -88,7 +86,6 @@ public void setUp() throws Exception { super.setUp(); setImposteriser(ByteBuddyClassImposteriser.INSTANCE); - saltMock = mock(SaltService.class); taskomaticMock = mock(TaskomaticApi.class); ActionManager.setTaskomaticApi(taskomaticMock); diff --git a/java/code/src/com/suse/manager/reactor/messaging/test/JobReturnEventMessageActionTest.java b/java/code/src/com/suse/manager/reactor/messaging/test/JobReturnEventMessageActionTest.java index 38a82dfc92ab..9302cf3240b3 100644 --- a/java/code/src/com/suse/manager/reactor/messaging/test/JobReturnEventMessageActionTest.java +++ b/java/code/src/com/suse/manager/reactor/messaging/test/JobReturnEventMessageActionTest.java @@ -86,9 +86,9 @@ import com.suse.manager.virtualization.VirtManagerSalt; import com.suse.manager.webui.services.SaltServerActionService; import com.suse.manager.webui.services.iface.MonitoringManager; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.VirtManager; import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.salt.netapi.calls.LocalCall; import com.suse.salt.netapi.calls.modules.Openscap; @@ -144,7 +144,7 @@ public class JobReturnEventMessageActionTest extends JMockBaseTestCaseWithUser { }); private TaskomaticApi taskomaticApi; - private SaltService saltServiceMock; + private SaltApi saltApiMock; private SystemEntitlementManager systemEntitlementManager; protected Path metadataDirOfficial; private SaltUtils saltUtils; @@ -159,17 +159,17 @@ public void setUp() throws Exception { setImposteriser(ByteBuddyClassImposteriser.INSTANCE); Config.get().setString("server.secret_key", DigestUtils.sha256Hex(TestUtils.randomString())); - saltServiceMock = context().mock(SaltService.class); - ServerGroupManager serverGroupManager = new ServerGroupManager(saltServiceMock); - VirtManager virtManager = new VirtManagerSalt(saltServiceMock); - MonitoringManager monitoringManager = new FormulaMonitoringManager(saltServiceMock); + saltApiMock = context().mock(SaltUtils.class); + ServerGroupManager serverGroupManager = new ServerGroupManager(saltApiMock); + VirtManager virtManager = new VirtManagerSalt(saltApiMock); + MonitoringManager monitoringManager = new FormulaMonitoringManager(saltApiMock); systemEntitlementManager = new SystemEntitlementManager( new SystemUnentitler(virtManager, monitoringManager, serverGroupManager), - new SystemEntitler(saltServiceMock, virtManager, monitoringManager, serverGroupManager) + new SystemEntitler(saltApiMock, virtManager, monitoringManager, serverGroupManager) ); - saltUtils = new SaltUtils(saltServiceMock, saltServiceMock); - saltServerActionService = new SaltServerActionService(saltServiceMock, saltUtils, - new SaltKeyUtils(saltServiceMock)); + saltUtils = new SaltUtils(saltApiMock); + saltServerActionService = new SaltServerActionService(saltApiMock, saltUtils, + new SaltKeyUtils(saltApiMock)); metadataDirOfficial = Files.createTempDirectory("meta"); formulaDataDir = Files.createTempDirectory("data"); FormulaFactory.setMetadataDirOfficial(metadataDirOfficial.toString()); @@ -180,7 +180,7 @@ public void setUp() throws Exception { Files.createFile(systemLockFile); context().checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); } @@ -1401,7 +1401,7 @@ public void testOpenscap() throws Exception { .orElseThrow(() -> new RuntimeException("missiong scap result")); context().checking(new Expectations() {{ - oneOf(saltServiceMock).storeMinionScapFiles( + oneOf(saltApiMock).storeMinionScapFiles( with(any(MinionServer.class)), with(openscapResult.getUploadDir()), with(action.getId())); @@ -1615,7 +1615,7 @@ private ImageInfo doTestContainerImageBuild(MinionServer server, String imageNam ImageProfile profile, Consumer assertions) throws Exception { context().checking(new Expectations() {{ - allowing(saltServiceMock).copyFile(with(any(Path.class)), with(any(Path.class))); + allowing(saltApiMock).copyFile(with(any(Path.class)), with(any(Path.class))); will(returnValue(Optional.empty())); }}); // schedule the build @@ -1686,40 +1686,40 @@ public void testKiwiImageBuild() throws Exception { MgrUtilRunner.ExecResult mockResult = new MgrUtilRunner.ExecResult(); context().checking(new Expectations() {{ - allowing(saltServiceMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); - allowing(saltServiceMock).collectKiwiImage(with(equal(server)), + allowing(saltApiMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); + allowing(saltApiMock).collectKiwiImage(with(equal(server)), with(equal("/var/lib/Kiwi/build129/images.build/POS_Image_JeOS7.x86_64-7.0.0")), with(equal(String.format("/srv/www/os-images/%d/POS_Image_JeOS7-7.0.0-1/", user.getOrg().getId())))); will(returnValue(Optional.of(mockResult))); - allowing(saltServiceMock).collectKiwiImage(with(equal(server)), + allowing(saltApiMock).collectKiwiImage(with(equal(server)), with(equal("/var/lib/Kiwi/build129/images.build/POS_Image_JeOS7.x86_64-7.0.0.initrd")), with(equal(String.format("/srv/www/os-images/%d/POS_Image_JeOS7-7.0.0-1/", user.getOrg().getId())))); will(returnValue(Optional.of(mockResult))); - allowing(saltServiceMock).collectKiwiImage(with(equal(server)), + allowing(saltApiMock).collectKiwiImage(with(equal(server)), with(equal("/var/lib/Kiwi/build129/images.build/POS_Image_JeOS7.x86_64-7.0.0-5.3.18-150300.59.54" + "-default.kernel")), with(equal(String.format("/srv/www/os-images/%d/POS_Image_JeOS7-7.0.0-1/", user.getOrg().getId())))); will(returnValue(Optional.of(mockResult))); - allowing(saltServiceMock).removeFile( + allowing(saltApiMock).removeFile( with(equal(Paths.get(String.format( "/srv/www/os-images/%d/POS_Image_JeOS7-7.0.0-1/POS_Image_JeOS7.x86_64-7.0.0", user.getOrg().getId()))))); will(returnValue(Optional.of(true))); - allowing(saltServiceMock).removeFile( + allowing(saltApiMock).removeFile( with(equal(Paths.get(String.format( "/srv/www/os-images/%d/POS_Image_JeOS7-7.0.0-1/POS_Image_JeOS7.x86_64-7.0.0.initrd", user.getOrg().getId()))))); will(returnValue(Optional.of(true))); - allowing(saltServiceMock).removeFile( + allowing(saltApiMock).removeFile( with(equal(Paths.get(String.format( "/srv/www/os-images/%d/POS_Image_JeOS7-7.0.0-1/POS_Image_JeOS7.x86_64-7.0.0" + "-5.3.18-150300.59.54-default.kernel", user.getOrg().getId()))))); will(returnValue(Optional.of(true))); - allowing(saltServiceMock).copyFile(with(any(Path.class)), with(any(Path.class))); + allowing(saltApiMock).copyFile(with(any(Path.class)), with(any(Path.class))); will(returnValue(Optional.empty())); }}); systemEntitlementManager.addEntitlementToServer(server, EntitlementManager.OSIMAGE_BUILD_HOST); @@ -1750,7 +1750,7 @@ public void testKiwiImageBuild() throws Exception { assertEquals("7057ea9a15784f469e03f2de045d3c73", file.getChecksum().getChecksum()); assertEquals(pathPrefix + "POS_Image_JeOS7.x86_64-7.0.0", file.getFile()); }); - ImageInfoFactory.delete(image, saltServiceMock); + ImageInfoFactory.delete(image, saltApiMock); HibernateFactory.getSession().flush(); } @@ -1764,19 +1764,19 @@ public void testKiwiImageBuildWithBundle() throws Exception { MgrUtilRunner.ExecResult mockResult = new MgrUtilRunner.ExecResult(); context().checking(new Expectations() {{ - allowing(saltServiceMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); - allowing(saltServiceMock).collectKiwiImage(with(equal(server)), + allowing(saltApiMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); + allowing(saltApiMock).collectKiwiImage(with(equal(server)), with(equal("/var/lib/Kiwi/build137/images/POS_Image_JeOS7.x86_64-7.0.0-build129.tar.xz")), with(equal(String.format("/srv/www/os-images/%d/POS_Image_JeOS7-7.0.0-1/", user.getOrg().getId())))); will(returnValue(Optional.of(mockResult))); - allowing(saltServiceMock).removeFile( + allowing(saltApiMock).removeFile( with(equal(Paths.get(String.format( "/srv/www/os-images/%d/POS_Image_JeOS7-7.0.0-1/POS_Image_JeOS7.x86_64-7.0.0" + "-build129.tar.xz", user.getOrg().getId()))))); will(returnValue(Optional.of(true))); - allowing(saltServiceMock).copyFile(with(any(Path.class)), with(any(Path.class))); + allowing(saltApiMock).copyFile(with(any(Path.class)), with(any(Path.class))); will(returnValue(Optional.empty())); }}); systemEntitlementManager.addEntitlementToServer(server, EntitlementManager.OSIMAGE_BUILD_HOST); @@ -1797,7 +1797,7 @@ public void testKiwiImageBuildWithBundle() throws Exception { assertEquals("POS_Image_JeOS7-7.0.0-1/POS_Image_JeOS7.x86_64-7.0.0-build129.tar.xz", info.getImageFiles().stream().findFirst().get().getFile()); }); - ImageInfoFactory.delete(image, saltServiceMock); + ImageInfoFactory.delete(image, saltApiMock); HibernateFactory.getSession().flush(); } @@ -1811,17 +1811,17 @@ public void testKiwiImageInspect() throws Exception { MgrUtilRunner.ExecResult mockResult = new MgrUtilRunner.ExecResult(); context().checking(new Expectations() {{ - allowing(saltServiceMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); - allowing(saltServiceMock).removeFile( + allowing(saltApiMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); + allowing(saltApiMock).removeFile( with(equal(Paths.get(String.format("/srv/www/os-images/%d/image", user.getOrg().getId()))))); will(returnValue(Optional.of(true))); - allowing(saltServiceMock).removeFile( + allowing(saltApiMock).removeFile( with(equal(Paths.get(String.format("/srv/www/os-images/%d/kernel", user.getOrg().getId()))))); will(returnValue(Optional.of(true))); - allowing(saltServiceMock).removeFile( + allowing(saltApiMock).removeFile( with(equal(Paths.get(String.format("/srv/www/os-images/%d/initrd", user.getOrg().getId()))))); will(returnValue(Optional.of(true))); - allowing(saltServiceMock).copyFile(with(any(Path.class)), with(any(Path.class))); + allowing(saltApiMock).copyFile(with(any(Path.class)), with(any(Path.class))); will(returnValue(Optional.empty())); }}); systemEntitlementManager.addEntitlementToServer(server, EntitlementManager.OSIMAGE_BUILD_HOST); @@ -1867,7 +1867,7 @@ public void testKiwiImageInspect() throws Exception { HibernateFactory.getSession().flush(); - ImageInfoFactory.delete(image, saltServiceMock); + ImageInfoFactory.delete(image, saltApiMock); HibernateFactory.getSession().flush(); @@ -1886,13 +1886,13 @@ public void testKiwiImageInspectCompressedImage() throws Exception { MgrUtilRunner.ExecResult mockResult = new MgrUtilRunner.ExecResult(); context().checking(new Expectations() {{ - allowing(saltServiceMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); - allowing(saltServiceMock).collectKiwiImage(with(equal(server)), + allowing(saltApiMock).generateSSHKey(with(equal(SaltSSHService.SSH_KEY_PATH))); + allowing(saltApiMock).collectKiwiImage(with(equal(server)), with(equal("/var/lib/Kiwi/build06/images/POS_Image_JeOS6.x86_64-6.0.0-build06.tgz")), with(equal(String.format("/srv/www/os-images/%d/POS_Image_JeOS6-6.0.0-0/", user.getOrg().getId())))); will(returnValue(Optional.of(mockResult))); - allowing(saltServiceMock).copyFile(with(any(Path.class)), with(any(Path.class))); + allowing(saltApiMock).copyFile(with(any(Path.class)), with(any(Path.class))); will(returnValue(Optional.empty())); }}); systemEntitlementManager.addEntitlementToServer(server, EntitlementManager.OSIMAGE_BUILD_HOST); diff --git a/java/code/src/com/suse/manager/reactor/messaging/test/MinionActionCleanupTest.java b/java/code/src/com/suse/manager/reactor/messaging/test/MinionActionCleanupTest.java index 51b693539fe8..821e2709447d 100644 --- a/java/code/src/com/suse/manager/reactor/messaging/test/MinionActionCleanupTest.java +++ b/java/code/src/com/suse/manager/reactor/messaging/test/MinionActionCleanupTest.java @@ -39,7 +39,7 @@ import com.suse.manager.utils.SaltKeyUtils; import com.suse.manager.utils.SaltUtils; import com.suse.manager.webui.services.SaltServerActionService; -import com.suse.manager.webui.services.impl.SaltService; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.utils.MinionActionUtils; import com.suse.salt.netapi.calls.modules.SaltUtil; import com.suse.salt.netapi.calls.runner.Jobs; @@ -110,19 +110,19 @@ public void testMinionActionCleanup() throws Exception { running.put(minion2.getMinionId(), new Result<>(Xor.right(Collections.emptyList()))); Jobs.Info listJobResult = listJob("jobs.list_job.state.apply.json", action.getId()); - SaltService saltServiceMock = mock(SaltService.class); + SaltApi saltApiMock = mock(SaltApi.class); context().checking(new Expectations() { { - allowing(saltServiceMock).getRunning(with(any(MinionList.class))); + allowing(saltApiMock).getRunning(with(any(MinionList.class))); will(returnValue(running)); - never(saltServiceMock).getJobsByMetadata(with(any(Object.class))); - never(saltServiceMock).getListJob(with(any(String.class))); + never(saltApiMock).getJobsByMetadata(with(any(Object.class))); + never(saltApiMock).getListJob(with(any(String.class))); } }); - SaltUtils saltUtils = new SaltUtils(saltServiceMock, saltServiceMock); - SaltServerActionService saltServerActionService = new SaltServerActionService(saltServiceMock, saltUtils, - new SaltKeyUtils(saltServiceMock)); - MinionActionUtils minionActionUtils = new MinionActionUtils(saltServerActionService, saltServiceMock, + SaltUtils saltUtils = new SaltUtils(saltApiMock); + SaltServerActionService saltServerActionService = new SaltServerActionService(saltApiMock, saltUtils, + new SaltKeyUtils(saltApiMock)); + MinionActionUtils minionActionUtils = new MinionActionUtils(saltServerActionService, saltApiMock, saltUtils); minionActionUtils.cleanupMinionActions(); } @@ -174,7 +174,7 @@ public void testMinionActionChainCleanupAllCompleted() throws Exception { SystemManager.giveCapability(minion2.getId(), SystemManager.CAP_SCRIPT_RUN, 1L); TaskomaticApi taskomaticMock = mock(TaskomaticApi.class); - SaltService saltServiceMock = mock(SaltService.class); + SaltService saltApiMock = mock(SaltService.class); ActionManager.setTaskomaticApi(taskomaticMock); ActionChainManager.setTaskomaticApi(taskomaticMock); @@ -227,7 +227,7 @@ public void testMinionActionChainCleanupAllCompleted() throws Exception { allowing(taskomaticMock).scheduleActionExecution(with(any(Action.class))); allowing(taskomaticMock).scheduleActionChainExecution(with(any(ActionChain.class))); - allowing(saltServiceMock).getJobsByMetadata( + allowing(saltApiMock).getJobsByMetadata( with(any(Object.class)), with(any(LocalDateTime.class)), with(any(LocalDateTime.class))); will(returnValue(Optional.of(jobsByMetadata("jobs.list_jobs.actionchains.json", 0)))); @@ -238,8 +238,8 @@ public void testMinionActionChainCleanupAllCompleted() throws Exception { } private void mockListJob(String jid) { - never(saltServiceMock).getJobsByMetadata(with(any(Object.class))); - never(saltServiceMock).getListJob(with(any(String.class))); + never(saltApiMock).getJobsByMetadata(with(any(Object.class))); + never(saltApiMock).getListJob(with(any(String.class))); } }); @@ -247,10 +247,8 @@ private void mockListJob(String jid) { ActionChainFactory.delete(actionChain); - SaltUtils saltUtils = new SaltUtils(saltServiceMock, saltServiceMock); - SaltServerActionService saltServerActionService = new SaltServerActionService(saltServiceMock, saltUtils, - new SaltKeyUtils(saltServiceMock)); - MinionActionUtils minionActionUtils = new MinionActionUtils(saltServerActionService, saltServiceMock, - saltUtils); + SaltUtils saltUtils = new SaltUtils(saltApiMock); + SaltServerActionService saltServerActionService = new SaltServerActionService(saltApiMock); + MinionActionUtils minionActionUtils = new MinionActionUtils(saltServerActionService); } } diff --git a/java/code/src/com/suse/manager/reactor/test/MinionStartupActionTest.java b/java/code/src/com/suse/manager/reactor/test/MinionStartupActionTest.java index e759883e818f..89091f5d5b5c 100644 --- a/java/code/src/com/suse/manager/reactor/test/MinionStartupActionTest.java +++ b/java/code/src/com/suse/manager/reactor/test/MinionStartupActionTest.java @@ -20,7 +20,7 @@ import com.suse.manager.reactor.messaging.MinionStartEventMessage; import com.suse.manager.reactor.messaging.MinionStartEventMessageAction; -import com.suse.manager.webui.services.impl.SaltService; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.salt.netapi.datatypes.target.MinionList; import org.cobbler.test.MockConnection; @@ -35,7 +35,7 @@ public class MinionStartupActionTest extends JMockBaseTestCaseWithUser { private static final String MINION_ID = "suma3pg.vagrant.local"; - private SaltService saltServiceMock; + private SaltApi saltApiMock; @Override @BeforeEach @@ -43,7 +43,7 @@ public void setUp() throws Exception { super.setUp(); setImposteriser(ByteBuddyClassImposteriser.INSTANCE); MockConnection.clear(); - saltServiceMock = mock(SaltService.class); + saltApiMock = mock(SaltApi.class); } @@ -53,10 +53,10 @@ public void testStarupEventFired() throws Exception { minion.setMinionId(MINION_ID); // Verify the resulting system entry context().checking(new Expectations() {{ - allowing(saltServiceMock).updateSystemInfo(with(any(MinionList.class))); + allowing(saltApiMock).updateSystemInfo(with(any(MinionList.class))); }}); // On minion start up apply state via mocked SaltService - MinionStartEventMessageAction action = new MinionStartEventMessageAction(saltServiceMock); + MinionStartEventMessageAction action = new MinionStartEventMessageAction(saltApiMock); action.execute(new MinionStartEventMessage(MINION_ID)); } } diff --git a/java/code/src/com/suse/manager/reactor/test/RegisterMinionActionTest.java b/java/code/src/com/suse/manager/reactor/test/RegisterMinionActionTest.java index 0524038980bd..e2d1a53c37e6 100644 --- a/java/code/src/com/suse/manager/reactor/test/RegisterMinionActionTest.java +++ b/java/code/src/com/suse/manager/reactor/test/RegisterMinionActionTest.java @@ -84,8 +84,8 @@ import com.suse.manager.webui.controllers.utils.ContactMethodUtil; import com.suse.manager.webui.services.ConfigChannelSaltManager; import com.suse.manager.webui.services.iface.RedhatProductInfo; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.impl.MinionPendingRegistrationService; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.manager.webui.utils.salt.custom.MinionStartupGrains; import com.suse.salt.netapi.calls.LocalCall; @@ -137,7 +137,7 @@ public class RegisterMinionActionTest extends JMockBaseTestCaseWithUser { private static final String SSH_PUSH_CONTACT_METHOD = "ssh-push"; private static final String DEFAULT_CONTACT_METHOD = "default"; private Path metadataDirOfficial; - private SaltService saltServiceMock; + private SaltApi saltApiMock; private SystemManager systemManager; @FunctionalInterface @@ -162,42 +162,42 @@ private interface Assertions { private ExpectationsFunction SLES_EXPECTATIONS = (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); MinionStartupGrains minionStartUpGrains = new MinionStartupGrains.MinionStartupGrainsBuilder() .machineId(MACHINE_ID).saltbootInitrd(false) .createMinionStartUpGrains(); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).removeSaltSSHKnownHost(with(any(String.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).removeSaltSSHKnownHost(with(any(String.class))); will(returnValue(Optional.of(new MgrUtilRunner.RemoveKnowHostResult("removed", "")))); }}; @SuppressWarnings("unchecked") private final ExpectationsFunction SLES_NO_AK_EXPECTATIONS = (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); MinionStartupGrains minionStartUpGrains = new MinionStartupGrains.MinionStartupGrainsBuilder() .machineId(MACHINE_ID).saltbootInitrd(false) .createMinionStartUpGrains(); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getProducts(with(any(String.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getProducts(with(any(String.class))); will(returnValue(Optional.empty())); }}; @@ -286,11 +286,11 @@ public void setUp() throws Exception { Path testFormulaFile = Paths.get(testFormulaDir.toString(), "form.yml"); Files.createFile(testFormulaFile); - saltServiceMock = mock(SaltService.class); - systemManager = new SystemManager(ServerFactory.SINGLETON, ServerGroupFactory.SINGLETON, saltServiceMock); + saltApiMock = mock(SaltApi.class); + systemManager = new SystemManager(ServerFactory.SINGLETON, ServerGroupFactory.SINGLETON, saltApiMock); context().checking(new Expectations() {{ - allowing(saltServiceMock).refreshPillar(with(any(MinionList.class))); + allowing(saltApiMock).refreshPillar(with(any(MinionList.class))); }}); } @@ -353,12 +353,11 @@ public void executeTest(ExpectationsFunction expectations, ActivationKeySupplier }}); } - RegisterMinionEventMessageAction action = new RegisterMinionEventMessageAction(saltServiceMock, - saltServiceMock); + RegisterMinionEventMessageAction action = new RegisterMinionEventMessageAction(saltApiMock); action.execute(new RegisterMinionEventMessage(MINION_ID, startupGrains)); // Verify the resulting system entry - String machineId = saltServiceMock.getMachineId(MINION_ID).get(); + String machineId = saltApiMock.getMachineId(MINION_ID).get(); Optional optMinion = MinionServerFactory.findByMachineId(machineId); if (assertions != null) { @@ -460,23 +459,23 @@ public void testAlreadyRegisteredMinionWithSameMachineId2() throws Exception { .createMinionStartUpGrains(); try { executeTest((key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); MinionStartupGrains minionStartUpGrains = new MinionStartupGrains.MinionStartupGrainsBuilder() .machineId(MACHINE_ID).saltbootInitrd(false) .createMinionStartUpGrains(); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).removeSaltSSHKnownHost(with(any(String.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).removeSaltSSHKnownHost(with(any(String.class))); will(returnValue(Optional.of(new MgrUtilRunner.RemoveKnowHostResult("removed", "")))); - allowing(saltServiceMock).deleteKey(server2.getMinionId()); + allowing(saltApiMock).deleteKey(server2.getMinionId()); }}, ACTIVATION_KEY_SUPPLIER, (optMinion, machineId, key) -> assertFalse(optMinion.isPresent()), null, DEFAULT_CONTACT_METHOD, Optional.of(minionStartUpGrains)); } @@ -504,15 +503,15 @@ public void testAlreadyRegisteredMinionWithNewMinionId() throws Exception { .machineId(MACHINE_ID).saltbootInitrd(false) .createMinionStartUpGrains(); executeTest((key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - exactly(1).of(saltServiceMock).deleteKey(server.getMinionId()); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + exactly(1).of(saltApiMock).deleteKey(server.getMinionId()); }}, null, (minion, machineId, key) -> assertTrue(MinionServerFactory.findByMinionId(MINION_ID).isPresent()), null, DEFAULT_CONTACT_METHOD, Optional.of(minionStartUpGrains)); } @@ -522,7 +521,7 @@ public void testWithMissingMachineIdStartUpGrains() throws Exception { MinionServer server = MinionServerFactoryTest.createTestMinionServer(user); server.setMinionId(MINION_ID); executeTest((key) -> new Expectations() {{ - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); }}, null, (minion, machineId, key) -> assertFalse(MinionServerFactory.findByMachineId(MACHINE_ID).isPresent()), @@ -542,9 +541,9 @@ public void testAlreadyRegisteredRetailMinion() throws Exception { .machineId(MACHINE_ID).saltbootInitrd(true) .createMinionStartUpGrains(); executeTest((key) -> new Expectations() {{ - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); }}, null, (minion, machineId, key) -> assertTrue(MinionServerFactory.findByMinionId(MINION_ID).isPresent()), null, DEFAULT_CONTACT_METHOD, Optional.of(minionStartUpGrains)); @@ -586,7 +585,7 @@ public void testReRegisterTraditionalAsMinionInvalidActKey() throws Exception { setupStubs(product); // Verify the resulting system entry - String machineId = saltServiceMock.getMachineId(MINION_ID).get(); + String machineId = saltApiMock.getMachineId(MINION_ID).get(); Optional optMinion = MinionServerFactory.findByMachineId(machineId); assertTrue(optMinion.isPresent()); MinionServer minion = optMinion.get(); @@ -626,23 +625,23 @@ public void testReRegisterMinionResetProxyPath() throws Exception { executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); MinionStartupGrains.SuseManagerGrain suseManagerGrain = new MinionStartupGrains.SuseManagerGrain(Optional.of(key)); MinionStartupGrains minionStartUpGrains = new MinionStartupGrains.MinionStartupGrainsBuilder() .machineId(MACHINE_ID).saltbootInitrd(true).susemanagerGrain(suseManagerGrain) .createMinionStartUpGrains(); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, null, key))); - allowing(saltServiceMock).getProducts(with(any(String.class))); + allowing(saltApiMock).getProducts(with(any(String.class))); will(returnValue(Optional.empty())); }}, (contactMethod) -> { @@ -668,16 +667,16 @@ public void testRegisterMinionWithoutActivationKeyNoSyncProducts() throws Except .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, null))); List pil = new ArrayList<>(); ProductInfo pi = new ProductInfo( @@ -687,7 +686,7 @@ public void testRegisterMinionWithoutActivationKeyNoSyncProducts() throws Except "test", "repo", "shortname", "summary", "vendor", product.getVersion()); pil.add(pi); - allowing(saltServiceMock).getProducts(with(any(String.class))); + allowing(saltApiMock).getProducts(with(any(String.class))); will(returnValue(Optional.of(pil))); }}, (contactMethod) -> null, @@ -713,16 +712,16 @@ public void testRegisterMinionWithoutActivationKey() throws Exception { .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, null))); List pil = new ArrayList<>(); ProductInfo pi = new ProductInfo( @@ -732,7 +731,7 @@ public void testRegisterMinionWithoutActivationKey() throws Exception { "test", "repo", "shortname", "summary", "vendor", product.getVersion()); pil.add(pi); - allowing(saltServiceMock).getProducts(with(any(String.class))); + allowing(saltApiMock).getProducts(with(any(String.class))); will(returnValue(Optional.of(pil))); }}, (contactMethod) -> null, @@ -765,16 +764,16 @@ public void testRegisterMinionWithInvalidActivationKeyNoSyncProducts() throws Ex .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key"))); List pil = new ArrayList<>(); ProductInfo pi = new ProductInfo( @@ -784,7 +783,7 @@ public void testRegisterMinionWithInvalidActivationKeyNoSyncProducts() throws Ex "test", "repo", "shortname", "summary", "vendor", product.getVersion()); pil.add(pi); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); will(returnValue(Optional.of(pil))); @@ -824,16 +823,16 @@ public void testRegisterMinionWithInvalidActivationKey() .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key"))); List pil = new ArrayList<>(); ProductInfo pi = new ProductInfo( @@ -843,7 +842,7 @@ public void testRegisterMinionWithInvalidActivationKey() "test", "repo", "shortname", "summary", "vendor", product.getVersion()); pil.add(pi); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); will(returnValue(Optional.of(pil))); @@ -886,21 +885,21 @@ public void testRegisterMinionWithActivationKey() throws Exception { HibernateFactory.getSession().flush(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); MinionStartupGrains.SuseManagerGrain suseManagerGrain = new MinionStartupGrains.SuseManagerGrain(Optional.of(key)); MinionStartupGrains minionStartUpGrains = new MinionStartupGrains.MinionStartupGrainsBuilder() .machineId(MACHINE_ID).saltbootInitrd(false).susemanagerGrain(suseManagerGrain) .createMinionStartUpGrains(); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); }}, (contactMethod) -> { @@ -963,16 +962,16 @@ public void testRegisterMinionWithActivationKeySUSEManagerDefault() throws Excep executeTest((key) -> new Expectations() { { - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); List pil = new ArrayList<>(); ProductInfo pi = new ProductInfo(product.getName(), @@ -980,7 +979,7 @@ public void testRegisterMinionWithActivationKeySUSEManagerDefault() throws Excep true, true, "productline", Optional.of("registerrelease"), "test", "repo", "shortname", "summary", "vendor", product.getVersion()); pil.add(pi); - allowing(saltServiceMock).getProducts(with(any(String.class))); + allowing(saltApiMock).getProducts(with(any(String.class))); will(returnValue(Optional.of(pil))); } }, (contactMethod) -> { @@ -1022,25 +1021,25 @@ public void testRegisterRHELMinionWithoutActivationKey() throws Exception { .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, "rhel", null))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --whatprovides --queryformat \"%{NAME}\\n\" redhat-release")); will(returnValue(singletonMap(MINION_ID, new Result<>(Xor.right("redhat-release-server\n"))))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --queryformat \"VERSION=%{VERSION}\\nPROVIDENAME=[%{PROVIDENAME},]" + "\\nPROVIDEVERSION=[%{PROVIDEVERSION},]\\n\" redhat-release-server")); @@ -1049,7 +1048,7 @@ public void testRegisterRHELMinionWithoutActivationKey() throws Exception { "redhat-release-server(x86-64),system-release,system-release(releasever),\n" + "PROVIDEVERSION=7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7,7Server,\n"))))); - allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); + allowing(saltApiMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)"), @@ -1082,25 +1081,25 @@ public void testRegisterRHELMinionWithRESActivationKeyOneBaseChannel() throws Ex .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, "rhel", key))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --whatprovides --queryformat \"%{NAME}\\n\" redhat-release")); will(returnValue(singletonMap(MINION_ID, new Result<>(Xor.right("redhat-release-server\n"))))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --queryformat \"VERSION=%{VERSION}\\nPROVIDENAME=[%{PROVIDENAME},]\\n" + "PROVIDEVERSION=[%{PROVIDEVERSION},]\\n\" redhat-release-server")); @@ -1109,7 +1108,7 @@ public void testRegisterRHELMinionWithRESActivationKeyOneBaseChannel() throws Ex "redhat-release-server(x86-64),system-release,system-release(releasever),\n" + "PROVIDEVERSION=7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7,7Server,\n"))))); - allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); + allowing(saltApiMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)"), @@ -1154,27 +1153,27 @@ public void testRegisterRHELMinionWithRESActivationKeyTwoBaseChannels() throws E try { executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, "rhel", key))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --whatprovides --queryformat \"%{NAME}\\n\" redhat-release")); will(returnValue(Collections.singletonMap( MINION_ID, new Result<>(Xor.right("redhat-release-server\n"))))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --queryformat \"VERSION=%{VERSION}\\nPROVIDENAME=[%{PROVIDENAME},]\\n" + "PROVIDEVERSION=[%{PROVIDEVERSION},]\\n\" redhat-release-server")); @@ -1184,7 +1183,7 @@ public void testRegisterRHELMinionWithRESActivationKeyTwoBaseChannels() throws E "PROVIDEVERSION=7.2-9.el7,7.2-9.el7,7.2-9.el7,7.2-9.el7," + "7.2-9.el7,7Server,\n"))))); - allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); + allowing(saltApiMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)"), @@ -1235,25 +1234,25 @@ public void testRegisterRESMinionWithoutActivationKey() throws Exception { .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, "res", null))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --whatprovides --queryformat \"%{NAME}\\n\" redhat-release")); will(returnValue(singletonMap(MINION_ID, new Result<>(Xor.right("sles_es-release-server\n"))))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --queryformat \"VERSION=%{VERSION}\\nPROVIDENAME=[%{PROVIDENAME},]\\n" + "PROVIDEVERSION=[%{PROVIDEVERSION},]\\n\" sles_es-release-server")); @@ -1264,7 +1263,7 @@ public void testRegisterRESMinionWithoutActivationKey() throws Exception { "PROVIDEVERSION=,7.2-9.el7.2.1,7.2-9.el7.2.1,7.2-9.el7.2.1,7.2-9.el7.2.1," + "7.2-9.el7.2.1,7.2-9.el7.2.1,7Server,\n"))))); - allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); + allowing(saltApiMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)\n" + @@ -1301,20 +1300,20 @@ public void testRegisterRHELMinionWithMultipleReleasePackages() throws Exception .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, "rhel", null))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --whatprovides --queryformat \"%{NAME}\\n\" redhat-release")); // Minion returns multiple release packages/versions @@ -1322,7 +1321,7 @@ public void testRegisterRHELMinionWithMultipleReleasePackages() throws Exception "redhat-release-server\nsles_es-release-server\nsles_es-release-server\n" + "redhat-release-server\n"))))); - allowing(saltServiceMock).runRemoteCommand( + allowing(saltApiMock).runRemoteCommand( with(any(MinionList.class)), with("rpm -q --queryformat \"VERSION=%{VERSION}\\nPROVIDENAME=[%{PROVIDENAME},]\\n" + "PROVIDEVERSION=[%{PROVIDEVERSION},]\\n\" redhat-release-server")); @@ -1352,7 +1351,7 @@ public void testRegisterRHELMinionWithMultipleReleasePackages() throws Exception "PROVIDEVERSION=,7.3-7.el7,7.3-7.el7,7.3-7.el7,7.3-7.el7,7.3-7.el7,7.3-7.el7," + "7Server,\n"))))); - allowing(saltServiceMock).getRedhatProductInfo(MINION_ID); + allowing(saltApiMock).getRedhatProductInfo(MINION_ID); will(returnValue(Optional.of(new RedhatProductInfo( Optional.empty(), Optional.of("Red Hat Enterprise Linux Server release 7.2 (Maipo)"), @@ -1460,16 +1459,16 @@ public void testRegisterRetailTerminal() throws Exception { .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); // Notice product name has spaces in the string. It is intentional to test hw string preprocessing will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { @@ -1479,7 +1478,7 @@ public void testRegisterRetailTerminal() throws Exception { map.put("minion_id_prefix", "Branch001"); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1509,17 +1508,17 @@ public void testRegisterRetailMinionTerminalGroupMissing() throws Exception { try { executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { map.put("saltboot_initrd", true); @@ -1528,7 +1527,7 @@ public void testRegisterRetailMinionTerminalGroupMissing() throws Exception { map.put("minion_id_prefix", "Branch001"); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1557,17 +1556,17 @@ public void testRegisterRetailMinionBranchGroupMissing() throws Exception { try { executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock) + allowing(saltApiMock) .getGrains(with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { map.put("saltboot_initrd", true); @@ -1576,7 +1575,7 @@ public void testRegisterRetailMinionBranchGroupMissing() throws Exception { map.put("minion_id_prefix", "Branch001"); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1606,16 +1605,16 @@ public void testRegisterRetailMinionHwGroupMissing() throws Exception { .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { map.put("saltboot_initrd", true); @@ -1624,7 +1623,7 @@ public void testRegisterRetailMinionHwGroupMissing() throws Exception { map.put("minion_id_prefix", "Branch001"); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1662,16 +1661,16 @@ public void testRegisterRetailMinionHwGroupAlreadyAssigned() throws Exception { .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { map.put("saltboot_initrd", true); @@ -1683,7 +1682,7 @@ public void testRegisterRetailMinionHwGroupAlreadyAssigned() throws Exception { map.put("hwaddr_interfaces", interfaces); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1725,16 +1724,16 @@ public void testRegisterRetailTerminalNonDefaultOrg() throws Exception { try { executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { map.put("saltboot_initrd", true); @@ -1743,7 +1742,7 @@ public void testRegisterRetailTerminalNonDefaultOrg() throws Exception { map.put("minion_id_prefix", "Branch001"); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1782,16 +1781,16 @@ public void testRegisterRetailTerminalNonDefaultOrgFailWithoutProxy() throws Exc try { executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { map.put("saltboot_initrd", true); @@ -1800,7 +1799,7 @@ public void testRegisterRetailTerminalNonDefaultOrgFailWithoutProxy() throws Exc map.put("minion_id_prefix", "Branch001"); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1838,16 +1837,16 @@ public void testRegisterRetailTerminalNonDefaultOrgWithoutCreator() throws Excep executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { map.put("saltboot_initrd", true); @@ -1856,7 +1855,7 @@ public void testRegisterRetailTerminalNonDefaultOrgWithoutCreator() throws Excep map.put("minion_id_prefix", "Branch001"); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1884,16 +1883,16 @@ public void testEmptyProfileRegistration() throws Exception { .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "non-existent-key") .map(map -> { map.put("saltboot_initrd", false); @@ -1905,7 +1904,7 @@ public void testEmptyProfileRegistration() throws Exception { map.put("hwaddr_interfaces", interfaces); return map; }))); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); }}, @@ -1981,16 +1980,16 @@ public void testMigrationSystemWithChannelsAndAK() throws Exception { .createMinionStartUpGrains(); executeTest((key) -> new Expectations() { { - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); } }, (contactMethod) -> { @@ -2044,16 +2043,16 @@ public void testMigrationSystemWithChannelsAndAKSameBase() throws Exception { .createMinionStartUpGrains(); executeTest((key) -> new Expectations() { { - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); } }, (contactMethod) -> { @@ -2103,16 +2102,16 @@ public void testMigrationSystemWithChannelsNoAK() throws Exception { .createMinionStartUpGrains(); executeTest((key) -> new Expectations() { { - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, key))); } }, (contactMethod) -> null, @@ -2148,21 +2147,21 @@ public void testMigrationMinionWithReActivationKey() throws Exception { HibernateFactory.getSession().flush(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); MinionStartupGrains.SuseManagerGrain suseManagerGrain = new MinionStartupGrains.SuseManagerGrain(Optional.of(key)); MinionStartupGrains minionStartUpGrains = new MinionStartupGrains.MinionStartupGrainsBuilder() .machineId(MACHINE_ID).saltbootInitrd(true).susemanagerGrain(suseManagerGrain) .createMinionStartUpGrains(); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, null, key))); }}, (contactMethod) -> { @@ -2207,21 +2206,21 @@ public void testMinionWithUsedReActivationKey() throws Exception { ServerFactory.save(oldMinion); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); MinionStartupGrains.SuseManagerGrain suseManagerGrain = new MinionStartupGrains.SuseManagerGrain(Optional.of(key)); MinionStartupGrains minionStartUpGrains = new MinionStartupGrains.MinionStartupGrainsBuilder() .machineId(MACHINE_ID).saltbootInitrd(true).susemanagerGrain(suseManagerGrain) .createMinionStartUpGrains(); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, null, key))); }}, (contactMethod) -> "1-re-already-used", @@ -2258,13 +2257,13 @@ public void testMinionWithUsedReActivationKeyWithStartUpGrains() throws Exceptio .createMinionStartUpGrains(); executeTest( (key) -> new Expectations() {{ - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, null, key))); }}, (contactMethod) -> "1-re-already-used", @@ -2309,17 +2308,17 @@ private void setupStubs(SUSEProduct product) .createMinionStartUpGrains(); MinionServerFactory.findByMachineId(MACHINE_ID).ifPresent(ServerFactory::delete); context().checking(new Expectations() { { - allowing(saltServiceMock).getMasterHostname(MINION_ID); + allowing(saltApiMock).getMasterHostname(MINION_ID); will(returnValue(Optional.of(MINION_ID))); - allowing(saltServiceMock).getMachineId(MINION_ID); + allowing(saltApiMock).getMachineId(MINION_ID); will(returnValue(Optional.of(MACHINE_ID))); - allowing(saltServiceMock).getGrains( + allowing(saltApiMock).getGrains( with(any(String.class)), with(any(TypeToken.class)), with(any(String[].class))); will(returnValue(Optional.of(minionStartUpGrains))); - allowing(saltServiceMock).getGrains(MINION_ID); + allowing(saltApiMock).getGrains(MINION_ID); will(returnValue(getGrains(MINION_ID, null, "foo"))); - allowing(saltServiceMock).syncGrains(with(any(MinionList.class))); - allowing(saltServiceMock).syncModules(with(any(MinionList.class))); + allowing(saltApiMock).syncGrains(with(any(MinionList.class))); + allowing(saltApiMock).syncModules(with(any(MinionList.class))); List pil = new ArrayList<>(); ProductInfo pi = new ProductInfo( product.getName(), @@ -2328,7 +2327,7 @@ private void setupStubs(SUSEProduct product) "test", "repo", "shortname", "summary", "vendor", product.getVersion()); pil.add(pi); - allowing(saltServiceMock).callSync( + allowing(saltApiMock).callSync( with(any(LocalCall.class)), with(any(String.class))); will(returnValue(Optional.of(pil))); @@ -2341,7 +2340,7 @@ private void setupStubs(SUSEProduct product) } }); RegisterMinionEventMessageAction action = - new RegisterMinionEventMessageAction(saltServiceMock, saltServiceMock); + new RegisterMinionEventMessageAction(saltApiMock, saltApiMock); action.execute(new RegisterMinionEventMessage(MINION_ID, Optional.empty())); } diff --git a/java/code/src/com/suse/manager/utils/SaltUtils.java b/java/code/src/com/suse/manager/utils/SaltUtils.java index d19c56ee1d89..ad08fefefbc6 100644 --- a/java/code/src/com/suse/manager/utils/SaltUtils.java +++ b/java/code/src/com/suse/manager/utils/SaltUtils.java @@ -227,9 +227,9 @@ public enum PackageChangeOutcome { * @param systemQueryIn * @param saltApiIn */ - public SaltUtils(SystemQuery systemQueryIn, SaltApi saltApiIn) { + public SaltUtils(SaltApi saltApiIn) { this.saltApi = saltApiIn; - this.systemQuery = systemQueryIn; + this.systemQuery = new SystemQuery(saltApiIn); } /** diff --git a/java/code/src/com/suse/manager/webui/controllers/bootstrap/AbstractMinionBootstrapper.java b/java/code/src/com/suse/manager/webui/controllers/bootstrap/AbstractMinionBootstrapper.java index a9d73f6cd75a..6d682c0dc22e 100644 --- a/java/code/src/com/suse/manager/webui/controllers/bootstrap/AbstractMinionBootstrapper.java +++ b/java/code/src/com/suse/manager/webui/controllers/bootstrap/AbstractMinionBootstrapper.java @@ -37,8 +37,8 @@ import com.suse.manager.webui.controllers.utils.ContactMethodUtil; import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.SystemQuery; +import com.suse.manager.webui.services.iface.SystemQuery.KeyStatus; import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService.KeyStatus; import com.suse.manager.webui.utils.gson.BootstrapHostsJson; import com.suse.manager.webui.utils.gson.BootstrapParameters; import com.suse.salt.netapi.calls.LocalCall; diff --git a/java/code/src/com/suse/manager/webui/controllers/utils/test/AbstractMinionBootstrapperTestBase.java b/java/code/src/com/suse/manager/webui/controllers/utils/test/AbstractMinionBootstrapperTestBase.java index 35524c29bac1..36f341a57146 100644 --- a/java/code/src/com/suse/manager/webui/controllers/utils/test/AbstractMinionBootstrapperTestBase.java +++ b/java/code/src/com/suse/manager/webui/controllers/utils/test/AbstractMinionBootstrapperTestBase.java @@ -28,9 +28,9 @@ import com.suse.manager.webui.controllers.bootstrap.AbstractMinionBootstrapper; import com.suse.manager.webui.controllers.bootstrap.BootstrapResult; +import com.suse.manager.webui.services.iface.SaltApi; +import com.suse.manager.webui.services.iface.SystemQuery.KeyStatus; import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService; -import com.suse.manager.webui.services.impl.SaltService.KeyStatus; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.manager.webui.utils.gson.BootstrapHostsJson; import com.suse.manager.webui.utils.gson.BootstrapParameters; @@ -56,7 +56,7 @@ */ public abstract class AbstractMinionBootstrapperTestBase extends JMockBaseTestCaseWithUser { - protected SaltService saltServiceMock; + protected SaltApi saltApiMock; // tested object, initialized in subclasses protected AbstractMinionBootstrapper bootstrapper; @@ -66,7 +66,7 @@ public abstract class AbstractMinionBootstrapperTestBase extends JMockBaseTestCa public void setUp() throws Exception { super.setUp(); setImposteriser(ByteBuddyClassImposteriser.INSTANCE); - saltServiceMock = mock(SaltService.class); + saltApiMock = mock(SaltApi.class); } /** @@ -79,7 +79,7 @@ public void testBootstrapFailsWhenKeysExist() throws Exception { setEmptyActivationKeys(input); context().checking(new Expectations() {{ - allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltApiMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(true)); }}); @@ -142,7 +142,7 @@ public void testBootstrapFailsWhenMinionExists() setEmptyActivationKeys(input); context().checking(new Expectations() {{ - allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltApiMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); }}); @@ -162,28 +162,28 @@ public void testBootstrapSuccess() throws Exception { Key.Pair keyPair = mockKeyPair(); context().checking(new Expectations() {{ - allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltApiMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); - allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); + allowing(saltApiMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); will(returnValue(false)); - allowing(saltServiceMock).generateKeysAndAccept("myhost", false); + allowing(saltApiMock).generateKeysAndAccept("myhost", false); will(returnValue(keyPair)); MgrUtilRunner.ExecResult mockResult = new MgrUtilRunner.SshKeygenResult("key", "pubkey"); - allowing(saltServiceMock).generateSSHKey(SaltSSHService.SSH_KEY_PATH); + allowing(saltApiMock).generateSSHKey(SaltSSHService.SSH_KEY_PATH); will(returnValue(of(mockResult))); List bootstrapMods = bootstrapMods(); Map pillarData = createPillarData(Optional.empty(), Optional.empty()); // return success when calling low-level bootstrap - allowing(saltServiceMock).bootstrapMinion(with(any(BootstrapParameters.class)), + allowing(saltApiMock).bootstrapMinion(with(any(BootstrapParameters.class)), with(bootstrapMods), with(pillarData)); SSHResult> sshResult = createSuccessResult(); will(returnValue(new Result<>(Xor.right(sshResult)))); // we expect the key NOT to be deleted - atMost(0).of(saltServiceMock).deleteKey("myhost"); + atMost(0).of(saltApiMock).deleteKey("myhost"); }}); BootstrapHostsJson input = mockStandardInput(); @@ -230,22 +230,22 @@ protected void testCompatibleActivationKeysBase(ActivationKey key) throws Except allowing(input).maybeGetReactivationKey(); will(returnValue(empty())); - allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltApiMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); - allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); + allowing(saltApiMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); will(returnValue(false)); Key.Pair keyPair = mockKeyPair(); - allowing(saltServiceMock).generateKeysAndAccept("myhost", false); + allowing(saltApiMock).generateKeysAndAccept("myhost", false); will(returnValue(keyPair)); MgrUtilRunner.ExecResult mockResult = new MgrUtilRunner.SshKeygenResult("key", "pubkey"); - allowing(saltServiceMock).generateSSHKey(SaltSSHService.SSH_KEY_PATH); + allowing(saltApiMock).generateSSHKey(SaltSSHService.SSH_KEY_PATH); will(returnValue(of(mockResult))); List bootstrapMods = bootstrapMods(); Map pillarData = createPillarData(Optional.of(key), Optional.empty()); - allowing(saltServiceMock).bootstrapMinion(with(any(BootstrapParameters.class)), + allowing(saltApiMock).bootstrapMinion(with(any(BootstrapParameters.class)), with(bootstrapMods), with(pillarData)); Object sshResult = createSuccessResult(); will(returnValue(new Result<>(Xor.right(sshResult)))); @@ -272,18 +272,18 @@ protected void testCompatibleActivationKeysBase(ActivationKey key, ActivationKey allowing(input).maybeGetReactivationKey(); will(returnValue(Optional.of(reactKey.getKey()))); - allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); + allowing(saltApiMock).isKeyExists("myhost", KeyStatus.ACCEPTED, KeyStatus.DENIED, KeyStatus.REJECTED); will(returnValue(false)); - allowing(saltServiceMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); + allowing(saltApiMock).isKeyExists("myhost", KeyStatus.UNACCEPTED); will(returnValue(false)); Key.Pair keyPair = mockKeyPair(); - allowing(saltServiceMock).generateKeysAndAccept("myhost", false); + allowing(saltApiMock).generateKeysAndAccept("myhost", false); will(returnValue(keyPair)); List bootstrapMods = bootstrapMods(); Map pillarData = createPillarData(Optional.of(key), Optional.of(reactKey)); - allowing(saltServiceMock).bootstrapMinion(with(any(BootstrapParameters.class)), + allowing(saltApiMock).bootstrapMinion(with(any(BootstrapParameters.class)), with(bootstrapMods), with(pillarData)); Object sshResult = createSuccessResult(); will(returnValue(new Result<>(Xor.right(sshResult)))); diff --git a/java/code/src/com/suse/manager/webui/controllers/utils/test/RegularMinionBootstrapperTest.java b/java/code/src/com/suse/manager/webui/controllers/utils/test/RegularMinionBootstrapperTest.java index d7314adb8c5f..853295196b13 100644 --- a/java/code/src/com/suse/manager/webui/controllers/utils/test/RegularMinionBootstrapperTest.java +++ b/java/code/src/com/suse/manager/webui/controllers/utils/test/RegularMinionBootstrapperTest.java @@ -29,7 +29,7 @@ import com.suse.manager.webui.controllers.bootstrap.BootstrapResult; import com.suse.manager.webui.controllers.bootstrap.RegularMinionBootstrapper; import com.suse.manager.webui.controllers.utils.ContactMethodUtil; -import com.suse.manager.webui.services.impl.SaltService.KeyStatus; +import com.suse.manager.webui.services.iface.SystemQuery.KeyStatus; import com.suse.manager.webui.utils.gson.BootstrapHostsJson; import com.suse.manager.webui.utils.gson.BootstrapParameters; import com.suse.salt.netapi.calls.wheel.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 9b29f0e6e1a9..5a56820a49fd 100644 --- a/java/code/src/com/suse/manager/webui/services/SaltServerActionService.java +++ b/java/code/src/com/suse/manager/webui/services/SaltServerActionService.java @@ -249,8 +249,18 @@ public class SaltServerActionService { SaltActionChainGeneratorService.INSTANCE; private SaltApi saltApi; + + public SaltApi getSaltApi() { + return saltApi; + } + private SaltSSHService saltSSHService = GlobalInstanceHolder.SALT_API.getSaltSSHService(); private SaltUtils saltUtils; + + public SaltUtils getSaltUtils() { + return saltUtils; + } + private SaltKeyUtils saltKeyUtils; private boolean skipCommandScriptPerms; private TaskomaticApi taskomaticApi = new TaskomaticApi(); @@ -260,10 +270,10 @@ public class SaltServerActionService { * @param saltUtilsIn * @param saltKeyUtilsIn */ - public SaltServerActionService(SaltApi saltApiIn, SaltUtils saltUtilsIn, SaltKeyUtils saltKeyUtilsIn) { + public SaltServerActionService(SaltApi saltApiIn) { this.saltApi = saltApiIn; - this.saltUtils = saltUtilsIn; - this.saltKeyUtils = saltKeyUtilsIn; + this.saltUtils = new SaltUtils(saltApi); + this.saltKeyUtils = new SaltKeyUtils(saltApi); } /** diff --git a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java index 8e66ad3bfc96..c2a7b2c6fa68 100644 --- a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java +++ b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java @@ -78,6 +78,8 @@ import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.impl.nio.client.HttpAsyncClients; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.IOException; import java.net.URI; @@ -109,6 +111,8 @@ import java.util.logging.LogManager; import java.util.stream.Collectors; + + /** * Interface for interacting with salt. */ @@ -586,7 +590,8 @@ public Result>> bootstrapMinion( Optional portForwarding = SaltSSHService.remotePortForwarding(bootstrapProxyPath, contactMethod); // private key handling just for bootstrap - Optional tmpKeyFileAbsolutePath = parameters.getPrivateKey().map(key -> SaltSSHService.createTempKeyFilePath()); + Optional tmpKeyFileAbsolutePath = parameters.getPrivateKey().map(key -> + SaltSSHService.createTempKeyFilePath()); try { tmpKeyFileAbsolutePath.ifPresent(p -> parameters.getPrivateKey().ifPresent(k -> diff --git a/java/code/src/com/suse/manager/webui/services/iface/SystemQuery.java b/java/code/src/com/suse/manager/webui/services/iface/SystemQuery.java index 76effc0965db..3edcd636adac 100644 --- a/java/code/src/com/suse/manager/webui/services/iface/SystemQuery.java +++ b/java/code/src/com/suse/manager/webui/services/iface/SystemQuery.java @@ -57,12 +57,17 @@ import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + /** * Interface containing methods for directly interacting and getting information from a system. * Note: This interface should be split up further at some point. */ public class SystemQuery { + private static final Logger LOG = LogManager.getLogger(SystemQuery.class); + /** * Enum of all the available status for Salt keys. */ diff --git a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java index 2582e4b91e4d..dc4f79229fdf 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java +++ b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java @@ -42,7 +42,6 @@ import com.suse.manager.webui.utils.SaltRoster; import com.suse.manager.webui.utils.SaltState; import com.suse.manager.webui.utils.SaltTop; -import com.suse.manager.webui.utils.gson.BootstrapParameters; import com.suse.salt.netapi.AuthModule; import com.suse.salt.netapi.calls.LocalCall; import com.suse.salt.netapi.calls.SaltSSHConfig; @@ -475,7 +474,10 @@ private boolean addSaltSSHMinionsFromDb(SaltRoster roster) { return !minions.isEmpty(); } - // create temp key absolute path + /** + * create a temporary temp key + * @return Temp Key file path + */ public static Path createTempKeyFilePath() { String fileName = "boostrapKeyTmp-" + UUID.randomUUID(); return Path.of(SSH_TEMP_BOOTSTRAP_KEY_DIR).resolve(fileName).toAbsolutePath(); @@ -487,6 +489,12 @@ private void cleanUpTempKeyFile(Path path) { .orElseThrow(() -> new IllegalStateException("Can't remove file " + path)); } + /** + * Return Minion option + * @param minionId Minion ID + * @param sshContactMethod the ssh contact method + * @return Minion option + */ public static Optional> getMinionOpts(String minionId, String sshContactMethod) { if (ContactMethodUtil.SSH_PUSH_TUNNEL.equals(sshContactMethod)) { @@ -497,6 +505,12 @@ public static Optional> getMinionOpts(String minionId, return Optional.empty(); } + /** + * Return port forwarding address if exists + * @param proxyPath a list of proxy hostnames + * @param sshContactMethod the ssh contact method + * @return port forwarding address + */ public static Optional remotePortForwarding(List proxyPath, String sshContactMethod) { if (ContactMethodUtil.SSH_PUSH_TUNNEL.equals(sshContactMethod)) { @@ -506,10 +520,19 @@ public static Optional remotePortForwarding(List proxyPath, return Optional.empty(); } + /** + * return ssh push remote port + * @return ssh push remote port + */ public static Integer getSshPushRemotePort() { return Config.get().getInt("ssh_push_port_https"); } + /** + * Check if user is a superuser + * @param user + * @return true if superuser + */ public static boolean isSudoUser(String user) { return !CommonConstants.ROOT.equals(user); } diff --git a/java/code/src/com/suse/manager/webui/services/impl/test/SaltServiceTest.java b/java/code/src/com/suse/manager/webui/services/impl/test/SaltServiceTest.java index e05e31e407a5..b89ab4282894 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/test/SaltServiceTest.java +++ b/java/code/src/com/suse/manager/webui/services/impl/test/SaltServiceTest.java @@ -25,7 +25,6 @@ import com.suse.manager.reactor.messaging.test.SaltTestUtils; import com.suse.manager.webui.controllers.utils.ContactMethodUtil; import com.suse.manager.webui.services.iface.SaltApi; -import com.suse.manager.webui.services.iface.SaltSSHApi; import com.suse.manager.webui.services.impl.MinionPendingRegistrationService; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.salt.netapi.calls.Client; diff --git a/java/code/src/com/suse/manager/webui/services/test/SaltServerActionServiceTest.java b/java/code/src/com/suse/manager/webui/services/test/SaltServerActionServiceTest.java index 4b41003681a3..fffe4a96c127 100644 --- a/java/code/src/com/suse/manager/webui/services/test/SaltServerActionServiceTest.java +++ b/java/code/src/com/suse/manager/webui/services/test/SaltServerActionServiceTest.java @@ -83,7 +83,6 @@ import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.SystemQuery; import com.suse.manager.webui.services.iface.VirtManager; -import com.suse.manager.webui.services.impl.SaltService; import com.suse.manager.webui.utils.SaltModuleRun; import com.suse.manager.webui.utils.SaltState; import com.suse.manager.webui.utils.SaltSystemReboot; @@ -140,7 +139,7 @@ public void setUp() throws Exception { public void updateLibvirtEngine(MinionServer minionIn) { } }; - SaltService saltService = new SaltService() { + SaltApi saltApi = new SaltApi() { @Override public Optional> rawJsonCall(LocalCall call, String minionId) { return Optional.of(Result.success(new JsonObject())); @@ -151,21 +150,19 @@ public void refreshPillar(MinionList minionList) { } }; minion = MinionServerFactoryTest.createTestMinionServer(user); - saltServerActionService = createSaltServerActionService(saltService, saltService); - MonitoringManager monitoringManager = new FormulaMonitoringManager(saltService); - serverGroupManager = new ServerGroupManager(saltService); + MonitoringManager monitoringManager = new FormulaMonitoringManager(saltApi); + serverGroupManager = new ServerGroupManager(saltApi); systemEntitlementManager = new SystemEntitlementManager( new SystemUnentitler(virtManager, monitoringManager, serverGroupManager), - new SystemEntitler(saltService, virtManager, monitoringManager, serverGroupManager) + new SystemEntitler(saltApi, virtManager, monitoringManager, serverGroupManager) ); taskomaticMock = mock(TaskomaticApi.class); saltServerActionService.setTaskomaticApi(taskomaticMock); } - private SaltServerActionService createSaltServerActionService(SystemQuery systemQuery, SaltApi saltApi) { - SaltUtils saltUtils = new SaltUtils(systemQuery, saltApi); - SaltServerActionService service = new SaltServerActionService(saltApi, saltUtils, new SaltKeyUtils(saltApi)); + private SaltServerActionService createSaltServerActionService(SaltApi saltApi) { + SaltServerActionService service = new SaltServerActionService(saltApi); service.setSkipCommandScriptPerms(true); return service; } @@ -1059,12 +1056,12 @@ public void testExectueSSHAction() throws Exception { createChildServerAction(action, STATUS_QUEUED, testMinionServer, 5L); HibernateFactory.getSession().flush(); - SaltService saltServiceMock = mock(SaltService.class); - SaltServerActionService testService = createSaltServerActionService(saltServiceMock, saltServiceMock); + SaltApi saltApiMock = mock(SaltApi.class); + SaltServerActionService testService = createSaltServerActionService(saltApiMock, saltApiMock); testService.setTaskomaticApi(taskomaticMock); context().checking(new Expectations() { { oneOf(taskomaticMock).scheduleSSHActionExecution(action, sshMinion, false); - oneOf(saltServiceMock).callAsync( + oneOf(saltApiMock).callAsync( with(any(LocalCall.class)), with(any(Target.class)), with(any(Optional.class))); LocalAsyncResult result = new LocalAsyncResult() { public List getMinions() { diff --git a/java/code/src/com/suse/manager/webui/services/test/TestSaltApi.java b/java/code/src/com/suse/manager/webui/services/test/TestSaltApi.java index 6ad26b1b96ea..64da39b32041 100644 --- a/java/code/src/com/suse/manager/webui/services/test/TestSaltApi.java +++ b/java/code/src/com/suse/manager/webui/services/test/TestSaltApi.java @@ -18,17 +18,12 @@ import com.suse.manager.ssl.SSLCertPair; import com.suse.manager.webui.services.iface.SaltApi; -import com.suse.manager.webui.services.impl.SaltSSHService; -import com.suse.manager.webui.services.impl.SaltService; -import com.suse.manager.webui.services.impl.runner.MgrK8sRunner; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.manager.webui.utils.gson.BootstrapParameters; import com.suse.manager.webui.utils.salt.custom.ScheduleMetadata; import com.suse.salt.netapi.calls.LocalAsyncResult; import com.suse.salt.netapi.calls.LocalCall; -import com.suse.salt.netapi.calls.modules.SaltUtil; import com.suse.salt.netapi.calls.modules.State; -import com.suse.salt.netapi.calls.runner.Jobs; import com.suse.salt.netapi.calls.wheel.Key; import com.suse.salt.netapi.datatypes.target.MinionList; import com.suse.salt.netapi.datatypes.target.Target; @@ -39,38 +34,26 @@ import com.suse.salt.netapi.results.SSHResult; import com.google.gson.JsonElement; -import com.google.gson.reflect.TypeToken; import java.nio.file.Path; -import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -public class TestSaltApi implements SaltApi { +public class TestSaltApi extends SaltApi { @Override public void deployChannels(List minionIds) throws SaltException { throw new UnsupportedOperationException(); } - @Override - public Optional> getAllContainers(String kubeconfig, String context) { - throw new UnsupportedOperationException(); - } - @Override public void storeSshKeyFile(Path path, String contents) { throw new UnsupportedOperationException(); } - @Override - public List matchCompoundSync(String target) { - throw new UnsupportedOperationException(); - } - @Override public Optional removeFile(Path path) { throw new UnsupportedOperationException(); @@ -96,11 +79,6 @@ public Map storeMinionScapFiles(MinionServer minion, String upl throw new UnsupportedOperationException(); } - @Override - public Map> getShowHighstate(String minionId) throws SaltException { - throw new UnsupportedOperationException(); - } - @Override public Optional generateSSHKey(String path) { throw new UnsupportedOperationException(); @@ -163,21 +141,6 @@ public void rejectKey(String minionId) { throw new UnsupportedOperationException(); } - @Override - public Optional getGrains(String minionId, TypeToken type, String... grainNames) { - throw new UnsupportedOperationException(); - } - - @Override - public Optional> getGrains(String minionId) { - throw new UnsupportedOperationException(); - } - - @Override - public Optional getMasterHostname(String minionId) { - throw new UnsupportedOperationException(); - } - @Override public Map> runRemoteCommand(MinionList target, String cmd) { throw new UnsupportedOperationException(); @@ -188,31 +151,11 @@ public Optional deleteRejectedKey(String minionId) { throw new UnsupportedOperationException(); } - @Override - public Key.Names getKeys() { - throw new UnsupportedOperationException(); - } - @Override public Key.Pair generateKeysAndAccept(String id, boolean force) { throw new UnsupportedOperationException(); } - @Override - public boolean isKeyExists(String id, SaltService.KeyStatus... statusIn) { - throw new UnsupportedOperationException(); - } - - @Override - public Key.Fingerprints getFingerprints() { - throw new UnsupportedOperationException(); - } - - @Override - public Optional> cleanupMinion(MinionServer minion, int timeout) { - throw new UnsupportedOperationException(); - } - @Override public Result>> bootstrapMinion(BootstrapParameters parameters, List bootstrapMods, @@ -232,60 +175,17 @@ public Map>> runRemoteCommandAsync(Minion throw new UnsupportedOperationException(); } - @Override - public Map>> running(MinionList target) { - throw new UnsupportedOperationException(); - } - - @Override - public Optional listJob(String jid) { - throw new UnsupportedOperationException(); - } - - @Override - public Map>> getMatchAsync(String target, - CompletableFuture cancel) { - throw new UnsupportedOperationException(); - } - - @Override - public Optional> jobsByMetadata(Object metadata) { - throw new UnsupportedOperationException(); - } - - @Override - public Optional> jobsByMetadata(Object metadata, LocalDateTime startTime, - LocalDateTime endTime) { - throw new UnsupportedOperationException(); - } - - @Override - public Optional>>> getMatchAsyncSSH( - String target, CompletableFuture cancel) { - throw new UnsupportedOperationException(); - } - @Override public Optional> callAsync(LocalCall callIn, Target target, Optional metadataIn) throws SaltException { return Optional.empty(); } - @Override - public Map>> getPendingResume(List minionIds) throws SaltException { - throw new UnsupportedOperationException(); - } - @Override public Optional> rawJsonCall(LocalCall call, String minionId) { throw new UnsupportedOperationException(); } - @Override - public SaltSSHService getSaltSSHService() { - throw new UnsupportedOperationException(); - } - @Override public void refreshPillar(MinionList minionList) { } diff --git a/java/code/src/com/suse/manager/webui/services/test/TestSystemQuery.java b/java/code/src/com/suse/manager/webui/services/test/TestSystemQuery.java index 09dc11087b6a..98fac35cfbee 100644 --- a/java/code/src/com/suse/manager/webui/services/test/TestSystemQuery.java +++ b/java/code/src/com/suse/manager/webui/services/test/TestSystemQuery.java @@ -17,6 +17,7 @@ import com.redhat.rhn.domain.server.MinionServer; import com.suse.manager.webui.services.iface.RedhatProductInfo; +import com.suse.manager.webui.services.iface.SaltApi; import com.suse.manager.webui.services.iface.SystemQuery; import com.suse.manager.webui.services.impl.runner.MgrUtilRunner; import com.suse.salt.netapi.calls.LocalCall; @@ -26,7 +27,11 @@ import java.util.List; import java.util.Optional; -public class TestSystemQuery implements SystemQuery { +public class TestSystemQuery extends SystemQuery { + + public TestSystemQuery(SaltApi saltApiIn) { + super(saltApiIn); + } @Override public Optional getMachineId(String minionId) { @@ -43,17 +48,6 @@ public Optional> getProducts(String minionId) { throw new UnsupportedOperationException(); } - @Override - public Optional callSync(LocalCall call, String minionId) { - return Optional.empty(); - } - - @Override - public Optional collectKiwiImage( - MinionServer minion, String filepath, String imageStore) { - throw new UnsupportedOperationException(); - } - @Override public Optional getRedhatProductInfo(String minionId) { throw new UnsupportedOperationException(); diff --git a/java/code/src/com/suse/manager/webui/utils/MinionActionUtils.java b/java/code/src/com/suse/manager/webui/utils/MinionActionUtils.java index db99fdc72783..0c4b109ec061 100644 --- a/java/code/src/com/suse/manager/webui/utils/MinionActionUtils.java +++ b/java/code/src/com/suse/manager/webui/utils/MinionActionUtils.java @@ -74,11 +74,10 @@ public class MinionActionUtils { * @param saltApiIn * @param saltUtilsIn */ - public MinionActionUtils(SaltServerActionService saltServerActionServiceIn, SaltApi saltApiIn, - SaltUtils saltUtilsIn) { + public MinionActionUtils(SaltServerActionService saltServerActionServiceIn) { this.saltServerActionService = saltServerActionServiceIn; - this.saltApi = saltApiIn; - this.saltUtils = saltUtilsIn; + this.saltApi = saltServerActionServiceIn.getSaltApi(); + this.saltUtils = saltServerActionServiceIn.getSaltUtils(); } /** From 96953ab38a38d6af95da8c82ca2721395cdc8b64 Mon Sep 17 00:00:00 2001 From: mbussolotto Date: Wed, 28 Sep 2022 15:49:37 +0200 Subject: [PATCH 6/8] fix --- .../suse/manager/webui/services/iface/SaltApi.java | 13 +++++++++---- .../manager/webui/services/impl/SaltSSHService.java | 5 ----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java index c2a7b2c6fa68..7f3aea11e056 100644 --- a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java +++ b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java @@ -108,7 +108,6 @@ import java.util.concurrent.CompletionStage; import java.util.function.Function; import java.util.function.Predicate; -import java.util.logging.LogManager; import java.util.stream.Collectors; @@ -118,6 +117,8 @@ */ public class SaltApi { + private static final Logger LOG = LogManager.getLogger(SaltApi.class); + enum Messages { CLEANUP_MINION_SALT_STATE("cleanup_minion"), GENERIC_RUNNER_ERROR( @@ -139,7 +140,6 @@ public String toString() { } - private static final Logger LOG = LogManager.getLogger(SaltApi.class); // Reconnecting time (in seconds) to Salt event bus private static final int DELAY_TIME_SECONDS = 5; @@ -595,7 +595,7 @@ public Result>> bootstrapMinion( try { tmpKeyFileAbsolutePath.ifPresent(p -> parameters.getPrivateKey().ifPresent(k -> - GlobalInstanceHolder.SALT_API.storeSshKeyFile(p, k))); + this.storeSshKeyFile(p, k))); SaltRoster roster = new SaltRoster(); roster.addHost(parameters.getHost(), parameters.getUser(), @@ -635,6 +635,11 @@ public Result>> bootstrapMinion( } } + private void cleanUpTempKeyFile(Path path) { + this.removeFile(path) + .orElseThrow(() -> new IllegalStateException("Can't remove file " + path)); + } + /** * Chain ssh calls over one or more hops to run a command on the last host in the chain. * This calls the mgrutil.chain_ssh_command runner. @@ -1228,7 +1233,7 @@ public void eventStreamClosed(int code, String phrase) { return eventStream; } - private Optional>>> completableAsyncCall( + Optional>>> completableAsyncCall( LocalCall callIn, Target target, EventStream events, CompletableFuture cancel) throws SaltException { diff --git a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java index dc4f79229fdf..4ed77474de0f 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java +++ b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java @@ -483,11 +483,6 @@ public static Path createTempKeyFilePath() { return Path.of(SSH_TEMP_BOOTSTRAP_KEY_DIR).resolve(fileName).toAbsolutePath(); } - private void cleanUpTempKeyFile(Path path) { - GlobalInstanceHolder.SALT_API - .removeFile(path) - .orElseThrow(() -> new IllegalStateException("Can't remove file " + path)); - } /** * Return Minion option From f9a2655627e9c3bd264f4e1a96ab421ed67ea153 Mon Sep 17 00:00:00 2001 From: mbussolotto Date: Wed, 28 Sep 2022 16:13:44 +0200 Subject: [PATCH 7/8] fix --- .../manager/webui/services/iface/SaltApi.java | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java index 7f3aea11e056..0744625c1c4d 100644 --- a/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java +++ b/java/code/src/com/suse/manager/webui/services/iface/SaltApi.java @@ -193,20 +193,6 @@ public SaltApi() { new SaltClient(SALT_MASTER_URI, new HttpAsyncClientImpl(asyncHttpClient)); } - /** - * Constructor to use for unit testing - * - * @param client Salt client - */ - public SaltApi(SaltClient client) { - defaultBatch = Batch.custom() - .withBatchAsAmount(ConfigDefaults.get().getSaltBatchSize()) - .withDelay(ConfigDefaults.get().getSaltBatchDelay()) - .withPresencePingTimeout(ConfigDefaults.get().getSaltPresencePingTimeout()) - .withPresencePingGatherJobTimeout( - ConfigDefaults.get().getSaltPresencePingGatherJobTimeout()) - .build(); - } /** * Sync the channels of a list of minions @@ -545,10 +531,9 @@ public Optional deleteRejectedKey(String minionId) { * @return the generated key pair */ public Key.Pair generateKeysAndAccept(String id, boolean force) { - if (callSync(Key.genAccept(id, Optional.of(force))).isEmpty()) { - throw new NoWheelResultsException("no wheel results"); + return callSync(Key.genAccept(id, Optional.of(force))). + orElseThrow( () -> new NoWheelResultsException("no wheel results")); } - } /** * Bootstrap a system using salt-ssh. From 202c5b3b5a2bc5f8719c23bb01f7df3a53baa7a2 Mon Sep 17 00:00:00 2001 From: mbussolotto Date: Thu, 29 Sep 2022 10:26:10 +0200 Subject: [PATCH 8/8] fix --- .../webui/services/impl/SaltSSHService.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java index 4ed77474de0f..d964d00ee37a 100644 --- a/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java +++ b/java/code/src/com/suse/manager/webui/services/impl/SaltSSHService.java @@ -948,4 +948,82 @@ else if (err != null) { } })); } + /** + * Boilerplate for executing a synchronous salt-ssh code. This involves: + * - generating the salt config, + * - generating the roster, storing it on the disk, + * - calling the salt-ssh via salt-api, + * - cleaning up the roster file after the job is done. + * + * Note on the SSH identity (key/cert pair): + * This call uses the SSH key stored on SSH_KEY_PATH. If such file doesn't + * exist, salt automatically generates a key/cert pair on this path. + * + * @param the return type of the call + * @param call the call to execute + * @param target minions targeted by the call, only Glob and MinionList is supported + * @param roster salt-ssh roster + * @param ignoreHostKeys use this option to disable 'StrictHostKeyChecking' + * @param sudo run command via sudo (default: false) + * + * @throws SaltException if something goes wrong during command execution or + * during manipulation the salt-ssh roster + * + * @return result of the call + */ + private Map>> callSyncSSHInternal(LocalCall call, + SSHTarget target, Optional roster, boolean ignoreHostKeys, boolean sudo) + throws SaltException { + return callSyncSSHInternal(call, target, roster, ignoreHostKeys, sudo, Optional.empty()); + } + + private Map>> callSyncSSHInternal(LocalCall call, + SSHTarget target, Optional roster, boolean ignoreHostKeys, boolean sudo, + Optional extraFilerefs) throws SaltException { + if (!(target instanceof MinionList || target instanceof Glob)) { + throw new UnsupportedOperationException("Only MinionList and Glob supported."); + } + + try { + final Path rosterPath; + if (roster.isPresent()) { + rosterPath = roster.get().persistInTempFile(); + } + else { + rosterPath = null; + } + + SaltSSHConfig.Builder sshConfigBuilder = new SaltSSHConfig.Builder() + .ignoreHostKeys(ignoreHostKeys) + .priv(SSH_KEY_PATH) + .sudo(sudo) + .refreshCache(true); + roster.ifPresentOrElse( + r -> sshConfigBuilder.rosterFile(rosterPath.getFileName().toString()), + () -> { + sshConfigBuilder.roster("uyuni"); + LOG.info("No roster file used, using Uyuni roster module!"); + }); + extraFilerefs.ifPresent(sshConfigBuilder::extraFilerefs); + SaltSSHConfig sshConfig = sshConfigBuilder.build(); + + LOG.debug("Local callSyncSSH: {}", SaltService.localCallToString(call)); + return SaltService.adaptException(call.callSyncSSH(saltClient, target, sshConfig, PW_AUTH) + .whenComplete((r, e) -> { + if (roster.isPresent()) { + try { + Files.deleteIfExists(rosterPath); + } + catch (IOException ex) { + LOG.error("Can't delete roster file: {}", ex.getMessage()); + } + } + })); + } + catch (IOException e) { + LOG.error("Error operating on roster file: {}", e.getMessage()); + throw new SaltException(e); + } + } + }