Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issv3 align custom channels at sync #9701

Draft
wants to merge 8 commits into
base: issv3
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import com.redhat.rhn.taskomatic.task.payg.beans.PaygProductInfo;

import com.suse.cloud.CloudPaygManager;
import com.suse.manager.model.hub.IssHub;
import com.suse.manager.webui.services.pillar.MinionGeneralPillarGenerator;
import com.suse.mgrsync.MgrSyncStatus;
import com.suse.salt.netapi.parser.JsonParser;
Expand Down Expand Up @@ -868,11 +869,12 @@ public void refreshRepositoriesAuthentication(
List<Long> availableRepoIds = SCCCachingFactory.lookupRepositories().stream()
.map(SCCRepository::getSccId)
.toList();
List<SCCRepositoryJson> ptfRepos = repositories.stream()
Map<Boolean, List<SCCRepositoryJson>> ptfOrCustomRepos = repositories.stream()
.filter(r -> !availableRepoIds.contains(r.getSCCId()))
.filter(SCCRepositoryJson::isPtfRepository)
.toList();
generatePtfChannels(ptfRepos);
.collect(Collectors.groupingBy(SCCRepositoryJson::isPtfRepository,
Collectors.toList()));

generatePtfChannels(ptfOrCustomRepos.getOrDefault(Boolean.TRUE, Collections.emptyList()));
Map<Long, SCCRepository> availableReposById = SCCCachingFactory.lookupRepositories().stream()
.collect(Collectors.toMap(SCCRepository::getSccId, r -> r));

Expand Down Expand Up @@ -979,6 +981,11 @@ public void refreshRepositoriesAuthentication(
source.getCredentials(SCCCredentials.class)
.ifPresent(scc -> repoIdsFromCredential.addAll(refreshOESRepositoryAuth(scc, mirrorUrl, oesRepos)));

// Custom Channels
source.getCredentials(SCCCredentials.class)
.ifPresent(scc -> refreshCustomRepoAuthentication(
ptfOrCustomRepos.getOrDefault(Boolean.FALSE, Collections.emptyList()), scc));

//DELETE OLD
// check if we have to remove auths which exists before
List<SCCRepositoryAuth> authList = SCCCachingFactory.lookupRepositoryAuthByCredential(source);
Expand All @@ -991,6 +998,47 @@ 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;
}
boolean metadataSigned = !StringUtils.isBlank(issHub.getGpgKey());
for (SCCRepositoryJson repo : customReposIn) {
Channel customChannel = ChannelFactory.lookupByLabel(repo.getName());
if (!repo.getSCCId().equals(-42L) || customChannel == null) {
continue;
}
Set<ContentSource> css = customChannel.getSources();
if (css.isEmpty()) {
// new channel; need to add the source
ContentSource source = new ContentSource();
source.setLabel(customChannel.getLabel());
source.setOrg(customChannel.getOrg());
source.setSourceUrl(repo.getUrl());
source.setType(ChannelManager.findCompatibleContentSourceType(customChannel.getChannelArch()));
source.setMetadataSigned(metadataSigned);
ChannelFactory.save(source);

css.add(source);
customChannel.setSources(css);
ChannelFactory.save(customChannel);
}
else if (css.size() == 1) {
// found the repo; update the URL
ContentSource source = css.iterator().next();
source.setSourceUrl(repo.getUrl());
source.setMetadataSigned(metadataSigned);
ChannelFactory.save(source);
}
else {
LOG.error("Multiple repositories not allowed for this custom channel {}. Skipping",
customChannel.getName());
}
}
}

private void generatePtfChannels(List<SCCRepositoryJson> repositories) {
List<SCCRepository> reposToSave = new ArrayList<>();
List<ChannelTemplate> templatesToSave = new ArrayList<>();
Expand Down
12 changes: 12 additions & 0 deletions java/code/src/com/redhat/rhn/taskomatic/TaskomaticApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -858,4 +858,16 @@ public void scheduleSingleRootCaCertUpdate(Map<String, String> filenameToRootCaC
paramList.put("filename_to_root_ca_cert_map", sanitisedFilenameToRootCaCertMap);
invoke("tasko.scheduleSingleSatBunchRun", "root-ca-cert-update-bunch", paramList);
}

/**
* Schedule an import of a GPG key.
* @param gpgKey the GPG key (armored text)
* @throws TaskomaticApiException if there was an error
*/
public void scheduleSingleGpgKeyImport(String gpgKey) throws TaskomaticApiException {
if (StringUtils.isBlank(gpgKey)) {
return;
}
invoke("tasko.scheduleSingleSatBunchRun", "custom-gpg-key-import-bunch", Map.of("gpg-key", gpgKey));
}
}
47 changes: 47 additions & 0 deletions java/code/src/com/redhat/rhn/taskomatic/task/GpgImportTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 SUSE LLC
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*/
package com.redhat.rhn.taskomatic.task;

import com.suse.utils.CertificateUtils;

import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
* Taskomatic task that import a GPG keys to the customer keyring
* After saving the GPG key, the system configuration is refreshed.
*/
public class GpgImportTask extends RhnJavaJob {

@Override
public String getConfigNamespace() {
return "gpg-update";
}

/**
* {@inheritDoc}
*/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
final JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
if (!jobDataMap.containsKey("gpg-key")) {
log.error("No GPG key provided");
return;
}
try {
CertificateUtils.importGpgKey((String)jobDataMap.get("gpg-key"));
}
catch (Exception e) {
log.error("Importing the GPG key failed", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ public DefaultHubInternalClient(String remoteHostIn, String tokenIn, Optional<Ce
}

@Override
public void registerHub(String token, String rootCA) throws IOException {
invokePostMethod("hub/sync", "registerHub", new RegisterJson(token, rootCA), Void.class);
public void registerHub(String token, String rootCA, String gpgKey) throws IOException {
invokePostMethod("hub/sync", "registerHub", new RegisterJson(token, rootCA, gpgKey), Void.class);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion java/code/src/com/suse/manager/hub/HubController.java
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ private String registerHub(Request request, Response response, IssAccessToken to

try {
hubManager.storeAccessToken(token, tokenToStore);
hubManager.saveNewServer(token, IssRole.HUB, registerRequest.getRootCA());
hubManager.saveNewServer(token, IssRole.HUB, registerRequest.getRootCA(), registerRequest.getGpgKey());

return success(response);
}
Expand Down
3 changes: 2 additions & 1 deletion java/code/src/com/suse/manager/hub/HubInternalClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ public interface HubInternalClient {
* Register a remote server as a hub
* @param token the token issued by the remote server to grant access
* @param rootCA the root certificate, if needed
* @param gpgKey the gpg key, if needed
* @throws IOException when the communication fails
*/
void registerHub(String token, String rootCA) throws IOException;
void registerHub(String token, String rootCA, String gpgKey) throws IOException;

/**
* Store the SCC credentials on the remote peripheral server
Expand Down
31 changes: 26 additions & 5 deletions java/code/src/com/suse/manager/hub/HubManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,28 @@ public IssServer findServer(IssAccessToken accessToken, IssRole role) {
* @param accessToken the access token granting access and identifying the caller
* @param role the role of the server
* @param rootCA the root certificate, if needed
* @param gpgKey the gpg key, if needed
* @return the persisted remote server
*/
public IssServer saveNewServer(IssAccessToken accessToken, IssRole role, String rootCA)
public IssServer saveNewServer(IssAccessToken accessToken, IssRole role, String rootCA, String gpgKey)
throws TaskomaticApiException {
ensureValidToken(accessToken);

return createServer(role, accessToken.getServerFqdn(), rootCA, null);
return createServer(role, accessToken.getServerFqdn(), rootCA, gpgKey, null);
}

public void deletePeripheral(User user, String peripheralFqdn) {
ensureSatAdmin(user);

Optional<IssPeripheral> issPeripheral = hubFactory.lookupIssPeripheralByFqdn(peripheralFqdn);
if (issPeripheral.isEmpty()) {
LOG.info("Peripheral Server with name {} not found", peripheralFqdn);
return; // no error as the state is already as wanted.
}
IssPeripheral peripheral = issPeripheral.get();
CredentialsFactory.removeCredentials(peripheral.getMirrorCredentials());
hubFactory.remove(peripheral);
hubFactory.removeAccessTokensFor(peripheralFqdn);
}

/**
Expand Down Expand Up @@ -354,7 +369,7 @@ private void registerWithToken(User user, String remoteServer, String rootCA, St
TaskomaticApiException {
parseAndSaveToken(remoteServer, remoteToken);

IssServer registeredServer = createServer(IssRole.PERIPHERAL, remoteServer, rootCA, user);
IssServer registeredServer = createServer(IssRole.PERIPHERAL, remoteServer, rootCA, null, user);
registerToRemote(user, registeredServer, remoteToken, rootCA);
}

Expand All @@ -373,9 +388,12 @@ private void registerToRemote(User user, IssServer remoteServer, String remoteTo
Token localAccessToken = createAndSaveToken(remoteServer.getFqdn());
// Send the local trusted root, if we needed a different certificate to connect
String localRootCA = rootCA != null ? CertificateUtils.loadLocalTrustedRoot() : null;
// Send the local GPG key used to sign metadata, if configured.
// This force metadata checking on the peripheral server when mirroring from the Hub
String localGpgKey = (ConfigDefaults.get().isMetadataSigningEnabled()) ? CertificateUtils.loadGpgKey() : null;

// Register this server on the remote with the hub role
internalApi.registerHub(localAccessToken.getSerializedForm(), localRootCA);
internalApi.registerHub(localAccessToken.getSerializedForm(), localRootCA, localGpgKey);

// Generate the scc credentials and send them to the peripheral
HubSCCCredentials credentials = generateCredentials(peripheral);
Expand Down Expand Up @@ -467,13 +485,15 @@ private static String computeRootCaFileName(IssRole role, String serverFqdn) {
return String.format(ROOT_CA_FILENAME_TEMPLATE, role.getLabel(), serverFqdn);
}

private IssServer createServer(IssRole role, String serverFqdn, String rootCA, User user)
private IssServer createServer(IssRole role, String serverFqdn, String rootCA, String gpgKey, User user)
throws TaskomaticApiException {
taskomaticApi.scheduleSingleRootCaCertUpdate(computeRootCaFileName(role, serverFqdn), rootCA);
return switch (role) {
case HUB -> {
IssHub hub = new IssHub(serverFqdn, rootCA);
hub.setGpgKey(gpgKey);
hubFactory.save(hub);
taskomaticApi.scheduleSingleGpgKeyImport(gpgKey);
yield hub;
}
case PERIPHERAL -> {
Expand Down Expand Up @@ -577,4 +597,5 @@ private Server getOrCreateManagerSystem(
systemEntitlementManagerIn.setBaseEntitlement(server, EntitlementManager.FOREIGN);
return server;
}

}
Loading
Loading