diff --git a/java/code/src/com/redhat/rhn/domain/channel/AccessTokenFactory.java b/java/code/src/com/redhat/rhn/domain/channel/AccessTokenFactory.java index 147adb3fe195..236d93f92dcb 100644 --- a/java/code/src/com/redhat/rhn/domain/channel/AccessTokenFactory.java +++ b/java/code/src/com/redhat/rhn/domain/channel/AccessTokenFactory.java @@ -25,7 +25,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.hibernate.criterion.Restrictions; import org.jose4j.lang.JoseException; import java.time.Duration; @@ -58,12 +57,10 @@ public class AccessTokenFactory extends HibernateFactory { * @return optional of AccessToken */ public static Optional lookupById(long id) { - return Optional.ofNullable( - (AccessToken)HibernateFactory.getSession() - .createCriteria(AccessToken.class) - .add(Restrictions.eq("id", id)) - .uniqueResult() - ); + return getSession() + .createQuery("FROM AccessToken WHERE id = :id", AccessToken.class) + .setParameter("id", id) + .uniqueResultOptional(); } /** @@ -71,8 +68,21 @@ public static Optional lookupById(long id) { * @return list of AccessTokens */ public static List all() { - return (List) HibernateFactory.getSession() - .createCriteria(AccessToken.class) + return getSession() + .createQuery("FROM AccessToken", AccessToken.class) + .list(); + } + + + /** + * Queries all AccessTokens for a specific minion + * @param minion the minion + * @return list of AccessTokens + */ + public static List listByMinion(MinionServer minion) { + return getSession() + .createQuery("FROM AccessToken WHERE minion = :minion", AccessToken.class) + .setParameter("minion", minion) .list(); } @@ -82,12 +92,10 @@ public static List all() { * @return optional of AccessToken */ public static Optional lookupByToken(String token) { - return Optional.ofNullable( - (AccessToken)HibernateFactory.getSession() - .createCriteria(AccessToken.class) - .add(Restrictions.eq("token", token)) - .uniqueResult() - ); + return getSession() + .createQuery("FROM AccessToken WHERE token = :token", AccessToken.class) + .setParameter("token", token) + .uniqueResultOptional(); } /** @@ -210,6 +218,8 @@ public static boolean refreshTokens(MinionServer minion, Collection minion.getAccessTokens().add(toActivate); }); + LOG.debug("Token refresh finished. Got Unneeded {} Got Updated {} Got New {}", + !unneededTokens.isEmpty(), !update.isEmpty(), !newTokens.isEmpty()); return !unneededTokens.isEmpty() || !update.isEmpty() || !newTokens.isEmpty(); } diff --git a/java/code/src/com/redhat/rhn/domain/kickstart/KickstartableTree.java b/java/code/src/com/redhat/rhn/domain/kickstart/KickstartableTree.java index 163b9593da64..cab8ed3f20c3 100644 --- a/java/code/src/com/redhat/rhn/domain/kickstart/KickstartableTree.java +++ b/java/code/src/com/redhat/rhn/domain/kickstart/KickstartableTree.java @@ -36,10 +36,15 @@ import java.io.File; import java.io.IOException; +import java.nio.file.FileSystem; +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.StandardCopyOption; +import java.nio.file.attribute.UserPrincipal; +import java.nio.file.attribute.UserPrincipalLookupService; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -588,6 +593,7 @@ public void createOrUpdateSaltFS() { Path copyTo = fullDir.resolve(copyFrom.getFileName()); if (!Files.exists(copyTo) || Files.isSymbolicLink(copyTo)) { Files.copy(copyFrom, copyTo, StandardCopyOption.REPLACE_EXISTING); + modifyOwner(copyTo, true); } } } @@ -596,6 +602,28 @@ public void createOrUpdateSaltFS() { } } + /** + * Change the owner to user tomcat when the current owner is root + * @param pathIn the path to change + * @param setOwner set to true if the owner should be changed, otherwise false + * @throws IOException + */ + protected void modifyOwner(Path pathIn, boolean setOwner) throws IOException { + if (!setOwner) { + return; + } + UserPrincipal tomcatUser = null; + UserPrincipal rootUser = null; + FileSystem fileSystem = FileSystems.getDefault(); + UserPrincipalLookupService service = fileSystem.getUserPrincipalLookupService(); + tomcatUser = service.lookupPrincipalByName("tomcat"); + rootUser = service.lookupPrincipalByName("root"); + + if (Files.getOwner(pathIn, LinkOption.NOFOLLOW_LINKS).equals(rootUser)) { + Files.setOwner(pathIn, tomcatUser); + } + } + /** * Remove the Salt Filesystem */ diff --git a/java/code/src/com/redhat/rhn/domain/server/MinionServer.java b/java/code/src/com/redhat/rhn/domain/server/MinionServer.java index c7150f82ea92..5d37415107fd 100644 --- a/java/code/src/com/redhat/rhn/domain/server/MinionServer.java +++ b/java/code/src/com/redhat/rhn/domain/server/MinionServer.java @@ -15,6 +15,8 @@ package com.redhat.rhn.domain.server; import com.redhat.rhn.domain.channel.AccessToken; +import com.redhat.rhn.domain.channel.AccessTokenFactory; +import com.redhat.rhn.domain.channel.Channel; import com.redhat.rhn.domain.config.ConfigChannel; import com.redhat.rhn.domain.user.User; import com.redhat.rhn.manager.configuration.SaltConfigSubscriptionService; @@ -24,11 +26,13 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; +import java.time.Instant; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; /** * MinionServer @@ -221,6 +225,22 @@ public boolean equals(Object other) { .isEquals(); } + + /** + * @return Return true when all assigned software channels have valid access tokens. + */ + public boolean hasValidTokensForAllChannels() { + + Set tokenChannels = AccessTokenFactory.listByMinion(this) + .stream() + .filter(AccessToken::getValid) + .filter(t -> t.getExpiration().toInstant().isAfter(Instant.now())) + .flatMap(t -> t.getChannels().stream()) + .collect(Collectors.toSet()); + + return tokenChannels.containsAll(getChannels()) && getChannels().containsAll(tokenChannels); + } + /** * Get channel access tokens assigned to this minion. * @return set of access tokens diff --git a/java/code/src/com/redhat/rhn/domain/server/MinionServerFactory.java b/java/code/src/com/redhat/rhn/domain/server/MinionServerFactory.java index 93b7fa2d8da3..4b91353cfe68 100644 --- a/java/code/src/com/redhat/rhn/domain/server/MinionServerFactory.java +++ b/java/code/src/com/redhat/rhn/domain/server/MinionServerFactory.java @@ -53,7 +53,7 @@ */ public class MinionServerFactory extends HibernateFactory { - private static Logger log = LogManager.getLogger(MinionServerFactory.class); + private static final Logger LOG = LogManager.getLogger(MinionServerFactory.class); /** * Lookup all Servers that belong to an org @@ -75,13 +75,13 @@ public static List lookupByOrg(Long orgId) { */ public static Stream lookupVisibleToUser(User user) { return user.getServers().stream().flatMap( - s -> s.asMinionServer().map(Stream::of).orElseGet(Stream::empty) + s -> s.asMinionServer().stream() ); } @Override protected Logger getLogger() { - return log; + return LOG; } /** @@ -153,7 +153,7 @@ public static Optional lookupById(Long id) { */ public static Stream lookupByIds(List ids) { return ServerFactory.lookupByIds(ids).stream().flatMap(server -> - server.asMinionServer().map(Stream::of).orElseGet(Stream::empty) + server.asMinionServer().stream() ); } @@ -324,7 +324,7 @@ public static List findByosServers(Action action) { List allMinions = MinionServerFactory.findQueuedMinionSummaries(action.getId()); return allMinions.stream().filter( minionSummary -> MinionServerFactory.findByMinionId(minionSummary.getMinionId()) - .map(server -> server.isDeniedOnPayg()) + .map(Server::isDeniedOnPayg) .orElse(false)).toList(); } } diff --git a/java/code/src/com/redhat/rhn/frontend/xmlrpc/channel/software/test/ChannelSoftwareHandlerTest.java b/java/code/src/com/redhat/rhn/frontend/xmlrpc/channel/software/test/ChannelSoftwareHandlerTest.java index 23aeef849f5d..f3d91a51671e 100644 --- a/java/code/src/com/redhat/rhn/frontend/xmlrpc/channel/software/test/ChannelSoftwareHandlerTest.java +++ b/java/code/src/com/redhat/rhn/frontend/xmlrpc/channel/software/test/ChannelSoftwareHandlerTest.java @@ -16,14 +16,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.redhat.rhn.FaultException; -import com.redhat.rhn.common.hibernate.HibernateFactory; import com.redhat.rhn.common.validator.ValidatorException; import com.redhat.rhn.domain.action.Action; import com.redhat.rhn.domain.action.channel.SubscribeChannelsAction; @@ -48,7 +46,6 @@ import com.redhat.rhn.frontend.context.Context; import com.redhat.rhn.frontend.dto.ErrataOverview; import com.redhat.rhn.frontend.dto.PackageDto; -import com.redhat.rhn.frontend.xmlrpc.InvalidChannelException; import com.redhat.rhn.frontend.xmlrpc.InvalidChannelLabelException; import com.redhat.rhn.frontend.xmlrpc.InvalidChannelNameException; import com.redhat.rhn.frontend.xmlrpc.InvalidParentChannelException; @@ -58,7 +55,6 @@ import com.redhat.rhn.frontend.xmlrpc.ValidationException; import com.redhat.rhn.frontend.xmlrpc.channel.software.ChannelSoftwareHandler; import com.redhat.rhn.frontend.xmlrpc.errata.ErrataHandler; -import com.redhat.rhn.frontend.xmlrpc.system.SystemHandler; import com.redhat.rhn.frontend.xmlrpc.system.XmlRpcSystemHelper; import com.redhat.rhn.frontend.xmlrpc.test.BaseHandlerTestCase; import com.redhat.rhn.manager.channel.ChannelManager; @@ -135,8 +131,6 @@ public class ChannelSoftwareHandlerTest extends BaseHandlerTestCase { ); private SystemManager systemManager = new SystemManager(ServerFactory.SINGLETON, ServerGroupFactory.SINGLETON, saltApi); - private SystemHandler systemHandler = new SystemHandler(taskomaticApi, xmlRpcSystemHelper, systemEntitlementManager, - systemManager, serverGroupManager, new TestCloudPaygManagerBuilder().build(), new AttestationManager()); private ChannelSoftwareHandler handler = new ChannelSoftwareHandler(taskomaticApi, xmlRpcSystemHelper); private ErrataHandler errataHandler = new ErrataHandler(); @@ -312,109 +306,6 @@ public void testIsExisting() throws Exception { assertFalse(handler.isExisting(admin, c1.getLabel() + UUID.randomUUID())); } - @Test - public void testSetSystemChannelsBaseChannel() throws Exception { - - Channel base = ChannelFactoryTest.createTestChannel(admin); - assertTrue(base.isBaseChannel()); - Server server = ServerFactoryTest.createTestServer(admin, true); - Channel child = ChannelFactoryTest.createTestChannel(admin); - child.setParentChannel(base); - ChannelFactory.save(child); - assertFalse(child.isBaseChannel()); - - Channel child2 = ChannelFactoryTest.createTestChannel(admin); - child2.setParentChannel(base); - ChannelFactory.save(child2); - assertFalse(child2.isBaseChannel()); - - SystemHandler sh = new SystemHandler(taskomaticApi, xmlRpcSystemHelper, systemEntitlementManager, systemManager, - serverGroupManager, new TestCloudPaygManagerBuilder().build(), new AttestationManager()); - - int sid = server.getId().intValue(); - int rc1 = sh.setBaseChannel(admin, sid, base.getLabel()); - int rc2 = sh.setChildChannels(admin, sid, List.of(child.getLabel())); - - server = reload(server); - - // now verify - assertEquals(1, rc1); - assertEquals(1, rc2); - assertEquals(2, server.getChannels().size()); - Channel newBase = server.getBaseChannel(); - assertNotNull(newBase); - assertEquals(newBase.getLabel(), base.getLabel()); - - try { - sh.setBaseChannel(admin, sid, child.getLabel()); - fail("setBaseChannel didn't complain when given no base channel"); - } - catch (InvalidChannelException ice) { - // ice ice baby - } - - } - - @Test - public void testSetBaseChannel() throws Exception { - SystemHandler sh = new SystemHandler(taskomaticApi, xmlRpcSystemHelper, systemEntitlementManager, systemManager, - serverGroupManager, new TestCloudPaygManagerBuilder().build(), new AttestationManager()); - - Channel c1 = ChannelFactoryTest.createTestChannel(admin); - Server server = ServerFactoryTest.createTestServer(admin, true); - - assertEquals(0, server.getChannels().size()); - int result = sh.setBaseChannel(admin, server.getId().intValue(), c1.getLabel()); - - server = reload(server); - - assertEquals(1, result); - assertEquals(1, server.getChannels().size()); - - Channel c2 = ChannelFactoryTest.createTestChannel(admin); - assertNotEquals(c1.getLabel(), c2.getLabel()); - result = sh.setBaseChannel(admin, server.getId().intValue(), c2.getLabel()); - - server = reload(server); - - assertEquals(1, result); - assertTrue(server.getChannels().contains(c2)); - - //try to make it break - try { - sh.setBaseChannel(admin, server.getId().intValue(), TestUtils.randomString()); - fail("subscribed system to invalid channel."); - } - catch (Exception e) { - //success - } - - server = reload(server); - //make sure servers channel subscriptions weren't changed - assertEquals(1, result); - Channel subscribed = server.getChannels().iterator().next(); - assertEquals(c2.getLabel(), subscribed.getLabel()); - - // try setting the base channel of an s390 server to - // IA-32. - try { - - Channel c3 = ChannelFactoryTest.createTestChannel(admin); - - // change the arch of the server - server.setServerArch( - ServerFactory.lookupServerArchByLabel("s390-redhat-linux")); - ServerFactory.save(server); - - int rc = sh.setBaseChannel(admin, server.getId().intValue(), c3.getLabel()); - - fail("allowed incompatible channel arch to be set, returned: " + rc); - } - catch (InvalidChannelException e) { - // success - } - } - @Test public void testListSystemChannels() throws Exception { ChannelSoftwareHandler csh = new ChannelSoftwareHandler(taskomaticApi, xmlRpcSystemHelper); @@ -871,35 +762,6 @@ public void xxxtestListPackagesWithoutChannel() throws Exception { assertEquals(1, secondList.length - iniailList.length); } - @Test - public void testChannelSubscription() throws Exception { - Server server = ServerFactoryTest.createTestServer(admin); - Channel baseChan = ChannelFactoryTest.createBaseChannel(admin); - Channel childChan = ChannelFactoryTest.createTestChannel(admin); - childChan.setParentChannel(baseChan); - - SystemHandler sh = new SystemHandler(taskomaticApi, xmlRpcSystemHelper, systemEntitlementManager, systemManager, - serverGroupManager, new TestCloudPaygManagerBuilder().build(), new AttestationManager()); - - int return1 = sh.setBaseChannel(admin, server.getId().intValue(), baseChan.getLabel()); - int return2 = sh.setChildChannels(admin, server.getId().intValue(), List.of(childChan.getLabel())); - - assertEquals(1, return1); - assertEquals(1, return2); - server = HibernateFactory.reload(server); - assertEquals(2, server.getChannels().size()); - assertTrue(server.getChannels().contains(baseChan)); - assertTrue(server.getChannels().contains(childChan)); - - return1 = sh.setBaseChannel(admin, server.getId().intValue(), ""); - return2 = sh.setChildChannels(admin, server.getId().intValue(), List.of()); - assertEquals(1, return1); - assertEquals(1, return2); - server = HibernateFactory.reload(server); - assertEquals(0, server.getChannels().size()); - } - - @Test public void testCloneAll() throws Exception { Channel original = ChannelFactoryTest.createTestChannel(admin, false); diff --git a/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/SystemHandler.java b/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/SystemHandler.java index ffeb08e5560d..88c2bc9c1d01 100644 --- a/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/SystemHandler.java +++ b/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/SystemHandler.java @@ -14,6 +14,7 @@ */ package com.redhat.rhn.frontend.xmlrpc.system; +import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static java.util.Optional.empty; import static java.util.Optional.of; @@ -181,8 +182,6 @@ import com.redhat.rhn.manager.system.ServerGroupManager; import com.redhat.rhn.manager.system.SystemManager; import com.redhat.rhn.manager.system.SystemsExistException; -import com.redhat.rhn.manager.system.UpdateBaseChannelCommand; -import com.redhat.rhn.manager.system.UpdateChildChannelsCommand; import com.redhat.rhn.manager.system.entitling.SystemEntitlementManager; import com.redhat.rhn.manager.token.ActivationKeyManager; import com.redhat.rhn.taskomatic.TaskomaticApi; @@ -405,7 +404,7 @@ public int upgradeEntitlement(User loggedInUser, Integer sid, String entitlement * @throws FaultException A FaultException is thrown if: * - the server corresponding to sid cannot be found. * - the channel corresponding to cid is not a valid child channel. - * - the user doesn't have subscribe access to any one of the current or + * - the user doesn't have subscribed access to any one of the current or * new child channels. * @deprecated being replaced by system.scheduleChangeChannels(string sessionKey, * int serverId, String baseChannelLabel, array_single channelLabels, date earliestOccurrence). @@ -424,12 +423,12 @@ public int upgradeEntitlement(User loggedInUser, Integer sid, String entitlement * @apidoc.returntype #return_int_success() */ @Deprecated - public int setChildChannels(User loggedInUser, Integer sid, - List channelIdsOrLabels) - throws FaultException { + public int setChildChannels(User loggedInUser, Integer sid, List channelIdsOrLabels) throws FaultException { //Get the logged in user and server Server server = lookupServer(loggedInUser, sid); + List channelLabels = new ArrayList<>(); + String baseChannel = ""; // Determine if user passed in a list of channel ids or labels... note: the list // must contain all ids or labels (i.e. not a combination of both) @@ -437,6 +436,21 @@ public int setChildChannels(User loggedInUser, Integer sid, if (!channelIdsOrLabels.isEmpty()) { if (channelIdsOrLabels.get(0) instanceof String) { receivedLabels = true; + Channel channel = ChannelFactory.lookupByLabel((String) channelIdsOrLabels.get(0)); + if (channel == null) { + throw new InvalidChannelLabelException(); + } + if (channel.getParentChannel() == null) { + throw new InvalidChannelException(); + } + baseChannel = channel.getParentChannel().getLabel(); + } + else { + Channel channel = ChannelFactory.lookupById(Long.valueOf((Integer) channelIdsOrLabels.get(0))); + if (channel == null || channel.getParentChannel() == null) { + throw new InvalidChannelException(); + } + baseChannel = channel.getParentChannel().getLabel(); } // check to make sure that the objects are all the same type @@ -445,38 +459,18 @@ public int setChildChannels(User loggedInUser, Integer sid, if (!(object instanceof String)) { throw new InvalidChannelListException(); } + channelLabels.add((String) object); } else { if (!(object instanceof Integer)) { throw new InvalidChannelListException(); } + channelLabels.add(ChannelFactory.lookupById(Long.valueOf((Integer) object)).getLabel()); } } } - List channelIds = new ArrayList<>(); - if (receivedLabels) { - channelIds = ChannelFactory.getChannelIds(channelIdsOrLabels); - - // if we weren't able to retrieve channel ids for all labels provided, - // one or more of the labels must be invalid... - if (channelIds.size() != channelIdsOrLabels.size()) { - throw new InvalidChannelLabelException(); - } - } - else { - // unfortunately, the interface only allows Integer input (not Long); - // therefore, convert the input to Long, since channel ids are - // internally represented as Long - for (Object channelId : channelIdsOrLabels) { - channelIds.add(Long.valueOf((Integer) channelId)); - } - } - - UpdateChildChannelsCommand cmd = new UpdateChildChannelsCommand(loggedInUser, - server, channelIds); - cmd.setScheduleApplyChannelsState(true); - cmd.store(); + scheduleChangeChannels(loggedInUser, sid, baseChannel, channelLabels, new Date()); SystemManager.snapshotServer(server, LocalizationService .getInstance().getMessage("snapshots.childchannel")); @@ -484,44 +478,6 @@ public int setChildChannels(User loggedInUser, Integer sid, return 1; } - /** - * Sets the base channel for the given server to the given channel - * @param loggedInUser The current user - * @param sid The id for the server - * @param cid The id for the channel - * @return Returns 1 if successful, exception otherwise - * @throws FaultException A FaultException is thrown if: - * - the server corresponding to sid cannot be found. - * - the channel corresponding to cid is not a base channel. - * - the user doesn't have subscribe access to either the current or - * the new base channel. - * @deprecated being replaced by system.setBaseChannel(string sessionKey, - * int serverId, string channelLabel) - * - * @apidoc.doc Assigns the server to a new baseChannel. - * @apidoc.param #session_key() - * @apidoc.param #param("int", "sid") - * @apidoc.param #param_desc("int", "cid", "channel ID") - * @apidoc.returntype #return_int_success() - */ - @Deprecated - public int setBaseChannel(User loggedInUser, Integer sid, Integer cid) - throws FaultException { - //Get the logged in user and server - Server server = lookupServer(loggedInUser, sid); - UpdateBaseChannelCommand cmd = - new UpdateBaseChannelCommand( - loggedInUser, server, cid.longValue()); - cmd.setScheduleApplyChannelsState(true); - ValidatorError ve = cmd.store(); - if (ve != null) { - throw new InvalidChannelException( - LocalizationService.getInstance() - .getMessage(ve.getKey(), ve.getValues())); - } - return 1; - } - /** * Sets the base channel for the given server to the given channel * @param loggedInUser The current user @@ -531,10 +487,11 @@ public int setBaseChannel(User loggedInUser, Integer sid, Integer cid) * @throws FaultException A FaultException is thrown if: * - the server corresponding to sid cannot be found. * - the channel corresponding to cid is not a base channel. - * - the user doesn't have subscribe access to either the current or + * - the user doesn't have subscribed access to either the current or * the new base channel. * @deprecated being replaced by system.scheduleChangeChannels(string sessionKey, * int serverId, String baseChannelLabel, array_single channelLabels, date earliestOccurrence). + * Internally it is already implemented using scheduleChangeChannels * * * @apidoc.doc Assigns the server to a new base channel. If the user provides an empty @@ -550,31 +507,8 @@ public int setBaseChannel(User loggedInUser, Integer sid, String channelLabel) throws FaultException { Server server = lookupServer(loggedInUser, sid); - UpdateBaseChannelCommand cmd = null; - if (StringUtils.isEmpty(channelLabel)) { - // if user provides an empty string for the channel label, they are requesting - // to remove the base channel - cmd = new UpdateBaseChannelCommand(loggedInUser, server, -1L); - } - else { - List channelLabels = new ArrayList<>(); - channelLabels.add(channelLabel); - - List channelIds = ChannelFactory.getChannelIds(channelLabels); + scheduleChangeChannels(loggedInUser, sid, channelLabel, emptyList(), new Date()); - if (!channelIds.isEmpty()) { - cmd = new UpdateBaseChannelCommand(loggedInUser, server, channelIds.get(0)); - cmd.setScheduleApplyChannelsState(true); - } - else { - throw new InvalidChannelLabelException(); - } - } - ValidatorError ve = cmd.store(); - if (ve != null) { - throw new InvalidChannelException(LocalizationService.getInstance() - .getMessage(ve.getKey(), ve.getValues())); - } SystemManager.snapshotServer(server, LocalizationService .getInstance().getMessage("snapshots.basechannel")); return 1; @@ -641,11 +575,6 @@ public long scheduleChangeChannels(User loggedInUser, Integer sid, String baseCh */ public List scheduleChangeChannels(User loggedInUser, List sids, String baseChannelLabel, List childLabels, Date earliestOccurrence) { - //Get the logged in user and server - Set servers = sids.stream() - .map(sid -> lookupServer(loggedInUser, sid)) - .map(Server::getId) - .collect(toSet()); Optional baseChannel = Optional.empty(); // base channel @@ -653,10 +582,30 @@ public List scheduleChangeChannels(User loggedInUser, List sids, List channelIds = ChannelFactory.getChannelIds(singletonList(baseChannelLabel)); long baseChannelId = channelIds.stream().findFirst().orElseThrow(InvalidChannelLabelException::new); baseChannel = Optional.of(ChannelManager.lookupByIdAndUser(baseChannelId, loggedInUser)); + if (baseChannel.filter(Channel::isBaseChannel).isEmpty()) { + throw new InvalidChannelException(); + } } // else if the user provides an empty string for the channel label, they are requesting // to remove the base channel + //Get the logged in user and server + Set servers = sids.stream() + .map(sid -> lookupServer(loggedInUser, sid)) + .collect(toSet()); + + for (Server s : servers) { + if (baseChannel.map(Channel::getChannelArch) + .filter(arch -> arch.isCompatible(s.getServerArch())) + .isEmpty()) { + throw new InvalidChannelException(); + } + } + + Set serverIds = servers.stream() + .map(Server::getId) + .collect(toSet()); + // check if user passed a list of labels for the child channels if (childLabels.stream().anyMatch(e -> !(e instanceof String))) { throw new InvalidChannelListException(); @@ -677,7 +626,7 @@ public List scheduleChangeChannels(User loggedInUser, List sids, try { Set action = ActionChainManager.scheduleSubscribeChannelsAction(loggedInUser, - servers, + serverIds, baseChannel, childChannels, earliestOccurrence, null); diff --git a/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/test/SystemHandlerTest.java b/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/test/SystemHandlerTest.java index b4ee6a93bdf4..d44197ccaeca 100644 --- a/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/test/SystemHandlerTest.java +++ b/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/test/SystemHandlerTest.java @@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.fail; import com.redhat.rhn.FaultException; +import com.redhat.rhn.GlobalInstanceHolder; import com.redhat.rhn.common.client.ClientCertificate; import com.redhat.rhn.common.conf.ConfigDefaults; import com.redhat.rhn.common.db.datasource.DataResult; @@ -170,6 +171,7 @@ import com.suse.manager.virtualization.VirtManagerSalt; import com.suse.manager.webui.controllers.bootstrap.RegularMinionBootstrapper; import com.suse.manager.webui.controllers.bootstrap.SSHMinionBootstrapper; +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.SystemQuery; @@ -348,8 +350,14 @@ public void xxxtestUpgradeEntitlement() { public void testSetChildChannelsDeprecated() throws Exception { // the usage of setChildChannels API as tested by this junit where // channel ids are passed as arguments is being deprecated... + SystemHandler mockedHandler = getMockedHandler(); + ActionChainManager.setTaskomaticApi(mockedHandler.getTaskomaticApi()); + SaltServerActionService sa = GlobalInstanceHolder.SALT_SERVER_ACTION_SERVICE; + sa.setCommitTransaction(false); + sa.setSaltApi(saltApi); + List finishedActions = new ArrayList<>(); - Server server = ServerFactoryTest.createTestServer(admin, true); + Server server = MinionServerFactoryTest.createTestMinionServer(admin); assertNull(server.getBaseChannel()); Integer sid = server.getId().intValue(); @@ -364,6 +372,7 @@ public void testSetChildChannelsDeprecated() throws Exception { //subscribe to base channel. SystemManager.subscribeServerToChannel(admin, server, base); + TestUtils.flushAndEvict(server); server = reload(server); assertNotNull(server.getBaseChannel()); @@ -372,6 +381,14 @@ public void testSetChildChannelsDeprecated() throws Exception { cids.add(child2.getId().intValue()); int result = handler.setChildChannels(admin, sid, cids); + Optional first = ActionFactory.listActionsForServer(admin, server).stream() + .filter(a -> a.getActionType().equals(ActionFactory.TYPE_SUBSCRIBE_CHANNELS)) + .findFirst(); + assertTrue(first.isPresent(), "No Subscribe Channels Action created"); + sa.execute(first.get(), false, false, Optional.empty()); + finishedActions.add(first.get().getId()); + + TestUtils.flushAndEvict(server); server = TestUtils.reload(server); assertEquals(1, result); assertEquals(3, server.getChannels().size()); @@ -382,6 +399,15 @@ public void testSetChildChannelsDeprecated() throws Exception { assertEquals(1, cids.size()); result = handler.setChildChannels(admin, sid, cids); + first = ActionFactory.listActionsForServer(admin, server).stream() + .filter(a -> !finishedActions.contains(a.getId())) + .filter(a -> a.getActionType().equals(ActionFactory.TYPE_SUBSCRIBE_CHANNELS)) + .findFirst(); + assertTrue(first.isPresent(), "No Subscribe Channels Action created"); + sa.execute(first.get(), false, false, Optional.empty()); + finishedActions.add(first.get().getId()); + + TestUtils.flushAndEvict(server); server = TestUtils.reload(server); assertEquals(1, result); assertEquals(2, server.getChannels().size()); @@ -414,6 +440,7 @@ public void testSetChildChannelsDeprecated() throws Exception { //success } + TestUtils.flushAndEvict(server); server = reload(server); assertEquals(2, server.getChannels().size()); @@ -440,7 +467,14 @@ public void testSetChildChannelsDeprecated() throws Exception { @Test public void testSetChildChannels() throws Exception { - Server server = ServerFactoryTest.createTestServer(admin, true); + SystemHandler mockedHandler = getMockedHandler(); + ActionChainManager.setTaskomaticApi(mockedHandler.getTaskomaticApi()); + SaltServerActionService sa = GlobalInstanceHolder.SALT_SERVER_ACTION_SERVICE; + sa.setCommitTransaction(false); + sa.setSaltApi(saltApi); + List finishedActions = new ArrayList<>(); + + Server server = MinionServerFactoryTest.createTestMinionServer(admin); assertNull(server.getBaseChannel()); Integer sid = server.getId().intValue(); @@ -449,37 +483,60 @@ public void testSetChildChannels() throws Exception { Channel child1 = ChannelFactoryTest.createTestChannel(admin); child1.setParentChannel(base); + String child1Label = child1.getLabel(); Channel child2 = ChannelFactoryTest.createTestChannel(admin); child2.setParentChannel(base); + String child2Label = child2.getLabel(); + + TestUtils.flushAndEvict(child1); + TestUtils.flushAndEvict(child2); //subscribe to base channel. SystemManager.subscribeServerToChannel(admin, server, base); + TestUtils.saveAndFlush(server); server = reload(server); assertNotNull(server.getBaseChannel()); - List channelLabels = new ArrayList<>(); - channelLabels.add(new String(child1.getLabel())); - channelLabels.add(new String(child2.getLabel())); + List channelLabels = new ArrayList<>(); + channelLabels.add(child1Label); + channelLabels.add(child2Label); int result = handler.setChildChannels(admin, sid, channelLabels); + Optional first = ActionFactory.listActionsForServer(admin, server).stream() + .filter(a -> a.getActionType().equals(ActionFactory.TYPE_SUBSCRIBE_CHANNELS)) + .findFirst(); + assertTrue(first.isPresent(), "No Subscribe Channels Action created"); + sa.execute(first.get(), false, false, Optional.empty()); + finishedActions.add(first.get().getId()); + + TestUtils.flushAndEvict(server); server = TestUtils.reload(server); assertEquals(1, result); assertEquals(3, server.getChannels().size()); //Try 'unsubscribing' from child1... channelLabels = new ArrayList<>(); - channelLabels.add(new String(child2.getLabel())); + channelLabels.add(child2Label); assertEquals(1, channelLabels.size()); result = handler.setChildChannels(admin, sid, channelLabels); + first = ActionFactory.listActionsForServer(admin, server).stream() + .filter(a -> !finishedActions.contains(a.getId())) + .filter(a -> a.getActionType().equals(ActionFactory.TYPE_SUBSCRIBE_CHANNELS)) + .findFirst(); + assertTrue(first.isPresent(), "No Subscribe Channels Action created"); + sa.execute(first.get(), false, false, Optional.empty()); + finishedActions.add(first.get().getId()); + + TestUtils.flushAndEvict(server); server = TestUtils.reload(server); assertEquals(1, result); assertEquals(2, server.getChannels().size()); //Try putting an invalid channel in there channelLabels = new ArrayList<>(); - channelLabels.add(new String("invalid-unknown-channel-label")); + channelLabels.add("invalid-unknown-channel-label"); assertEquals(1, channelLabels.size()); try { @@ -491,10 +548,13 @@ public void testSetChildChannels() throws Exception { } assertEquals(2, server.getChannels().size()); + TestUtils.flushAndEvict(server); + server = TestUtils.reload(server); + Channel base2 = ChannelFactoryTest.createTestChannel(admin); base2.setParentChannel(null); channelLabels = new ArrayList<>(); - channelLabels.add(new String(base2.getLabel())); + channelLabels.add(base2.getLabel()); assertEquals(1, channelLabels.size()); try { @@ -505,6 +565,7 @@ public void testSetChildChannels() throws Exception { //success } + TestUtils.flushAndEvict(server); server = reload(server); assertEquals(2, server.getChannels().size()); @@ -532,8 +593,14 @@ public void testSetChildChannels() throws Exception { @Test public void testSetBaseChannelDeprecated() throws Exception { // the setBaseChannel API tested by this junit is being deprecated + SystemHandler mockedHandler = getMockedHandler(); + ActionChainManager.setTaskomaticApi(mockedHandler.getTaskomaticApi()); + SaltServerActionService sa = GlobalInstanceHolder.SALT_SERVER_ACTION_SERVICE; + sa.setCommitTransaction(false); + sa.setSaltApi(saltApi); + List finishedActions = new ArrayList<>(); - Server server = ServerFactoryTest.createTestServer(admin, true); + MinionServer server = MinionServerFactoryTest.createTestMinionServer(admin); assertNull(server.getBaseChannel()); Integer sid = server.getId().intValue(); @@ -547,16 +614,34 @@ public void testSetBaseChannelDeprecated() throws Exception { child1.setParentChannel(base1); // Set base channel to base1 - int result = handler.setBaseChannel(admin, sid, - base1.getId().intValue()); + int result = handler.setBaseChannel(admin, sid, base1.getLabel()); + Optional first = ActionFactory.listActionsForServer(admin, server).stream() + .filter(a -> a.getActionType().equals(ActionFactory.TYPE_SUBSCRIBE_CHANNELS)) + .findFirst(); + assertTrue(first.isPresent(), "No Subscribe Channels Action created"); + sa.execute(first.get(), false, false, Optional.empty()); + finishedActions.add(first.get().getId()); + + HibernateFactory.getSession().flush(); + HibernateFactory.getSession().clear(); + server = reload(server); assertEquals(1, result); assertNotNull(server.getBaseChannel()); assertEquals(server.getBaseChannel().getLabel(), base1.getLabel()); // Set base channel to base2 - result = handler.setBaseChannel(admin, sid, - base2.getId().intValue()); + result = handler.setBaseChannel(admin, sid, base2.getLabel()); + first = ActionFactory.listActionsForServer(admin, server).stream() + .filter(a -> !finishedActions.contains(a.getId())) + .filter(a -> a.getActionType().equals(ActionFactory.TYPE_SUBSCRIBE_CHANNELS)) + .findFirst(); + assertTrue(first.isPresent(), "No Subscribe Channels Action created"); + sa.execute(first.get(), false, false, Optional.empty()); + + HibernateFactory.getSession().flush(); + HibernateFactory.getSession().clear(); + server = TestUtils.reload(server); assertEquals(1, result); assertNotNull(server.getBaseChannel()); @@ -564,8 +649,7 @@ public void testSetBaseChannelDeprecated() throws Exception { // Try setting base channel to child try { - result = handler.setBaseChannel(admin, sid, - child1.getId().intValue()); + result = handler.setBaseChannel(admin, sid, child1.getLabel()); fail("SystemHandler.setBaseChannel allowed invalid base channel to be set."); } catch (InvalidChannelException e) { @@ -582,8 +666,7 @@ public void testSetBaseChannelDeprecated() throws Exception { ServerFactory.save(server); - result = handler.setBaseChannel(admin, sid, - base1.getId().intValue()); + result = handler.setBaseChannel(admin, sid, base1.getLabel()); fail("allowed channel with incompatible arch to be set"); } catch (InvalidChannelException e) { @@ -593,7 +676,14 @@ public void testSetBaseChannelDeprecated() throws Exception { @Test public void testSetBaseChannel() throws Exception { - Server server = ServerFactoryTest.createTestServer(admin, true); + SystemHandler mockedHandler = getMockedHandler(); + ActionChainManager.setTaskomaticApi(mockedHandler.getTaskomaticApi()); + SaltServerActionService sa = GlobalInstanceHolder.SALT_SERVER_ACTION_SERVICE; + sa.setCommitTransaction(false); + sa.setSaltApi(saltApi); + List finishedActions = new ArrayList<>(); + + MinionServer server = MinionServerFactoryTest.createTestMinionServer(admin); assertNull(server.getBaseChannel()); Integer sid = server.getId().intValue(); @@ -608,6 +698,16 @@ public void testSetBaseChannel() throws Exception { // Set base channel to base1 int result = handler.setBaseChannel(admin, sid, base1.getLabel()); + Optional first = ActionFactory.listActionsForServer(admin, server).stream() + .filter(a -> a.getActionType().equals(ActionFactory.TYPE_SUBSCRIBE_CHANNELS)) + .findFirst(); + assertTrue(first.isPresent(), "No Subscribe Channels Action created"); + sa.execute(first.get(), false, false, Optional.empty()); + finishedActions.add(first.get().getId()); + + HibernateFactory.getSession().flush(); + HibernateFactory.getSession().clear(); + server = reload(server); assertEquals(1, result); assertNotNull(server.getBaseChannel()); @@ -615,6 +715,17 @@ public void testSetBaseChannel() throws Exception { // Set base channel to base2 result = handler.setBaseChannel(admin, sid, base2.getLabel()); + first = ActionFactory.listActionsForServer(admin, server).stream() + .filter(a -> !finishedActions.contains(a.getId())) + .filter(a -> a.getActionType().equals(ActionFactory.TYPE_SUBSCRIBE_CHANNELS)) + .findFirst(); + assertTrue(first.isPresent(), "No Subscribe Channels Action created"); + sa.execute(first.get(), false, false, Optional.empty()); + finishedActions.add(first.get().getId()); + + HibernateFactory.getSession().flush(); + HibernateFactory.getSession().clear(); + server = TestUtils.reload(server); assertEquals(1, result); assertNotNull(server.getBaseChannel()); diff --git a/java/code/src/com/redhat/rhn/manager/system/SystemManager.java b/java/code/src/com/redhat/rhn/manager/system/SystemManager.java index 892b7338ec14..8d7d1d949fe6 100644 --- a/java/code/src/com/redhat/rhn/manager/system/SystemManager.java +++ b/java/code/src/com/redhat/rhn/manager/system/SystemManager.java @@ -123,6 +123,7 @@ import com.suse.manager.model.maintenance.MaintenanceSchedule; import com.suse.manager.reactor.messaging.ApplyStatesEventMessage; import com.suse.manager.reactor.messaging.ChannelsChangedEventMessage; +import com.suse.manager.reactor.messaging.ChannelsChangedEventMessageAction; import com.suse.manager.reactor.utils.ValueMap; import com.suse.manager.ssl.SSLCertData; import com.suse.manager.ssl.SSLCertGenerationException; @@ -166,6 +167,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; /** @@ -3623,8 +3625,10 @@ public static void addMinionInfoToServer(Long sid, String minionId) { } /** - * Update the the base and child channels of a server. Calls + * Update the base and child channels of a server. Calls * the {@link UpdateBaseChannelCommand} and {@link UpdateChildChannelsCommand}. + * This method regenerate the Tokens and Pillar Data synchronous, but does not + * trigger a 'state.apply' for channels. * * @param user the user changing the channels * @param server the server for which to change channels @@ -3632,7 +3636,7 @@ public static void addMinionInfoToServer(Long sid, String minionId) { * @param childChannels the full list of child channels to set. Any channel no provided will be unsubscribed. * and will be used when regenerating the Pillar data for Salt minions. */ - public static void updateServerChannels(User user, + public void updateServerChannels(User user, Server server, Optional baseChannel, Collection childChannels) { @@ -3641,7 +3645,7 @@ public static void updateServerChannels(User user, // if there's no base channel present the there are no child channels to set List childChannelIds = baseChannel.isPresent() ? - childChannels.stream().map(Channel::getId).toList() : + childChannels.stream().map(Channel::getId).collect(Collectors.toList()) : emptyList(); UpdateBaseChannelCommand baseChannelCommand = @@ -3661,7 +3665,11 @@ public static void updateServerChannels(User user, childChannelsCommand.skipChannelChangedEvent(true); childChannelsCommand.store(); - MessageQueue.publish(new ChannelsChangedEventMessage(server.getId(), user.getId())); + // Calling this asynchronous block execution util main thread close the Hibernate Session + // as we require the result, we must call this synchronous + ChannelsChangedEventMessageAction channelsChangeAction = + new ChannelsChangedEventMessageAction(saltApi); + channelsChangeAction.execute(new ChannelsChangedEventMessage(server.getId(), user.getId())); } 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 4484abe9af92..8b4a147abfbe 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 @@ -1353,7 +1353,7 @@ public void testUpdateServerChannels() throws Exception { HibernateFactory.getSession().flush(); - SystemManager.updateServerChannels(user, server, of(base2), Arrays.asList(ch21, ch22)); + systemManager.updateServerChannels(user, server, of(base2), Arrays.asList(ch21, ch22)); assertEquals(base2.getId(), server.getBaseChannel().getId()); assertEquals(2, server.getChildChannels().size()); @@ -1383,7 +1383,7 @@ public void testUpdateServerChannelsNoChildren() throws Exception { HibernateFactory.getSession().flush(); - SystemManager.updateServerChannels(user, server, of(base2), Collections.emptyList()); + systemManager.updateServerChannels(user, server, of(base2), Collections.emptyList()); assertEquals(base2.getId(), server.getBaseChannel().getId()); assertEquals(0, server.getChildChannels().size()); @@ -1411,7 +1411,7 @@ public void testUpdateServerChannelsNoBase() throws Exception { HibernateFactory.getSession().flush(); - SystemManager.updateServerChannels(user, server, empty(), Arrays.asList(ch21, ch22)); + systemManager.updateServerChannels(user, server, empty(), Arrays.asList(ch21, ch22)); assertNull(server.getBaseChannel()); assertEquals(0, server.getChildChannels().size()); diff --git a/java/code/src/com/redhat/rhn/taskomatic/task/MinionActionExecutor.java b/java/code/src/com/redhat/rhn/taskomatic/task/MinionActionExecutor.java index 630d422e5d9c..b18c88c7645b 100644 --- a/java/code/src/com/redhat/rhn/taskomatic/task/MinionActionExecutor.java +++ b/java/code/src/com/redhat/rhn/taskomatic/task/MinionActionExecutor.java @@ -18,12 +18,8 @@ import com.redhat.rhn.common.localization.LocalizationService; import com.redhat.rhn.domain.action.Action; import com.redhat.rhn.domain.action.ActionFactory; -import com.redhat.rhn.domain.action.channel.SubscribeChannelsAction; -import com.redhat.rhn.domain.action.server.ServerAction; -import com.redhat.rhn.domain.server.MinionServerFactory; import com.redhat.rhn.domain.user.User; import com.redhat.rhn.domain.user.UserFactory; -import com.redhat.rhn.manager.system.SystemManager; import com.redhat.rhn.taskomatic.TaskoQuartzHelper; import com.suse.cloud.CloudPaygManager; @@ -36,7 +32,6 @@ import java.time.Duration; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.Date; import java.util.List; import java.util.Optional; @@ -181,10 +176,6 @@ public void execute(JobExecutionContext context) { log.info("Executing action: {}", actionId); - if (ActionFactory.TYPE_SUBSCRIBE_CHANNELS.equals(action.getActionType())) { - handleTraditionalClients(user, (SubscribeChannelsAction) action); - } - saltServerActionService.execute(action, forcePackageListRefresh, isStagingJob, Optional.ofNullable(stagingJobMinionServerId)); @@ -194,21 +185,6 @@ public void execute(JobExecutionContext context) { } } - // for traditional systems only the subscribe channels action will be handled here - // all other actions are still handled like before - private void handleTraditionalClients(User user, SubscribeChannelsAction sca) { - List serverActions = MinionServerFactory.findTradClientServerActions(sca.getId()); - - serverActions.forEach(sa -> { - SystemManager.updateServerChannels(user, sa.getServer(), - Optional.ofNullable(sca.getDetails().getBaseChannel()), - sca.getDetails().getChannels()); - sa.setStatus(ActionFactory.STATUS_COMPLETED); - sa.setCompletionTime(new Date()); - sa.setResultCode(0L); - sa.setResultMsg("Successfully changed channels"); - }); - } private long countQueuedServerActions(Action action) { if (action == null || CollectionUtils.isEmpty(action.getServerActions())) { diff --git a/java/code/src/com/suse/manager/reactor/messaging/ChannelsChangedEventMessageAction.java b/java/code/src/com/suse/manager/reactor/messaging/ChannelsChangedEventMessageAction.java index 5a5e79ab2ff2..6f0609f63d9d 100644 --- a/java/code/src/com/suse/manager/reactor/messaging/ChannelsChangedEventMessageAction.java +++ b/java/code/src/com/suse/manager/reactor/messaging/ChannelsChangedEventMessageAction.java @@ -17,9 +17,6 @@ import com.redhat.rhn.common.messaging.EventMessage; import com.redhat.rhn.common.messaging.MessageAction; import com.redhat.rhn.domain.action.salt.ApplyStatesAction; -import com.redhat.rhn.domain.rhnpackage.Package; -import com.redhat.rhn.domain.rhnpackage.PackageFactory; -import com.redhat.rhn.domain.server.MinionServer; import com.redhat.rhn.domain.server.Server; import com.redhat.rhn.domain.server.ServerFactory; import com.redhat.rhn.domain.user.User; @@ -39,8 +36,6 @@ import java.util.Collections; import java.util.Date; -import java.util.List; -import java.util.Optional; /** * Handle changes of channel assignments on minions: trigger a refresh of the errata cache, @@ -73,52 +68,33 @@ public void execute(EventMessage event) { log.error("Server with id {} not found.", serverId); return; } - Optional optMinion = s.asMinionServer(); - optMinion.ifPresent(minion -> { - // This code acts only on salt minions - - // Trigger update of the errata cache - ErrataManager.insertErrataCacheTask(minion); - - // Regenerate the pillar data - MinionPillarManager.INSTANCE.generatePillar(minion); - - // push the changed pillar data to the minion - saltApi.refreshPillar(new MinionList(minion.getMinionId())); - - if (msg.isScheduleApplyChannelsState()) { - User user = UserFactory.lookupById(event.getUserId()); - ApplyStatesAction action = ActionManager.scheduleApplyStates(user, - Collections.singletonList(minion.getId()), - Collections.singletonList(ApplyStatesEventMessage.CHANNELS), - new Date()); - try { - TASKOMATIC_API.scheduleActionExecution(action, false); - } - catch (TaskomaticApiException e) { - log.error("Could not schedule channels state application for system: {}", s.getId()); - } - } - - }); - if (!optMinion.isPresent()) { - try { - // This code acts only on traditional systems - List prodPkgs = - PackageFactory.findMissingProductPackagesOnServer(serverId); - if (event.getUserId() != null) { - User user = UserFactory.lookupById(event.getUserId()); - ActionManager.schedulePackageInstall(user, prodPkgs, s, new Date()); - } - else if (s.getCreator() != null) { - ActionManager.schedulePackageInstall(s.getCreator(), prodPkgs, s, - new Date()); - } - } - catch (TaskomaticApiException e) { - log.error("Could not schedule state application for system: {}", s.getId()); - throw new RuntimeException(e); - } - } + s.asMinionServer().ifPresentOrElse( + minion -> { + // This code acts only on salt minions + + // Trigger update of the errata cache + ErrataManager.insertErrataCacheTask(minion); + + // Regenerate the pillar data + MinionPillarManager.INSTANCE.generatePillar(minion); + + // push the changed pillar data to the minion + saltApi.refreshPillar(new MinionList(minion.getMinionId())); + + if (msg.isScheduleApplyChannelsState()) { + User user = UserFactory.lookupById(event.getUserId()); + ApplyStatesAction action = ActionManager.scheduleApplyStates(user, + Collections.singletonList(minion.getId()), + Collections.singletonList(ApplyStatesEventMessage.CHANNELS), + new Date()); + try { + TASKOMATIC_API.scheduleActionExecution(action, false); + } + catch (TaskomaticApiException e) { + log.error("Could not schedule channels state application for system: {}", s.getId()); + } + } + }, + () -> log.error("Traditional Clients are not supported")); } } 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 17ce1260bb23..3426c8d8fd66 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 @@ -2055,6 +2055,13 @@ public void testSubscribeChannelsActionSuccess() throws Exception { with(any(User.class)), with(any(SubscribeChannelsAction.class))); } }); + SaltService saltService = new SaltService() { + @Override + public void refreshPillar(MinionList minionList) { + } + }; + saltServerActionService.setSaltApi(saltService); + MinionServer minion = MinionServerFactoryTest.createTestMinionServer(user); minion.setMinionId("dev-minsles12sp2.test.local"); @@ -2081,10 +2088,8 @@ public void testSubscribeChannelsActionSuccess() throws Exception { ServerAction sa = ActionFactoryTest.createServerAction(minion, action); action.addServerAction(sa); - saltServerActionService.setCommitTransaction(false); - Map, List> calls = saltServerActionService.callsForAction(action); - - HibernateFactory.getSession().flush(); + saltServerActionService.callsForAction(action); + assertTrue(minion.hasValidTokensForAllChannels(), "Unexpected minion miss tokens for some channels"); // Setup an event message from file contents Optional event = JobReturnEvent.parse( @@ -2143,24 +2148,18 @@ public void testSubscribeChannelsActionNullTokens() throws Exception { ServerAction sa = ActionFactoryTest.createServerAction(minion, action); action.addServerAction(sa); - saltServerActionService.setCommitTransaction(false); Map, List> calls = saltServerActionService.callsForAction(action); - - // artifically expire tokens - action.getDetails().getAccessTokens().forEach(t -> t.setMinion(null)); HibernateFactory.getSession().flush(); - // Setup an event message from file contents - Optional event = JobReturnEvent.parse( - getJobReturnEvent("subscribe.channels.success.json", action.getId())); - JobReturnEventMessage message = new JobReturnEventMessage(event.get()); + // artifically expire tokens + minion.getAccessTokens().forEach(t -> t.setMinion(null)); - // Process the event message - JobReturnEventMessageAction messageAction = new JobReturnEventMessageAction(saltServerActionService, saltUtils); - messageAction.execute(message); + HibernateFactory.getSession().flush(); + HibernateFactory.getSession().clear(); + MinionServer reloaded = HibernateFactory.reload(minion); // check that tokens are really gone - assertEquals(0, minion.getAccessTokens().size()); + assertEquals(0, reloaded.getAccessTokens().size()); } private void assertTokenChannel(MinionServer minion, Channel channel) { diff --git a/java/code/src/com/suse/manager/utils/SaltUtils.java b/java/code/src/com/suse/manager/utils/SaltUtils.java index 768cb33b0c87..d2792c121a19 100644 --- a/java/code/src/com/suse/manager/utils/SaltUtils.java +++ b/java/code/src/com/suse/manager/utils/SaltUtils.java @@ -27,7 +27,6 @@ import com.redhat.rhn.domain.action.ActionFactory; import com.redhat.rhn.domain.action.ActionStatus; import com.redhat.rhn.domain.action.ActionType; -import com.redhat.rhn.domain.action.channel.SubscribeChannelsAction; import com.redhat.rhn.domain.action.config.ConfigRevisionActionResult; import com.redhat.rhn.domain.action.config.ConfigVerifyAction; import com.redhat.rhn.domain.action.dup.DistUpgradeAction; @@ -46,7 +45,6 @@ import com.redhat.rhn.domain.action.virtualization.BaseVirtualizationGuestAction; import com.redhat.rhn.domain.action.virtualization.BaseVirtualizationNetworkAction; import com.redhat.rhn.domain.action.virtualization.BaseVirtualizationPoolAction; -import com.redhat.rhn.domain.channel.AccessTokenFactory; import com.redhat.rhn.domain.channel.Channel; import com.redhat.rhn.domain.config.ConfigRevision; import com.redhat.rhn.domain.image.ImageFile; @@ -837,24 +835,8 @@ private String getJsonResultWithPrettyPrint(JsonElement jsonResult) { private void handleSubscribeChannels(ServerAction serverAction, JsonElement jsonResult, Action action) { if (serverAction.getStatus().equals(ActionFactory.STATUS_COMPLETED)) { serverAction.setResultMsg("Successfully applied state: " + ApplyStatesEventMessage.CHANNELS); - SubscribeChannelsAction sca = (SubscribeChannelsAction)action; - - // if successful update channels in db and trigger pillar refresh - SystemManager.updateServerChannels( - action.getSchedulerUser(), - serverAction.getServer(), - Optional.ofNullable(sca.getDetails().getBaseChannel()), - sca.getDetails().getChannels()); } else { - //set the token as invalid - SubscribeChannelsAction sca = (SubscribeChannelsAction)action; - sca.getDetails().getAccessTokens().forEach(token -> { - token.setValid(false); - token.setMinion(null); - AccessTokenFactory.save(token); - }); - serverAction.setResultMsg("Failed to apply state: " + ApplyStatesEventMessage.CHANNELS + ".\n" + getJsonResultWithPrettyPrint(jsonResult)); } diff --git a/java/code/src/com/suse/manager/webui/controllers/contentmanagement/handlers/ProjectSourcesApiController.java b/java/code/src/com/suse/manager/webui/controllers/contentmanagement/handlers/ProjectSourcesApiController.java index 7b05e0b3e10f..94dcc1df22e4 100644 --- a/java/code/src/com/suse/manager/webui/controllers/contentmanagement/handlers/ProjectSourcesApiController.java +++ b/java/code/src/com/suse/manager/webui/controllers/contentmanagement/handlers/ProjectSourcesApiController.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import spark.Request; import spark.Response; @@ -78,7 +79,7 @@ public static String updateContentSoftwareSources(Request req, Response res, Use List sourceLabelsToAttach = createSourceRequest.getSoftwareSources() .stream() .map(ProjectSoftwareSourceRequest::getLabel) - .toList(); + .collect(Collectors.toList()); Collections.reverse(sourceLabelsToAttach); sourceLabelsToAttach.forEach(sourceLabel -> CONTENT_MGR.attachSource( projectLabel, 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 529ed31f0e73..6b1285ce23ed 100644 --- a/java/code/src/com/suse/manager/webui/services/SaltServerActionService.java +++ b/java/code/src/com/suse/manager/webui/services/SaltServerActionService.java @@ -90,8 +90,6 @@ import com.redhat.rhn.domain.action.virtualization.VirtualizationShutdownGuestAction; import com.redhat.rhn.domain.action.virtualization.VirtualizationStartGuestAction; import com.redhat.rhn.domain.action.virtualization.VirtualizationSuspendGuestAction; -import com.redhat.rhn.domain.channel.AccessToken; -import com.redhat.rhn.domain.channel.AccessTokenFactory; import com.redhat.rhn.domain.channel.Channel; import com.redhat.rhn.domain.config.ConfigRevision; import com.redhat.rhn.domain.errata.Errata; @@ -116,6 +114,7 @@ import com.redhat.rhn.domain.server.MinionSummary; import com.redhat.rhn.domain.server.Server; import com.redhat.rhn.domain.server.ServerFactory; +import com.redhat.rhn.domain.server.ServerGroupFactory; import com.redhat.rhn.domain.server.VirtualInstance; import com.redhat.rhn.domain.server.VirtualInstanceFactory; import com.redhat.rhn.domain.token.ActivationKey; @@ -141,7 +140,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.pillar.MinionGeneralPillarGenerator; import com.suse.manager.webui.services.pillar.MinionPillarManager; import com.suse.manager.webui.utils.DownloadTokenBuilder; import com.suse.manager.webui.utils.SaltModuleRun; @@ -208,7 +206,6 @@ import java.util.Date; import java.util.Deque; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -1434,56 +1431,22 @@ private Map, List> applyStatesAction( private Map, List> subscribeChanelsAction( List minionSummaries, SubscribeChannelsActionDetails actionDetails) { Map, List> ret = new HashMap<>(); - - Stream minions = MinionServerFactory.lookupByIds( - minionSummaries.stream().map(MinionSummary::getServerId).toList()); - - minions.forEach(minion -> { - // generate access tokens - Set allChannels = new HashSet<>(actionDetails.getChannels()); - if (actionDetails.getBaseChannel() != null) { - allChannels.add(actionDetails.getBaseChannel()); - } - - List newTokens = allChannels.stream() - .map(channel -> - AccessTokenFactory.generate(minion, Collections.singleton(channel)) - .orElseThrow(() -> - new RuntimeException( - "Could not generate new channel access token for minion " + - minion.getMinionId() + " and channel " + - channel.getName()))) - .toList(); - - newTokens.forEach(newToken -> { - // set the token as valid, then if something is wrong, the state chanel will disable it - newToken.setValid(true); - actionDetails.getAccessTokens().add(newToken); - }); - - MinionGeneralPillarGenerator minionGeneralPillarGenerator = new MinionGeneralPillarGenerator(); - Map chanPillar = new HashMap<>(); - newTokens.forEach(accessToken -> - accessToken.getChannels().forEach(chan -> { - Map chanProps = - minionGeneralPillarGenerator.getChannelPillarData(minion, accessToken, chan); - chanPillar.put(chan.getLabel(), chanProps); - }) - ); - - Map pillar = new HashMap<>(); - pillar.put("_mgr_channels_items_name", "mgr_channels_new"); - pillar.put("mgr_channels_new", chanPillar); - - ret.put(State.apply(List.of(ApplyStatesEventMessage.CHANNELS), - Optional.of(pillar)), Collections.singletonList(new MinionSummary(minion))); - - }); - if (commitTransaction) { - // we must be sure that tokens and action Details are in the database - // before we return and send the salt calls to update the minions. - HibernateFactory.commitTransaction(); - } + SystemManager sysMgr = new SystemManager(ServerFactory.SINGLETON, ServerGroupFactory.SINGLETON, saltApi); + + List minions = MinionServerFactory.lookupByMinionIds( + minionSummaries.stream().map(MinionSummary::getMinionId).collect(Collectors.toSet())); + + minions.forEach(minion -> + // change channels in DB and execult the ChannelsChangedEventMessageAction + // which regenerate pillar and refresh Tokens but does not execute a "state.apply channels" + sysMgr.updateServerChannels( + actionDetails.getParentAction().getSchedulerUser(), + minion, + Optional.ofNullable(actionDetails.getBaseChannel()), + actionDetails.getChannels()) + ); + ret.put(State.apply(List.of(ApplyStatesEventMessage.CHANNELS), Optional.empty()), + minionSummaries); return ret; } 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 8d8a5e1adaf8..9e4b2cc2af86 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 @@ -42,7 +42,6 @@ import com.redhat.rhn.domain.action.virtualization.BaseVirtualizationGuestAction; import com.redhat.rhn.domain.action.virtualization.VirtualizationRebootGuestAction; import com.redhat.rhn.domain.action.virtualization.VirtualizationShutdownGuestAction; -import com.redhat.rhn.domain.channel.AccessToken; import com.redhat.rhn.domain.channel.Channel; import com.redhat.rhn.domain.channel.test.ChannelFactoryTest; import com.redhat.rhn.domain.config.ConfigRevision; @@ -109,7 +108,6 @@ import java.nio.file.Files; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -701,7 +699,7 @@ public void testSubscribeChannels() throws Exception { MinionServer minion1 = MinionServerFactoryTest.createTestMinionServer(user); final ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault()); - SubscribeChannelsAction action = (SubscribeChannelsAction)ActionManager.createAction( + SubscribeChannelsAction action = (SubscribeChannelsAction) ActionManager.createAction( user, ActionFactory.TYPE_SUBSCRIBE_CHANNELS, "Subscribe to channels", Date.from(now.toInstant())); SubscribeChannelsActionDetails details = new SubscribeChannelsActionDetails(); @@ -713,7 +711,6 @@ public void testSubscribeChannels() throws Exception { ActionFactory.addServerToAction(minion1, action); - saltServerActionService.setCommitTransaction(false); Map, List> calls = saltServerActionService.callsForAction(action); HibernateFactory.getSession().flush(); @@ -721,42 +718,30 @@ public void testSubscribeChannels() throws Exception { assertEquals(1, calls.size()); - Map pillar = (Map)((Map)calls.keySet().stream() + Map payload = calls.keySet().stream() .findFirst() - .get().getPayload().get("kwarg")).get("pillar"); - assertEquals("mgr_channels_new", pillar.get("_mgr_channels_items_name")); - Map channels = (Map)pillar.get("mgr_channels_new"); - assertEquals(3, channels.size()); - assertTrue(channels.keySet().contains(base.getLabel())); - assertTrue(channels.keySet().contains(ch1.getLabel())); - assertTrue(channels.keySet().contains(ch2.getLabel())); - - assertTokenPillarValue(base, action, channels); - assertTokenPillarValue(ch1, action, channels); - assertTokenPillarValue(ch2, action, channels); - - action = (SubscribeChannelsAction)ActionFactory.lookupById(action.getId()); - - assertEquals(3, action.getDetails().getAccessTokens().size()); - assertTrue(action.getDetails().getAccessTokens().stream() - .allMatch(token -> - token.getStart().toInstant().isAfter(now.toInstant()) && - token.getStart().toInstant().isBefore(now.toInstant().plus(10, ChronoUnit.SECONDS)))); - assertTrue(action.getDetails().getAccessTokens().stream().allMatch(AccessToken::getValid)); - assertTokenExists(base, action); - assertTokenExists(ch1, action); - assertTokenExists(ch2, action); + .get().getPayload(); + + assertEquals("state.apply", payload.get("fun")); + assertEquals("channels", ((List) ((Map) payload.get("kwarg")).get("mods")).get(0)); + + minion1 = TestUtils.reload(minion1); + assertEquals(3, minion1.getChannels().size()); + assertEquals(base.getId(), minion1.getBaseChannel().getId()); + assertEquals(2, minion1.getChildChannels().size()); + assertTrue(minion1.getChildChannels().stream().anyMatch(cc -> cc.getId().equals(ch1.getId()))); + assertTrue(minion1.getChildChannels().stream().anyMatch(cc -> cc.getId().equals(ch2.getId()))); + + assertEquals(3, minion1.getAccessTokens().size()); + assertTokenChannel(minion1, base); + assertTokenChannel(minion1, ch1); + assertTokenChannel(minion1, ch2); } - private void assertTokenExists(Channel channel, SubscribeChannelsAction action) { - assertEquals(1, action.getDetails().getAccessTokens().stream() - .filter(token -> token.getChannels().size() == 1 && token.getChannels().contains(channel)).count()); - } - - private void assertTokenPillarValue(Channel channel, SubscribeChannelsAction action, Map channels) { - AccessToken tokenForChannel = action.getDetails().getAccessTokens().stream() - .filter(token -> token.getChannels().contains(channel)).findFirst().get(); - assertEquals(tokenForChannel.getToken(), ((Map)channels.get(channel.getLabel())).get("token")); + private void assertTokenChannel(MinionServer minionIn, Channel channel) { + assertTrue(minionIn.getAccessTokens().stream() + .anyMatch(token -> token.getChannels().size() == 1 && token.getChannels().contains(channel)), + channel.getLabel()); } private SaltServerActionService countSaltActionCalls(AtomicInteger counter) { diff --git a/java/spacewalk-java.changes.mc.adapt-channel-change b/java/spacewalk-java.changes.mc.adapt-channel-change new file mode 100644 index 000000000000..7c194430fc35 --- /dev/null +++ b/java/spacewalk-java.changes.mc.adapt-channel-change @@ -0,0 +1,3 @@ +- adapt changing software channels to first perform the changes in + the database and apply the channel state later to. This allow better + handling of offline minions (bsc#1216683)