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

[TH2-5132] behaviour.permittedToRemoveNamespace option #92

Merged
merged 18 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# infra-mgr
# infra-mgr (2.4.0)
infra-mgr is a component responsible for rolling out schemas from git repository to kubernetes.
It watches for changes in the repositories and deploys changed components to kubernets.
Depending on the schema configuration, it also monitors kubernetes and if it detects external manipulation on deployed component, redeploys them from latest repository version.
Expand Down Expand Up @@ -89,8 +89,22 @@ infra-mgr configuration is given with *config.yml* file that should be on the cl
rabbitmqManagement: rabbitmq-mng-params
# individual ConfigMaps for components to be copied from infra-mgr namespace to schema namespace
# this ConfigMaps will be populated with schema specific data before copying to target namespace


behaviour:
permittedToRemoveNamespace: true
# Has infra-mgr got permission to remove Kubernetes namespace when
# branch is disabled (spec.k8s-propagation: deny) or removed.
# Infra-manager removes Kubernetes namespace when this option is true otherwise
# stops maintenance for the namespace without deleting any resources.
# Maintenance is continued when user enable or create the branch related to namespace: `<prefix><branch name>`
# Default value is `true`
```
##
## For API documentation please refer to
[API Documentation](API.md)
[API Documentation](API.md)

## Changes:

### 2.4.0
+ Added `behaviour.permittedToRemoveNamespace` option
+ Added value format check for secret when user uploads secrets via HTTP API
17 changes: 7 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
id 'org.springframework.boot' version "${springboot_version}"
id 'java'
Expand All @@ -16,7 +19,7 @@ repositories {
mavenCentral()
}

configurations.all {
configurations.configureEach {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
exclude group: 'org.springframework.boot', module: 'logback-classic'
}
Expand Down Expand Up @@ -125,15 +128,9 @@ test {
useJUnitPlatform()
}

compileKotlin {
kotlinOptions {
jvmTarget = "17"
}
}

compileTestKotlin {
kotlinOptions {
jvmTarget = "17"
tasks.withType(KotlinCompile).configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# limitations under the License.
#

release_version = 2.3.7
release_version = 2.4.0

springboot_version = 3.1.0
kotlin_version = 1.8.22
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/com/exactpro/th2/inframgr/Config.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 Exactpro (Exactpro Systems Limited)
* Copyright 2020-2023 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -46,6 +46,8 @@ public class Config {
private String configDir;

// config fields
private BehaviourCfg behaviour;
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved

private GitCfg git;

private RabbitMQConfig rabbitmq;
Expand All @@ -56,6 +58,10 @@ public class Config {

private K8sConfig kubernetes;

public BehaviourCfg getBehaviour() {
return behaviour;
}

public GitCfg getGit() {
return git;
}
Expand All @@ -77,6 +83,10 @@ public K8sConfig getKubernetes() {
return kubernetes;
}

public void setBehaviour(BehaviourCfg behaviour) {
this.behaviour = behaviour;
}

public void setGit(GitCfg git) {
this.git = git;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 Exactpro (Exactpro Systems Limited)
* Copyright 2020-2023 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -69,7 +69,8 @@ public Response getDescriptor(@PathVariable(name = "schema") String schemaName,

String descriptor;
try {
Kubernetes kube = new Kubernetes(Config.getInstance().getKubernetes(), schemaName);
Config config = Config.getInstance();
Kubernetes kube = new Kubernetes(config.getBehaviour(), config.getKubernetes(), schemaName);
RegistryCredentialLookup secretMapper = new RegistryCredentialLookup(kube);
RegistryConnection registryConnection = new RegistryConnection(secretMapper.getCredentials());
DescriptorExtractor descriptorExtractor = new DescriptorExtractor(registryConnection, kube);
Expand All @@ -91,7 +92,7 @@ public Response getDescriptor(@PathVariable(name = "schema") String schemaName,
return null;
}

private static class Response {
public static class Response {
private final String descriptor;

private final String content;
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/exactpro/th2/inframgr/JobController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 Exactpro (Exactpro Systems Limited)
* Copyright 2020-2023 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -59,7 +59,9 @@ public void putSecrets(@PathVariable(name = "schemaName") String schemaName,
throw new NotAcceptableException(BAD_RESOURCE_NAME, "Invalid resource name");
}

try (Kubernetes kube = new Kubernetes(Config.getInstance().getKubernetes(), schemaName)) {
try (Kubernetes kube = new Kubernetes(
Config.getInstance().getBehaviour(), Config.getInstance().getKubernetes(), schemaName
)) {
RepositoryResource resource;
String resourceLabel;
GitterContext ctx = GitterContext.getContext(Config.getInstance().getGit());
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/com/exactpro/th2/inframgr/PodController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 Exactpro (Exactpro Systems Limited)
* Copyright 2020-2023 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,9 +35,9 @@
import static com.exactpro.th2.inframgr.statuswatcher.ResourcePath.annotationFor;

@Controller
public class PodController {

private static final Logger logger = LoggerFactory.getLogger(PodController.class);
public class PodController {
private static final Logger LOGGER = LoggerFactory.getLogger(PodController.class);

private static final String UNKNOWN_ERROR = "UNKNOWN_ERROR";

Expand All @@ -59,23 +59,25 @@ public ResponseEntity<?> deleteResourcePods(
throw new NotAcceptableException(BAD_RESOURCE_NAME, "Invalid schema name");
}

Kubernetes kubernetes = new Kubernetes(Config.getInstance().getKubernetes(), schemaName);
Config config = Config.getInstance();
Kubernetes kubernetes = new Kubernetes(config.getBehaviour(), config.getKubernetes(), schemaName);
for (var resource : statusCache.getResourceDependencyStatuses(schemaName, kind, resourceName)) {
if (resource.getKind().equals(Kubernetes.KIND_POD)) {
try {
kubernetes.deletePodWithName(resource.getName(), force);
} catch (KubernetesClientException e) {
logger.error("Could not delete pod \"{}\"",
LOGGER.error("Could not delete pod \"{}\"",
annotationFor(kubernetes.getNamespaceName(), Kubernetes.KIND_POD, resource.getName()));
}
}
}

return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (ServiceException e) {
LOGGER.error(e.getMessage(), e);
throw e;
} catch (Exception e) {
logger.error("Exception deleting pods for \"{}/{}\" in schema \"{}\"", kind, resourceName, schemaName, e);
LOGGER.error("Exception deleting pods for \"{}/{}\" in schema \"{}\"", kind, resourceName, schemaName, e);
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage());
}
}
Expand Down
28 changes: 26 additions & 2 deletions src/main/java/com/exactpro/th2/inframgr/SecretsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,29 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.kotlin.KotlinModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Base64;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Controller
public class SecretsController {
private static final Logger LOGGER = LoggerFactory.getLogger(SecretsController.class);

private static final String UNKNOWN_ERROR = "UNKNOWN_ERROR";

private static final String BAD_RESOURCE_NAME = "BAD_RESOURCE_NAME";

private static final Base64.Decoder DECODER = Base64.getDecoder();

@GetMapping("/secrets/{schemaName}")
@ResponseBody
public Set<String> getSecrets(@PathVariable(name = "schemaName") String schemaName) throws ServiceException {
Expand All @@ -52,6 +59,7 @@ public Set<String> getSecrets(@PathVariable(name = "schemaName") String schemaNa
Map<String, String> secretData = secretsManager.getCustomSecret(schemaName).getData();
return secretData != null ? secretData.keySet() : Collections.emptySet();
} catch (Exception e) {
LOGGER.error("get secrets failure, schema name: {}", schemaName, e);
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage());
}
}
Expand All @@ -68,16 +76,30 @@ public Set<String> putSecrets(@PathVariable(name = "schemaName") String schemaNa
ObjectMapper mapper = new ObjectMapper()
.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION)
.registerModule(new KotlinModule.Builder().build());
secretEntries = mapper.readValue(requestBody, new TypeReference<>() {
});
secretEntries = mapper.readValue(requestBody, new TypeReference<>() { });
} catch (Exception e) {
// TODO: exclude secret body form log
LOGGER.error("Parsing secret body failure, schema name: {}", schemaName, e);
throw new BadRequestException(e.getMessage());
}

Set<String> secretKeys = new HashSet<>();
for (SecretsRequestEntry secretEntry : secretEntries) {
try {
DECODER.decode(secretEntry.data);
} catch (IllegalArgumentException e) {
secretKeys.add(secretEntry.key);
}
}
if (!secretKeys.isEmpty()) {
throw new BadRequestException("Values for secrets " + secretKeys + " haven't got base 64 format");
}

try {
SecretsManager secretsManager = new SecretsManager();
return secretsManager.createOrReplaceSecrets(schemaName, secretEntries);
} catch (Exception e) {
LOGGER.error("Create or replace secrets failure, schema name: {}", schemaName, e);
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage());
}
}
Expand All @@ -97,13 +119,15 @@ public Set<String> deleteSecrets(@PathVariable(name = "schemaName") String schem
secretsNames = mapper.readValue(requestBody, new TypeReference<>() {
});
} catch (Exception e) {
LOGGER.error("Parsing secret body failure, schema name: {}, body: {}", schemaName, requestBody, e);
throw new BadRequestException(e.getMessage());
}

try {
SecretsManager secretsManager = new SecretsManager();
return secretsManager.deleteSecrets(schemaName, secretsNames);
} catch (Exception e) {
LOGGER.error("Delete secrets failure, schema name: {}", schemaName, e);
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 Exactpro (Exactpro Systems Limited)
* Copyright 2020-2023 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@
import com.exactpro.th2.infrarepo.repo.RepositoryResource;
import jakarta.annotation.PostConstruct;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -133,20 +134,26 @@ private static void updateTrackedResources(String schema,
@PostConstruct
public void start() throws IOException {
try {
Kubernetes kube = new Kubernetes(Config.getInstance().getKubernetes(), null);
RegistryCredentialLookup secretMapper = new RegistryCredentialLookup(kube);
RegistryConnection registryConnection = new RegistryConnection(secretMapper.getCredentials());
RegistryWatcher registryWatcher = new RegistryWatcher(
REGISTRY_CHECK_INITIAL_DELAY_SECONDS,
REGISTRY_CHECK_PERIOD_SECONDS,
registryConnection
);
registryWatcher.startWatchingRegistry();
getRegistryWatcher()
.startWatchingRegistry();
logger.info("DynamicResourceProcessor has been started");
} catch (Exception e) {
logger.error("Exception while starting DynamicResourceProcessor. " +
"resources with dynamic versions will not be monitored", e);
throw e;
}
}

@NotNull
private static RegistryWatcher getRegistryWatcher() throws IOException {
Config config = Config.getInstance();
Kubernetes kube = new Kubernetes(config.getBehaviour(), config.getKubernetes(), null);
RegistryCredentialLookup secretMapper = new RegistryCredentialLookup(kube);
RegistryConnection registryConnection = new RegistryConnection(secretMapper.getCredentials());
return new RegistryWatcher(
REGISTRY_CHECK_INITIAL_DELAY_SECONDS,
REGISTRY_CHECK_PERIOD_SECONDS,
registryConnection
);
}
}
4 changes: 2 additions & 2 deletions src/main/java/com/exactpro/th2/inframgr/k8s/K8sOperator.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 Exactpro (Exactpro Systems Limited)
* Copyright 2020-2023 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -72,7 +72,7 @@ private void startInformers() {
}

logger.info("Creating informers");
Kubernetes kube = new Kubernetes(config.getKubernetes(), null);
Kubernetes kube = new Kubernetes(config.getBehaviour(), config.getKubernetes(), null);
cache = K8sResourceCache.INSTANCE;

kube.registerCustomResourceSharedInformers(new ResourceEventHandler<K8sCustomResource>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 Exactpro (Exactpro Systems Limited)
* Copyright 2020-2023 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -70,7 +70,7 @@ public class K8sSynchronization {
private final K8sSynchronizationJobQueue jobQueue = new K8sSynchronizationJobQueue();

private void deleteNamespace(String schemaName) {
try (Kubernetes kube = new Kubernetes(config.getKubernetes(), schemaName)) {
try (Kubernetes kube = new Kubernetes(config.getBehaviour(), config.getKubernetes(), schemaName)) {
if (kube.existsNamespace()) {
logger.info("Removing schema \"{}\" from kubernetes", schemaName);
DynamicResourceProcessor.deleteSchema(schemaName);
Expand All @@ -89,7 +89,7 @@ private void synchronizeNamespace(String schemaName,

Histogram.Timer timer = ManagerMetrics.getCommitTimer();
String shortCommitRef = getShortCommitRef(fullCommitRef);
try (Kubernetes kube = new Kubernetes(config.getKubernetes(), schemaName)) {
try (Kubernetes kube = new Kubernetes(config.getBehaviour(), config.getKubernetes(), schemaName)) {
SchemaInitializer.ensureSchema(schemaName, kube);
validateSchema(schemaName, repositoryResources, repositorySettings, shortCommitRef);

Expand Down
Loading
Loading