From 1f9dce1cba67a999d4375228596f080581941e68 Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Thu, 4 Nov 2021 00:25:44 -0300 Subject: [PATCH 1/9] ... --- README.md | 58 +++++++++++++++------------------ examples/basic/README.md | 1 - examples/k6/README.md | 2 +- examples/split-data/README.md | 22 +++++++++++++ examples/split-data/data.tf | 7 ++++ examples/split-data/main.tf | 17 ++++++++++ examples/split-data/output.tf | 19 +++++++++++ examples/split-data/provider.tf | 3 ++ 8 files changed, 95 insertions(+), 34 deletions(-) create mode 100644 examples/split-data/README.md create mode 100644 examples/split-data/data.tf create mode 100644 examples/split-data/main.tf create mode 100644 examples/split-data/output.tf create mode 100644 examples/split-data/provider.tf diff --git a/README.md b/README.md index 7f5eb13..43e2f3f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ In its basic use it is necessary to provide information about which network will module "loadtest" { source = "marcosborges/loadtest-distribuited/aws" - version = "0.0.4-alpha" name = "nome-da-implantacao" executor = "bzt" @@ -42,7 +41,6 @@ data "aws_subnet" "current" { module "loadtest" { source = "marcosborges/loadtest-distribuited/aws" - version = "0.0.4-alpha" name = "nome-da-implantacao" executor = "jmeter" @@ -81,7 +79,6 @@ The module also provides advanced settings. module "loadtest" { source = "marcosborges/loadtest-distribuited/aws" - version = "0.0.3-alpha" subnet_id = data.aws_subnet.current.id @@ -180,13 +177,6 @@ data "aws_ami" "my_image" { -## Examples with another executors - -- [Taurus](#taurus) -- [Jmeter](#jmeter) -- [Locust](#locust) -- [K6](#k6) - --- @@ -198,16 +188,14 @@ data "aws_ami" "my_image" { |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.1 | | [aws](#requirement\_aws) | >= 3.63 | -| [null](#requirement\_null) | >= 3.1.0 | -| [tls](#requirement\_tls) | >= 3.1.0 | ## Providers | Name | Version | |------|---------| | [aws](#provider\_aws) | >= 3.63 | -| [null](#provider\_null) | >= 3.1.0 | -| [tls](#provider\_tls) | >= 3.1.0 | +| [null](#provider\_null) | n/a | +| [tls](#provider\_tls) | n/a | ## Modules @@ -217,15 +205,18 @@ No modules. | Name | Type | |------|------| -| [aws_iam_instance_profile.jmeter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | -| [aws_iam_role.jmeter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_instance_profile.loadtest](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | +| [aws_iam_role.loadtest](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_instance.leader](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | | [aws_instance.nodes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | -| [aws_key_pair.jmeter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/key_pair) | resource | -| [aws_security_group.jmeter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | -| [null_resource.publish_split_data](https://registry.terraform.io/providers/hashicorp/terraform-provider-null/latest/docs/resources/resource) | resource | -| [null_resource.split_data](https://registry.terraform.io/providers/hashicorp/terraform-provider-null/latest/docs/resources/resource) | resource | -| [tls_private_key.jmeter](https://registry.terraform.io/providers/hashicorp/terraform-provider-tls/latest/docs/resources/private_key) | resource | +| [aws_key_pair.loadtest](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/key_pair) | resource | +| [aws_security_group.loadtest](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [null_resource.executor](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.key_pair_exporter](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.publish_split_data](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.split_data](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [tls_private_key.loadtest](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource | +| [aws_ami.amazon_linux_2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | | [aws_subnet.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | | [aws_vpc.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source | @@ -233,35 +224,38 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [auto\_execute](#input\_auto\_execute) | Execute Loadtest after leader and nodes available | `bool` | `true` | no | | [auto\_setup](#input\_auto\_setup) | Install and configure instances Amazon Linux2 with JMeter and Taurus | `bool` | `true` | no | | [executor](#input\_executor) | Executor of the loadtest | `string` | `"jmeter"` | no | | [jmeter\_version](#input\_jmeter\_version) | JMeter version | `string` | `"5.4.1"` | no | -| [leader\_ami\_id](#input\_leader\_ami\_id) | Id of the AMI | `string` | n/a | yes | +| [leader\_ami\_id](#input\_leader\_ami\_id) | Id of the AMI | `string` | `""` | no | | [leader\_associate\_public\_ip\_address](#input\_leader\_associate\_public\_ip\_address) | Associate public IP address to the leader | `bool` | `true` | no | +| [leader\_custom\_setup\_base64](#input\_leader\_custom\_setup\_base64) | Custom bash script encoded in base64 to setup the leader | `string` | `""` | no | | [leader\_instance\_type](#input\_leader\_instance\_type) | Instance type of the cluster leader | `string` | `"t2.medium"` | no | -| [leader\_jvm\_args](#input\_leader\_jvm\_args) | JVM Leader JVM\_ARGS | `string` | `" -Xms2g -Xmx4g -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1ReservePercent=20 "` | no | +| [leader\_jvm\_args](#input\_leader\_jvm\_args) | JVM Leader JVM\_ARGS | `string` | `" -Xms2g -Xmx2g -XX:MaxMetaspaceSize=256m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1ReservePercent=20 "` | no | | [leader\_monitoring](#input\_leader\_monitoring) | Enable monitoring for the leader | `bool` | `true` | no | -| [leader\_tags](#input\_leader\_tags) | Tags of the cluster leader | `map` | n/a | yes | +| [leader\_tags](#input\_leader\_tags) | Tags of the cluster leader | `map` | `{}` | no | | [loadtest\_dir\_destination](#input\_loadtest\_dir\_destination) | Path to the destination loadtest directory | `string` | `"/loadtest"` | no | | [loadtest\_dir\_source](#input\_loadtest\_dir\_source) | Path to the source loadtest directory | `string` | n/a | yes | | [loadtest\_entrypoint](#input\_loadtest\_entrypoint) | Path to the entrypoint command | `string` | `"bzt -q -o execution.0.distributed=\"{NODES_IPS}\" *.yml"` | no | | [name](#input\_name) | Name of the provision | `string` | n/a | yes | -| [nodes\_ami\_id](#input\_nodes\_ami\_id) | Id of the AMI | `string` | n/a | yes | +| [nodes\_ami\_id](#input\_nodes\_ami\_id) | Id of the AMI | `string` | `""` | no | | [nodes\_associate\_public\_ip\_address](#input\_nodes\_associate\_public\_ip\_address) | Associate public IP address to the nodes | `bool` | `true` | no | +| [nodes\_custom\_setup\_base64](#input\_nodes\_custom\_setup\_base64) | Custom bash script encoded in base64 to setup the nodes | `string` | `""` | no | | [nodes\_intance\_type](#input\_nodes\_intance\_type) | Instance type of the cluster nodes | `string` | `"t2.medium"` | no | -| [nodes\_jvm\_args](#input\_nodes\_jvm\_args) | JVM Nodes JVM\_ARGS | `string` | `"-Xms4g -Xmx8g -XX:MaxMetaspaceSize=256m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1ReservePercent=20 -Dnashorn.args=--no-deprecation-warning -XX:+HeapDumpOnOutOfMemoryError "` | no | +| [nodes\_jvm\_args](#input\_nodes\_jvm\_args) | JVM Nodes JVM\_ARGS | `string` | `"-Xms2g -Xmx2g -XX:MaxMetaspaceSize=256m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1ReservePercent=20 -Dnashorn.args=--no-deprecation-warning -XX:+HeapDumpOnOutOfMemoryError "` | no | | [nodes\_monitoring](#input\_nodes\_monitoring) | Enable monitoring for the leader | `bool` | `true` | no | | [nodes\_size](#input\_nodes\_size) | Total number of nodes in the cluster | `number` | `2` | no | -| [nodes\_tags](#input\_nodes\_tags) | Tags of the cluster nodes | `map` | n/a | yes | +| [nodes\_tags](#input\_nodes\_tags) | Tags of the cluster nodes | `map` | `{}` | no | | [region](#input\_region) | Name of the region | `string` | `"us-east-1"` | no | -| [split\_data\_mass\_between\_nodes](#input\_split\_data\_mass\_between\_nodes) | Split data mass between nodes |
object({
enable = bool
data_mass_filename = string
})
|
{
"data_mass_filename": "../plan/data/data.csv",
"enable": true
}
| no | -| [ssh\_cidr\_ingress\_block](#input\_ssh\_cidr\_ingress\_block) | SSH user for the leader | `list` |
[
"0.0.0.0/0"
]
| no | -| [ssh\_export\_pem](#input\_ssh\_export\_pem) | n/a | `bool` | `true` | no | +| [split\_data\_mass\_between\_nodes](#input\_split\_data\_mass\_between\_nodes) | Split data mass between nodes |
object({
enable = bool
data_mass_filename = string
})
|
{
"data_mass_filename": "../plan/data/data.csv",
"enable": false
}
| no | +| [ssh\_cidr\_ingress\_blocks](#input\_ssh\_cidr\_ingress\_blocks) | SSH user for the leader | `list` |
[
"0.0.0.0/0"
]
| no | +| [ssh\_export\_pem](#input\_ssh\_export\_pem) | n/a | `bool` | `false` | no | | [ssh\_user](#input\_ssh\_user) | SSH user for the leader | `string` | `"ec2-user"` | no | | [subnet\_id](#input\_subnet\_id) | Id of the subnet | `string` | n/a | yes | -| [tags](#input\_tags) | Common tags | `map` | n/a | yes | +| [tags](#input\_tags) | Common tags | `map` | `{}` | no | | [taurus\_version](#input\_taurus\_version) | Taurus version | `string` | `"1.16.0"` | no | -| [vpc\_id](#input\_vpc\_id) | Id of the VPC | `string` | n/a | yes | +| [web\_cidr\_ingress\_blocks](#input\_web\_cidr\_ingress\_blocks) | web for the leader | `list` |
[
"0.0.0.0/0"
]
| no | ## Outputs diff --git a/examples/basic/README.md b/examples/basic/README.md index 27b526b..c1ebbfe 100644 --- a/examples/basic/README.md +++ b/examples/basic/README.md @@ -7,7 +7,6 @@ module "loadtest-distribuited" { source = "../../" #source = "marcosborges/loadtest-distribuited/aws" - #version = "0.0.8-alpha" name = "nome-da-implantacao" executor = "jmeter" diff --git a/examples/k6/README.md b/examples/k6/README.md index 53343a0..2efb34d 100644 --- a/examples/k6/README.md +++ b/examples/k6/README.md @@ -1,2 +1,2 @@ -# ManAtWork +# MenAtWork diff --git a/examples/split-data/README.md b/examples/split-data/README.md new file mode 100644 index 0000000..c1ebbfe --- /dev/null +++ b/examples/split-data/README.md @@ -0,0 +1,22 @@ +# Basic Config: + +In its basic use it is necessary to provide information about which network will be used, where are your test plan scripts and finally define the number of nodes needed to carry out the desired load. + +```hcl +module "loadtest-distribuited" { + + source = "../../" + #source = "marcosborges/loadtest-distribuited/aws" + + name = "nome-da-implantacao" + executor = "jmeter" + loadtest_dir_source = "../plan/" + nodes_size = 2 + + loadtest_entrypoint = "jmeter -n -t jmeter/*.jmx -R \"{NODES_IPS}\" -l /var/logs/loadtest -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + + subnet_id = data.aws_subnet.current.id +} +``` + +--- diff --git a/examples/split-data/data.tf b/examples/split-data/data.tf new file mode 100644 index 0000000..9ad876f --- /dev/null +++ b/examples/split-data/data.tf @@ -0,0 +1,7 @@ + +data "aws_subnet" "current" { + filter { + name = "tag:Name" + values = ["subnet-prd-a"] + } +} \ No newline at end of file diff --git a/examples/split-data/main.tf b/examples/split-data/main.tf new file mode 100644 index 0000000..f283e52 --- /dev/null +++ b/examples/split-data/main.tf @@ -0,0 +1,17 @@ +module "loadtest" { + + source = "../../" + #source = "marcosborges/loadtest-distribuited/aws" + #version = "0.0.8-alpha" + + name = "nome-da-implantacao" + executor = "jmeter" + loadtest_dir_source = "../plan/" + nodes_size = 2 + + loadtest_entrypoint = "jmeter -n -t jmeter/*.jmx -R \"{NODES_IPS}\" -l /var/logs/loadtest -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + + ssh_export_pem = true + subnet_id = data.aws_subnet.current.id +} + diff --git a/examples/split-data/output.tf b/examples/split-data/output.tf new file mode 100644 index 0000000..e03ffc0 --- /dev/null +++ b/examples/split-data/output.tf @@ -0,0 +1,19 @@ +output "leader_public_ip" { + value = module.loadtest.leader_public_ip + description = "The public IP address of the leader server instance." +} + +output "leader_private_ip" { + value = module.loadtest.leader_private_ip + description = "The private IP address of the leader server instance." +} + +output "nodes_public_ip" { + value = module.loadtest.nodes_public_ip + description = "The public IP address of the nodes instances." +} + +output "nodes_private_ip" { + value = module.loadtest.nodes_private_ip + description = "The private IP address of the nodes instances." +} \ No newline at end of file diff --git a/examples/split-data/provider.tf b/examples/split-data/provider.tf new file mode 100644 index 0000000..521cb84 --- /dev/null +++ b/examples/split-data/provider.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = "us-east-1" +} \ No newline at end of file From d2dea4b9e207c1823909d5d5bb4c65f29df03856 Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Thu, 4 Nov 2021 10:38:46 -0300 Subject: [PATCH 2/9] ... --- README.md | 14 +- examples/basic/README.md | 2 +- examples/basic/main.tf | 2 +- examples/plan/data/{data.csv => users.csv} | 0 examples/plan/data/users.csv000 | 100 +++++++++++ examples/plan/data/users.csv001 | 99 +++++++++++ examples/plan/jmeter/basic-with-data.jmx | 182 +++++++++++++++++++++ examples/split-data/README.md | 13 +- examples/split-data/main.tf | 13 +- executor.tf | 5 +- scripts/entrypoint.leader.full.sh.tpl | 6 +- scripts/entrypoint.node.full.sh.tpl | 7 +- slipter.tf | 6 +- variables.tf | 4 +- 14 files changed, 426 insertions(+), 27 deletions(-) rename examples/plan/data/{data.csv => users.csv} (100%) create mode 100644 examples/plan/data/users.csv000 create mode 100644 examples/plan/data/users.csv001 create mode 100644 examples/plan/jmeter/basic-with-data.jmx diff --git a/README.md b/README.md index 2b4cd4e..371b1d0 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,11 @@ This module proposes a simple and uncomplicated way to run your load tests creat module "loadtest" { source = "marcosborges/loadtest-distribuited/aws" -<<<<<<< HEAD -======= - version = "1.0.0" ->>>>>>> master name = "nome-da-implantacao" executor = "jmeter" loadtest_dir_source = "./assets" - loadtest_entrypoint = "jmeter -n -t -R \"{NODES_IPS}\" *.jmx" + loadtest_entrypoint = "jmeter -n -t jmeter/basic.jmx -R \"{NODES_IPS}\" *.jmx" nodes_size = 2 subnet_id = data.aws_subnet.current.id @@ -89,10 +85,6 @@ The module also provides advanced settings. module "loadtest" { source = "marcosborges/loadtest-distribuited/aws" -<<<<<<< HEAD -======= - version = "1.0.0" ->>>>>>> master subnet_id = data.aws_subnet.current.id @@ -106,7 +98,9 @@ module "loadtest" { #AUTO SPLIT split_data_mass_between_nodes = { enable = true - data_mass_filename = "../plan/data/data.csv" + data_mass_filenames = [ + "../plan/data/data.csv" + ] } #EXPORT SSH KEY diff --git a/examples/basic/README.md b/examples/basic/README.md index c505fee..cc2e4fe 100644 --- a/examples/basic/README.md +++ b/examples/basic/README.md @@ -13,7 +13,7 @@ module "loadtest-distribuited" { loadtest_dir_source = "../plan/" nodes_size = 2 - loadtest_entrypoint = "jmeter -n -t jmeter/*.jmx -R \"{NODES_IPS}\" -l /var/logs/loadtest -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + loadtest_entrypoint = "jmeter -n -t jmeter/basic.jmx -R \"{NODES_IPS}\" -l /var/logs/loadtest -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " subnet_id = data.aws_subnet.current.id } diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 1756464..2d49b9c 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -9,7 +9,7 @@ module "loadtest" { loadtest_dir_source = "../plan/" nodes_size = 2 - loadtest_entrypoint = "jmeter -n -t jmeter/*.jmx -R \"{NODES_IPS}\" -l /loadtest/logs -e -o /var/www/html/jmeter -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + loadtest_entrypoint = "jmeter -n -t jmeter/basic.jmx -R \"{NODES_IPS}\" -l /loadtest/logs -e -o /var/www/html/jmeter -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " ssh_export_pem = true subnet_id = data.aws_subnet.current.id diff --git a/examples/plan/data/data.csv b/examples/plan/data/users.csv similarity index 100% rename from examples/plan/data/data.csv rename to examples/plan/data/users.csv diff --git a/examples/plan/data/users.csv000 b/examples/plan/data/users.csv000 new file mode 100644 index 0000000..44f7295 --- /dev/null +++ b/examples/plan/data/users.csv000 @@ -0,0 +1,100 @@ +"Emanuelly Santos","emanuelly.santos.2751@stresstest.com","#2F3lq8f" +"Igor da Cunha","igor.da.cunha.5764@stresstest.com","!N6U5cV)" +"Sophia Mendes","sophia.mendes.2362@stresstest.com","&Gv9MTPm" +"Gustavo Henrique Vieira","gustavo.henrique.vieira.4365@stresstest.com","#v6#lS*h" +"Mariana da Cruz","mariana.da.cruz.7315@stresstest.com","&)dv1rUv" +"Theo Lopes","theo.lopes.5610@stresstest.com","d#o3DDae" +"Rafaela Pinto","rafaela.pinto.3598@stresstest.com","#6%MUN4r" +"Isabella Rodrigues","isabella.rodrigues.8283@stresstest.com","^2iR*Tv8" +"Nicole Farias","nicole.farias.7672@stresstest.com","_2*RJdp7" +"Lucca Correia","lucca.correia.7843@stresstest.com","#vx5Jyk5" +"Maria Julia Costa","maria.julia.costa.1410@stresstest.com","!5+ZM2rr" +"Stephany da Cruz","stephany.da.cruz.4002@stresstest.com","@3Mkdp$E" +"Rebeca Mendes","rebeca.mendes.7105@stresstest.com","!!x$2Tnr" +"Emanuel Jesus","emanuel.jesus.566@stresstest.com","$7_Cl34^" +"Gabriela Castro","gabriela.castro.6551@stresstest.com","@+2(kDqg" +"Fernando Carvalho","fernando.carvalho.1128@stresstest.com","!@7BIv9M" +"Lucas Gabriel Ferreira","lucas.gabriel.ferreira.2925@stresstest.com","wH#8HacJ" +"Joana Jesus","joana.jesus.9009@stresstest.com","*1$2^lEn" +"Pedro Miguel Costa","pedro.miguel.costa.5145@stresstest.com","W(8FDaDl" +"Alícia Santos","alicia.santos.2485@stresstest.com","(d78HOtj" +"Yuri Cardoso","yuri.cardoso.1370@stresstest.com","ap@4(Y_d" +"Ana Júlia Rezende","ana.julia.rezende.7055@stresstest.com","%00ljKPe" +"Sophie da Mota","sophie.da.mota.4821@stresstest.com","_)n11eZt" +"Joaquim da Mata","joaquim.da.mata.7914@stresstest.com","h&!4Vxc8" +"Yuri Peixoto","yuri.peixoto.2727@stresstest.com","#D)37QGa" +"Thomas Martins","thomas.martins.876@stresstest.com","_#1HRiC5" +"Gustavo Gonçalves","gustavo.goncalves.177@stresstest.com","*4FEJBwk" +"Julia Rocha","julia.rocha.9292@stresstest.com","#+7(#Fn2" +"Rafael Alves","rafael.alves.2848@stresstest.com","U&HB6PRy" +"Davi Lucas Moura","davi.lucas.moura.6287@stresstest.com","%MJ8E#Sn" +"Davi Lucca Cunha","davi.lucca.cunha.2075@stresstest.com","!!c5KAwq" +"Heloísa Viana","heloisa.viana.3661@stresstest.com","N#2q$ZkC" +"João Vitor Fernandes","joao.vitor.fernandes.9081@stresstest.com","G(1f+Dh9" +"Miguel Monteiro","miguel.monteiro.73@stresstest.com","^*b0YGql" +"Cauã Teixeira","caua.teixeira.2807@stresstest.com","$!9eCnOq" +"Nina Costela","nina.costela.1446@stresstest.com","9@7LUu+t" +"João Melo","joao.melo.5445@stresstest.com","m%5eRf(v" +"Thiago Fernandes","thiago.fernandes.7312@stresstest.com","!71OOnq9" +"Luana Monteiro","luana.monteiro.3438@stresstest.com",")4Z3UATs" +"Sofia Pires","sofia.pires.6472@stresstest.com","PW)T3_Xv" +"Nathan Barbosa","nathan.barbosa.3838@stresstest.com","(HQ7QFXk" +"Carolina da Cunha","carolina.da.cunha.2486@stresstest.com","+o7lNG!c" +"Emanuelly Pinto","emanuelly.pinto.5923@stresstest.com","7*42)Efc" +"Maria Julia Pinto","maria.julia.pinto.3870@stresstest.com","_u5NAUo(" +"Júlia Alves","julia.alves.6894@stresstest.com","^4ZyZjsD" +"Noah Nunes","noah.nunes.6637@stresstest.com","c&3NuqrW" +"Heitor Fernandes","heitor.fernandes.5924@stresstest.com","J$0w5CQu" +"Maitê Pires","maite.pires.7971@stresstest.com","@0VYF+s5" +"Diogo Martins","diogo.martins.3707@stresstest.com","(3zwHPuv" +"Bruno Oliveira","bruno.oliveira.9742@stresstest.com","+m4QZcZp" +"Ana Teixeira","ana.teixeira.5801@stresstest.com","@+1sCIRn" +"Thales Freitas","thales.freitas.1440@stresstest.com","+9C7vXrt" +"Antônio Oliveira","antonio.oliveira.9782@stresstest.com","&&7B5ekb" +"Ian da Rosa","ian.da.rosa.9874@stresstest.com","FJ)31RxJ" +"Kamilly Barbosa","kamilly.barbosa.7152@stresstest.com","V4&4DbKF" +"Henrique Moura","henrique.moura.2260@stresstest.com","p2$6TfUj" +"Lavínia Rezende","lavinia.rezende.1313@stresstest.com","H+39GwMq" +"Ana Sophia Rezende","ana.sophia.rezende.8119@stresstest.com","M+3E&!Ac" +"Ryan Teixeira","ryan.teixeira.4565@stresstest.com","RDC#4CTc" +"Isabelly Dias","isabelly.dias.3624@stresstest.com","@I4_qqKc" +"Alexandre Almeida","alexandre.almeida.6935@stresstest.com","^u#0O#wc" +"Camila Viana","camila.viana.9964@stresstest.com","@6yWwewC" +"Beatriz da Mata","beatriz.da.mata.475@stresstest.com","4%wJ1Nys" +"Brenda Nogueira","brenda.nogueira.9789@stresstest.com","&4Qcxzv%" +"Juan Pires","juan.pires.340@stresstest.com","w#48e%Mx" +"Luiz Gustavo Correia","luiz.gustavo.correia.1100@stresstest.com","&Ew*9Uhr" +"Danilo da Rosa","danilo.da.rosa.4976@stresstest.com","T#U6BBWv" +"Larissa da Paz","larissa.da.paz.549@stresstest.com","#8tQ_N2p" +"Carolina da Rocha","carolina.da.rocha.5134@stresstest.com","ob)uE5Sz" +"Valentina Correia","valentina.correia.8443@stresstest.com","@0^2dU5j" +"Maria Gonçalves","maria.goncalves.5317@stresstest.com","#b0i@9Ze" +"Maria Cecília Ferreira","maria.cecilia.ferreira.515@stresstest.com","vl!2vYoP" +"Pedro Porto","pedro.porto.6311@stresstest.com","!uYc45Rd" +"Theo Farias","theo.farias.7169@stresstest.com","+3DpMdlr" +"Brenda Duarte","brenda.duarte.6356@stresstest.com","%#1Bpstd" +"Sofia da Mota","sofia.da.mota.1857@stresstest.com","(2nh#Ii(" +"Caio Fernandes","caio.fernandes.7417@stresstest.com","^64mYaWG" +"Juan Rodrigues","juan.rodrigues.1331@stresstest.com","Q9@2U4fV" +"Luiz Henrique Castro","luiz.henrique.castro.5808@stresstest.com","(VA296Yz" +"Valentina Moraes","valentina.moraes.3059@stresstest.com","&(0JUjjP" +"Pedro Ramos","pedro.ramos.535@stresstest.com","*6lXPOhM" +"Daniela Castro","daniela.castro.8001@stresstest.com",")5V$88m+" +"Luiz Fernando Ferreira","luiz.fernando.ferreira.5297@stresstest.com","!scx5hSq" +"Luiza Dias","luiza.dias.6762@stresstest.com","7^5S9%wh" +"Vitor Hugo Teixeira","vitor.hugo.teixeira.9957@stresstest.com",")1Rt0IvR" +"Vicente Ferreira","vicente.ferreira.6646@stresstest.com","vcl_8fKu" +"Levi Rezende","levi.rezende.712@stresstest.com",")1Hm2rYb" +"Nicole Rocha","nicole.rocha.8261@stresstest.com","&(V8xeLq" +"Juan Vieira","juan.vieira.1716@stresstest.com","(%9Iswd)" +"Júlia Freitas","julia.freitas.2515@stresstest.com","!7LCVvwE" +"Maria Alice Almeida","maria.alice.almeida.4400@stresstest.com","h(6Stn$n" +"Breno Moura","breno.moura.4636@stresstest.com",")FC3KvyH" +"Mirella Jesus","mirella.jesus.1580@stresstest.com","+Q59Of6j" +"Benjamin Melo","benjamin.melo.8468@stresstest.com","t*6WPi6Z" +"Bryan Moreira","bryan.moreira.3104@stresstest.com","N)2CXUWj" +"Leandro Viana","leandro.viana.3245@stresstest.com","O#o7LkDq" +"Raquel Novaes","raquel.novaes.7120@stresstest.com","*4ZRpRwi" +"Vicente da Rosa","vicente.da.rosa.5597@stresstest.com","H+@5ntQb" +"Ana Clara Rocha","ana.clara.rocha.5483@stresstest.com","+dfy6FHo" +"Emanuelly Jesus","emanuelly.jesus.38@stresstest.com","D%9TxjRk" \ No newline at end of file diff --git a/examples/plan/data/users.csv001 b/examples/plan/data/users.csv001 new file mode 100644 index 0000000..b1fc588 --- /dev/null +++ b/examples/plan/data/users.csv001 @@ -0,0 +1,99 @@ +"Ana Teixeira","ana.teixeira.5801@stresstest.com","@+1sCIRn" +"Thales Freitas","thales.freitas.1440@stresstest.com","+9C7vXrt" +"Antônio Oliveira","antonio.oliveira.9782@stresstest.com","&&7B5ekb" +"Ian da Rosa","ian.da.rosa.9874@stresstest.com","FJ)31RxJ" +"Kamilly Barbosa","kamilly.barbosa.7152@stresstest.com","V4&4DbKF" +"Henrique Moura","henrique.moura.2260@stresstest.com","p2$6TfUj" +"Lavínia Rezende","lavinia.rezende.1313@stresstest.com","H+39GwMq" +"Ana Sophia Rezende","ana.sophia.rezende.8119@stresstest.com","M+3E&!Ac" +"Ryan Teixeira","ryan.teixeira.4565@stresstest.com","RDC#4CTc" +"Isabelly Dias","isabelly.dias.3624@stresstest.com","@I4_qqKc" +"Alexandre Almeida","alexandre.almeida.6935@stresstest.com","^u#0O#wc" +"Camila Viana","camila.viana.9964@stresstest.com","@6yWwewC" +"Beatriz da Mata","beatriz.da.mata.475@stresstest.com","4%wJ1Nys" +"Brenda Nogueira","brenda.nogueira.9789@stresstest.com","&4Qcxzv%" +"Juan Pires","juan.pires.340@stresstest.com","w#48e%Mx" +"Luiz Gustavo Correia","luiz.gustavo.correia.1100@stresstest.com","&Ew*9Uhr" +"Danilo da Rosa","danilo.da.rosa.4976@stresstest.com","T#U6BBWv" +"Larissa da Paz","larissa.da.paz.549@stresstest.com","#8tQ_N2p" +"Carolina da Rocha","carolina.da.rocha.5134@stresstest.com","ob)uE5Sz" +"Valentina Correia","valentina.correia.8443@stresstest.com","@0^2dU5j" +"Maria Gonçalves","maria.goncalves.5317@stresstest.com","#b0i@9Ze" +"Maria Cecília Ferreira","maria.cecilia.ferreira.515@stresstest.com","vl!2vYoP" +"Pedro Porto","pedro.porto.6311@stresstest.com","!uYc45Rd" +"Theo Farias","theo.farias.7169@stresstest.com","+3DpMdlr" +"Brenda Duarte","brenda.duarte.6356@stresstest.com","%#1Bpstd" +"Sofia da Mota","sofia.da.mota.1857@stresstest.com","(2nh#Ii(" +"Caio Fernandes","caio.fernandes.7417@stresstest.com","^64mYaWG" +"Juan Rodrigues","juan.rodrigues.1331@stresstest.com","Q9@2U4fV" +"Luiz Henrique Castro","luiz.henrique.castro.5808@stresstest.com","(VA296Yz" +"Valentina Moraes","valentina.moraes.3059@stresstest.com","&(0JUjjP" +"Pedro Ramos","pedro.ramos.535@stresstest.com","*6lXPOhM" +"Daniela Castro","daniela.castro.8001@stresstest.com",")5V$88m+" +"Luiz Fernando Ferreira","luiz.fernando.ferreira.5297@stresstest.com","!scx5hSq" +"Luiza Dias","luiza.dias.6762@stresstest.com","7^5S9%wh" +"Vitor Hugo Teixeira","vitor.hugo.teixeira.9957@stresstest.com",")1Rt0IvR" +"Vicente Ferreira","vicente.ferreira.6646@stresstest.com","vcl_8fKu" +"Levi Rezende","levi.rezende.712@stresstest.com",")1Hm2rYb" +"Nicole Rocha","nicole.rocha.8261@stresstest.com","&(V8xeLq" +"Juan Vieira","juan.vieira.1716@stresstest.com","(%9Iswd)" +"Júlia Freitas","julia.freitas.2515@stresstest.com","!7LCVvwE" +"Maria Alice Almeida","maria.alice.almeida.4400@stresstest.com","h(6Stn$n" +"Breno Moura","breno.moura.4636@stresstest.com",")FC3KvyH" +"Mirella Jesus","mirella.jesus.1580@stresstest.com","+Q59Of6j" +"Benjamin Melo","benjamin.melo.8468@stresstest.com","t*6WPi6Z" +"Bryan Moreira","bryan.moreira.3104@stresstest.com","N)2CXUWj" +"Leandro Viana","leandro.viana.3245@stresstest.com","O#o7LkDq" +"Raquel Novaes","raquel.novaes.7120@stresstest.com","*4ZRpRwi" +"Vicente da Rosa","vicente.da.rosa.5597@stresstest.com","H+@5ntQb" +"Ana Clara Rocha","ana.clara.rocha.5483@stresstest.com","+dfy6FHo" +"Emanuelly Jesus","emanuelly.jesus.38@stresstest.com","D%9TxjRk""Emanuelly Santos","emanuelly.santos.2751@stresstest.com","#2F3lq8f" +"Igor da Cunha","igor.da.cunha.5764@stresstest.com","!N6U5cV)" +"Sophia Mendes","sophia.mendes.2362@stresstest.com","&Gv9MTPm" +"Gustavo Henrique Vieira","gustavo.henrique.vieira.4365@stresstest.com","#v6#lS*h" +"Mariana da Cruz","mariana.da.cruz.7315@stresstest.com","&)dv1rUv" +"Theo Lopes","theo.lopes.5610@stresstest.com","d#o3DDae" +"Rafaela Pinto","rafaela.pinto.3598@stresstest.com","#6%MUN4r" +"Isabella Rodrigues","isabella.rodrigues.8283@stresstest.com","^2iR*Tv8" +"Nicole Farias","nicole.farias.7672@stresstest.com","_2*RJdp7" +"Lucca Correia","lucca.correia.7843@stresstest.com","#vx5Jyk5" +"Maria Julia Costa","maria.julia.costa.1410@stresstest.com","!5+ZM2rr" +"Stephany da Cruz","stephany.da.cruz.4002@stresstest.com","@3Mkdp$E" +"Rebeca Mendes","rebeca.mendes.7105@stresstest.com","!!x$2Tnr" +"Emanuel Jesus","emanuel.jesus.566@stresstest.com","$7_Cl34^" +"Gabriela Castro","gabriela.castro.6551@stresstest.com","@+2(kDqg" +"Fernando Carvalho","fernando.carvalho.1128@stresstest.com","!@7BIv9M" +"Lucas Gabriel Ferreira","lucas.gabriel.ferreira.2925@stresstest.com","wH#8HacJ" +"Joana Jesus","joana.jesus.9009@stresstest.com","*1$2^lEn" +"Pedro Miguel Costa","pedro.miguel.costa.5145@stresstest.com","W(8FDaDl" +"Alícia Santos","alicia.santos.2485@stresstest.com","(d78HOtj" +"Yuri Cardoso","yuri.cardoso.1370@stresstest.com","ap@4(Y_d" +"Ana Júlia Rezende","ana.julia.rezende.7055@stresstest.com","%00ljKPe" +"Sophie da Mota","sophie.da.mota.4821@stresstest.com","_)n11eZt" +"Joaquim da Mata","joaquim.da.mata.7914@stresstest.com","h&!4Vxc8" +"Yuri Peixoto","yuri.peixoto.2727@stresstest.com","#D)37QGa" +"Thomas Martins","thomas.martins.876@stresstest.com","_#1HRiC5" +"Gustavo Gonçalves","gustavo.goncalves.177@stresstest.com","*4FEJBwk" +"Julia Rocha","julia.rocha.9292@stresstest.com","#+7(#Fn2" +"Rafael Alves","rafael.alves.2848@stresstest.com","U&HB6PRy" +"Davi Lucas Moura","davi.lucas.moura.6287@stresstest.com","%MJ8E#Sn" +"Davi Lucca Cunha","davi.lucca.cunha.2075@stresstest.com","!!c5KAwq" +"Heloísa Viana","heloisa.viana.3661@stresstest.com","N#2q$ZkC" +"João Vitor Fernandes","joao.vitor.fernandes.9081@stresstest.com","G(1f+Dh9" +"Miguel Monteiro","miguel.monteiro.73@stresstest.com","^*b0YGql" +"Cauã Teixeira","caua.teixeira.2807@stresstest.com","$!9eCnOq" +"Nina Costela","nina.costela.1446@stresstest.com","9@7LUu+t" +"João Melo","joao.melo.5445@stresstest.com","m%5eRf(v" +"Thiago Fernandes","thiago.fernandes.7312@stresstest.com","!71OOnq9" +"Luana Monteiro","luana.monteiro.3438@stresstest.com",")4Z3UATs" +"Sofia Pires","sofia.pires.6472@stresstest.com","PW)T3_Xv" +"Nathan Barbosa","nathan.barbosa.3838@stresstest.com","(HQ7QFXk" +"Carolina da Cunha","carolina.da.cunha.2486@stresstest.com","+o7lNG!c" +"Emanuelly Pinto","emanuelly.pinto.5923@stresstest.com","7*42)Efc" +"Maria Julia Pinto","maria.julia.pinto.3870@stresstest.com","_u5NAUo(" +"Júlia Alves","julia.alves.6894@stresstest.com","^4ZyZjsD" +"Noah Nunes","noah.nunes.6637@stresstest.com","c&3NuqrW" +"Heitor Fernandes","heitor.fernandes.5924@stresstest.com","J$0w5CQu" +"Maitê Pires","maite.pires.7971@stresstest.com","@0VYF+s5" +"Diogo Martins","diogo.martins.3707@stresstest.com","(3zwHPuv" +"Bruno Oliveira","bruno.oliveira.9742@stresstest.com","+m4QZcZp" diff --git a/examples/plan/jmeter/basic-with-data.jmx b/examples/plan/jmeter/basic-with-data.jmx new file mode 100644 index 0000000..57dc6cf --- /dev/null +++ b/examples/plan/jmeter/basic-with-data.jmx @@ -0,0 +1,182 @@ + + + + + + false + true + false + + + + + + + + , + + ./data/users.csv + false + true + true + shareMode.all + false + name,email,password + + + + continue + + false + 1 + + 20 + 10 + true + 30 + 5 + true + true + + + + + + + google.com + 443 + https + + ?email=${email} + GET + false + true + true + false + true + true + + 1000 + 6000 + + + + + + + microsoft.com + 443 + https + + + GET + false + true + true + false + true + true + + 1000 + 6000 + + + + + + + facebook.com + 443 + https + + + GET + false + true + true + false + true + true + + 1000 + 6000 + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + diff --git a/examples/split-data/README.md b/examples/split-data/README.md index c1ebbfe..edde095 100644 --- a/examples/split-data/README.md +++ b/examples/split-data/README.md @@ -1,6 +1,6 @@ -# Basic Config: +# Split data between nodes: -In its basic use it is necessary to provide information about which network will be used, where are your test plan scripts and finally define the number of nodes needed to carry out the desired load. + ```hcl module "loadtest-distribuited" { @@ -13,7 +13,14 @@ module "loadtest-distribuited" { loadtest_dir_source = "../plan/" nodes_size = 2 - loadtest_entrypoint = "jmeter -n -t jmeter/*.jmx -R \"{NODES_IPS}\" -l /var/logs/loadtest -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + loadtest_entrypoint = "jmeter -n -t jmeter/*.jmx -R \"{NODES_IPS}\" -l /loadtest/logs -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + + split_data_mass_between_nodes = { + enable = true + data_mass_filenames = [ + "../plan/data/data.csv" + ] + } subnet_id = data.aws_subnet.current.id } diff --git a/examples/split-data/main.tf b/examples/split-data/main.tf index f283e52..f505880 100644 --- a/examples/split-data/main.tf +++ b/examples/split-data/main.tf @@ -2,16 +2,21 @@ module "loadtest" { source = "../../" #source = "marcosborges/loadtest-distribuited/aws" - #version = "0.0.8-alpha" - + name = "nome-da-implantacao" executor = "jmeter" loadtest_dir_source = "../plan/" nodes_size = 2 - loadtest_entrypoint = "jmeter -n -t jmeter/*.jmx -R \"{NODES_IPS}\" -l /var/logs/loadtest -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + loadtest_entrypoint = "jmeter -n -t jmeter/basic-with-data.jmx -R \"{NODES_IPS}\" -l /loadtest/logs -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + + split_data_mass_between_nodes = { + enable = true + data_mass_filenames = [ + "data/users.csv" + ] + } - ssh_export_pem = true subnet_id = data.aws_subnet.current.id } diff --git a/executor.tf b/executor.tf index a1a92f0..44d8320 100644 --- a/executor.tf +++ b/executor.tf @@ -31,8 +31,9 @@ resource "null_resource" "executor" { count = local.auto_execute ? 1 : 0 depends_on = [ - aws_instance.leader, - aws_instance.nodes + null_resource.publish_split_data, + aws_instance.nodes, + aws_instance.leader ] connection { diff --git a/scripts/entrypoint.leader.full.sh.tpl b/scripts/entrypoint.leader.full.sh.tpl index e1a4a1a..8552583 100644 --- a/scripts/entrypoint.leader.full.sh.tpl +++ b/scripts/entrypoint.leader.full.sh.tpl @@ -1,7 +1,7 @@ #!/bin/bash sudo yum update -y -sudo yum install -y pcre2-devel.x86_64 python gcc python3-devel tzdata curl unzip bash java-11-amazon-corretto htop httpd +sudo yum install -y pcre2-devel.x86_64 python gcc python3-devel tzdata curl unzip bash java-11-amazon-corretto htop httpd k6 # APACHE sudo systemctl enable httpd @@ -13,6 +13,10 @@ sudo rm -rf /var/www/html/* export BZT_VERSION="1.16.0" sudo pip3 install bzt==$BZT_VERSION +# LOCUST +export LOCUST_VERSION="2.4.3" +sudo pip3 install locust==$LOCUST_VERSION + # JMETER export MIRROR_HOST=https://archive.apache.org/dist/jmeter export JMETER_VERSION="5.4.1" diff --git a/scripts/entrypoint.node.full.sh.tpl b/scripts/entrypoint.node.full.sh.tpl index 3555112..1fa10a1 100644 --- a/scripts/entrypoint.node.full.sh.tpl +++ b/scripts/entrypoint.node.full.sh.tpl @@ -1,12 +1,17 @@ #!/bin/bash sudo yum update -y -sudo yum install -y pcre2-devel.x86_64 python gcc python3-devel tzdata curl unzip bash java-11-amazon-corretto htop +sudo yum install -y pcre2-devel.x86_64 python gcc python3-devel tzdata curl unzip bash java-11-amazon-corretto htop k6 # TAURUS export BZT_VERSION="1.16.0" sudo pip3 install bzt==$BZT_VERSION +# LOCUST +export LOCUST_VERSION="2.4.3" +sudo pip3 install locust==$LOCUST_VERSION + + # JMETER export MIRROR_HOST=https://archive.apache.org/dist/jmeter export JMETER_VERSION="5.4.1" diff --git a/slipter.tf b/slipter.tf index 5217d65..1dce8bf 100644 --- a/slipter.tf +++ b/slipter.tf @@ -1,12 +1,14 @@ locals { split_enable = var.split_data_mass_between_nodes.enable - split_data_mass_filename = var.split_data_mass_between_nodes.data_mass_filename + split_data_mass_filename = var.split_data_mass_between_nodes.data_mass_filenames[0] split_size = var.nodes_size - split_cmd = local.split_enable ? "split -a 3 -d -nr/${local.split_size} ${local.split_data_mass_filename} ${local.split_data_mass_filename}" : "echo 'auto split disabled'" + split_cmd = local.split_enable ? "cd ${var.loadtest_dir_source} && split -a 3 -d -nr/${local.split_size} ${local.split_data_mass_filename} ${local.split_data_mass_filename}" : "echo 'auto split disabled'" } resource "null_resource" "split_data" { + provisioner "local-exec" { + command = local.split_cmd } } diff --git a/variables.tf b/variables.tf index 268949e..b1157a1 100644 --- a/variables.tf +++ b/variables.tf @@ -26,11 +26,11 @@ variable "loadtest_dir_destination" { variable "split_data_mass_between_nodes" { type = object({ enable = bool - data_mass_filename = string + data_mass_filenames = list(string) }) default = { enable = false - data_mass_filename = "../plan/data/data.csv" + data_mass_filenames = ["../plan/data/data.csv"] } description = "Split data mass between nodes" } From 4d49388cbffadae45f35bbfa7ffc5ccc6fb58e08 Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Fri, 5 Nov 2021 22:19:03 -0300 Subject: [PATCH 3/9] .... --- examples/basic/main.tf | 2 +- examples/plan/jmeter/basic-with-data.jmx | 2 +- examples/split-data/main.tf | 4 ++- examples/split-data/output.tf | 5 ++++ examples/split-data/variables.tf | 4 +++ executor.tf | 35 +++++++++++++++++------- nodes.tf | 6 +++- slipter.tf | 21 +++++++++++--- 8 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 examples/split-data/variables.tf diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 2d49b9c..1eb0ae8 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -11,6 +11,6 @@ module "loadtest" { loadtest_entrypoint = "jmeter -n -t jmeter/basic.jmx -R \"{NODES_IPS}\" -l /loadtest/logs -e -o /var/www/html/jmeter -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " - ssh_export_pem = true + ssh_export_pem = false subnet_id = data.aws_subnet.current.id } \ No newline at end of file diff --git a/examples/plan/jmeter/basic-with-data.jmx b/examples/plan/jmeter/basic-with-data.jmx index 57dc6cf..d316a9f 100644 --- a/examples/plan/jmeter/basic-with-data.jmx +++ b/examples/plan/jmeter/basic-with-data.jmx @@ -15,7 +15,7 @@ , - ./data/users.csv + /loadtest/data/users.csv false true true diff --git a/examples/split-data/main.tf b/examples/split-data/main.tf index f505880..d8752a0 100644 --- a/examples/split-data/main.tf +++ b/examples/split-data/main.tf @@ -8,7 +8,7 @@ module "loadtest" { loadtest_dir_source = "../plan/" nodes_size = 2 - loadtest_entrypoint = "jmeter -n -t jmeter/basic-with-data.jmx -R \"{NODES_IPS}\" -l /loadtest/logs -e -o /var/www/html -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true " + loadtest_entrypoint = "jmeter -n -t jmeter/basic-with-data.jmx -R \"{NODES_IPS}\" -l /loadtest/logs -e -o /var/www/html/jmeter -Dnashorn.args=--no-deprecation-warning -Dserver.rmi.ssl.disable=true -LDEBUG " split_data_mass_between_nodes = { enable = true @@ -18,5 +18,7 @@ module "loadtest" { } subnet_id = data.aws_subnet.current.id + + ssh_export_pem = true } diff --git a/examples/split-data/output.tf b/examples/split-data/output.tf index e03ffc0..8c7dc97 100644 --- a/examples/split-data/output.tf +++ b/examples/split-data/output.tf @@ -16,4 +16,9 @@ output "nodes_public_ip" { output "nodes_private_ip" { value = module.loadtest.nodes_private_ip description = "The private IP address of the nodes instances." +} + +output "dashboard_url" { + value = "http://${coalesce(module.loadtest.leader_public_ip, module.loadtest.leader_private_ip)}/${var.executor}" + description = "The URL of the loadtest dashboard." } \ No newline at end of file diff --git a/examples/split-data/variables.tf b/examples/split-data/variables.tf new file mode 100644 index 0000000..c8d1955 --- /dev/null +++ b/examples/split-data/variables.tf @@ -0,0 +1,4 @@ +variable "executor" { + description = "Executor name" + default = "jmeter" +} \ No newline at end of file diff --git a/executor.tf b/executor.tf index 44d8320..5a1a69c 100644 --- a/executor.tf +++ b/executor.tf @@ -24,6 +24,16 @@ locals { waiting_command = "while [ ! -f /tmp/finished-setup ]; do echo 'waiting setup to be instaled'; sleep 5; done" nodes_ips = local.executor.nodes_ips + entrypoint = replace( + replace( + var.loadtest_entrypoint, + "{NODES_IPS}", + local.nodes_ips + ), + "{LEADER_IP}", + local.leader_private_ip + ) + } resource "null_resource" "executor" { @@ -43,7 +53,7 @@ resource "null_resource" "executor" { private_key = tls_private_key.loadtest.private_key_pem } - #EXECUTE SCRIPTS + #WAITING FOR INSTANCE FINISHING SETUP provisioner "remote-exec" { inline = [ "echo 'START EXECUTION'", @@ -51,22 +61,27 @@ resource "null_resource" "executor" { ] } + #CLEANING UP provisioner "remote-exec" { inline = [ - "echo DIR: ${var.loadtest_dir_destination}", - "cd ${var.loadtest_dir_destination}", - "echo PATH: $PATH", - "echo JVM_ARGS: $JVM_ARGS", "sudo chmod 777 /var/www/html -Rf", "sudo rm -rf /var/www/html/*", "sudo rm -rf /loadtest/logs", - "echo ${replace(var.loadtest_entrypoint, "{NODES_IPS}", local.nodes_ips)}", - replace(var.loadtest_entrypoint, "{NODES_IPS}", local.nodes_ips) ] } - # triggers = { - # always_run = timestamp() - # } + #EXECUTING LOAD TEST + provisioner "remote-exec" { + inline = [ + "echo DIR: ${var.loadtest_dir_destination}", + "cd ${var.loadtest_dir_destination}", + "echo ${local.entrypoint}", + local.entrypoint + ] + } + + triggers = { + always_run = timestamp() + } } diff --git a/nodes.tf b/nodes.tf index c6d9583..a099f01 100644 --- a/nodes.tf +++ b/nodes.tf @@ -14,7 +14,7 @@ resource "aws_instance" "nodes" { iam_instance_profile = aws_iam_instance_profile.loadtest.name user_data_base64 = local.nodes_user_data_base64 - #PUBLISHING SCRIPTS AND DATA + key_name = aws_key_pair.loadtest.key_name connection { host = coalesce(self.public_ip, self.private_ip) @@ -23,6 +23,8 @@ resource "aws_instance" "nodes" { private_key = tls_private_key.loadtest.private_key_pem } + + # CONFIG FILESYSTEM AND PERMITIONS provisioner "remote-exec" { inline = [ "sudo mkdir -p ${var.loadtest_dir_destination} || true", @@ -30,11 +32,13 @@ resource "aws_instance" "nodes" { ] } + # PUBLISH ALL FILES provisioner "file" { destination = var.loadtest_dir_destination source = var.loadtest_dir_source } + # WAITING FOR NODES TO BE READY provisioner "remote-exec" { inline = [ "echo 'START EXECUTION'", diff --git a/slipter.tf b/slipter.tf index 1dce8bf..4383da6 100644 --- a/slipter.tf +++ b/slipter.tf @@ -3,6 +3,19 @@ locals { split_data_mass_filename = var.split_data_mass_between_nodes.data_mass_filenames[0] split_size = var.nodes_size split_cmd = local.split_enable ? "cd ${var.loadtest_dir_source} && split -a 3 -d -nr/${local.split_size} ${local.split_data_mass_filename} ${local.split_data_mass_filename}" : "echo 'auto split disabled'" + + publish_split_data_count = local.split_enable ? var.nodes_size : 0 + + # publish_files = flatten([ + # for network_key, network in var.networks : [ + # for subnet_key, subnet in network.subnets : { + # network_key = network_key + # subnet_key = subnet_key + # network_id = aws_vpc.example[network_key].id + # cidr_block = subnet.cidr_block + # } + # ] + # ]) } resource "null_resource" "split_data" { @@ -13,10 +26,9 @@ resource "null_resource" "split_data" { } } - resource "null_resource" "publish_split_data" { - count = local.split_enable ? var.nodes_size : 0 + count = local.publish_split_data_count depends_on = [ null_resource.split_data, @@ -39,8 +51,9 @@ resource "null_resource" "publish_split_data" { provisioner "remote-exec" { inline = [ - #"while [ ! -f /var/lib/apache-jmeter-5.3/bin/jmeter ]; do sleep 10; done", - "echo ${var.loadtest_dir_destination} ${format("%03d", count.index)}" + "rm ${var.loadtest_dir_destination}/${var.split_data_mass_between_nodes.data_mass_filenames[0]}", + "echo XXXXXX ${var.loadtest_dir_destination}/${var.split_data_mass_between_nodes.data_mass_filenames[0]}${format("%03d", count.index)}", + "mv ${var.loadtest_dir_destination}/${var.split_data_mass_between_nodes.data_mass_filenames[0]}${format("%03d", count.index)} ${var.loadtest_dir_destination}/${var.split_data_mass_between_nodes.data_mass_filenames[0]}" ] } From 8174f15fda7d10e4dc200e303b1e8f7bd10153e6 Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Fri, 5 Nov 2021 22:20:11 -0300 Subject: [PATCH 4/9] ... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 371b1d0..f699c63 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This module proposes a simple and uncomplicated way to run your load tests created with JMeter or TaurusBzt on AWS as IaaS. -![bp](https://raw.githubusercontent.com/marcosborges/terraform-aws-loadtest-distribuited/v0.0.7-alpha/assets/blueprint.png) +![bp](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/master/assets/assets/blueprint.png) ## Basic usage with JMeter From 92775ad5cc3e56e9dd555c9233cd00b7797825a8 Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Sat, 6 Nov 2021 18:00:11 -0300 Subject: [PATCH 5/9] .... --- assets/split-cmd-result.png | Bin 0 -> 10567 bytes assets/split-cmd.png | Bin 0 -> 7821 bytes assets/split-transfer.png | Bin 0 -> 21990 bytes assets/split.png | Bin 0 -> 13412 bytes examples/plan/data/users.csv000 | 100 ------------------------ examples/plan/data/users.csv001 | 99 ------------------------ examples/split-data/README.md | 40 ++++++++-- executor.tf | 2 +- leader.tf | 9 +++ nodes.tf | 2 + scripts/entrypoint.leader.full.sh.tpl | 4 + scripts/entrypoint.node.full.sh.tpl | 4 + security.tf | 3 + slipter.tf | 107 +++++++++++++++----------- 14 files changed, 116 insertions(+), 254 deletions(-) create mode 100644 assets/split-cmd-result.png create mode 100644 assets/split-cmd.png create mode 100644 assets/split-transfer.png create mode 100644 assets/split.png delete mode 100644 examples/plan/data/users.csv000 delete mode 100644 examples/plan/data/users.csv001 diff --git a/assets/split-cmd-result.png b/assets/split-cmd-result.png new file mode 100644 index 0000000000000000000000000000000000000000..cb358299b9e3935500f6f840c1bcd4de73a9cdb2 GIT binary patch literal 10567 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 NC?O}FEvDuEe*m;eeP93p literal 0 HcmV?d00001 diff --git a/assets/split-cmd.png b/assets/split-cmd.png new file mode 100644 index 0000000000000000000000000000000000000000..95a16c0a6dbc24da9d4eb9698e0fe4b15b1c9e21 GIT binary patch literal 7821 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 G2K)!Zf1KX{ literal 0 HcmV?d00001 diff --git a/assets/split-transfer.png b/assets/split-transfer.png new file mode 100644 index 0000000000000000000000000000000000000000..09d5137d75a2ad77d068af0c1e6186db150036f0 GIT binary patch literal 21990 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 YL-(h(-~>)QsC9$nC0WsT7jHlOKdUx{kpKVy literal 0 HcmV?d00001 diff --git a/assets/split.png b/assets/split.png new file mode 100644 index 0000000000000000000000000000000000000000..c814cd6e07d86f34e67c8473441438ff060d0f6c GIT binary patch literal 13412 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 afMuD?*NA ~/.ssh/id_rsa", + "chmod 600 ~/.ssh/id_rsa", "sudo mkdir -p ${var.loadtest_dir_destination} || true", "sudo chown ${var.ssh_user}:${var.ssh_user} ${var.loadtest_dir_destination} || true" ] } + #-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null provisioner "file" { destination = var.loadtest_dir_destination @@ -42,3 +45,9 @@ resource "aws_instance" "leader" { } ) } + + +resource "null_resource" "push_key_pair_to_leader" { + + +} \ No newline at end of file diff --git a/nodes.tf b/nodes.tf index a099f01..2f038e2 100644 --- a/nodes.tf +++ b/nodes.tf @@ -27,6 +27,8 @@ resource "aws_instance" "nodes" { # CONFIG FILESYSTEM AND PERMITIONS provisioner "remote-exec" { inline = [ + "echo '${tls_private_key.loadtest.private_key_pem}' > ~/.ssh/id_rsa", + "chmod 600 ~/.ssh/id_rsa", "sudo mkdir -p ${var.loadtest_dir_destination} || true", "sudo chown ${var.ssh_user}:${var.ssh_user} ${var.loadtest_dir_destination} || true" ] diff --git a/scripts/entrypoint.leader.full.sh.tpl b/scripts/entrypoint.leader.full.sh.tpl index 8552583..db8eb53 100644 --- a/scripts/entrypoint.leader.full.sh.tpl +++ b/scripts/entrypoint.leader.full.sh.tpl @@ -60,4 +60,8 @@ sudo curl -L --silent https://search.maven.org/remotecontent?filepath=kg/apc/jme sudo curl -L --silent https://search.maven.org/remotecontent?filepath=kg/apc/jmeter-plugins-tst/2.5/jmeter-plugins-tst-2.5.jar -o $JMETER_PLUGINS_FOLDER/jmeter-plugins-tst-2.5.jar +mkdir -p ~/.ssh +echo 'Host *' > ~/.ssh/config +echo 'StrictHostKeyChecking no' >> ~/.ssh/config + touch /tmp/finished-setup diff --git a/scripts/entrypoint.node.full.sh.tpl b/scripts/entrypoint.node.full.sh.tpl index 1fa10a1..fe82a43 100644 --- a/scripts/entrypoint.node.full.sh.tpl +++ b/scripts/entrypoint.node.full.sh.tpl @@ -56,6 +56,10 @@ sudo curl -L --silent https://search.maven.org/remotecontent?filepath=kg/apc/jme source ~/.bashrc +mkdir -p ~/.ssh +echo 'Host *' > ~/.ssh/config +echo 'StrictHostKeyChecking no' >> ~/.ssh/config + touch /tmp/finished-setup # START JMETER NODE diff --git a/security.tf b/security.tf index 2e18808..d714e0e 100644 --- a/security.tf +++ b/security.tf @@ -114,3 +114,6 @@ resource "null_resource" "key_pair_exporter" { } + + + diff --git a/slipter.tf b/slipter.tf index 4383da6..e153eb9 100644 --- a/slipter.tf +++ b/slipter.tf @@ -1,64 +1,79 @@ locals { - split_enable = var.split_data_mass_between_nodes.enable - split_data_mass_filename = var.split_data_mass_between_nodes.data_mass_filenames[0] + spliter_enable = var.split_data_mass_between_nodes.enable split_size = var.nodes_size - split_cmd = local.split_enable ? "cd ${var.loadtest_dir_source} && split -a 3 -d -nr/${local.split_size} ${local.split_data_mass_filename} ${local.split_data_mass_filename}" : "echo 'auto split disabled'" + split_data_mass_filenames = local.spliter_enable ? var.split_data_mass_between_nodes.data_mass_filenames : [] - publish_split_data_count = local.split_enable ? var.nodes_size : 0 + # SPLIT COMMAND TEMPLATE + split_cmd_tpl = "split -a 3 -d -nr/${local.split_size} ${var.loadtest_dir_destination}/{FILENAME} ${var.loadtest_dir_destination}/{FILENAME}" + # RENDERIZATION COMMAND TEMPLATE + leader_split_cmds = [for file in local.split_data_mass_filenames : replace(local.split_cmd_tpl, "{FILENAME}", file)] + # SKIP SSH/SCP HOST VERIFICATION + ssh_skip_hosts_verification = " -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null " - # publish_files = flatten([ - # for network_key, network in var.networks : [ - # for subnet_key, subnet in network.subnets : { - # network_key = network_key - # subnet_key = subnet_key - # network_id = aws_vpc.example[network_key].id - # cidr_block = subnet.cidr_block - # } - # ] - # ]) -} + # TRANSFER SCP COMMAND TEMPLATE + scp_cmd_tpl = "echo scp ${local.ssh_skip_hosts_verification} ${var.loadtest_dir_destination}/{FILE_IN} ${var.ssh_user}@{HOST}:${var.loadtest_dir_destination}/{FILE_OUT}" + # RENDERIZATION CLEANUP SCP COMMAND + leader_scp_cmds = flatten([ + for file in local.split_data_mass_filenames : [ + for index, host in aws_instance.nodes : [ + replace( + replace( + replace( + local.scp_cmd_tpl, + "{FILE_IN}", + "${file}${format("%03d", index)}" + ), + "{FILE_OUT}", + file + ), + "{HOST}", + host.private_ip + ) + ] + ] + ]) -resource "null_resource" "split_data" { - - provisioner "local-exec" { + # CLEANUP SSH COMMAND TEMPLATE OF INTO NODES BY LEADER + leader_ssh_nodes_cleanup_cmd_tpl = "echo ssh ${local.ssh_skip_hosts_verification} ${var.ssh_user}@{HOST} -c \"rm -rf ${var.loadtest_dir_destination}/{FILE}\" || true" + # RENDERIZATION CLEANUP SSH COMMAND + leader_ssh_nodes_cleanup_cmds = flatten([ + for file in local.split_data_mass_filenames : [ + for host in aws_instance.nodes : [ + replace( + replace( + local.leader_ssh_nodes_cleanup_cmd_tpl, + "{FILE}", + file + ), + "{HOST}", + host.private_ip + ) + ] + ] + ]) - command = local.split_cmd - } + # JOIN COMMANDS TO BE EXECUTED BY LEADER + leader_cmds = concat( + local.leader_split_cmds, + local.leader_ssh_nodes_cleanup_cmds, + local.leader_scp_cmds + ) } -resource "null_resource" "publish_split_data" { - - count = local.publish_split_data_count - - depends_on = [ - null_resource.split_data, - aws_instance.nodes - ] +resource "null_resource" "spliter_execute_command" { + + # CONNECT TO LEADER connection { - host = coalesce(aws_instance.nodes[count.index].public_ip, aws_instance.nodes[count.index].private_ip) + host = coalesce(aws_instance.leader.public_ip, aws_instance.leader.private_ip) type = "ssh" user = var.ssh_user private_key = tls_private_key.loadtest.private_key_pem } + # EXECUTION OF ALL COMMANDS DEFINED INTO local.leader_cmds provisioner "remote-exec" { - inline = [ - "sudo mkdir -p ${var.loadtest_dir_destination}|| true", - "sudo chown ${var.ssh_user}:${var.ssh_user} ${var.loadtest_dir_destination} || true" - ] + inline = local.leader_cmds } - - provisioner "remote-exec" { - inline = [ - "rm ${var.loadtest_dir_destination}/${var.split_data_mass_between_nodes.data_mass_filenames[0]}", - "echo XXXXXX ${var.loadtest_dir_destination}/${var.split_data_mass_between_nodes.data_mass_filenames[0]}${format("%03d", count.index)}", - "mv ${var.loadtest_dir_destination}/${var.split_data_mass_between_nodes.data_mass_filenames[0]}${format("%03d", count.index)} ${var.loadtest_dir_destination}/${var.split_data_mass_between_nodes.data_mass_filenames[0]}" - ] - } - - #provisioner "file" { - # source = "${var.loadtest_dir_source}${format("%03d", count.index)}" - # destination = "${var.loadtest_dir_destination}/${var.loadtest_dir_source}" - #} + } From 96d9fdf1e53cc45603731e4ec78c2d2b521243bf Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Sat, 6 Nov 2021 18:00:58 -0300 Subject: [PATCH 6/9] ... --- examples/split-data/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/split-data/README.md b/examples/split-data/README.md index 351c906..5f27128 100644 --- a/examples/split-data/README.md +++ b/examples/split-data/README.md @@ -33,20 +33,20 @@ module "loadtest" { Behind the scene the terraform script sends all data mass files to the payload leader. -![split](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/master/assets/split-cmd.png) +![split](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-cmd.png) After submission, the files are divided by the leader into fragments by us. -![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/master/assets/split-cmd-result.png) +![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-cmd-result.png) The last action is to send each fragment to its respective node. -![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/master/assets/split-transfer.png) +![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-transfer.png) Splitting the files uses the linux `split` command and splits the main file into 1 fragment for each node. -![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/master/assets/split-transfer.png) +![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-transfer.png) More info: [Split doc](https://man7.org/linux/man-pages/man1/split.1.html) From 9fc9addcbd37595c598ad8774ab2b38e6ee1ee14 Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Sat, 6 Nov 2021 18:05:38 -0300 Subject: [PATCH 7/9] ... --- examples/split-data/README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/split-data/README.md b/examples/split-data/README.md index 5f27128..a428890 100644 --- a/examples/split-data/README.md +++ b/examples/split-data/README.md @@ -31,22 +31,21 @@ module "loadtest" { } ``` -Behind the scene the terraform script sends all data mass files to the payload leader. +Behind the scenes: + +1. sends all data mass files to the leader. ![split](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-cmd.png) -After submission, the files are divided by the leader into fragments by us. +2. After submission, the files are divided by the leader into fragments by us for each nodes. ![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-cmd-result.png) -The last action is to send each fragment to its respective node. +3. The last action is to send each fragment to its respective node. ![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-transfer.png) - -Splitting the files uses the linux `split` command and splits the main file into 1 fragment for each node. - -![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-transfer.png) +*Splitting the files uses the linux `split` command and splits the main file into 1 fragment for each node.* More info: [Split doc](https://man7.org/linux/man-pages/man1/split.1.html) From 358c4df5e52bc4330d55622202d014d2d3329995 Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Sat, 6 Nov 2021 18:06:33 -0300 Subject: [PATCH 8/9] ... --- examples/split-data/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/split-data/README.md b/examples/split-data/README.md index a428890..9aaa4ca 100644 --- a/examples/split-data/README.md +++ b/examples/split-data/README.md @@ -8,6 +8,8 @@ Just set the `split_data_mass_between_nodes` variable by activating the feature See the example below... +## Example + ```hcl module "loadtest" { @@ -30,8 +32,9 @@ module "loadtest" { subnet_id = data.aws_subnet.current.id } ``` +--- -Behind the scenes: +## Behind the scene: 1. sends all data mass files to the leader. From 17f27c1ca03765e47f3a6ddd825513abcf28cbf0 Mon Sep 17 00:00:00 2001 From: codecommiter-at-183233357101 Date: Sat, 6 Nov 2021 18:16:29 -0300 Subject: [PATCH 9/9] ... --- examples/split-data/README.md | 6 +++--- slipter.tf | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/split-data/README.md b/examples/split-data/README.md index 9aaa4ca..06cd5d6 100644 --- a/examples/split-data/README.md +++ b/examples/split-data/README.md @@ -38,15 +38,15 @@ module "loadtest" { 1. sends all data mass files to the leader. -![split](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-cmd.png) +![split](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/master/assets/split-cmd.png) 2. After submission, the files are divided by the leader into fragments by us for each nodes. -![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-cmd-result.png) +![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/master/assets/split-cmd-result.png) 3. The last action is to send each fragment to its respective node. -![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/feat/spliter/assets/split-transfer.png) +![split-result](https://github.com/marcosborges/terraform-aws-loadtest-distribuited/raw/master/assets/split-transfer.png) *Splitting the files uses the linux `split` command and splits the main file into 1 fragment for each node.* diff --git a/slipter.tf b/slipter.tf index e153eb9..68dd580 100644 --- a/slipter.tf +++ b/slipter.tf @@ -11,7 +11,7 @@ locals { ssh_skip_hosts_verification = " -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null " # TRANSFER SCP COMMAND TEMPLATE - scp_cmd_tpl = "echo scp ${local.ssh_skip_hosts_verification} ${var.loadtest_dir_destination}/{FILE_IN} ${var.ssh_user}@{HOST}:${var.loadtest_dir_destination}/{FILE_OUT}" + scp_cmd_tpl = "scp ${local.ssh_skip_hosts_verification} ${var.loadtest_dir_destination}/{FILE_IN} ${var.ssh_user}@{HOST}:${var.loadtest_dir_destination}/{FILE_OUT}" # RENDERIZATION CLEANUP SCP COMMAND leader_scp_cmds = flatten([ for file in local.split_data_mass_filenames : [ @@ -34,7 +34,7 @@ locals { ]) # CLEANUP SSH COMMAND TEMPLATE OF INTO NODES BY LEADER - leader_ssh_nodes_cleanup_cmd_tpl = "echo ssh ${local.ssh_skip_hosts_verification} ${var.ssh_user}@{HOST} -c \"rm -rf ${var.loadtest_dir_destination}/{FILE}\" || true" + leader_ssh_nodes_cleanup_cmd_tpl = "ssh ${local.ssh_skip_hosts_verification} ${var.ssh_user}@{HOST} -c \"rm -rf ${var.loadtest_dir_destination}/{FILE}\" || true" # RENDERIZATION CLEANUP SSH COMMAND leader_ssh_nodes_cleanup_cmds = flatten([ for file in local.split_data_mass_filenames : [