Skip to content

Commit

Permalink
CB-21069 Install SafeLogic modules
Browse files Browse the repository at this point in the history
SafeLogic modules' paths are configured by properties to not expose their URLs.
These modules had to be installed with a custom script because salt's file.present could not follow the redirects. Related issue: saltstack/salt#63212
  • Loading branch information
Bajzathd authored and lajosrodek committed Mar 23, 2023
1 parent 2c0a5bd commit be4d7cd
Show file tree
Hide file tree
Showing 8 changed files with 292 additions and 37 deletions.
Expand Up @@ -30,7 +30,7 @@ public Variant variant() {
public enum AwsVariant {
AWS_VARIANT(CloudConstants.AWS),
AWS_NATIVE_GOV_VARIANT(CloudConstants.AWS_NATIVE_GOV),
AWS_NATIVE_VARIANT("AWS_NATIVE");
AWS_NATIVE_VARIANT(CloudConstants.AWS_NATIVE);

private final Variant variant;

Expand Down
Expand Up @@ -22,6 +22,7 @@
import com.sequenceiq.cloudbreak.common.domain.IdAware;
import com.sequenceiq.cloudbreak.common.orchestration.Node;
import com.sequenceiq.cloudbreak.common.orchestration.OrchestratorAware;
import com.sequenceiq.cloudbreak.common.type.CloudConstants;
import com.sequenceiq.cloudbreak.domain.Blueprint;
import com.sequenceiq.cloudbreak.domain.FileSystem;
import com.sequenceiq.cloudbreak.domain.Network;
Expand Down Expand Up @@ -425,4 +426,8 @@ public boolean isStackInStopPhase() {
return STOP_IN_PROGRESS.equals(getStatus()) || STOPPED.equals(getStatus());
}

public boolean isOnGovPlatformVariant() {
return CloudConstants.AWS_NATIVE_GOV.equals(getPlatformVariant());
}

}
Expand Up @@ -57,7 +57,6 @@
import com.sequenceiq.cloudbreak.auth.altus.VirtualGroupService;
import com.sequenceiq.cloudbreak.auth.crn.Crn;
import com.sequenceiq.cloudbreak.auth.crn.RegionAwareInternalCrnGeneratorFactory;
import com.sequenceiq.cloudbreak.cloud.aws.common.AwsConstants;
import com.sequenceiq.cloudbreak.cloud.model.ClouderaManagerRepo;
import com.sequenceiq.cloudbreak.cloud.model.StackTags;
import com.sequenceiq.cloudbreak.cloud.scheduler.CancellationException;
Expand All @@ -75,6 +74,7 @@
import com.sequenceiq.cloudbreak.core.bootstrap.service.container.postgres.PostgresConfigService;
import com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator.CsdParcelDecorator;
import com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator.HostAttributeDecorator;
import com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator.JavaPillarDecorator;
import com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator.NameserverPillarDecorator;
import com.sequenceiq.cloudbreak.domain.stack.DnsResolverType;
import com.sequenceiq.cloudbreak.domain.stack.cluster.IdBroker;
Expand Down Expand Up @@ -256,6 +256,9 @@ public class ClusterHostServiceRunner {
@Inject
private RdsViewProvider rdsViewProvider;

@Inject
private JavaPillarDecorator javaPillarDecorator;

public NodeReachabilityResult runClusterServices(@Nonnull StackDto stackDto,
Map<String, String> candidateAddresses, boolean runPreServiceDeploymentRecipe) {
try {
Expand Down Expand Up @@ -453,7 +456,7 @@ private SaltConfig createSaltConfig(StackDto stackDto, List<GrainProperties> gra
Map<String, ? extends Serializable> clusterProperties = Map.of(
"name", stackDto.getCluster().getName(),
"deployedInChildEnvironment", deployedInChildEnvironment,
"gov_cloud", govCluster(stackDto.getPlatformVariant()));
"gov_cloud", stackDto.isOnGovPlatformVariant());
servicePillar.put("metadata", new SaltPillarProperties("/metadata/init.sls", singletonMap("cluster", clusterProperties)));
ClusterPreCreationApi connector = clusterApiConnectors.getConnector(cluster);
Map<String, List<String>> serviceLocations = getServiceLocations(stackDto);
Expand All @@ -463,7 +466,7 @@ private SaltConfig createSaltConfig(StackDto stackDto, List<GrainProperties> gra
clouderaManagerRepo));
saveIdBrokerPillar(cluster, servicePillar);
postgresConfigService.decorateServicePillarWithPostgresIfNeeded(servicePillar, stackDto);
servicePillar.putAll(createJavaPillar(stack.getJavaVersion()));
javaPillarDecorator.decorateWithJavaProperties(stackDto, servicePillar);

addClouderaManagerConfig(stackDto, servicePillar, clouderaManagerRepo, primaryGatewayConfig);
ldapView.ifPresent(ldap -> saveLdapPillar(ldap, servicePillar));
Expand Down Expand Up @@ -508,7 +511,7 @@ private SaltConfig createSaltConfigWithGatewayPillarOnly(StackDto stackDto, List
Map<String, SaltPillarProperties> servicePillar =
new HashMap<>(createGatewayPillar(primaryGatewayConfig, stackDto, virtualGroupRequest, connector, kerberosConfig, serviceLocations,
clouderaManagerRepo));
servicePillar.putAll(createJavaPillar(stack.getJavaVersion()));
javaPillarDecorator.decorateWithJavaProperties(stackDto, servicePillar);

return new SaltConfig(servicePillar, grainsProperties);
}
Expand Down Expand Up @@ -677,13 +680,6 @@ private boolean isCloudProviderSetupSupported(StackDto stackDto, String cmVersio
return supportedCloudProviders.contains(stackDto.getCloudPlatform()) && isVersionNewerOrEqualThanLimited(cmVersion, CLOUDERAMANAGER_VERSION_7_6_2);
}

private boolean govCluster(String platformVariant) {
if (platformVariant.equals(AwsConstants.AwsVariant.AWS_NATIVE_GOV_VARIANT.variant().value())) {
return true;
}
return false;
}

private void decoratePillarWithTags(StackView stack, Map<String, SaltPillarProperties> servicePillarConfig) {
if (stack.getTags() != null && isNotBlank(stack.getTags().getValue())) {
try {
Expand Down Expand Up @@ -994,19 +990,6 @@ private OrchestratorGrainRunnerParams createOrchestratorGrainRunnerParams(StackD
return grainRunnerParams;
}

private Map<String, SaltPillarProperties> createJavaPillar(Integer javaVersion) {
if (javaVersion != null) {
LOGGER.debug("Creating java pillar with version {}", javaVersion);
Map<String, Object> config = new HashMap<>();
config.put("version", javaVersion);

return Map.of("java", new SaltPillarProperties("/java/init.sls", singletonMap("java", config)));
} else {
LOGGER.debug("Skip java pillar as the version is not specified");
}
return Collections.emptyMap();
}

public void createCronForUserHomeCreation(StackDto stackDto, Set<String> candidateHostNames) throws CloudbreakException {
Set<String> reachableTargets = stackUtil.collectReachableAndUnreachableCandidateNodes(stackDto, candidateHostNames).getReachableHosts();
ExitCriteriaModel exitModel = ClusterDeletionBasedExitCriteriaModel.clusterDeletionBasedModel(stackDto.getId(), stackDto.getCluster().getId());
Expand Down
@@ -0,0 +1,71 @@
package com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator;

import static java.util.Collections.singletonMap;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.sequenceiq.cloudbreak.dto.StackDto;
import com.sequenceiq.cloudbreak.orchestrator.model.SaltPillarProperties;

@Component
public class JavaPillarDecorator {

private static final Logger LOGGER = LoggerFactory.getLogger(JavaPillarDecorator.class);

@Value("${cb.safelogic.cryptocomply.path:}")
private String cryptoComplyPath;

@Value("${cb.safelogic.cryptocomply.hash:}")
private String cryptoComplyHash;

@Value("${cb.safelogic.bouncycastletls.path:}")
private String bouncyCastleTlsPath;

@Value("${cb.safelogic.bouncycastletls.hash:}")
private String bouncyCastleTlsHash;

public void decorateWithJavaProperties(StackDto stackDto, Map<String, SaltPillarProperties> servicePillar) {
Map<String, Object> config = new HashMap<>();
addVersion(stackDto, config);
addSafeLogicProperties(stackDto, config);
servicePillar.put("java", new SaltPillarProperties("/java/init.sls", singletonMap("java", config)));
}

private void addVersion(StackDto stackDto, Map<String, Object> config) {
Integer javaVersion = stackDto.getStack().getJavaVersion();
if (javaVersion != null) {
LOGGER.debug("Creating java pillar with version {}", javaVersion);
config.put("version", javaVersion);
} else {
LOGGER.debug("Skip java version pillar as the version is not specified");
}
}

private void addSafeLogicProperties(StackDto stackDto, Map<String, Object> config) {
if (stackDto.isOnGovPlatformVariant()) {
LOGGER.debug("Adding SafeLogic properties");
Map<String, Object> safeLogicProperties = new HashMap<>();
addSafeLogicProperty(safeLogicProperties, "cryptoComplyPath", cryptoComplyPath);
addSafeLogicProperty(safeLogicProperties, "cryptoComplyHash", cryptoComplyHash);
addSafeLogicProperty(safeLogicProperties, "bouncyCastleTlsPath", bouncyCastleTlsPath);
addSafeLogicProperty(safeLogicProperties, "bouncyCastleTlsHash", bouncyCastleTlsHash);
config.put("safelogic", safeLogicProperties);
}
}

private void addSafeLogicProperty(Map<String, Object> config, String name, String value) {
if (StringUtils.isBlank(value)) {
String message = "Required SafeLogic property is blank for application: " + name;
LOGGER.error(message);
throw new IllegalStateException(message);
}
config.put(name, value);
}
}
Expand Up @@ -69,6 +69,7 @@
import com.sequenceiq.cloudbreak.core.bootstrap.service.container.postgres.PostgresConfigService;
import com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator.CsdParcelDecorator;
import com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator.HostAttributeDecorator;
import com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator.JavaPillarDecorator;
import com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator.NameserverPillarDecorator;
import com.sequenceiq.cloudbreak.domain.Blueprint;
import com.sequenceiq.cloudbreak.domain.Template;
Expand Down Expand Up @@ -256,6 +257,9 @@ class ClusterHostServiceRunnerTest {
@Mock
private RdsViewProvider rdsViewProvider;

@Mock
private JavaPillarDecorator javaPillarDecorator;

@BeforeEach
void setUp() {
lenient().when(stack.getCluster()).thenReturn(cluster);
Expand Down Expand Up @@ -548,31 +552,23 @@ void testCreateCronForUserHomeCreation() throws CloudbreakException, CloudbreakO
@Test
void testAddJavaPillarToSaltConfig() throws CloudbreakOrchestratorException {
setupMocksForRunClusterServices();
when(stackView.getJavaVersion()).thenReturn(11);
when(stackView.getPlatformVariant()).thenReturn(AwsConstants.AWS_DEFAULT_VARIANT.value());

underTest.runClusterServices(stack, Collections.emptyMap(), false);
ArgumentCaptor<SaltConfig> saltConfig = ArgumentCaptor.forClass(SaltConfig.class);
verify(hostOrchestrator).runService(any(), any(), saltConfig.capture(), any());

assertEquals(getJavaProperties(saltConfig.getValue()).get("version"), 11);
verify(hostOrchestrator).runService(any(), any(), any(), any());
verify(javaPillarDecorator).decorateWithJavaProperties(eq(stack), any());
}

@Test
@MockitoSettings(strictness = Strictness.LENIENT)
void testAddJavaPillarToRedeployGateway() throws CloudbreakOrchestratorException {
setupMocksForRunClusterServices();
when(stackView.getJavaVersion()).thenReturn(11);

underTest.redeployGatewayPillarOnly(stack);
ArgumentCaptor<SaltConfig> saltConfig = ArgumentCaptor.forClass(SaltConfig.class);
verify(hostOrchestrator).uploadGatewayPillar(any(), allNodesCaptor.capture(), any(), saltConfig.capture());

assertEquals(getJavaProperties(saltConfig.getValue()).get("version"), 11);
}

private Map<String, Object> getJavaProperties(SaltConfig saltConfig) {
return (Map<String, Object>) saltConfig.getServicePillarConfig().get("java").getProperties().get("java");
verify(hostOrchestrator).uploadGatewayPillar(any(), allNodesCaptor.capture(), any(), any());
verify(javaPillarDecorator).decorateWithJavaProperties(eq(stack), any());
}

@Test
Expand Down
@@ -0,0 +1,118 @@
package com.sequenceiq.cloudbreak.core.bootstrap.service.host.decorator;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.when;

import java.util.HashMap;
import java.util.Map;

import org.assertj.core.api.MapAssert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;

import com.sequenceiq.cloudbreak.domain.stack.Stack;
import com.sequenceiq.cloudbreak.dto.StackDto;
import com.sequenceiq.cloudbreak.orchestrator.model.SaltPillarProperties;

@ExtendWith(MockitoExtension.class)
class JavaPillarDecoratorTest {

private static final int JAVA_VERSION = 11;

private JavaPillarDecorator underTest;

@Mock
private StackDto stackDto;

@Mock
private Stack stack;

private Map<String, SaltPillarProperties> servicePillar;

@BeforeEach
void setUp() {
underTest = new JavaPillarDecorator();
ReflectionTestUtils.setField(underTest, "cryptoComplyPath", "ccj-path");
ReflectionTestUtils.setField(underTest, "cryptoComplyHash", "ccj-hash");
ReflectionTestUtils.setField(underTest, "bouncyCastleTlsPath", "bctls-path");
ReflectionTestUtils.setField(underTest, "bouncyCastleTlsHash", "bctls-hash");

when(stackDto.isOnGovPlatformVariant()).thenReturn(true);
when(stackDto.getStack()).thenReturn(stack);
when(stack.getJavaVersion()).thenReturn(JAVA_VERSION);
servicePillar = new HashMap<>();
}

@Test
void noJavaVersion() {
when(stack.getJavaVersion()).thenReturn(null);

underTest.decorateWithJavaProperties(stackDto, servicePillar);

assertThatJavaProperties().doesNotContainKey("version");
}

@Test
void javaVersion() {
underTest.decorateWithJavaProperties(stackDto, servicePillar);

assertThatJavaProperties().containsEntry("version", JAVA_VERSION);
}

@ParameterizedTest
@ValueSource(strings = {"cryptoComplyPath", "cryptoComplyHash", "bouncyCastleTlsPath", "bouncyCastleTlsHash"})
void missingSafeLogicPropertyForNonGovStack(String property) {
when(stackDto.isOnGovPlatformVariant()).thenReturn(false);
ReflectionTestUtils.setField(underTest, property, null);

assertThatCode(() -> underTest.decorateWithJavaProperties(stackDto, servicePillar)).doesNotThrowAnyException();
}

@ParameterizedTest
@ValueSource(strings = {"cryptoComplyPath", "cryptoComplyHash", "bouncyCastleTlsPath", "bouncyCastleTlsHash"})
void missingSafeLogicPropertyForGovStack(String property) {
ReflectionTestUtils.setField(underTest, property, null);

assertThatThrownBy(() -> underTest.decorateWithJavaProperties(stackDto, servicePillar))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Required SafeLogic property is blank for application: " + property);
}

@Test
void noSafeLogicPropertiesForNonGovStack() {
when(stackDto.isOnGovPlatformVariant()).thenReturn(false);

underTest.decorateWithJavaProperties(stackDto, servicePillar);

assertThatJavaProperties().doesNotContainKey("safelogic");
}

@Test
void safeLogicPropertiesForGovStack() {
underTest.decorateWithJavaProperties(stackDto, servicePillar);

assertThatSafeLogicProperties()
.containsEntry("cryptoComplyPath", "ccj-path")
.containsEntry("cryptoComplyHash", "ccj-hash")
.containsEntry("bouncyCastleTlsPath", "bctls-path")
.containsEntry("bouncyCastleTlsHash", "bctls-hash");
}

private MapAssert<String, Object> assertThatJavaProperties() {
return assertThat((Map<String, Object>) servicePillar.get("java").getProperties().get("java"));
}

private MapAssert<String, Object> assertThatSafeLogicProperties() {
Map<String, Object> javaProperties = (Map<String, Object>) servicePillar.get("java").getProperties().get("java");
return assertThat((Map<String, Object>) javaProperties.get("safelogic"));
}

}
24 changes: 24 additions & 0 deletions orchestrator-salt/src/main/resources/salt/salt/java/init.sls
Expand Up @@ -37,3 +37,27 @@ set_dns_negativ_ttl:
- pattern: "#?networkaddress.cache.negative.ttl=.*"
- repl: "networkaddress.cache.negative.ttl=0"
- unless: cat {{ java_home }}/jre/lib/security/java.security | grep ^networkaddress.cache.negative.ttl=0$

{% if salt['pillar.get']('cluster:gov_cloud', False) == True %}

/opt/salt/scripts/install_safelogic_binaries.sh:
file.managed:
- source: salt://java/scripts/install_safelogic_binaries.sh
- makedirs: True
- mode: 700

install_safelogic_binaries:
cmd.run:
- name: /opt/salt/scripts/install_safelogic_binaries.sh 2>&1 | tee -a /var/log/install_safelogic_binaries.log && [[ 0 -eq ${PIPESTATUS[0]} ]] && echo $(date +%Y-%m-%d:%H:%M:%S) >> /var/log/safelogic_binaries_installed || exit ${PIPESTATUS[0]}
- env:
JRE_EXT_PATH: "{{ java_home }}/jre/lib/ext"
PAYWALL_AUTH: "{{ salt['pillar.get']('cloudera-manager:paywall_username') }}:{{ salt['pillar.get']('cloudera-manager:paywall_password') }}"
CCJ_PATH: "{{ salt['pillar.get']('java:safelogic:cryptoComplyPath') }}"
CCJ_HASH_PATH: "{{ salt['pillar.get']('java:safelogic:cryptoComplyHash') }}"
BCTLS_PATH: "{{ salt['pillar.get']('java:safelogic:bouncyCastleTlsPath') }}"
BCTLS_HASH_PATH: "{{ salt['pillar.get']('java:safelogic:bouncyCastleTlsHash') }}"
- require:
- file: /opt/salt/scripts/install_safelogic_binaries.sh
- failhard: True

{% endif %}

0 comments on commit be4d7cd

Please sign in to comment.