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

ci: added nightly-running chaos test supported by NBomber stress-test #37

Merged
merged 3 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .checkov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
skip-check:
- CKV_ARGO_2
- CKV_DOCKER_2
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
!src
!.editorconfig
!*.sln
!Directory.Build.props
!tests/chaos
src/**/bin
src/**/obj
80 changes: 80 additions & 0 deletions .github/workflows/nightly-chaos.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: nightly chaos testing

on:
schedule:
# daily at 04:07.
- cron: "07 04 * * *"

# Declare default permissions as read only.
permissions: read-all

jobs:
chaos-testing:
name: chaos testing
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
with:
fetch-depth: 0

- name: Build stress testing image
id: build-image
uses: docker/build-push-action@c84f38281176d4c9cdb1626ffafcd6b3911b5d94 # tag=v3
with:
push: false
load: true
tags: ghcr.io/miracum/vfps/stress-test:v1
cache-from: type=gha
cache-to: type=gha,mode=max
target: stress-test

- name: Create KinD cluster
uses: helm/kind-action@9e8295d178de23cbfbd8fa16cf844eec1d773a07 # tag=v1.4.0
with:
cluster_name: kind

- name: Load stress-test image into KinD
run: |
kind load docker-image ghcr.io/miracum/vfps/stress-test:v1

- name: Install prerequisites
working-directory: tests/chaos
run: |
curl -sL -o - https://github.com/argoproj/argo-workflows/releases/download/v3.4.3/argo-linux-amd64.gz | gunzip > argo

kubectl create ns vfps

helm repo add chaos-mesh https://charts.chaos-mesh.org
helm upgrade --install chaos-mesh chaos-mesh/chaos-mesh \
--create-namespace \
--wait \
-n chaos-mesh \
--set chaosDaemon.runtime=containerd \
--set chaosDaemon.socketPath='/run/containerd/containerd.sock' \
--version 2.4.3

kubectl apply -f chaos-mesh-rbac.yaml

helm repo add argo https://argoproj.github.io/argo-helm
helm upgrade --install argo-workflows argo/argo-workflows \
--create-namespace \
--wait \
-n argo-workflows \
-f argo-workflows-values.yaml

- name: Install vfps
working-directory: tests/chaos
run: |
helm repo add miracum https://miracum.github.io/charts
helm upgrade --install \
-n vfps \
-f vfps-values.yaml \
--wait \
--version=^1.0.0 \
vfps miracum/vfps

- name: Run chaos testing workflow
working-directory: tests/chaos
run: |
./argo submit tests/chaos/argo-workflow.yaml -n vfps --wait
3 changes: 2 additions & 1 deletion .github/workflows/release-please.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jobs:
with:
token: ${{ secrets.RELEASE_PLEASE_GITHUB_TOKEN }}
release-type: simple
package-name: release-please-action
extra-files: |
src/Directory.Build.props
changelog-types: |
[
{ "type": "feat", "section": "Features" },
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,6 @@ coverage/
coveragereport/

megalinter-reports/

# used by NBomber
reports/
31 changes: 28 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ WORKDIR /build
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \
PATH="/root/.dotnet/tools:${PATH}"

RUN dotnet tool install --global dotnet-ef
RUN dotnet tool install --global dotnet-ef --version=7.0.0

COPY src/Vfps/Vfps.csproj src/Vfps/Vfps.csproj
COPY src/Directory.Build.props src/
COPY src/Vfps/Vfps.csproj src/Vfps/

RUN dotnet restore --runtime=linux-x64 src/Vfps/Vfps.csproj

Expand All @@ -37,7 +38,6 @@ dotnet ef migrations bundle \
--project=src/Vfps/Vfps.csproj \
--startup-project=src/Vfps/Vfps.csproj \
--configuration=Release \
--target-runtime=linux-x64 \
--verbose \
-o /build/efbundle
EOF
Expand All @@ -51,6 +51,31 @@ RUN dotnet test \
-l "console;verbosity=detailed" \
--settings=runsettings.xml

FROM build AS build-stress-test
WORKDIR /build/src/Vfps.LoadTests
RUN <<EOF
dotnet build \
--configuration=Release

dotnet publish \
--no-restore \
--no-build \
--configuration=Release \
-o /build/publish
EOF

# re-use the runtime base-image even though we technically don't need a full aspnet image for simplicity
FROM runtime AS stress-test
WORKDIR /opt/vfps-stress

# https://github.com/hadolint/hadolint/pull/815 isn't yet in mega-linter
# hadolint ignore=DL3022
COPY --from=docker.io/bitnami/kubectl:1.24.8@sha256:539065b76186a6091e814296a98965c65bc16eed9b6edd647628979d2a1eece5 /opt/bitnami/kubectl/bin/kubectl /usr/bin/kubectl

COPY --chown=65532:65532 tests/chaos/chaos.yaml /tmp/
COPY --chown=65532:65532 --from=build-stress-test /build/publish .
ENTRYPOINT ["dotnet", "/opt/vfps-stress/Vfps.LoadTests.dll"]

FROM runtime
COPY --chown=65532:65532 --from=build /build/publish .
COPY --chown=65532:65532 --from=build /build/efbundle .
Expand Down
21 changes: 21 additions & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AnalysisLevel>latest</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>

<Version>1.1.0</Version> <!-- x-release-please-version -->
<Company>miracum.org</Company>
<Description>A very fast and resource-efficient pseudonym service.</Description>
<Copyright>© miracum.org. All rights reserved.</Copyright>
<NeutralLanguage>en-US</NeutralLanguage>
<Authors>miracum.org</Authors>
</PropertyGroup>

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
</Project>
3 changes: 0 additions & 3 deletions src/Vfps.Benchmarks/Vfps.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ValidateExecutableReferencesMatchSelfContained>false</ValidateExecutableReferencesMatchSelfContained>
</PropertyGroup>

Expand Down
3 changes: 0 additions & 3 deletions src/Vfps.IntegrationTests/Vfps.IntegrationTests.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

Expand Down
108 changes: 75 additions & 33 deletions src/Vfps.LoadTests/Program.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,96 @@
using System.Diagnostics;
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Net.Client.Configuration;
using NBomber.Contracts;
using NBomber.CSharp;
using NBomber.Plugins.Http.CSharp;
using NBomber.Plugins.Network.Ping;
using System.Net.Http.Json;
using Vfps.Protos;

var baseUrl = "https://localhost:7078/v1";
var grpcAddress = new Uri(Environment.GetEnvironmentVariable("VFPS_GRPC_ADDRESS") ?? "http://localhost:8081");

var httpFactory = HttpClientFactory.Create();
var defaultMethodConfig = new MethodConfig
{
Names = { MethodName.Default },
RetryPolicy = new RetryPolicy
{
MaxAttempts = 3,
InitialBackoff = TimeSpan.FromSeconds(5),
MaxBackoff = TimeSpan.FromSeconds(30),
BackoffMultiplier = 2,
RetryableStatusCodes = { StatusCode.Unavailable, StatusCode.Internal }
}
};

var createNamespace = Step.Create("create_namespace",
clientFactory: httpFactory,
execute: context =>
{
var request = Http.CreateRequest("POST", baseUrl + "/namespaces")
.WithHeader("Accept", "application/json")
.WithBody(JsonContent.Create(new
{
Name = "load-test",
PseudonymLength = 32,
PseudonymGenerationMethod = 1,
}));

return Http.Send(request, context);
});
using var channel = GrpcChannel.ForAddress(grpcAddress, new GrpcChannelOptions()
{
ServiceConfig = new ServiceConfig()
{
MethodConfigs = { defaultMethodConfig }
},
Credentials = ChannelCredentials.Insecure,
UnsafeUseInsecureChannelCallCredentials = true,
});

var namespaceClient = new NamespaceService.NamespaceServiceClient(channel);
var pseudonymClient = new PseudonymService.PseudonymServiceClient(channel);

var namespaceRequest = new NamespaceServiceCreateRequest()
{
Name = "stress-test",
PseudonymGenerationMethod = PseudonymGenerationMethod.SecureRandomBase64UrlEncoded,
PseudonymLength = 16,
PseudonymPrefix = "stress-",
};

var createPseudonyms = Step.Create("create_pseudonyms",
clientFactory: httpFactory,
execute: context =>
execute: async context =>
{
var request = Http.CreateRequest("POST", baseUrl + "/namespaces/load-test/pseudonyms")
.WithHeader("Accept", "application/json")
.WithBody(JsonContent.Create(new
{
OriginalValue = Guid.NewGuid().ToString(),
}));

return Http.Send(request, context);
var request = new PseudonymServiceCreateRequest()
{
Namespace = namespaceRequest.Name,
OriginalValue = Guid.NewGuid().ToString(),
};

try
{
var response = await pseudonymClient.CreateAsync(request);
return Response.Ok(statusCode: 200, sizeBytes: request.CalculateSize() + response.CalculateSize());
}
catch (RpcException exc)
{
context.Logger.Error(exc, "Pseudonym creation failed");
return Response.Fail();
}
});

var scenario = ScenarioBuilder
.CreateScenario("create_namespace_and_pseudonyms", createNamespace, createPseudonyms)
.CreateScenario("stress_pseudonym_creation", createPseudonyms)
.WithInit(async context =>
{
try
{
var response = await namespaceClient.CreateAsync(namespaceRequest);
}
catch (RpcException exc) when (exc.StatusCode == StatusCode.AlreadyExists)
{
context.Logger.Warning($"Namespace {namespaceRequest.Name} already exists. Continuing anyway.");
}
})
.WithWarmUpDuration(TimeSpan.FromSeconds(5))
.WithLoadSimulations(
Simulation.InjectPerSec(rate: 100, during: TimeSpan.FromSeconds(30))
Simulation.RampConstant(copies: 10, during: TimeSpan.FromMinutes(4)),
Simulation.KeepConstant(copies: 100, during: TimeSpan.FromMinutes(4)),
Simulation.InjectPerSecRandom(minRate: 10, maxRate: 50, during: TimeSpan.FromMinutes(4))
);

// creates ping plugin that brings additional reporting data
var pingPluginConfig = PingPluginConfig.CreateDefault(new[] { "127.0.0.1" });
var pingPluginConfig = PingPluginConfig.CreateDefault(new[] { grpcAddress.Host });
var pingPlugin = new PingPlugin(pingPluginConfig);

NBomberRunner
var stats = NBomberRunner
.RegisterScenarios(scenario)
.WithWorkerPlugins(pingPlugin)
.Run();

Debug.Assert(stats.FailCount < 100);
10 changes: 4 additions & 6 deletions src/Vfps.LoadTests/Vfps.LoadTests.csproj
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NBomber" Version="3.3.0" />
<PackageReference Include="NBomber.Http" Version="3.0.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Vfps\Vfps.csproj" />
</ItemGroup>

</Project>
3 changes: 0 additions & 3 deletions src/Vfps.Tests/Vfps.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

Expand Down
Loading