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 10 commits
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
16 changes: 5 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,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 @@ -70,6 +70,8 @@ configurations.configureEach() {

dependencies {
implementation platform("org.springframework.boot:spring-boot-dependencies:${springboot_version}")
implementation "org.springframework.boot:spring-boot-starter-security:${springboot_version}"
implementation "org.springframework.boot:spring-boot-starter-web:${springboot_version}"

implementation "com.exactpro.th2:infra-repo:${infra_repo_version}"

Expand Down Expand Up @@ -125,16 +127,8 @@ test {
useJUnitPlatform()
}

compileKotlin {
kotlinOptions {
jvmTarget = "17"
}
}

compileTestKotlin {
kotlinOptions {
jvmTarget = "17"
}
kotlin {
jvmToolchain(17)
}

jar {
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2020-2022 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 All @@ -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
76 changes: 76 additions & 0 deletions src/main/java/com/exactpro/th2/inframgr/BasicAuthConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.exactpro.th2.inframgr;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import java.io.IOException;
import java.util.List;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration
@EnableWebSecurity
@SuppressWarnings("unused")
public class BasicAuthConfig {

private static final String ADMIN_ROLE = "ADMIN";

public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorizeHttpRequests) ->
authorizeHttpRequests
.requestMatchers("/secrets/**").hasRole(ADMIN_ROLE)
.requestMatchers("/namespace/**").hasRole(ADMIN_ROLE)
.requestMatchers("/**").permitAll()
).httpBasic(withDefaults())
// CSRF is disabled because user uses curl only to call REST API
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}

@Bean
public UserDetailsService userDetailsService() throws IOException {
Config config = Config.getInstance();
List<UserDetails> admins = config.getHttp().getAdminAccounts().entrySet().stream()
.map(entry -> User.builder()
.username(entry.getKey())
.password(entry.getValue())
.roles(ADMIN_ROLE)
.build())
.toList();
return new InMemoryUserDetailsManager(admins);
}

@Bean
public PasswordEncoder passwordEncoder() {
return PASSWORD_ENCODER;
}
}
103 changes: 67 additions & 36 deletions 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 All @@ -16,7 +16,13 @@

package com.exactpro.th2.inframgr;

import com.exactpro.th2.inframgr.util.cfg.*;
import com.exactpro.th2.inframgr.util.cfg.BehaviourCfg;
import com.exactpro.th2.inframgr.util.cfg.CassandraConfig;
import com.exactpro.th2.inframgr.util.cfg.GitCfg;
import com.exactpro.th2.inframgr.util.cfg.HttpCfg;
import com.exactpro.th2.inframgr.util.cfg.K8sConfig;
import com.exactpro.th2.inframgr.util.cfg.PrometheusConfig;
import com.exactpro.th2.inframgr.util.cfg.RabbitMQConfig;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
Expand All @@ -32,20 +38,25 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class Config {
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved

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

private static final String CONFIG_FILE = "config.yml";

private static final String CONFIG_DIR_SYSTEM_PROPERTY = "inframgr.config.dir";

private static volatile Config instance;

private final Logger logger;

private String configDir;
private static final Path CONFIG_DIR = Path.of(System.getProperty(CONFIG_DIR_SYSTEM_PROPERTY, "."));

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

private HttpCfg http;

private GitCfg git;

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

private K8sConfig kubernetes;

public BehaviourCfg getBehaviour() {
return behaviour;
}

public HttpCfg getHttp() {
return http;
}

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

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

public void setHttp(HttpCfg http) {
this.http = http;
}

public void setGit(GitCfg git) {
this.git = git;
}
Expand All @@ -98,13 +125,22 @@ public void setKubernetes(K8sConfig kubernetes) {
this.kubernetes = kubernetes;
}

private Config() {
logger = LoggerFactory.getLogger(Config.class);
configDir = System.getProperty(CONFIG_DIR_SYSTEM_PROPERTY, ".");
configDir += "/";
private Config() {}

public static Config getInstance() throws IOException {
if (instance == null) {
synchronized (Config.class) {
if (instance == null) {
Path file = CONFIG_DIR.resolve(CONFIG_FILE);
instance = readConfiguration(file);
}
}
}

return instance;
}

private void parseFile(File file, ObjectMapper mapper, Object object) throws IOException {
private static void parseFile(File file, ObjectMapper mapper, Object object) throws IOException {

String fileContent = new String(Files.readAllBytes(file.toPath()));
StringSubstitutor stringSubstitutor = new StringSubstitutor(
Expand All @@ -114,43 +150,38 @@ private void parseFile(File file, ObjectMapper mapper, Object object) throws IOE
mapper.readerForUpdating(object).readValue(enrichedContent);
}

private void readConfiguration() throws IOException {

static Config readConfiguration(Path configFile) throws IOException {
try {
File file = new File(configDir + CONFIG_FILE);
Config config = new Config();
parseFile(configFile.toFile(), new ObjectMapper(new YAMLFactory()).enable(
JsonParser.Feature.STRICT_DUPLICATE_DETECTION).
registerModule(new KotlinModule.Builder().build()), config);

parseFile(file, new ObjectMapper(new YAMLFactory()).enable(
JsonParser.Feature.STRICT_DUPLICATE_DETECTION).
registerModule(new KotlinModule.Builder().build()), this);

if (rabbitmq == null) {
rabbitmq = new RabbitMQConfig();
if (config.getRabbitMQ() == null) {
config.setRabbitMQ(new RabbitMQConfig());
}
if (config.getBehaviour() == null) {
config.setBehaviour(new BehaviourCfg());
}
if (config.getCassandra() == null) {
config.setCassandra(new CassandraConfig());
}
if (config.getHttp() == null) {
throw new IllegalStateException("'http' config can't be null");
}

config.getHttp().validate();

return config;
} catch (UnrecognizedPropertyException e) {
logger.error("Bad configuration: unknown property(\"{}\") specified in configuration file \"{}\""
LOGGER.error("Bad configuration: unknown property(\"{}\") specified in configuration file \"{}\""
, e.getPropertyName()
, CONFIG_FILE);
throw new RuntimeException("Configuration exception", e);
} catch (JsonParseException e) {
logger.error("Bad configuration: exception while parsing configuration file \"{}\""
LOGGER.error("Bad configuration: exception while parsing configuration file \"{}\""
, CONFIG_FILE);
throw new RuntimeException("Configuration exception", e);
}
}

public static Config getInstance() throws IOException {
if (instance == null) {
synchronized (Config.class) {
if (instance == null) {
Config config = new Config();
config.readConfiguration();

instance = config;
}
}
}

return instance;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,20 @@
import com.exactpro.th2.inframgr.statuswatcher.ResourceCondition;
import com.exactpro.th2.inframgr.statuswatcher.StatusCache;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;

@Controller
@SuppressWarnings("unused")
public class DeploymentController {

private static final Logger logger = LoggerFactory.getLogger(DeploymentController.class);

public static final String UNKNOWN_ERROR = "UNKNOWN_ERROR";

public static final String BAD_RESOURCE_NAME = "BAD_RESOURCE_NAME";
Expand All @@ -64,12 +63,11 @@ public List<ResponseEntry> getResourceDeploymentStatuses(
}

return response;

} catch (ServiceException e) {
throw e;
} catch (Exception e) {
logger.error("Exception retrieving schema {} from repository", schemaName, e);
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage());
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR,
"Exception retrieving schema " + schemaName + " from repository", e);
}
}

Expand Down
Loading
Loading