Repository: uyuni-project/uyuni
Pull request #10306: Issv3 fix channel sync (bsc#1242889, bsc#1242324)
Author: login: mcalmer
Labels: name: java
name: java_lint_checkstyle
name: java_pgsql_tests
name: data-model
name: ga-blocker
Created: 2025-05-08T14:26:09Z | Merged: 2025-05-13T09:06:04Z

## What does this PR change?

Channel syncronization from a hub server to a peripheral failed for custom channels.
The last component is not the SCCID, but the channel name.
Additionally the token for custom channels include the wrong Org and caused 

  Forbidden: Token does not provide access to channel

errors

## GUI diff

No difference.

- [x] **DONE**

## Documentation
- No documentation needed: only internal and user invisible changes

- [x] **DONE**

## Test coverage
- No tests: already covered

- [x] **DONE**

## Links

Issue(s): https://github.com/SUSE/spacewalk/issues/27167 https://github.com/SUSE/spacewalk/issues/27181

- [x] **DONE**

## Changelogs

Make sure the changelogs entries you are adding are compliant with https://github.com/uyuni-project/uyuni/wiki/Contributing#changelogs and https://github.com/uyuni-project/uyuni/wiki/Contributing#uyuni-projectuyuni-repository

If you don't need a changelog check, please mark this checkbox:

- [ ] No changelog needed

If you uncheck the checkbox after the PR is created, you will need to re-run `changelog_test` (see below)

## Re-run a test

If you need to re-run a test, please mark the related checkbox, it will be unchecked automatically once it has re-run:

- [ ] Re-run test "changelog_test"
- [ ] Re-run test "backend_unittests_pgsql"
- [ ] Re-run test "java_pgsql_tests"
- [ ] Re-run test "schema_migration_test_pgsql"
- [ ] Re-run test "susemanager_unittests"
- [ ] Re-run test "javascript_lint"
- [ ] Re-run test "spacecmd_unittests"

# Before you merge

Check [How to branch and merge properly](https://github.com/uyuni-project/uyuni/wiki/How-to-branch-and-merge-properly)!



Diff:
diff --git a/java/code/src/com/redhat/rhn/domain/channel/ChannelFactory.java b/java/code/src/com/redhat/rhn/domain/channel/ChannelFactory.java
index 2bfb10831a13..5373bb1de938 100644
--- a/java/code/src/com/redhat/rhn/domain/channel/ChannelFactory.java
+++ b/java/code/src/com/redhat/rhn/domain/channel/ChannelFactory.java
@@ -1390,6 +1390,22 @@ public static List<ContentSource> listVendorContentSources() {
                 ContentSource.class).getResultList();
     }
 
+    /**
+     * List all custom channels which URL access a specific FQDN
+     * Mainly used for finding repos pointing to a Hub
+     * @param fqdn the FQDN
+     * @return a list of {@link ContentSource}
+     */
+    public static List<ContentSource> findCustomContentSourcesForHubFqdn(String fqdn) {
+        return getSession().createNativeQuery("""
+                SELECT * FROM rhnContentSource
+                 WHERE org_id IS NOT NULL
+                 AND source_url like :urlstart
+                """, ContentSource.class)
+                .setParameter("urlstart", "https://%s/%%".formatted(fqdn))
+                .list();
+    }
+
     /**
      * Find a vendor content source (org is null) for a given repo URL.
      *
@@ -1604,7 +1620,6 @@ public static ChannelInfoDetailsJson toChannelInfo(Channel channel, Long periphe
                 channel.getProduct().getVersion();
         channelInfo.setChannelProductVersion(channelProductVersion);
 
-        channelInfo.setChannelAccess(channel.getAccess());
         channelInfo.setMaintainerName(channel.getMaintainerName());
         channelInfo.setMaintainerEmail(channel.getMaintainerEmail());
         channelInfo.setMaintainerPhone(channel.getMaintainerPhone());
@@ -1621,7 +1636,7 @@ public static ChannelInfoDetailsJson toChannelInfo(Channel channel, Long periphe
         String hostname = ConfigDefaults.get().getJavaHostname();
         String channelLabel = channel.getLabel();
 
-        Optional<String> tokenString = SCCEndpoints.buildHubRepositoryToken(channelLabel);
+        Optional<String> tokenString = SCCEndpoints.buildHubRepositoryToken(channel);
         if (tokenString.isPresent()) {
             SCCRepositoryJson repositoryInfo;
             if (channel.getOrg() != null) {
@@ -1734,13 +1749,23 @@ public static Channel toChannel(ChannelInfoDetailsJson channelInfo) {
                     channelInfo.getChannelProductProduct(), channelInfo.getChannelProductVersion()));
         }
 
-        channel.setAccess(channelInfo.getChannelAccess());
         channel.setMaintainerName(channelInfo.getMaintainerName());
         channel.setMaintainerEmail(channelInfo.getMaintainerEmail());
         channel.setMaintainerPhone(channelInfo.getMaintainerPhone());
         channel.setSupportPolicy(channelInfo.getSupportPolicy());
         channel.setUpdateTag(channelInfo.getUpdateTag());
         channel.setInstallerUpdates(channelInfo.isInstallerUpdates());
+        if (org != null) {
+            channel.addChannelFamily(org.getPrivateChannelFamily());
+        }
+
+        // need to save before calling stored proc below
+        ChannelFactory.save(channel);
+
+        if (org != null) {
+            // this ends up being a mode query call, must have saved the channel to get an id
+            channel.setGloballySubscribable(false, org);
+        }
 
         // rebuild repository
         ContentSyncManager csm = new ContentSyncManager();
@@ -1892,7 +1917,6 @@ public static Channel modifyChannel(ChannelInfoDetailsJson modifyChannelInfo) {
                     modifyChannelInfo.getChannelProductVersion()));
         }
 
-        setValueIfNotNull(channel, modifyChannelInfo.getChannelAccess(), Channel::setAccess);
         setValueIfNotNull(channel, modifyChannelInfo.getMaintainerName(), Channel::setMaintainerName);
         setValueIfNotNull(channel, modifyChannelInfo.getMaintainerEmail(), Channel::setMaintainerEmail);
         setValueIfNotNull(channel, modifyChannelInfo.getMaintainerPhone(), Channel::setMaintainerPhone);
diff --git a/java/code/src/com/redhat/rhn/manager/content/ContentSyncManager.java b/java/code/src/com/redhat/rhn/manager/content/ContentSyncManager.java
index 4f0666afe5fa..0aab1cd56de2 100644
--- a/java/code/src/com/redhat/rhn/manager/content/ContentSyncManager.java
+++ b/java/code/src/com/redhat/rhn/manager/content/ContentSyncManager.java
@@ -70,6 +70,7 @@
 import com.suse.manager.webui.services.pillar.MinionGeneralPillarGenerator;
 import com.suse.mgrsync.MgrSyncStatus;
 import com.suse.salt.netapi.parser.JsonParser;
+import com.suse.scc.SCCEndpoints;
 import com.suse.scc.client.SCCClient;
 import com.suse.scc.client.SCCClientException;
 import com.suse.scc.client.SCCClientUtils;
@@ -911,8 +912,9 @@ public void refreshRepositoriesAuthentication(
         //REPO HANDLING
         List<SCCRepository> oesRepos = SCCCachingFactory.lookupRepositoriesByChannelFamily(OES_CHANNEL_FAMILY);
         List<SCCRepositoryJson> nonOESJRepos = repositories.stream()
-            .filter(jsonRepo -> oesRepos.stream().noneMatch(oes -> oes.getSccId().equals(jsonRepo.getSCCId())))
-            .toList();
+                .filter(jsonRepo -> oesRepos.stream().noneMatch(oes -> oes.getSccId().equals(jsonRepo.getSCCId())))
+                .filter(jsonRepo -> !jsonRepo.getSCCId().equals(SCCEndpoints.CUSTOM_REPO_FAKE_SCC_ID))
+                .toList();
 
         for (SCCRepositoryJson jsonRepo : nonOESJRepos) {
             SCCRepository repo = availableReposById.get(jsonRepo.getSCCId());
@@ -1009,14 +1011,24 @@ public void refreshRepositoriesAuthentication(
     }
 
     private void refreshCustomRepoAuthentication(List<SCCRepositoryJson> customReposIn, SCCCredentials creds) {
+        IssHub issHub = creds.getIssHub();
+        if (issHub == null) {
+            LOG.debug("Only Peripheral server manage custom channels via SCC API");
+            return;
+        }
+        Set<ContentSource> toDeleteCustomRepos = new HashSet<>(
+                ChannelFactory.findCustomContentSourcesForHubFqdn(issHub.getFqdn()));
+
         for (SCCRepositoryJson repo : customReposIn) {
             Channel customChannel = ChannelFactory.lookupByLabel(repo.getName());
             if (!isValidCustomChannel(repo, customChannel)) {
                 LOG.error("Invalid custom repo/channel {} - {}", repo, customChannel);
                 return;
             }
-            refreshOrCreateRepository(repo, customChannel, creds.getIssHub());
+            toDeleteCustomRepos.removeAll(customChannel.getSources());
+            refreshOrCreateRepository(repo, customChannel, issHub);
         }
+        toDeleteCustomRepos.forEach(ChannelFactory::remove);
     }
 
     /**
@@ -1613,8 +1625,11 @@ public static SUSEProduct updateProduct(SCCProductJson p, SUSEProduct product,
 
         PackageArch pArch = packageArchMap.computeIfAbsent(p.getArch(), PackageFactory::lookupPackageArchByLabel);
         if (pArch == null && p.getArch() != null) {
-            // unsupported architecture, skip the product
-            LOG.error("Unknown architecture '{}'. This may be caused by a missing database migration", p.getArch());
+            if (!p.getArch().equals("multi-arch")) {
+                // unsupported architecture, skip the product
+                LOG.error("Unknown architecture '{}'. This may be caused by a missing database migration", p.getArch());
+            }
+            // multi-arch is used by SCC - we do not handle these products, no need to write an error message
         }
         else {
             product.setArch(pArch);
@@ -1660,8 +1675,11 @@ public static SUSEProduct createNewProduct(SCCProductJson p, Map<String, Channel
 
         PackageArch pArch = packageArchMap.computeIfAbsent(p.getArch(), PackageFactory::lookupPackageArchByLabel);
         if (pArch == null && p.getArch() != null) {
-            // unsupported architecture, skip the product
-            LOG.error("Unknown architecture '{}'. This may be caused by a missing database migration", p.getArch());
+            if (!p.getArch().equals("multi-arch")) {
+                // unsupported architecture, skip the product
+                LOG.error("Unknown architecture '{}'. This may be caused by a missing database migration", p.getArch());
+            }
+            // multi-arch is used by SCC - we do not handle these products, no need to write an error message
         }
         else {
             product.setArch(pArch);
diff --git a/java/code/src/com/suse/manager/hub/test/ControllerTestUtils.java b/java/code/src/com/suse/manager/hub/test/ControllerTestUtils.java
index 6407de6cb27f..6b27e9b6f7dc 100644
--- a/java/code/src/com/suse/manager/hub/test/ControllerTestUtils.java
+++ b/java/code/src/com/suse/manager/hub/test/ControllerTestUtils.java
@@ -267,7 +267,6 @@ public ChannelInfoDetailsJson createChannelInfoDetailsJson(Long orgId,
         info.setChecksumTypeLabel(checksumLabel);
         info.setChannelProductProduct("channelProductProduct");
         info.setChannelProductVersion("channelProductVersion");
-        info.setChannelAccess("chAccess"); // max 10
         info.setMaintainerName("maintainerName_" + channelLabel);
         info.setMaintainerEmail("maintainerEmail_" + channelLabel);
         info.setMaintainerPhone("maintainerPhone_" + channelLabel);
@@ -313,8 +312,6 @@ public void testCustomChannel(Channel ch, Long peripheralOrgId,
         assertEquals(checksumLabel, ch.getChecksumType().getLabel());
         assertTrue(isNowUtil(ch.getLastModified()));
 
-        assertEquals("chAccess", ch.getAccess());
-
         assertEquals("maintainerName_" + channelLabel, ch.getMaintainerName());
         assertEquals("maintainerEmail_" + channelLabel, ch.getMaintainerEmail());
         assertEquals("maintainerPhone_" + channelLabel, ch.getMaintainerPhone());
@@ -429,7 +426,6 @@ private void checkModifications(ChannelInfoDetailsJson modifyInfo, Channel ch,
 
         checkMethod.accept(modifyInfo.getChannelProductProduct(), ch.getProduct().getProduct());
         checkMethod.accept(modifyInfo.getChannelProductVersion(), ch.getProduct().getVersion());
-        checkMethod.accept(modifyInfo.getChannelAccess(), ch.getAccess());
         checkMethod.accept(modifyInfo.getMaintainerName(), ch.getMaintainerName());
         checkMethod.accept(modifyInfo.getMaintainerEmail(), ch.getMaintainerEmail());
         checkMethod.accept(modifyInfo.getMaintainerPhone(), ch.getMaintainerPhone());
diff --git a/java/code/src/com/suse/manager/hub/test/HubControllerTest.java b/java/code/src/com/suse/manager/hub/test/HubControllerTest.java
index 6d7fa42a7fc4..4ba074f2c92c 100644
--- a/java/code/src/com/suse/manager/hub/test/HubControllerTest.java
+++ b/java/code/src/com/suse/manager/hub/test/HubControllerTest.java
@@ -903,7 +903,6 @@ public void checkModifyCustomChannels() throws Exception {
 
         modifyInfo.setChannelProductProduct("channelProductProduct_diff");
         modifyInfo.setChannelProductVersion("channelProductVersion_diff");
-        modifyInfo.setChannelAccess("acc_diff"); //max 10
         modifyInfo.setMaintainerName("maintainerName__diff");
         modifyInfo.setMaintainerEmail("maintainerEmail_diff");
         modifyInfo.setMaintainerPhone("maintainerPhone_diff");
diff --git a/java/code/src/com/suse/manager/model/hub/ChannelInfoDetailsJson.java b/java/code/src/com/suse/manager/model/hub/ChannelInfoDetailsJson.java
index 7e2fbcec676e..47449cb61b96 100644
--- a/java/code/src/com/suse/manager/model/hub/ChannelInfoDetailsJson.java
+++ b/java/code/src/com/suse/manager/model/hub/ChannelInfoDetailsJson.java
@@ -11,8 +11,6 @@
 
 package com.suse.manager.model.hub;
 
-import com.redhat.rhn.domain.channel.Channel;
-
 import com.suse.scc.model.SCCRepositoryJson;
 
 import java.util.Date;
@@ -42,7 +40,6 @@ public class ChannelInfoDetailsJson {
 
     private String channelProductProduct;
     private String channelProductVersion;
-    private String channelAccess;
     private String maintainerName;
     private String maintainerEmail;
     private String maintainerPhone;
@@ -61,7 +58,6 @@ public ChannelInfoDetailsJson(String labelIn) {
         label = labelIn;
 
         gpgCheck = true;
-        channelAccess = Channel.PRIVATE;
         installerUpdates = false;
         originalChannelLabel = null;
     }
@@ -329,20 +325,6 @@ public void setChannelProductVersion(String channelProductVersionIn) {
         this.channelProductVersion = channelProductVersionIn;
     }
 
-    /**
-     * @param acc public, protected, or private
-     */
-    public void setChannelAccess(String acc) {
-        channelAccess = acc;
-    }
-
-    /**
-     * @return public, protected, or private
-     */
-    public String getChannelAccess() {
-        return channelAccess;
-    }
-
     /**
      * @return maintainer's name
      */
@@ -465,7 +447,6 @@ public boolean equals(Object oIn) {
                 Objects.equals(getEndOfLifeDate(), that.getEndOfLifeDate()) &&
                 Objects.equals(getChannelProductProduct(), that.getChannelProductProduct()) &&
                 Objects.equals(getChannelProductVersion(), that.getChannelProductVersion()) &&
-                Objects.equals(getChannelAccess(), that.getChannelAccess()) &&
                 Objects.equals(getMaintainerName(), that.getMaintainerName()) &&
                 Objects.equals(getMaintainerEmail(), that.getMaintainerEmail()) &&
                 Objects.equals(getMaintainerPhone(), that.getMaintainerPhone()) &&
@@ -481,7 +462,7 @@ public int hashCode() {
                 getChannelArchLabel(), getChecksumTypeLabel(), getBaseDir(), getName(),
                 getSummary(), getDescription(), getProductNameLabel(), isGpgCheck(), getGpgKeyUrl(), getGpgKeyId(),
                 getGpgKeyFp(), getEndOfLifeDate(), getChannelProductProduct(), getChannelProductVersion(),
-                getChannelAccess(), getMaintainerName(), getMaintainerEmail(), getMaintainerPhone(),
+                getMaintainerName(), getMaintainerEmail(), getMaintainerPhone(),
                 getSupportPolicy(), getUpdateTag(), isInstallerUpdates(), getRepositoryInfo());
     }
 
diff --git a/java/code/src/com/suse/manager/webui/controllers/DownloadController.java b/java/code/src/com/suse/manager/webui/controllers/DownloadController.java
index 1df9618522ef..89363416c4b4 100644
--- a/java/code/src/com/suse/manager/webui/controllers/DownloadController.java
+++ b/java/code/src/com/suse/manager/webui/controllers/DownloadController.java
@@ -279,14 +279,11 @@ private boolean isKeyOrSignatureFile(String filename) {
      * @return an object to make spark happy
      */
     public Object downloadMetadataHub(Request request, Response response) {
-        Long sccid = Long.parseLong(request.params(":sccrepoid"));
         String filename = request.params(":file");
-
-        List<Channel> vendorChannelBySccId = ChannelFactory.findVendorChannelBySccId(sccid);
-        String channelLabel = vendorChannelBySccId.stream().map(Channel::getLabel).findFirst().orElseThrow(() -> {
-            LOG.error("Repository for SCC ID {} not found", sccid);
-            return halt(HttpStatus.SC_NOT_FOUND, "Repository not found");
-        });
+        String channelLabel = getChannelLabelFromHubRequest(request).orElseThrow(() -> {
+                LOG.error("Repository for SCC ID {} not found", request.params(":sccrepoid"));
+                return halt(HttpStatus.SC_NOT_FOUND, "Repository not found");
+            });
         return downloadMetadata(request, response, channelLabel, filename);
     }
 
@@ -343,12 +340,9 @@ else if (filename.equals("modules.yaml")) {
      * @return an object to make spark happy
      */
     public Object downloadMediaFilesHub(Request request, Response response) {
-        Long sccid = Long.parseLong(request.params(":sccrepoid"));
         String filename = request.params(":file");
-
-        List<Channel> vendorChannelBySccId = ChannelFactory.findVendorChannelBySccId(sccid);
-        String channelLabel = vendorChannelBySccId.stream().map(Channel::getLabel).findFirst().orElseThrow(() -> {
-            LOG.error("Repository for SCC ID {} not found", sccid);
+        String channelLabel = getChannelLabelFromHubRequest(request).orElseThrow(() -> {
+            LOG.error("Repository for SCC ID {} not found", request.params(":sccrepoid"));
             return halt(HttpStatus.SC_NOT_FOUND, "Repository not found");
         });
         return downloadMediaFiles(request, response, channelLabel, filename);
@@ -455,18 +449,11 @@ private File getMediaProductsFile(Channel channel) {
      * @return an object to make spark happy
      */
     public Object downloadPackageHub(Request request, Response response) {
-
-        // we can't use request.params(:file)
-        // See https://bugzilla.suse.com/show_bug.cgi?id=972158
-        // https://github.com/perwendel/spark/issues/490
-        Long sccid = Long.parseLong(request.params(":sccrepoid"));
-
-        List<Channel> vendorChannelBySccId = ChannelFactory.findVendorChannelBySccId(sccid);
-        String channel = vendorChannelBySccId.stream().map(Channel::getLabel).findFirst().orElseThrow(() -> {
-            LOG.error("Repository for SCC ID {} not found", sccid);
+        String channelLabel = getChannelLabelFromHubRequest(request).orElseThrow(() -> {
+            LOG.error("Repository for SCC ID {} not found", request.params(":sccrepoid"));
             return halt(HttpStatus.SC_NOT_FOUND, "Repository not found");
         });
-        return downloadPackage(request, response, channel);
+        return downloadPackage(request, response, channelLabel);
     }
 
     /**
@@ -679,7 +666,7 @@ else if (!ChannelFactory.isAccessibleBy(channel, orgId)) {
         }
         catch (TokenParsingException e) {
             LOG.info("Forbidden: Token ...{} is not valid to access {} in {}: {}",
-                () -> sanitizeToken(token), () -> filename, () -> channel, () -> e.getMessage());
+                () -> sanitizeToken(token), () -> filename, () -> channel, e::getMessage);
             halt(HttpStatus.SC_FORBIDDEN,
                 "Token is not valid to access %s in %s: %s".formatted(filename, channel, e.getMessage()));
         }
@@ -759,4 +746,15 @@ public void validateMinionInPayg(String token) {
             }
         }
     }
+
+    private Optional<String> getChannelLabelFromHubRequest(Request request) {
+        try {
+            Long sccid = Long.parseLong(request.params(":sccrepoid"));
+            List<Channel> vendorChannelBySccId = ChannelFactory.findVendorChannelBySccId(sccid);
+            return vendorChannelBySccId.stream().map(Channel::getLabel).findFirst();
+        }
+        catch (NumberFormatException e) {
+            return Optional.ofNullable(request.params(":sccrepoid"));
+        }
+    }
 }
diff --git a/java/code/src/com/suse/scc/SCCEndpoints.java b/java/code/src/com/suse/scc/SCCEndpoints.java
index b36db3244503..3ce564b303bd 100644
--- a/java/code/src/com/suse/scc/SCCEndpoints.java
+++ b/java/code/src/com/suse/scc/SCCEndpoints.java
@@ -18,6 +18,7 @@
 import com.redhat.rhn.domain.credentials.CredentialsFactory;
 import com.redhat.rhn.domain.credentials.HubSCCCredentials;
 import com.redhat.rhn.domain.credentials.SCCCredentials;
+import com.redhat.rhn.domain.org.Org;
 import com.redhat.rhn.domain.product.ChannelTemplate;
 import com.redhat.rhn.domain.product.SUSEProductFactory;
 import com.redhat.rhn.domain.scc.SCCRepository;
@@ -168,12 +169,14 @@ public static SCCRepositoryJson buildCustomRepoJson(String label, String hostnam
 
     /**
      * Build and return a short living token for a Hub repository sync
-     * @param channelLabel the channel label to the create the token for
+     * @param channel the channel to the create the token for
      * @return the token
      */
-    public static Optional<String> buildHubRepositoryToken(String channelLabel) {
+    public static Optional<String> buildHubRepositoryToken(Channel channel) {
+        String channelLabel = channel.getLabel();
         try {
-            DownloadTokenBuilder builder = new DownloadTokenBuilder(0)
+            Long oid = Optional.ofNullable(channel.getOrg()).map(Org::getId).orElse(0L);
+            DownloadTokenBuilder builder = new DownloadTokenBuilder(oid)
                     .usingServerSecret()
                     // Short lived 2 day + 4 hours tokens refreshed on ever sync
                     .expiringAfterMinutes(2L * (24 + 2) * 60)
@@ -225,7 +228,7 @@ public String repositories(Request request, Response response, HubSCCCredentials
         var jsonRepos = channels.stream().map(c -> {
             Channel channel = c.getChannel();
             String label = channel.getLabel();
-            String tokenString = buildHubRepositoryToken(label).orElse("");
+            String tokenString = buildHubRepositoryToken(channel).orElse("");
             return SUSEProductFactory.lookupByChannelLabelFirst(label)
                     .map(channelTemplate -> buildVendorRepoJson(channelTemplate, hostname, tokenString))
                     .orElseGet(() -> buildCustomRepoJson(label, hostname, tokenString));
diff --git a/java/spacewalk-java.changes.mcalmer.issv3-fix-channel-sync b/java/spacewalk-java.changes.mcalmer.issv3-fix-channel-sync
new file mode 100644
index 000000000000..0fa050d5900f
--- /dev/null
+++ b/java/spacewalk-java.changes.mcalmer.issv3-fix-channel-sync
@@ -0,0 +1,7 @@
+- Set channel family and channel permissions when creating a
+  custom channel on the peripheral server (bsc#1242324)
+- Fix removal of custom channel URLs on the peripheral server
+  (bsc#1242324)
+- Fix custom channel sync from a peripheral server (bsc#1242889)
+- Generate tokens for custom channels which include the correct
+  organization id


Reviews:
user: login: CDellaGiusta
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T07:55:33Z
user: login: lucidd
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-05-09T08:31:41Z
user: login: mackdk
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T08:48:43Z
user: login: mcalmer
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T09:50:18Z
user: login: CDellaGiusta
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T10:08:34Z

Review comments:
diff_hunk: @@ -279,14 +279,19 @@ private boolean isKeyOrSignatureFile(String filename) {
      * @return an object to make spark happy
      */
     public Object downloadMetadataHub(Request request, Response response) {
-        Long sccid = Long.parseLong(request.params(":sccrepoid"));
path: java/code/src/com/suse/manager/webui/controllers/DownloadController.java
user: login: CDellaGiusta
body: The parameter `:sccrepoid` can then contain a long (meaning a "true" scc id) or a string (referring to a channel label "fake" scc id), right?
created_at: 2025-05-09T07:46:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -343,14 +348,20 @@ else if (filename.equals("modules.yaml")) {
      * @return an object to make spark happy
      */
     public Object downloadMediaFilesHub(Request request, Response response) {
-        Long sccid = Long.parseLong(request.params(":sccrepoid"));
path: java/code/src/com/suse/manager/webui/controllers/DownloadController.java
user: login: CDellaGiusta
body: downloadMetadataHub, downloadMediaFilesHub, downloadPackageHub share the same logic for obtaining the final "channelLabel", then why don't use a unique method to avoid code duplication?
created_at: 2025-05-09T07:53:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -455,17 +466,19 @@ private File getMediaProductsFile(Channel channel) {
      * @return an object to make spark happy
      */
     public Object downloadPackageHub(Request request, Response response) {
+        String channel;
path: java/code/src/com/suse/manager/webui/controllers/DownloadController.java
user: login: CDellaGiusta
body: if not refactored as common code, better call this string `channelString` for uniformity with other calls
created_at: 2025-05-09T07:53:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -279,14 +279,19 @@ private boolean isKeyOrSignatureFile(String filename) {
      * @return an object to make spark happy
      */
     public Object downloadMetadataHub(Request request, Response response) {
-        Long sccid = Long.parseLong(request.params(":sccrepoid"));
path: java/code/src/com/suse/manager/webui/controllers/DownloadController.java
user: login: mcalmer
body: yes
created_at: 2025-05-09T09:50:17Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-08T14:26:21Z
body: :wave: Hello! Thanks for contributing to our project.
Acceptance tests will take some time (aprox. 1h), please be patient :coffee:

You can see the progress at the end of this page and at https://github.com/uyuni-project/uyuni/pull/10306/checks
Once tests finish, if they fail, you can check :eyes: the cucumber report. See the link at the output of the action.
You can also check the artifacts section, which contains the logs at https://github.com/uyuni-project/uyuni/pull/10306/checks.

If you are unsure the failing tests are related to your code, you can check the "reference jobs". These are jobs that run on a scheduled time with code from master. If they fail for the same reason as your build, it means the tests or the infrastructure are broken. If they do not fail, but yours do, it means it is related to your code.

Reference tests:

  * https://github.com/uyuni-project/uyuni/actions/workflows/acceptance_tests_secondary_parallel.yml?query=event%3Aschedule

  * https://github.com/uyuni-project/uyuni/actions/workflows/acceptance_tests_secondary.yml?query=event%3Aschedule

KNOWN ISSUES

Sometimes the build can fail when pulling new jar files from download.opensuse.org . This is a known limitation. Given this happens rarely, when it does, all you need to do is rerun the test. Sorry for the inconvenience.


For more tips on troubleshooting, see the [troubleshooting guide](https://github.com/uyuni-project/uyuni/wiki/Running-Acceptance-Tests-at-PR#troubleshooting).

Happy hacking!
:warning: You should not merge if acceptance tests fail to pass. :warning:
author_association: CONTRIBUTOR
user: login: deneb-alpha
created_at: 2025-05-09T19:32:47Z
body: @mcalmer please, let me know if  you want  to have additional reviews after the force push
author_association: CONTRIBUTOR
user: login: mcalmer
created_at: 2025-05-10T07:18:10Z
body: > @mcalmer please, let me know if you want to have additional reviews after the force push

I think additional reviews are not needed.
Do you plan to merge this for RC? I thought this would go for GMC.

author_association: CONTRIBUTOR
user: login: mcalmer
created_at: 2025-05-12T14:33:08Z
body: @deneb-alpha but if you look for something which should still go into RC, feel free to merge every fixed bug help :-)
author_association: CONTRIBUTOR