Skip to content

Commit

Permalink
feat(kayenta): Prepare automated integration tests for kayenta (#594)
Browse files Browse the repository at this point in the history
* feat(kayenta): Prepare automated integration tests for kayenta, simple successfull and failure cases added based on prometheus and s3 integrations

* chore: add graphite into integration tests module

* chore: optionally enable integration tests

* chore: enable integration tests in travis and fix merge conflict

* fix(standalone-canary-analysis): Fixed bad Intellij refactor that changed the conditional prop for the module preventing it from being loaded
  • Loading branch information
Aloren authored and fieldju committed Aug 1, 2019
1 parent 5cc068a commit 22c5707
Show file tree
Hide file tree
Showing 30 changed files with 1,214 additions and 1 deletion.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ cache:
- "$HOME/.gradle"
env:
global:
- ENABLE_INTEGRATION_TESTS=true
- secure: VA8UxPAiEWYEEbG6B4QQ+77gXPCHCGSWU4NBGtwrnuKy8BLqKPLlumSeQbFI7+mamGOvTjqSCa04Gloz/rpbSIEe0NVTsBZoFQRvRx1xWpiPmBxXVJcyuEgnEtIoUq3p+4wSSapfCf/sZ5ixU7HXXbIxr7MlsYfnzXu4Wt2Pb9eotEqp9BJlskq5sYL93LpF8GfB8M/3Mu5URgTdhy4V+zdw4+fBF2my1wSIXhBwmX+lO95qMyWWzOOTUDZhg6Y48KC9iao9zkd86EKFYECnhZ2iUiuMQf1d8Lphd3qOY3w6aCRRpJBKkm35o7RaHsTwLXWSqAOKzvaOMmUYS/OWJ4SnXTJxEmwVqlCYEgf02JzC0RCV49Jn/JMK/By2XM0N1DalGvwujlvxb0c+HuUEYU5gZVV25rIjZFy2pSOxiwtsehWHDMuRlz+4SIX7rdJIy9gi49nIY3aF23TLW/2cBPzw6vchdNSlcetWTBAwYl3Xs2RX+DofL4eoQqdqYTPQ37YNeM1WUb1/1pKqJZyawc7aoqbi9JPgY0/YKMLsJ+SiDxEGkFhXbSkaI8XoVDoJtdXB1LLMpNj+VvHjhtYxs8o3eKDIWxNiFdMlqZ7Cu5anVIJbu2Wy88hq2AAiQN+B88gaPBz5LN0xpxBibSCYHD23mvU3IskIMrzLzb+chSg=
- secure: RxP+LUmGsidEOPTLHLo24L15v0OvreyvbNfcM1d6SqrAr/hyrf7OKMMN4v27xRUtlyUmKOUz/M1463caSS46qs5QaFqlO4boi1kwykKtqCEkAaoqhdX82QU4m73nzcSyug14X/r9EOdBlfeffNnZ+YpiDQ/ntZJHtLQHFKjnqj/Z+K+HXfa7LoB2cp0VTq8SgQrmU0a2rtWiA2sHEh39lw5JS6eSTxJ7xqhb/0TXuyqQ7pRrPIJ7pUkdcvawarCVMbt7ezGFel8oqMeiKjNHbztVFXgh8C9lu2ggvmpohg10zFqjzOdfJy7Vc/1VIOxgA5k2i7EpDmXonOENfkhNj0P/0Yv8H7Pb4xaGFXxvRk2bpypA7SNb9aCLRoYvZ2NDqLKs/N9NBkjcEfVRdVxAAlcxn8m6bBaLGAxDS7wjqtRTiNpHHb85lyibuIsmerVm0EiO3H8vIQAlMOS5yY7HAxe71i1b7XAjmmqRZRhjNrAOHqhObrIR7vtwNBU/xv2SKpRg30A3LYNt9d6dp/ndFIi/mo2tmLB+ZRMVd9BzlaXbf98GZJPtxHfH01u/mGsp6gwbFeUqyERh3QSFzoUKzNU13fQONNexj+syii1rqZTpHreQ30JtDHpKZVHzR1cPPZAYkF9Ue2YMjtlNZt/p6n0mN+cX5zOvMUbAFtc77IA=
23 changes: 23 additions & 0 deletions kayenta-integration-tests/kayenta-integration-tests.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
dependencies {

testCompile project(":kayenta-web")
testCompile 'io.rest-assured:rest-assured:3.1.1'
testCompile 'org.awaitility:awaitility:3.1.6'
testCompile 'io.micrometer:micrometer-registry-prometheus'
testCompile 'io.micrometer:micrometer-registry-graphite'
testCompile 'org.springframework.cloud:spring-cloud-starter:2.1.2.RELEASE'// needed for bootstrap phase when all embedded containers are setup
testCompile 'com.playtika.testcontainers:embedded-redis:1.25'
testCompile 'com.playtika.testcontainers:embedded-minio:1.25'
}

test.testLogging {
showStandardStreams = true
}

gradle.taskGraph.whenReady {
tasks.test.enabled = (
properties.getOrDefault('ENABLE_INTEGRATION_TESTS', 'false') == 'true'
|| System.getProperty('ENABLE_INTEGRATION_TESTS', 'false') == 'true'
|| System.getenv().getOrDefault('ENABLE_INTEGRATION_TESTS', 'false') == 'true'
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2019 Playtika
*
* 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.netflix.kayenta.configuration;

import static com.playtika.test.common.utils.ContainerUtils.containerLogsConsumer;

import com.netflix.kayenta.utils.EnvironmentUtils;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;

@Slf4j
@Configuration
public class EmbeddedGraphiteBootstrapConfiguration {
private static final int PICKLE_RECEIVER_PORT = 2004;
private static final int HTTP_PORT = 80;

@Bean(name = "graphiteWaitStrategy")
public WaitStrategy graphiteWaitStrategy() {
return new HostPortWaitStrategy();
}

@Bean(name = "graphite", destroyMethod = "stop")
public GenericContainer graphite(
ConfigurableEnvironment environment, WaitStrategy graphiteWaitStrategy) {

GenericContainer container =
new GenericContainer("graphiteapp/graphite-statsd:1.1.5-12")
.withLogConsumer(containerLogsConsumer(log))
.withExposedPorts(PICKLE_RECEIVER_PORT)
.waitingFor(graphiteWaitStrategy)
.withClasspathResourceMapping(
"/external/graphite/storage-schemas.conf",
"/opt/graphite/conf/storage-schemas.conf",
BindMode.READ_ONLY)
.withStartupTimeout(Duration.ofSeconds(30));
container.start();

Map<String, Object> map = registerEnvironment(environment, container);
log.info("Started Graphite server. Connection details: {}", map);
return container;
}

@NotNull
private Map<String, Object> registerEnvironment(
ConfigurableEnvironment environment, GenericContainer container) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("embedded.graphite.picklePort", container.getMappedPort(PICKLE_RECEIVER_PORT));
map.put("embedded.graphite.httpPort", container.getMappedPort(HTTP_PORT));
EnvironmentUtils.registerPropertySource("embeddedGraphiteInfo", environment, map);
return map;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2019 Playtika
*
* 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.netflix.kayenta.configuration;

import static com.playtika.test.common.utils.ContainerUtils.containerLogsConsumer;

import com.netflix.kayenta.utils.EnvironmentUtils;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.testcontainers.Testcontainers;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.utility.MountableFile;

@Slf4j
@Configuration
public class EmbeddedPrometheusBootstrapConfiguration {

// Exposes host machine port to be used by prometheus container for scraping metrics from
// /prometehus endpoint
// See for more details:
// https://www.testcontainers.org/features/networking/#exposing-host-ports-to-the-container
static {
Testcontainers.exposeHostPorts(8081);
}

private static final int PORT = 9090;

@Bean(name = "prometheusWaitStrategy")
public WaitStrategy prometheusWaitStrategy() {
return new HttpWaitStrategy().forPath("/status").forPort(PORT).forStatusCode(200);
}

@Bean(name = "prometheus", destroyMethod = "stop")
public GenericContainer prometheus(
ConfigurableEnvironment environment, WaitStrategy prometheusWaitStrategy) {

GenericContainer container =
new GenericContainer("prom/prometheus:v2.10.0")
.withLogConsumer(containerLogsConsumer(log))
.withExposedPorts(PORT)
.withCopyFileToContainer(
MountableFile.forClasspathResource("/external/prometheus/prometheus.yml"),
"/etc/prometheus/prometheus.yml")
.waitingFor(prometheusWaitStrategy)
.withStartupTimeout(Duration.ofSeconds(30));
container.start();
Map<String, Object> env = registerEnvironment(environment, container.getMappedPort(PORT));
log.info("Started Prometheus server. Connection details: {}", env);
return container;
}

static Map<String, Object> registerEnvironment(ConfigurableEnvironment environment, int port) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("embedded.prometheus.port", port);
EnvironmentUtils.registerPropertySource("embeddedPrometheusInfo", environment, map);
return map;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2019 Playtika
*
* 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.netflix.kayenta.configuration;

import com.netflix.kayenta.metrics.CanaryAnalysisCasesConfigurationProperties;
import com.netflix.kayenta.metrics.MetricsGenerator;
import com.netflix.kayenta.metrics.PercentilePrecisionMeterConfigurationFilter;
import com.netflix.kayenta.metrics.RandomProvider;
import com.netflix.kayenta.steps.StandaloneCanaryAnalysisSteps;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@EnableConfigurationProperties(CanaryAnalysisCasesConfigurationProperties.class)
@Configuration
public class MetricsReportingConfiguration {

@Bean
public RandomProvider randomProvider() {
return new RandomProvider();
}

@Bean
public MetricsGenerator metricsGenerator(
MeterRegistry registry,
RandomProvider randomProvider,
CanaryAnalysisCasesConfigurationProperties configuration) {
return new MetricsGenerator(registry, randomProvider, configuration);
}

@Bean
public StandaloneCanaryAnalysisSteps canaryAnalysisSteps(
@Value("${server.port}") int serverPort,
CanaryAnalysisCasesConfigurationProperties configuration) {
return new StandaloneCanaryAnalysisSteps(serverPort, configuration);
}

@Bean
public MeterFilter percentilePrecisionConfigurationFilter() {
return new PercentilePrecisionMeterConfigurationFilter();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2019 Playtika
*
* 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.netflix.kayenta.metrics;

import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Data
@Validated
@ConfigurationProperties("canary-analysis")
public class CanaryAnalysisCasesConfigurationProperties {

@Valid private Map<String, AnalysisConfiguration> cases;

public AnalysisConfiguration get(String caseName) {
AnalysisConfiguration config = cases.get(caseName);
if (config == null) {
throw new IllegalStateException("Case " + caseName + " not configured");
}
return config;
}

@Data
public static class AnalysisConfiguration {

@NotNull private Long lifetimeDurationMinutes;
@NotNull private Long analysisIntervalMinutes;
@NotNull private String namespace;
@NotNull private ScopeMetricsConfiguration control;
@NotNull private ScopeMetricsConfiguration experiment;
}

@Data
public static class ScopeMetricsConfiguration {

@NotNull private String scope;
@Valid @NotNull private List<MetricConfiguration> metrics;
}

@Data
public static class MetricConfiguration {

@NotEmpty private String name;
@NotNull private Integer lowerBound;
@NotNull private Integer upperBound;
@NotNull private String type;
}
}
Loading

0 comments on commit 22c5707

Please sign in to comment.