1
0
mirror of https://github.com/jcwimer/docker-swarm-autoscaler synced 2026-05-14 17:13:34 +00:00

First release with tests

This commit is contained in:
2019-12-18 13:35:37 -05:00
parent 4c4c972dbd
commit 0f2424221a
15 changed files with 797 additions and 44 deletions

View File

@@ -0,0 +1,71 @@
require 'spec_helper'
current_dir=Dir.pwd
# tests dir is current
autoscale="#{current_dir}/../docker-swarm-autoscaler/auto-scale.sh"
describe 'auto-scale.sh' do
create_standard_mocks
context 'scaling docker swarm services' do
it 'scales a service with lower than the minimum replicas' do
set_standard_mock_outputs
stdout, stderr, status = stubbed_env.execute("/bin/bash #{autoscale}", {'LOOP' => 'false'})
expect(stdout).to include("Service hello_helloworld_too_low_cpu has an autoscale label.")
expect(stdout).to include("Service hello_helloworld_too_low_cpu is below the minimum. Scaling to the minimum of 3")
expect(status.exitstatus).to eq 0
end
it 'scales a service with low cpu down by 1 replica' do
set_standard_mock_outputs
stdout, stderr, status = stubbed_env.execute("/bin/bash #{autoscale}", {'LOOP' => 'false'})
expect(stdout).to include("Service hello_helloworld_low_cpu has an autoscale label.")
expect(stdout).to include("Scaling down the service hello_helloworld_low_cpu to 3")
expect(status.exitstatus).to eq 0
end
it 'does not scale a service with low cpu when the minimum replicas is reached' do
set_standard_mock_outputs
stdout, stderr, status = stubbed_env.execute("/bin/bash #{autoscale}", {'LOOP' => 'false'})
expect(stdout).to include("Service hello_helloworld_min_replicas_low_cpu has an autoscale label.")
expect(stdout).to include("Service hello_helloworld_min_replicas_low_cpu has the minumum number of replicas.")
expect(status.exitstatus).to eq 0
end
it 'scales a service with high cpu up by 1 replica' do
set_standard_mock_outputs
stdout, stderr, status = stubbed_env.execute("/bin/bash #{autoscale}", {'LOOP' => 'false'})
expect(stdout).to include("Service hello_helloworld_high_cpu has an autoscale label.")
expect(stdout).to include("Scaling up the service hello_helloworld_high_cpu to 4")
expect(status.exitstatus).to eq 0
end
it 'does not scale a service with high cpu when the max replicas is reached' do
set_standard_mock_outputs
stdout, stderr, status = stubbed_env.execute("/bin/bash #{autoscale}", {'LOOP' => 'false'})
expect(stdout).to include("Service hello_helloworld_high_cpu_full_replicas has an autoscale label.")
expect(stdout).to include("Service hello_helloworld_high_cpu_full_replicas already has the maximum of 4 replicas")
expect(status.exitstatus).to eq 0
end
it 'scales a service with more than the maximum number of replicas' do
set_standard_mock_outputs
stdout, stderr, status = stubbed_env.execute("/bin/bash #{autoscale}", {'LOOP' => 'false'})
expect(stdout).to include("Service hello_helloworld_high_cpu_too_many_replicas has an autoscale label.")
expect(stdout).to include("Service hello_helloworld_high_cpu_too_many_replicas is above the maximum. Scaling to the maximum of 4")
expect(status.exitstatus).to eq 0
end
it 'does not scale a service without an autoscale label' do
set_standard_mock_outputs
stdout, stderr, status = stubbed_env.execute("/bin/bash #{autoscale}", {'LOOP' => 'false'})
expect(stdout).to include("Service autoscale_docker-swarm-autoscaler does not have an autoscale label.")
expect(status.exitstatus).to eq 0
end
end
end

413
tests/spec/spec_helper.rb Normal file
View File

@@ -0,0 +1,413 @@
require 'ap'
require 'pry'
require 'rspec/shell/expectations'
RSpec.configure do |c|
c.include Rspec::Shell::Expectations
end
def create_standard_mocks
let(:stubbed_env) { create_stubbed_env }
let(:curl_mock) { stubbed_env.stub_command('curl') }
let(:docker_mock) { stubbed_env.stub_command('docker') }
end
def set_standard_mock_outputs
# If you have something non standard need to be output, define your output before running this function. Outputs are stacked to stdout with new lines \n. Thus defining your non standard output first will output will be on top.
# If your non standard mock output is an exit code, define it after this function. Exit codes can be overwritten and whichever is deined last is what the test will use.
standard_prometheus_output='{
"status": "success",
"data": {
"resultType": "vector",
"result": [
{
"metric": {
"container_label_com_docker_swarm_service_name": "autoscale_docker-swarm-autoscaler",
"instance": "10.0.0.6:8080"
},
"value": [
1576602885.053,
"0.41103154419335"
]
},
{
"metric": {
"container_label_com_docker_swarm_service_name": "hello_helloworld_low_cpu",
"instance": "10.0.0.6:8080"
},
"value": [
1576602885.053,
"0.011596642816404852"
]
},
{
"metric": {
"container_label_com_docker_swarm_service_name": "hello_helloworld_too_low_cpu",
"instance": "10.0.0.6:8080"
},
"value": [
1576602885.053,
"0.011596642816404852"
]
},
{
"metric": {
"container_label_com_docker_swarm_service_name": "hello_helloworld_high_cpu",
"instance": "10.0.0.6:8080"
},
"value": [
1576602885.053,
"86.4"
]
},
{
"metric": {
"container_label_com_docker_swarm_service_name": "hello_helloworld_high_cpu_full_replicas",
"instance": "10.0.0.6:8080"
},
"value": [
1576602885.053,
"86.4"
]
},
{
"metric": {
"container_label_com_docker_swarm_service_name": "hello_helloworld_min_replicas_low_cpu",
"instance": "10.0.0.6:8080"
},
"value": [
1576602885.053,
"0.01"
]
},
{
"metric": {
"container_label_com_docker_swarm_service_name": "hello_helloworld_high_cpu_too_many_replicas",
"instance": "10.0.0.6:8080"
},
"value": [
1576602885.053,
"86.4"
]
}
]
}
}
'
helloworld_high_cpu_too_many_replicas_docker_inspect_output='[
{
"Spec": {
"Name": "hello_helloworld_high_cpu_too_many_replicas",
"Labels": {
"com.docker.stack.image": "tutum/hello-world",
"com.docker.stack.namespace": "hello",
"swarm.autoscaler": "true",
"swarm.autoscaler.maximum": "4",
"swarm.autoscaler.minimum": "3"
},
"Mode": {
"Replicated": {
"Replicas": 5
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
}
}
]'
helloworld_high_cpu_docker_inspect_output='[
{
"Spec": {
"Name": "hello_helloworld_high_cpu",
"Labels": {
"com.docker.stack.image": "tutum/hello-world",
"com.docker.stack.namespace": "hello",
"swarm.autoscaler": "true",
"swarm.autoscaler.maximum": "4",
"swarm.autoscaler.minimum": "3"
},
"Mode": {
"Replicated": {
"Replicas": 3
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
}
}
]'
helloworld_high_cpu_full_replicas_docker_inspect_output='[
{
"Spec": {
"Name": "hello_helloworld_high_cpu_full_replicas",
"Labels": {
"com.docker.stack.image": "tutum/hello-world",
"com.docker.stack.namespace": "hello",
"swarm.autoscaler": "true",
"swarm.autoscaler.maximum": "4",
"swarm.autoscaler.minimum": "3"
},
"Mode": {
"Replicated": {
"Replicas": 4
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
}
}
]'
helloworld_low_cpu_docker_inspect_output='[
{
"Spec": {
"Name": "hello_helloworld_low_cpu",
"Labels": {
"com.docker.stack.image": "tutum/hello-world",
"com.docker.stack.namespace": "hello",
"swarm.autoscaler": "true",
"swarm.autoscaler.maximum": "4",
"swarm.autoscaler.minimum": "3"
},
"Mode": {
"Replicated": {
"Replicas": 4
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
}
}
]'
helloworld_too_low_cpu_docker_inspect_output='[
{
"Spec": {
"Name": "hello_helloworld_too_low_cpu",
"Labels": {
"com.docker.stack.image": "tutum/hello-world",
"com.docker.stack.namespace": "hello",
"swarm.autoscaler": "true",
"swarm.autoscaler.maximum": "4",
"swarm.autoscaler.minimum": "3"
},
"Mode": {
"Replicated": {
"Replicas": 1
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
}
}
]'
docker_swarm_autoscaler_docker_inspect_output='[
{
"Spec": {
"Name": "autoscale_docker-swarm-autoscaler",
"Labels": {
"com.docker.stack.image": "tutum/hello-world",
"com.docker.stack.namespace": "autoscale"
},
"Mode": {
"Replicated": {
"Replicas": 1
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
}
}
]'
hello_helloworld_min_replicas_low_cpu_docker_inspect_output='[
{
"Spec": {
"Name": "hello_helloworld_min_replicas_low_cpu",
"Labels": {
"com.docker.stack.image": "tutum/hello-world",
"com.docker.stack.namespace": "hello",
"swarm.autoscaler": "true",
"swarm.autoscaler.maximum": "4",
"swarm.autoscaler.minimum": "3"
},
"Mode": {
"Replicated": {
"Replicas": 3
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
}
}
]'
curl_mock.with_args('--silent').outputs(standard_prometheus_output, to: :stdout)
docker_mock.with_args('service','inspect','hello_helloworld_high_cpu').outputs(helloworld_high_cpu_docker_inspect_output, to: :stdout)
docker_mock.with_args('service','inspect','hello_helloworld_low_cpu').outputs(helloworld_low_cpu_docker_inspect_output, to: :stdout)
docker_mock.with_args('service','inspect','hello_helloworld_too_low_cpu').outputs(helloworld_too_low_cpu_docker_inspect_output, to: :stdout)
docker_mock.with_args('service','inspect','autoscale_docker-swarm-autoscaler').outputs(docker_swarm_autoscaler_docker_inspect_output, to: :stdout)
docker_mock.with_args('service','inspect','hello_helloworld_high_cpu_full_replicas').outputs(helloworld_high_cpu_full_replicas_docker_inspect_output, to: :stdout)
docker_mock.with_args('service','inspect','hello_helloworld_high_cpu_too_many_replicas').outputs(helloworld_high_cpu_too_many_replicas_docker_inspect_output, to: :stdout)
docker_mock.with_args('service','inspect','hello_helloworld_min_replicas_low_cpu').outputs(hello_helloworld_min_replicas_low_cpu_docker_inspect_output, to: :stdout)
docker_mock.with_args('service', 'scale').returns_exitstatus(0)
end