Skip to content

Commit

Permalink
several fixes and unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rjpmestre committed Feb 21, 2025
1 parent a55417d commit 01af614
Show file tree
Hide file tree
Showing 42 changed files with 3,425 additions and 959 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,12 @@
<context context-type="sourcefile">Navigation Menu</context>
</context-group>
</trans-unit>
<trans-unit id="Clients" xml:space="preserve">
<source>Clients</source>
<context-group name="ctx">
<context context-type="sourcefile">Navigation Menu</context>
</context-group>
</trans-unit>
<trans-unit id="Activation" xml:space="preserve">
<source>Activation</source>
<context-group name="ctx">
Expand Down
913 changes: 522 additions & 391 deletions java/code/src/com/redhat/rhn/manager/system/SystemManager.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2025 SUSE LLC
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/

package com.redhat.rhn.manager.system.proxycontainerconfig;

import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.manager.system.entitling.SystemEntitlementManager;

import com.suse.manager.ssl.SSLCertData;
import com.suse.manager.ssl.SSLCertManager;
import com.suse.manager.ssl.SSLCertPair;
import com.suse.manager.webui.services.iface.SaltApi;

import java.util.List;
import java.util.Map;

public interface ProxyContainerConfigCreateFacade {

/**
* Create and provide proxy container configuration.
*
* @param saltApi the Salt API instance
* @param systemEntitlementManager the system entitlement manager instance
* @param user the current user
* @param serverFqdn the FQDN of the server the proxy uses
* @param proxyFqdn the FQDN of the proxy
* @param proxyPort the SSH port the proxy listens on
* @param maxCache the maximum memory cache size
* @param email the email of proxy admin
* @param rootCA root CA used to sign the SSL certificate in PEM format
* @param intermediateCAs intermediate CAs used to sign the SSL certificate in PEM format
* @param proxyCertKey proxy CRT and key pair
* @param caPair the CA certificate and key used to sign the certificate to generate.
* Can be omitted if proxyCertKey is not provided
* @param caPassword the CA private key password.
* Can be omitted if proxyCertKey is not provided
* @param certData the data needed to generate the new proxy SSL certificate.
* Can be omitted if proxyCertKey is not provided
* @param certManager the SSLCertManager to use
* @return the tarball configuration file as a byte array
*/
byte[] create(
SaltApi saltApi, SystemEntitlementManager systemEntitlementManager, User user,
String serverFqdn, String proxyFqdn, Integer proxyPort, Long maxCache, String email,
String rootCA, List<String> intermediateCAs, SSLCertPair proxyCertKey,
SSLCertPair caPair, String caPassword, SSLCertData certData, SSLCertManager certManager
);

/**
* Create and provide proxy container configuration files.
*
* @param saltApi the Salt API instance
* @param systemEntitlementManager the system entitlement manager instance
* @param user the current user
* @param serverFqdn the FQDN of the server the proxy uses
* @param proxyFqdn the FQDN of the proxy
* @param proxyPort the SSH port the proxy listens on
* @param maxCache the maximum memory cache size
* @param email the email of proxy admin
* @param rootCA root CA used to sign the SSL certificate in PEM format
* @param intermediateCAs intermediate CAs used to sign the SSL certificate in PEM format
* @param proxyCertKey proxy CRT and key pair
* @param caPair the CA certificate and key used to sign the certificate to generate.
* Can be omitted if proxyCertKey is not provided
* @param caPassword the CA private key password.
* Can be omitted if proxyCertKey is not provided
* @param certData the data needed to generate the new proxy SSL certificate.
* Can be omitted if proxyCertKey is not provided
* @param certManager the SSLCertManager to use
* @return the configuration files as a map
*/
Map<String, Object> createFiles(
SaltApi saltApi, SystemEntitlementManager systemEntitlementManager, User user,
String serverFqdn, String proxyFqdn, Integer proxyPort, Long maxCache, String email,
String rootCA, List<String> intermediateCAs, SSLCertPair proxyCertKey,
SSLCertPair caPair, String caPassword, SSLCertData certData, SSLCertManager certManager
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,23 @@
/**
* Main handler for creating proxy container configuration files.
* Three files should be created and compressed into a tarball:
* - config.yaml : yaml configuration file that contains the server FQDN, max cache size, email, server version,
* proxy FQDN, and root CA
* - httpd.yaml : yaml file containing the system ID, server certificate, and server key
* - ssh.yaml : yaml file containing the server SSH public key, proxy SSH key, and proxy SSH public key
*
* - config.yaml : yaml configuration file that contains the server FQDN, max cache size, email, server version,
* proxy FQDN, and root CA
* - httpd.yaml : yaml file containing the system ID, server certificate, and server key
* - ssh.yaml : yaml file containing the server SSH public key, proxy SSH key, and proxy SSH public key
* <p>
* This flow is divided into three main steps:
* - Acquire and validate all necessary data
* - Compute contents for files
* - Create tar archive with all necessary files
*
*/
public class ProxyContainerConfigCreate {
public class ProxyContainerConfigCreateFacadeImpl implements ProxyContainerConfigCreateFacade {
private final List<ProxyContainerConfigCreateContextHandler> contextHandlerChain = new ArrayList<>();

/**
* Constructor
*/
public ProxyContainerConfigCreate() {
public ProxyContainerConfigCreateFacadeImpl() {
this.contextHandlerChain.addAll(asList(
new ProxyContainerConfigCreateAcquisitor(),
new ProxyContainerConfigCreateGenerateFileMaps(),
Expand Down Expand Up @@ -81,6 +80,7 @@ public ProxyContainerConfigCreate() {
* @param certManager the SSLCertManager to use
* @return the tarball configuration file as a byte array
*/
@Override
public byte[] create(
SaltApi saltApi, SystemEntitlementManager systemEntitlementManager, User user,
String serverFqdn, String proxyFqdn, Integer proxyPort, Long maxCache, String email,
Expand Down Expand Up @@ -120,6 +120,7 @@ public byte[] create(
* @param certManager the SSLCertManager to use
* @return the configuration files as a map
*/
@Override
public Map<String, Object> createFiles(
SaltApi saltApi, SystemEntitlementManager systemEntitlementManager, User user,
String serverFqdn, String proxyFqdn, Integer proxyPort, Long maxCache, String email,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,37 +26,26 @@
import static spark.Spark.get;
import static spark.Spark.post;

import com.redhat.rhn.GlobalInstanceHolder;
import com.redhat.rhn.common.RhnGeneralException;
import com.redhat.rhn.common.RhnRuntimeException;
import com.redhat.rhn.common.conf.Config;
import com.redhat.rhn.common.conf.ConfigDefaults;
import com.redhat.rhn.common.db.datasource.DataResult;
import com.redhat.rhn.common.validator.ValidatorResult;
import com.redhat.rhn.domain.server.Server;
import com.redhat.rhn.domain.server.ServerFactory;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.frontend.dto.ShortSystemInfo;
import com.redhat.rhn.manager.entitlement.EntitlementManager;
import com.redhat.rhn.manager.system.SystemManager;
import com.redhat.rhn.manager.system.entitling.SystemEntitlementManager;

import com.suse.manager.api.ParseException;
import com.suse.manager.reactor.utils.LocalDateTimeISOAdapter;
import com.suse.manager.reactor.utils.OptionalTypeAdapterFactory;
import com.suse.manager.utils.MinionServerUtils;
import com.suse.manager.webui.services.iface.SaltApi;
import com.suse.manager.webui.utils.gson.ProxyConfigUpdateJson;
import com.suse.manager.webui.utils.gson.ResultJson;
import com.suse.manager.webui.utils.gson.SimpleMinionJson;
import com.suse.proxy.ProxyConfigUtils;
import com.suse.proxy.ProxyContainerImagesEnum;
import com.suse.proxy.ProxyRegistryUtils;
import com.suse.proxy.ProxyRegistryUtilsImpl;
import com.suse.proxy.RegistryUrl;
import com.suse.proxy.get.ProxyConfigGet;
import com.suse.proxy.update.ProxyConfigUpdate;
import com.suse.utils.Json;
import com.suse.proxy.get.ProxyConfigGetFacade;
import com.suse.proxy.get.ProxyConfigGetFacadeImpl;
import com.suse.proxy.update.ProxyConfigUpdateFacade;
import com.suse.proxy.update.ProxyConfigUpdateFacadeImpl;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
Expand All @@ -69,14 +58,10 @@
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import spark.ModelAndView;
import spark.Request;
Expand All @@ -95,8 +80,9 @@ public class ProxyConfigurationController {

private final SystemManager systemManager;
private final SaltApi saltApi;
private SystemEntitlementManager systemEntitlementManager = GlobalInstanceHolder.SYSTEM_ENTITLEMENT_MANAGER;
private ProxyRegistryUtils proxyRegistryUtils = new ProxyRegistryUtilsImpl();
private final ProxyRegistryUtils proxyRegistryUtils;
private final ProxyConfigUpdateFacade proxyConfigUpdateFacade;
private final ProxyConfigGetFacade proxyConfigGetFacade;

private static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeISOAdapter())
Expand All @@ -105,14 +91,42 @@ public class ProxyConfigurationController {
.create();

/**
* Create a new controller instance
* Create a new controller instance with default facades implementations
*
* @param systemManagerIn the system manager
* @param saltApiIn the salt API
*/
public ProxyConfigurationController(SystemManager systemManagerIn, SaltApi saltApiIn) {
this(
systemManagerIn,
saltApiIn,
new ProxyRegistryUtilsImpl(),
new ProxyConfigUpdateFacadeImpl(),
new ProxyConfigGetFacadeImpl()
);
}

/**
* Create a new controller instance
*
* @param systemManagerIn the system manager
* @param saltApiIn the salt API
* @param proxyRegistryUtilsIn the proxy registry utils
* @param proxyConfigUpdateFacadeIn the proxy config update facade
* @param proxyConfigGetFacadeIn the proxy config get facade
*/
public ProxyConfigurationController(
SystemManager systemManagerIn,
SaltApi saltApiIn,
ProxyRegistryUtils proxyRegistryUtilsIn,
ProxyConfigUpdateFacade proxyConfigUpdateFacadeIn,
ProxyConfigGetFacade proxyConfigGetFacadeIn
) {
this.systemManager = systemManagerIn;
this.saltApi = saltApiIn;
this.proxyRegistryUtils = proxyRegistryUtilsIn;
this.proxyConfigUpdateFacade = proxyConfigUpdateFacadeIn;
this.proxyConfigGetFacade = proxyConfigGetFacadeIn;
}

/**
Expand All @@ -123,7 +137,7 @@ public ProxyConfigurationController(SystemManager systemManagerIn, SaltApi saltA
*/
public void initRoutes(ProxyConfigurationController proxyController, JadeTemplateEngine jade) {
get("/manager/systems/details/proxy-config",
withCsrfToken(withDocsLocale(withUserAndServer(this::proxyConfig))),
withCsrfToken(withDocsLocale(withUserAndServer(this::getProxyConfiguration))),
jade
);
post("/manager/systems/details/proxy-config", withUser(this::updateProxyConfiguration));
Expand All @@ -139,70 +153,8 @@ public void initRoutes(ProxyConfigurationController proxyController, JadeTemplat
* @param server the current server
* @return the ModelAndView object to render the page
*/
public ModelAndView proxyConfig(Request request, Response response, User user, Server server) {
Map<String, Object> data = new HashMap<>();

// Handle the "Convert to Proxy" button: if server is convertible to proxy and doesn't have the proxy
// entitlement, add it
if (server.isConvertibleToProxy() && !server.hasProxyEntitlement()) {
user.getOrg().getValidAddOnEntitlementsForOrg().stream()
.filter(e -> e.getLabel().equalsIgnoreCase(EntitlementManager.PROXY_ENTITLED))
.findFirst()
.ifPresentOrElse(f -> {
ValidatorResult vr = systemEntitlementManager.addEntitlementToServer(server, f);
if (!vr.getErrors().isEmpty()) {
LOG.error("Failed to add proxy entitlement to server. Server ID: {}, " +
"hasProxyEntitlement: {}, isConvertibleToProxy: {}, errors: {}",
server.getId(),
server.hasProxyEntitlement(),
server.isConvertibleToProxy(),
vr.getErrors()
);
data.put("initFailMessage", "Failed to automatically add proxy entitlement to server.");
}
}, () -> {
});
}

Map<String, Object> proxyConfigDataMap =
ProxyConfigUtils.safeDataMapFromProxyConfig(new ProxyConfigGet().get(server));
data.put("currentConfig", Json.GSON.toJson(proxyConfigDataMap));
data.put("parents", Json.GSON.toJson(getElectableParents(user)));
data.put("isUyuni", ConfigDefaults.get().isUyuni());
return new ModelAndView(data, "templates/minion/proxy-config.jade");
}

/**
* Get the list of electable parents to use as parent.
*
* @param user the current user
* @return the list of electable parents
*/
public List<SimpleMinionJson> getElectableParents(User user) {
DataResult<ShortSystemInfo> dr = SystemManager.systemListShort(user, null);
dr.elaborate();
Set<Long> systems = Arrays.stream(dr.toArray())
.map(system -> ((ShortSystemInfo) system).getId()).
collect(Collectors.toSet());

List<Server> electableParentsServers =
ServerFactory.lookupByIdsAndOrg(systems, user.getOrg()).stream().filter(Server::isProxy).toList();
List<SimpleMinionJson> electableParentsAsSimpleMinionJson = new ArrayList<>(
MinionServerUtils.filterSaltMinions(electableParentsServers)
.map(SimpleMinionJson::fromMinionServer)
.toList()
);

String localManagerFqdn = Config.get().getString(ConfigDefaults.SERVER_HOSTNAME);
if (isAbsent(localManagerFqdn)) {
if (LOG.isErrorEnabled()) {
LOG.error("Could not determine the Server FQDN. Skipping it as a parent.");
}
return electableParentsAsSimpleMinionJson;
}

electableParentsAsSimpleMinionJson.add(new SimpleMinionJson(-1L, localManagerFqdn));
return electableParentsAsSimpleMinionJson;
public ModelAndView getProxyConfiguration(Request request, Response response, User user, Server server) {
return new ModelAndView(proxyConfigGetFacade.getFormData(user, server), "templates/minion/proxy-config.jade");
}

/**
Expand Down Expand Up @@ -330,7 +282,7 @@ public String updateProxyConfiguration(Request request, Response response, User
GSON.fromJson(request.body(), new TypeToken<ProxyConfigUpdateJson>() { }.getType());

try {
new ProxyConfigUpdate().update(data, systemManager, saltApi, user);
proxyConfigUpdateFacade.update(data, systemManager, saltApi, user);
return result(response, ResultJson.success("Proxy configuration applied"));
}
catch (RhnRuntimeException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public class SaltServerActionService {
public static final String APPSTREAMS_CONFIGURE = "appstreams.configure";
public static final String PARAM_APPSTREAMS_ENABLE = "param_appstreams_enable";
public static final String PARAM_APPSTREAMS_DISABLE = "param_appstreams_disable";
public static final String APPLY_PROXY_CONFIG = "apply_proxy_config";
public static final String APPLY_PROXY_CONFIG = "proxy.apply_proxy_config";

/** SLS pillar parameter name for the list of update stack patch names. */
public static final String PARAM_UPDATE_STACK_PATCHES = "param_update_stack_patches";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import java.util.List;

/**
* Represents the data sent from the UI to convert a minion into a proxy container configuration
* Represents the data received from the UI to convert a minion into a proxy container configuration
*/
public class ProxyConfigUpdateJson {

Expand Down
Loading

0 comments on commit 01af614

Please sign in to comment.