test: run_stress_test.sh — полный стресс-тест всех примеров
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
sless_function.hello_job: Refreshing state... [name=hello-job]
|
||||
sless_function.hello_http: Refreshing state... [name=hello-http]
|
||||
sless_trigger.hello_http: Refreshing state... [name=hello-http-trigger]
|
||||
sless_job.hello_run: Refreshing state... [name=hello-run]
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
~ update in-place
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.hello_http will be updated in-place
|
||||
~ resource "sless_function" "hello_http" {
|
||||
~ build_timeout_sec = 300 -> (known after apply)
|
||||
~ code_hash = "847def02d312614d511fab15b1c6fcf0ef883c564e79a5c52b0d25b917ebc130" -> (known after apply)
|
||||
+ env_vars = {
|
||||
+ "GREETING" = "e2e-test"
|
||||
}
|
||||
~ image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-hello-http:b2c713740a8d" -> (known after apply)
|
||||
~ memory_mb = 128 -> 256
|
||||
name = "hello-http"
|
||||
# (5 unchanged attributes hidden)
|
||||
}
|
||||
|
||||
Plan: 0 to add, 1 to change, 0 to destroy.
|
||||
sless_function.hello_http: Modifying... [name=hello-http]
|
||||
sless_function.hello_http: Modifications complete after 0s [name=hello-http]
|
||||
|
||||
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
|
||||
|
||||
Outputs:
|
||||
|
||||
job_completion_time = "2026-03-11T06:47:08Z"
|
||||
job_message = "{\"input\":[100,200,300],\"sum\":600,\"avg\":200,\"count\":3}"
|
||||
job_phase = "Succeeded"
|
||||
trigger_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/hello-http"
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
+ create
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.hello_http will be created
|
||||
+ resource "sless_function" "hello_http" {
|
||||
+ build_timeout_sec = (known after apply)
|
||||
+ code_hash = (known after apply)
|
||||
+ entrypoint = "handler-http.handle"
|
||||
+ image_ref = (known after apply)
|
||||
+ memory_mb = 128
|
||||
+ name = "hello-http"
|
||||
+ phase = (known after apply)
|
||||
+ runtime = "nodejs20"
|
||||
+ source_dir = "./code"
|
||||
+ timeout_sec = 30
|
||||
}
|
||||
|
||||
# sless_function.hello_job will be created
|
||||
+ resource "sless_function" "hello_job" {
|
||||
+ build_timeout_sec = (known after apply)
|
||||
+ code_hash = (known after apply)
|
||||
+ entrypoint = "handler-job.handle"
|
||||
+ image_ref = (known after apply)
|
||||
+ memory_mb = 128
|
||||
+ name = "hello-job"
|
||||
+ phase = (known after apply)
|
||||
+ runtime = "nodejs20"
|
||||
+ source_dir = "./code"
|
||||
+ timeout_sec = 30
|
||||
}
|
||||
|
||||
# sless_job.hello_run will be created
|
||||
+ resource "sless_job" "hello_run" {
|
||||
+ completion_time = (known after apply)
|
||||
+ event_json = jsonencode(
|
||||
{
|
||||
+ numbers = [
|
||||
+ 100,
|
||||
+ 200,
|
||||
+ 300,
|
||||
]
|
||||
}
|
||||
)
|
||||
+ function = "hello-job"
|
||||
+ message = (known after apply)
|
||||
+ name = "hello-run"
|
||||
+ phase = (known after apply)
|
||||
+ run_id = 9
|
||||
+ start_time = (known after apply)
|
||||
+ wait_timeout_sec = 600
|
||||
}
|
||||
|
||||
# sless_trigger.hello_http will be created
|
||||
+ resource "sless_trigger" "hello_http" {
|
||||
+ active = (known after apply)
|
||||
+ enabled = true
|
||||
+ function = "hello-http"
|
||||
+ name = "hello-http-trigger"
|
||||
+ type = "http"
|
||||
+ url = (known after apply)
|
||||
}
|
||||
|
||||
Plan: 4 to add, 0 to change, 0 to destroy.
|
||||
|
||||
Changes to Outputs:
|
||||
+ job_completion_time = (known after apply)
|
||||
+ job_message = (known after apply)
|
||||
+ job_phase = (known after apply)
|
||||
+ trigger_url = (known after apply)
|
||||
sless_function.hello_job: Creating...
|
||||
sless_function.hello_http: Creating...
|
||||
sless_function.hello_http: Still creating... [00m10s elapsed]
|
||||
sless_function.hello_job: Still creating... [00m10s elapsed]
|
||||
sless_function.hello_http: Still creating... [00m20s elapsed]
|
||||
sless_function.hello_job: Still creating... [00m20s elapsed]
|
||||
sless_function.hello_http: Creation complete after 21s [name=hello-http]
|
||||
sless_function.hello_job: Creation complete after 21s [name=hello-job]
|
||||
sless_trigger.hello_http: Creating...
|
||||
sless_job.hello_run: Creating...
|
||||
sless_trigger.hello_http: Creation complete after 2s [name=hello-http-trigger]
|
||||
sless_job.hello_run: Still creating... [00m10s elapsed]
|
||||
sless_job.hello_run: Creation complete after 10s [name=hello-run]
|
||||
|
||||
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
|
||||
|
||||
Outputs:
|
||||
|
||||
job_completion_time = "2026-03-11T06:47:08Z"
|
||||
job_message = "{\"input\":[100,200,300],\"sum\":600,\"avg\":200,\"count\":3}"
|
||||
job_phase = "Succeeded"
|
||||
trigger_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/hello-http"
|
||||
@@ -0,0 +1,112 @@
|
||||
sless_function.hello_job: Refreshing state... [name=hello-job]
|
||||
sless_function.hello_http: Refreshing state... [name=hello-http]
|
||||
sless_job.hello_run: Refreshing state... [name=hello-run]
|
||||
sless_trigger.hello_http: Refreshing state... [name=hello-http-trigger]
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
- destroy
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.hello_http will be destroyed
|
||||
- resource "sless_function" "hello_http" {
|
||||
- build_timeout_sec = 300 -> null
|
||||
- code_hash = "847def02d312614d511fab15b1c6fcf0ef883c564e79a5c52b0d25b917ebc130" -> null
|
||||
- entrypoint = "handler-http.handle" -> null
|
||||
- env_vars = {
|
||||
- "GREETING" = "e2e-test"
|
||||
} -> null
|
||||
- image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-hello-http:b2c713740a8d" -> null
|
||||
- memory_mb = 256 -> null
|
||||
- name = "hello-http" -> null
|
||||
- phase = "Ready" -> null
|
||||
- runtime = "nodejs20" -> null
|
||||
- source_dir = "./code" -> null
|
||||
- timeout_sec = 30 -> null
|
||||
}
|
||||
|
||||
# sless_function.hello_job will be destroyed
|
||||
- resource "sless_function" "hello_job" {
|
||||
- build_timeout_sec = 300 -> null
|
||||
- code_hash = "847def02d312614d511fab15b1c6fcf0ef883c564e79a5c52b0d25b917ebc130" -> null
|
||||
- entrypoint = "handler-job.handle" -> null
|
||||
- image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-hello-job:572595dae5fb" -> null
|
||||
- memory_mb = 128 -> null
|
||||
- name = "hello-job" -> null
|
||||
- phase = "Ready" -> null
|
||||
- runtime = "nodejs20" -> null
|
||||
- source_dir = "./code" -> null
|
||||
- timeout_sec = 30 -> null
|
||||
}
|
||||
|
||||
# sless_job.hello_run will be destroyed
|
||||
- resource "sless_job" "hello_run" {
|
||||
- completion_time = "2026-03-11T06:47:08Z" -> null
|
||||
- event_json = jsonencode(
|
||||
{
|
||||
- numbers = [
|
||||
- 100,
|
||||
- 200,
|
||||
- 300,
|
||||
]
|
||||
}
|
||||
) -> null
|
||||
- function = "hello-job" -> null
|
||||
- message = jsonencode(
|
||||
{
|
||||
- avg = 200
|
||||
- count = 3
|
||||
- input = [
|
||||
- 100,
|
||||
- 200,
|
||||
- 300,
|
||||
]
|
||||
- sum = 600
|
||||
}
|
||||
) -> null
|
||||
- name = "hello-run" -> null
|
||||
- phase = "Succeeded" -> null
|
||||
- run_id = 9 -> null
|
||||
- start_time = "2026-03-11T06:46:58Z" -> null
|
||||
- wait_timeout_sec = 600 -> null
|
||||
}
|
||||
|
||||
# sless_trigger.hello_http will be destroyed
|
||||
- resource "sless_trigger" "hello_http" {
|
||||
- active = true -> null
|
||||
- enabled = true -> null
|
||||
- function = "hello-http" -> null
|
||||
- name = "hello-http-trigger" -> null
|
||||
- type = "http" -> null
|
||||
- url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/hello-http" -> null
|
||||
}
|
||||
|
||||
Plan: 0 to add, 0 to change, 4 to destroy.
|
||||
|
||||
Changes to Outputs:
|
||||
- job_completion_time = "2026-03-11T06:47:08Z" -> null
|
||||
- job_message = jsonencode(
|
||||
{
|
||||
- avg = 200
|
||||
- count = 3
|
||||
- input = [
|
||||
- 100,
|
||||
- 200,
|
||||
- 300,
|
||||
]
|
||||
- sum = 600
|
||||
}
|
||||
) -> null
|
||||
- job_phase = "Succeeded" -> null
|
||||
- trigger_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/hello-http" -> null
|
||||
sless_trigger.hello_http: Destroying... [name=hello-http-trigger]
|
||||
sless_job.hello_run: Destroying... [name=hello-run]
|
||||
sless_job.hello_run: Destruction complete after 0s
|
||||
sless_function.hello_job: Destroying... [name=hello-job]
|
||||
sless_function.hello_job: Destruction complete after 0s
|
||||
sless_trigger.hello_http: Destruction complete after 3s
|
||||
sless_function.hello_http: Destroying... [name=hello-http]
|
||||
sless_function.hello_http: Destruction complete after 0s
|
||||
|
||||
Destroy complete! Resources: 4 destroyed.
|
||||
@@ -0,0 +1,22 @@
|
||||
Initializing the backend...
|
||||
Initializing provider plugins...
|
||||
- Finding terra.k8c.ru/naeel/sless versions matching "~> 0.1.13"...
|
||||
- Installing terra.k8c.ru/naeel/sless v0.1.13...
|
||||
- Installed terra.k8c.ru/naeel/sless v0.1.13 (self-signed, key ID CB3A0DF161ECC416)
|
||||
Partner and community providers are signed by their developers.
|
||||
If you'd like to know more about provider signing, you can read about it here:
|
||||
https://developer.hashicorp.com/terraform/cli/plugins/signing
|
||||
Terraform has created a lock file .terraform.lock.hcl to record the provider
|
||||
selections it made above. Include this file in your version control repository
|
||||
so that Terraform can guarantee to make the same selections by default when
|
||||
you run "terraform init" in the future.
|
||||
|
||||
Terraform has been successfully initialized!
|
||||
|
||||
You may now begin working with Terraform. Try running "terraform plan" to see
|
||||
any changes that are required for your infrastructure. All Terraform commands
|
||||
should now work.
|
||||
|
||||
If you ever set or change modules or backend configuration for Terraform,
|
||||
rerun this command to reinitialize your working directory. If you forget, other
|
||||
commands will detect it and remind you to do so if necessary.
|
||||
@@ -0,0 +1,32 @@
|
||||
sless_function.time_getter: Refreshing state... [name=simple-node-time-getter]
|
||||
sless_job.run_getter: Refreshing state... [name=simple-node-getter-run]
|
||||
sless_function.time_display: Refreshing state... [name=simple-node-time-display]
|
||||
sless_trigger.display_http: Refreshing state... [name=simple-node-display-http]
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
~ update in-place
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.time_display will be updated in-place
|
||||
~ resource "sless_function" "time_display" {
|
||||
~ build_timeout_sec = 300 -> (known after apply)
|
||||
~ code_hash = "cf78d23403217d9ca0871205966de6b9041e0187b8bb5bbf6b1502dd38951e80" -> (known after apply)
|
||||
~ image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-simple-node-time-display:96fbfb0d6a99" -> (known after apply)
|
||||
~ memory_mb = 64 -> 96
|
||||
name = "simple-node-time-display"
|
||||
~ timeout_sec = 30 -> (known after apply)
|
||||
# (5 unchanged attributes hidden)
|
||||
}
|
||||
|
||||
Plan: 0 to add, 1 to change, 0 to destroy.
|
||||
sless_function.time_display: Modifying... [name=simple-node-time-display]
|
||||
sless_function.time_display: Modifications complete after 0s [name=simple-node-time-display]
|
||||
|
||||
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
|
||||
|
||||
Outputs:
|
||||
|
||||
display_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/simple-node-time-display"
|
||||
job_result = "{\"time\":\"2026-03-11T06:47:47.229Z\"}"
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
+ create
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.time_display will be created
|
||||
+ resource "sless_function" "time_display" {
|
||||
+ build_timeout_sec = (known after apply)
|
||||
+ code_hash = (known after apply)
|
||||
+ entrypoint = "time_display.showTime"
|
||||
+ env_vars = {
|
||||
+ "JOB_TIME" = (known after apply)
|
||||
}
|
||||
+ image_ref = (known after apply)
|
||||
+ memory_mb = 64
|
||||
+ name = "simple-node-time-display"
|
||||
+ phase = (known after apply)
|
||||
+ runtime = "nodejs20"
|
||||
+ source_dir = "./code/time_display"
|
||||
+ timeout_sec = (known after apply)
|
||||
}
|
||||
|
||||
# sless_function.time_getter will be created
|
||||
+ resource "sless_function" "time_getter" {
|
||||
+ build_timeout_sec = (known after apply)
|
||||
+ code_hash = (known after apply)
|
||||
+ entrypoint = "time_getter.getTime"
|
||||
+ image_ref = (known after apply)
|
||||
+ memory_mb = 64
|
||||
+ name = "simple-node-time-getter"
|
||||
+ phase = (known after apply)
|
||||
+ runtime = "nodejs20"
|
||||
+ source_dir = "./code/time_getter"
|
||||
+ timeout_sec = (known after apply)
|
||||
}
|
||||
|
||||
# sless_job.run_getter will be created
|
||||
+ resource "sless_job" "run_getter" {
|
||||
+ completion_time = (known after apply)
|
||||
+ event_json = jsonencode({})
|
||||
+ function = "simple-node-time-getter"
|
||||
+ message = (known after apply)
|
||||
+ name = "simple-node-getter-run"
|
||||
+ phase = (known after apply)
|
||||
+ run_id = 1
|
||||
+ start_time = (known after apply)
|
||||
+ wait_timeout_sec = 120
|
||||
}
|
||||
|
||||
# sless_trigger.display_http will be created
|
||||
+ resource "sless_trigger" "display_http" {
|
||||
+ active = (known after apply)
|
||||
+ enabled = true
|
||||
+ function = "simple-node-time-display"
|
||||
+ name = "simple-node-display-http"
|
||||
+ type = "http"
|
||||
+ url = (known after apply)
|
||||
}
|
||||
|
||||
Plan: 4 to add, 0 to change, 0 to destroy.
|
||||
|
||||
Changes to Outputs:
|
||||
+ display_url = (known after apply)
|
||||
+ job_result = (known after apply)
|
||||
sless_function.time_getter: Creating...
|
||||
sless_function.time_getter: Still creating... [00m10s elapsed]
|
||||
sless_function.time_getter: Still creating... [00m20s elapsed]
|
||||
sless_function.time_getter: Creation complete after 20s [name=simple-node-time-getter]
|
||||
sless_job.run_getter: Creating...
|
||||
sless_job.run_getter: Still creating... [00m10s elapsed]
|
||||
sless_job.run_getter: Creation complete after 10s [name=simple-node-getter-run]
|
||||
sless_function.time_display: Creating...
|
||||
sless_function.time_display: Still creating... [00m10s elapsed]
|
||||
sless_function.time_display: Still creating... [00m20s elapsed]
|
||||
sless_function.time_display: Creation complete after 21s [name=simple-node-time-display]
|
||||
sless_trigger.display_http: Creating...
|
||||
sless_trigger.display_http: Creation complete after 2s [name=simple-node-display-http]
|
||||
|
||||
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
|
||||
|
||||
Outputs:
|
||||
|
||||
display_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/simple-node-time-display"
|
||||
job_result = "{\"time\":\"2026-03-11T06:47:47.229Z\"}"
|
||||
@@ -0,0 +1,92 @@
|
||||
sless_function.time_getter: Refreshing state... [name=simple-node-time-getter]
|
||||
sless_job.run_getter: Refreshing state... [name=simple-node-getter-run]
|
||||
sless_function.time_display: Refreshing state... [name=simple-node-time-display]
|
||||
sless_trigger.display_http: Refreshing state... [name=simple-node-display-http]
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
- destroy
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.time_display will be destroyed
|
||||
- resource "sless_function" "time_display" {
|
||||
- build_timeout_sec = 300 -> null
|
||||
- code_hash = "cf78d23403217d9ca0871205966de6b9041e0187b8bb5bbf6b1502dd38951e80" -> null
|
||||
- entrypoint = "time_display.showTime" -> null
|
||||
- env_vars = {
|
||||
- "JOB_TIME" = jsonencode(
|
||||
{
|
||||
- time = "2026-03-11T06:47:47.229Z"
|
||||
}
|
||||
)
|
||||
} -> null
|
||||
- image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-simple-node-time-display:96fbfb0d6a99" -> null
|
||||
- memory_mb = 96 -> null
|
||||
- name = "simple-node-time-display" -> null
|
||||
- phase = "Ready" -> null
|
||||
- runtime = "nodejs20" -> null
|
||||
- source_dir = "./code/time_display" -> null
|
||||
- timeout_sec = 30 -> null
|
||||
}
|
||||
|
||||
# sless_function.time_getter will be destroyed
|
||||
- resource "sless_function" "time_getter" {
|
||||
- build_timeout_sec = 300 -> null
|
||||
- code_hash = "bcbba3d63dcd3f1cce4ba48e8b2208bd398e6864e51b9c72887d027cc1385210" -> null
|
||||
- entrypoint = "time_getter.getTime" -> null
|
||||
- image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-simple-node-time-getter:d933956041b2" -> null
|
||||
- memory_mb = 64 -> null
|
||||
- name = "simple-node-time-getter" -> null
|
||||
- phase = "Ready" -> null
|
||||
- runtime = "nodejs20" -> null
|
||||
- source_dir = "./code/time_getter" -> null
|
||||
- timeout_sec = 30 -> null
|
||||
}
|
||||
|
||||
# sless_job.run_getter will be destroyed
|
||||
- resource "sless_job" "run_getter" {
|
||||
- completion_time = "2026-03-11T06:47:53Z" -> null
|
||||
- event_json = jsonencode({})
|
||||
- function = "simple-node-time-getter" -> null
|
||||
- message = jsonencode(
|
||||
{
|
||||
- time = "2026-03-11T06:47:47.229Z"
|
||||
}
|
||||
) -> null
|
||||
- name = "simple-node-getter-run" -> null
|
||||
- phase = "Succeeded" -> null
|
||||
- run_id = 1 -> null
|
||||
- start_time = "2026-03-11T06:47:43Z" -> null
|
||||
- wait_timeout_sec = 120 -> null
|
||||
}
|
||||
|
||||
# sless_trigger.display_http will be destroyed
|
||||
- resource "sless_trigger" "display_http" {
|
||||
- active = true -> null
|
||||
- enabled = true -> null
|
||||
- function = "simple-node-time-display" -> null
|
||||
- name = "simple-node-display-http" -> null
|
||||
- type = "http" -> null
|
||||
- url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/simple-node-time-display" -> null
|
||||
}
|
||||
|
||||
Plan: 0 to add, 0 to change, 4 to destroy.
|
||||
|
||||
Changes to Outputs:
|
||||
- display_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/simple-node-time-display" -> null
|
||||
- job_result = jsonencode(
|
||||
{
|
||||
- time = "2026-03-11T06:47:47.229Z"
|
||||
}
|
||||
) -> null
|
||||
sless_trigger.display_http: Destroying... [name=simple-node-display-http]
|
||||
sless_trigger.display_http: Destruction complete after 3s
|
||||
sless_function.time_display: Destroying... [name=simple-node-time-display]
|
||||
sless_function.time_display: Destruction complete after 0s
|
||||
sless_job.run_getter: Destroying... [name=simple-node-getter-run]
|
||||
sless_job.run_getter: Destruction complete after 0s
|
||||
sless_function.time_getter: Destroying... [name=simple-node-time-getter]
|
||||
sless_function.time_getter: Destruction complete after 0s
|
||||
|
||||
Destroy complete! Resources: 4 destroyed.
|
||||
@@ -0,0 +1,22 @@
|
||||
Initializing the backend...
|
||||
Initializing provider plugins...
|
||||
- Finding terra.k8c.ru/naeel/sless versions matching "~> 0.1.13"...
|
||||
- Installing terra.k8c.ru/naeel/sless v0.1.13...
|
||||
- Installed terra.k8c.ru/naeel/sless v0.1.13 (self-signed, key ID CB3A0DF161ECC416)
|
||||
Partner and community providers are signed by their developers.
|
||||
If you'd like to know more about provider signing, you can read about it here:
|
||||
https://developer.hashicorp.com/terraform/cli/plugins/signing
|
||||
Terraform has created a lock file .terraform.lock.hcl to record the provider
|
||||
selections it made above. Include this file in your version control repository
|
||||
so that Terraform can guarantee to make the same selections by default when
|
||||
you run "terraform init" in the future.
|
||||
|
||||
Terraform has been successfully initialized!
|
||||
|
||||
You may now begin working with Terraform. Try running "terraform plan" to see
|
||||
any changes that are required for your infrastructure. All Terraform commands
|
||||
should now work.
|
||||
|
||||
If you ever set or change modules or backend configuration for Terraform,
|
||||
rerun this command to reinitialize your working directory. If you forget, other
|
||||
commands will detect it and remind you to do so if necessary.
|
||||
@@ -0,0 +1,32 @@
|
||||
sless_function.time_getter: Refreshing state... [name=simple-py-time-getter]
|
||||
sless_job.run_getter: Refreshing state... [name=simple-py-getter-run]
|
||||
sless_function.time_display: Refreshing state... [name=simple-py-time-display]
|
||||
sless_trigger.display_http: Refreshing state... [name=simple-py-display-http]
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
~ update in-place
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.time_display will be updated in-place
|
||||
~ resource "sless_function" "time_display" {
|
||||
~ build_timeout_sec = 300 -> (known after apply)
|
||||
~ code_hash = "394881701022565dcc3e786917b541b2ff4763b1d36adb80e0110e17115a3d88" -> (known after apply)
|
||||
~ image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-simple-py-time-display:6f9aecbf3839" -> (known after apply)
|
||||
~ memory_mb = 64 -> 96
|
||||
name = "simple-py-time-display"
|
||||
~ timeout_sec = 30 -> (known after apply)
|
||||
# (5 unchanged attributes hidden)
|
||||
}
|
||||
|
||||
Plan: 0 to add, 1 to change, 0 to destroy.
|
||||
sless_function.time_display: Modifying... [name=simple-py-time-display]
|
||||
sless_function.time_display: Modifications complete after 0s [name=simple-py-time-display]
|
||||
|
||||
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
|
||||
|
||||
Outputs:
|
||||
|
||||
display_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/simple-py-time-display"
|
||||
job_result = "{\"time\": \"2026-03-11T06:45:01.234537+00:00\"}"
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
+ create
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.time_display will be created
|
||||
+ resource "sless_function" "time_display" {
|
||||
+ build_timeout_sec = (known after apply)
|
||||
+ code_hash = (known after apply)
|
||||
+ entrypoint = "time_display.show_time"
|
||||
+ env_vars = {
|
||||
+ "JOB_TIME" = (known after apply)
|
||||
}
|
||||
+ image_ref = (known after apply)
|
||||
+ memory_mb = 64
|
||||
+ name = "simple-py-time-display"
|
||||
+ phase = (known after apply)
|
||||
+ runtime = "python3.11"
|
||||
+ source_dir = "./code/time_display"
|
||||
+ timeout_sec = (known after apply)
|
||||
}
|
||||
|
||||
# sless_function.time_getter will be created
|
||||
+ resource "sless_function" "time_getter" {
|
||||
+ build_timeout_sec = (known after apply)
|
||||
+ code_hash = (known after apply)
|
||||
+ entrypoint = "time_getter.get_time"
|
||||
+ image_ref = (known after apply)
|
||||
+ memory_mb = 64
|
||||
+ name = "simple-py-time-getter"
|
||||
+ phase = (known after apply)
|
||||
+ runtime = "python3.11"
|
||||
+ source_dir = "./code/time_getter"
|
||||
+ timeout_sec = (known after apply)
|
||||
}
|
||||
|
||||
# sless_job.run_getter will be created
|
||||
+ resource "sless_job" "run_getter" {
|
||||
+ completion_time = (known after apply)
|
||||
+ event_json = jsonencode({})
|
||||
+ function = "simple-py-time-getter"
|
||||
+ message = (known after apply)
|
||||
+ name = "simple-py-getter-run"
|
||||
+ phase = (known after apply)
|
||||
+ run_id = 1
|
||||
+ start_time = (known after apply)
|
||||
+ wait_timeout_sec = 120
|
||||
}
|
||||
|
||||
# sless_trigger.display_http will be created
|
||||
+ resource "sless_trigger" "display_http" {
|
||||
+ active = (known after apply)
|
||||
+ enabled = true
|
||||
+ function = "simple-py-time-display"
|
||||
+ name = "simple-py-display-http"
|
||||
+ type = "http"
|
||||
+ url = (known after apply)
|
||||
}
|
||||
|
||||
Plan: 4 to add, 0 to change, 0 to destroy.
|
||||
|
||||
Changes to Outputs:
|
||||
+ display_url = (known after apply)
|
||||
+ job_result = (known after apply)
|
||||
sless_function.time_getter: Creating...
|
||||
sless_function.time_getter: Still creating... [00m10s elapsed]
|
||||
sless_function.time_getter: Still creating... [00m20s elapsed]
|
||||
sless_function.time_getter: Creation complete after 20s [name=simple-py-time-getter]
|
||||
sless_job.run_getter: Creating...
|
||||
sless_job.run_getter: Still creating... [00m10s elapsed]
|
||||
sless_job.run_getter: Creation complete after 10s [name=simple-py-getter-run]
|
||||
sless_function.time_display: Creating...
|
||||
sless_function.time_display: Still creating... [00m10s elapsed]
|
||||
sless_function.time_display: Still creating... [00m20s elapsed]
|
||||
sless_function.time_display: Creation complete after 21s [name=simple-py-time-display]
|
||||
sless_trigger.display_http: Creating...
|
||||
sless_trigger.display_http: Creation complete after 2s [name=simple-py-display-http]
|
||||
|
||||
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
|
||||
|
||||
Outputs:
|
||||
|
||||
display_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/simple-py-time-display"
|
||||
job_result = "{\"time\": \"2026-03-11T06:45:01.234537+00:00\"}"
|
||||
@@ -0,0 +1,92 @@
|
||||
sless_function.time_getter: Refreshing state... [name=simple-py-time-getter]
|
||||
sless_job.run_getter: Refreshing state... [name=simple-py-getter-run]
|
||||
sless_function.time_display: Refreshing state... [name=simple-py-time-display]
|
||||
sless_trigger.display_http: Refreshing state... [name=simple-py-display-http]
|
||||
|
||||
Terraform used the selected providers to generate the following execution
|
||||
plan. Resource actions are indicated with the following symbols:
|
||||
- destroy
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# sless_function.time_display will be destroyed
|
||||
- resource "sless_function" "time_display" {
|
||||
- build_timeout_sec = 300 -> null
|
||||
- code_hash = "394881701022565dcc3e786917b541b2ff4763b1d36adb80e0110e17115a3d88" -> null
|
||||
- entrypoint = "time_display.show_time" -> null
|
||||
- env_vars = {
|
||||
- "JOB_TIME" = jsonencode(
|
||||
{
|
||||
- time = "2026-03-11T06:45:01.234537+00:00"
|
||||
}
|
||||
)
|
||||
} -> null
|
||||
- image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-simple-py-time-display:6f9aecbf3839" -> null
|
||||
- memory_mb = 96 -> null
|
||||
- name = "simple-py-time-display" -> null
|
||||
- phase = "Ready" -> null
|
||||
- runtime = "python3.11" -> null
|
||||
- source_dir = "./code/time_display" -> null
|
||||
- timeout_sec = 30 -> null
|
||||
}
|
||||
|
||||
# sless_function.time_getter will be destroyed
|
||||
- resource "sless_function" "time_getter" {
|
||||
- build_timeout_sec = 300 -> null
|
||||
- code_hash = "5a15834dfe9de935f27cd54b2e851b738e63a17aec7aca938c2358b026feeaaa" -> null
|
||||
- entrypoint = "time_getter.get_time" -> null
|
||||
- image_ref = "naeel/sless-sless-cdd874dfa31ba6ca-simple-py-time-getter:cc24266fe22e" -> null
|
||||
- memory_mb = 64 -> null
|
||||
- name = "simple-py-time-getter" -> null
|
||||
- phase = "Ready" -> null
|
||||
- runtime = "python3.11" -> null
|
||||
- source_dir = "./code/time_getter" -> null
|
||||
- timeout_sec = 30 -> null
|
||||
}
|
||||
|
||||
# sless_job.run_getter will be destroyed
|
||||
- resource "sless_job" "run_getter" {
|
||||
- completion_time = "2026-03-11T06:45:07Z" -> null
|
||||
- event_json = jsonencode({})
|
||||
- function = "simple-py-time-getter" -> null
|
||||
- message = jsonencode(
|
||||
{
|
||||
- time = "2026-03-11T06:45:01.234537+00:00"
|
||||
}
|
||||
) -> null
|
||||
- name = "simple-py-getter-run" -> null
|
||||
- phase = "Succeeded" -> null
|
||||
- run_id = 1 -> null
|
||||
- start_time = "2026-03-11T06:44:57Z" -> null
|
||||
- wait_timeout_sec = 120 -> null
|
||||
}
|
||||
|
||||
# sless_trigger.display_http will be destroyed
|
||||
- resource "sless_trigger" "display_http" {
|
||||
- active = true -> null
|
||||
- enabled = true -> null
|
||||
- function = "simple-py-time-display" -> null
|
||||
- name = "simple-py-display-http" -> null
|
||||
- type = "http" -> null
|
||||
- url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/simple-py-time-display" -> null
|
||||
}
|
||||
|
||||
Plan: 0 to add, 0 to change, 4 to destroy.
|
||||
|
||||
Changes to Outputs:
|
||||
- display_url = "https://sless-api.kube5s.ru/fn/sless-cdd874dfa31ba6ca/simple-py-time-display" -> null
|
||||
- job_result = jsonencode(
|
||||
{
|
||||
- time = "2026-03-11T06:45:01.234537+00:00"
|
||||
}
|
||||
) -> null
|
||||
sless_trigger.display_http: Destroying... [name=simple-py-display-http]
|
||||
sless_trigger.display_http: Destruction complete after 3s
|
||||
sless_function.time_display: Destroying... [name=simple-py-time-display]
|
||||
sless_function.time_display: Destruction complete after 0s
|
||||
sless_job.run_getter: Destroying... [name=simple-py-getter-run]
|
||||
sless_job.run_getter: Destruction complete after 0s
|
||||
sless_function.time_getter: Destroying... [name=simple-py-time-getter]
|
||||
sless_function.time_getter: Destruction complete after 0s
|
||||
|
||||
Destroy complete! Resources: 4 destroyed.
|
||||
@@ -0,0 +1,22 @@
|
||||
Initializing the backend...
|
||||
Initializing provider plugins...
|
||||
- Finding terra.k8c.ru/naeel/sless versions matching "~> 0.1.13"...
|
||||
- Installing terra.k8c.ru/naeel/sless v0.1.13...
|
||||
- Installed terra.k8c.ru/naeel/sless v0.1.13 (self-signed, key ID CB3A0DF161ECC416)
|
||||
Partner and community providers are signed by their developers.
|
||||
If you'd like to know more about provider signing, you can read about it here:
|
||||
https://developer.hashicorp.com/terraform/cli/plugins/signing
|
||||
Terraform has created a lock file .terraform.lock.hcl to record the provider
|
||||
selections it made above. Include this file in your version control repository
|
||||
so that Terraform can guarantee to make the same selections by default when
|
||||
you run "terraform init" in the future.
|
||||
|
||||
Terraform has been successfully initialized!
|
||||
|
||||
You may now begin working with Terraform. Try running "terraform plan" to see
|
||||
any changes that are required for your infrastructure. All Terraform commands
|
||||
should now work.
|
||||
|
||||
If you ever set or change modules or backend configuration for Terraform,
|
||||
rerun this command to reinitialize your working directory. If you forget, other
|
||||
commands will detect it and remind you to do so if necessary.
|
||||
@@ -42,3 +42,4 @@ terraform/provider/build/
|
||||
examples/*/dist/
|
||||
**/handler.zip
|
||||
test.token
|
||||
*.tfvars
|
||||
|
||||
Executable
+662
@@ -0,0 +1,662 @@
|
||||
#!/usr/bin/env bash
|
||||
# 2026-03-11
|
||||
# run_stress_test.sh — Полный стресс-тест всех examples.
|
||||
#
|
||||
# Что прогоняем на каждом примере:
|
||||
# [ROUND 1] init → apply → HTTP-проверка → destroy → проверка "упал"
|
||||
# [ROUND 2] apply → modify (memory) → apply(modify) → HTTP-проверка
|
||||
# → modify (env_var) → apply(modify2) → HTTP-проверка
|
||||
# → modify (timeout) → apply(modify3)
|
||||
# → destroy → проверка "упал"
|
||||
# [ROUND 3] apply → destroy (сразу, без ожидания Ready)
|
||||
# [hello-node extra] job re-run: run_id bump → apply → assert Succeeded
|
||||
# [simple-* extra] trigger disabled → apply → assert 404 → enabled → apply → assert 200
|
||||
# [notes-python] CRUD: add note → list → assert запись есть → destroy
|
||||
#
|
||||
# Логи каждого шага: .stress-logs/<example>-<step>-<attempt>.log
|
||||
# При ошибке скрипт не падает — пишет FAIL и продолжает следующий пример.
|
||||
# В конце — сводная таблица PASS/FAIL по каждому примеру.
|
||||
#
|
||||
# Запуск:
|
||||
# ./run_stress_test.sh
|
||||
# ./run_stress_test.sh hello-node simple-node # конкретные примеры
|
||||
#
|
||||
# Время: ~30-50 мин (зависит от скорости сборки канико)
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# ── Конфигурация ──────────────────────────────────────────────────────────────
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
EXAMPLES_DIR="$REPO_ROOT/examples"
|
||||
LOGS_DIR="$REPO_ROOT/.stress-logs"
|
||||
mkdir -p "$LOGS_DIR"
|
||||
|
||||
API_BASE="https://sless-api.kube5s.ru"
|
||||
NS="sless-cdd874dfa31ba6ca" # вычислен из токена; не меняется
|
||||
|
||||
# ── Примеры ───────────────────────────────────────────────────────────────────
|
||||
if [ $# -gt 0 ]; then
|
||||
EXAMPLES=("$@")
|
||||
else
|
||||
EXAMPLES=("hello-node" "simple-node" "simple-python" "notes-python")
|
||||
fi
|
||||
|
||||
# ── Цвета / вывод ─────────────────────────────────────────────────────────────
|
||||
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||
ok() { echo -e "${GREEN} ✓ $*${RESET}"; }
|
||||
fail() { echo -e "${RED} ✗ $*${RESET}"; }
|
||||
info() { echo -e "${YELLOW} → $*${RESET}"; }
|
||||
step() { echo -e "${CYAN} [step] $*${RESET}"; }
|
||||
header(){ echo -e "\n${BOLD}${CYAN}══════════════════════════════════════════${RESET}"; \
|
||||
echo -e "${BOLD}${CYAN} $*${RESET}"; \
|
||||
echo -e "${BOLD}${CYAN}══════════════════════════════════════════${RESET}"; }
|
||||
|
||||
# ── Результаты ────────────────────────────────────────────────────────────────
|
||||
declare -A RESULTS=() # example → "PASS" | "FAIL: <reason>"
|
||||
declare -A TIMINGS=() # example → секунды
|
||||
|
||||
# ── Emergency destroy trap ────────────────────────────────────────────────────
|
||||
ACTIVE_EXAMPLE=""
|
||||
cleanup_trap() {
|
||||
if [ -n "$ACTIVE_EXAMPLE" ]; then
|
||||
local dir="$EXAMPLES_DIR/$ACTIVE_EXAMPLE"
|
||||
if [ -f "$dir/terraform.tfstate" ] && \
|
||||
python3 -c "
|
||||
import json,sys
|
||||
d=json.load(open('$dir/terraform.tfstate'))
|
||||
sys.exit(0 if d.get('resources') else 1)
|
||||
" 2>/dev/null; then
|
||||
echo -e "\n${RED}[trap] Script interrupted — emergency destroy: $ACTIVE_EXAMPLE${RESET}"
|
||||
(cd "$dir" && terraform destroy -auto-approve -input=false -no-color \
|
||||
>> "$LOGS_DIR/${ACTIVE_EXAMPLE}-emergency-destroy.log" 2>&1) || true
|
||||
fi
|
||||
restore_all_backups "$ACTIVE_EXAMPLE"
|
||||
fi
|
||||
}
|
||||
trap cleanup_trap EXIT INT TERM
|
||||
|
||||
# ── Утилиты: terraform ────────────────────────────────────────────────────────
|
||||
# tf_run <logfile> <chdir> <args...>
|
||||
# Ретраит до 4 раз при сетевых ошибках (TLS timeout, EOF, i/o timeout)
|
||||
tf_run() {
|
||||
local logfile="$1"; local chdir="$2"; shift 2
|
||||
local attempt=1 max=4
|
||||
while [ "$attempt" -le "$max" ]; do
|
||||
: > "$logfile"
|
||||
if (cd "$chdir" && terraform "$@" -no-color) 2>&1 | tee "$logfile"; then
|
||||
return 0
|
||||
fi
|
||||
local rc=${PIPESTATUS[0]}
|
||||
if grep -Eiq \
|
||||
'TLS handshake timeout|unexpected EOF|i/o timeout|context deadline exceeded|Client\.Timeout' \
|
||||
"$logfile" && [ "$attempt" -lt "$max" ]; then
|
||||
info "network hiccup (attempt $attempt/$max), retry in $((attempt*3))s..."
|
||||
sleep $((attempt * 3))
|
||||
attempt=$((attempt + 1))
|
||||
continue
|
||||
fi
|
||||
return "$rc"
|
||||
done
|
||||
}
|
||||
|
||||
tf_init() { tf_run "$1" "$2" init -input=false -upgrade; }
|
||||
tf_apply() { tf_run "$1" "$2" apply -auto-approve -input=false; }
|
||||
tf_destroy() { tf_run "$1" "$2" destroy -auto-approve -input=false; }
|
||||
|
||||
assert_apply_ok() { grep -q 'Apply complete' "$1"; }
|
||||
assert_destroy_ok() { grep -q 'Destroy complete' "$1"; }
|
||||
assert_no_changes() { grep -q 'No changes' "$1"; }
|
||||
|
||||
# ── Утилиты: HTTP-проверки ────────────────────────────────────────────────────
|
||||
# http_check <url> [expected_http_code] [max_attempts] [sleep_sec]
|
||||
http_check() {
|
||||
local url="$1"
|
||||
local expected="${2:-200}"
|
||||
local attempts="${3:-12}"
|
||||
local sleep_sec="${4:-10}"
|
||||
local try=1
|
||||
while [ "$try" -le "$attempts" ]; do
|
||||
local code
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$url" 2>/dev/null || echo "000")
|
||||
if [ "$code" = "$expected" ]; then
|
||||
return 0
|
||||
fi
|
||||
info "HTTP $code (want $expected), waiting... ($try/$attempts)"
|
||||
try=$((try + 1))
|
||||
sleep "$sleep_sec"
|
||||
done
|
||||
fail "HTTP check failed: got $code, wanted $expected after $((attempts*sleep_sec))s"
|
||||
return 1
|
||||
}
|
||||
|
||||
# http_post <url> <json_body> → возвращает тело в stdout
|
||||
http_post() {
|
||||
curl -sS -X POST -H 'Content-Type: application/json' -d "$2" \
|
||||
--max-time 15 "$1" 2>/dev/null
|
||||
}
|
||||
|
||||
http_get() {
|
||||
curl -sS --max-time 15 "$1" 2>/dev/null
|
||||
}
|
||||
|
||||
# ── Утилиты: бэкапы и патчи ───────────────────────────────────────────────────
|
||||
backup_file() {
|
||||
local f="$1"
|
||||
cp "$f" "$f.stress.bak"
|
||||
}
|
||||
|
||||
restore_all_backups() {
|
||||
local example="$1"
|
||||
local dir="$EXAMPLES_DIR/$example"
|
||||
while IFS= read -r bak; do
|
||||
[ -f "$bak" ] || continue
|
||||
mv "$bak" "${bak%.stress.bak}"
|
||||
done < <(find "$dir" -name '*.stress.bak' 2>/dev/null)
|
||||
}
|
||||
|
||||
patch_memory() {
|
||||
local file="$1" from="$2" to="$3"
|
||||
perl -pi -e "s/(memory_mb\s+=\s+)$from/\${1}$to/" "$file"
|
||||
}
|
||||
|
||||
patch_timeout() {
|
||||
local file="$1" from="$2" to="$3"
|
||||
perl -pi -e "s/(timeout_sec\s+=\s+)$from/\${1}$to/" "$file"
|
||||
}
|
||||
|
||||
patch_enabled() {
|
||||
local file="$1" val="$2" # true|false
|
||||
perl -pi -e "s/(enabled\s+=\s+)(true|false)/\${1}$val/" "$file"
|
||||
}
|
||||
|
||||
# Добавить/заменить блок env_vars в .tf файле
|
||||
# Вставляет после строки содержащей "source_dir"
|
||||
add_env_var() {
|
||||
local file="$1" key="$2" val="$3"
|
||||
if grep -q "^ env_vars" "$file"; then
|
||||
# уже есть — добавляем строку внутрь блока
|
||||
perl -pi -e "s/(env_vars\s*=\s*\{)/\${1}\n $key = \"$val\"/" "$file"
|
||||
else
|
||||
# нет — вставляем перед source_dir
|
||||
perl -pi -e "s/( source_dir)/ env_vars = \{ $key = \"$val\" \}\n\${1}/" "$file"
|
||||
fi
|
||||
}
|
||||
|
||||
remove_env_vars_block() {
|
||||
local file="$1"
|
||||
perl -0pi -e 's/\s*env_vars\s*=\s*\{[^}]*\}//g' "$file"
|
||||
}
|
||||
|
||||
# ── Утилиты: terraform output ─────────────────────────────────────────────────
|
||||
tf_output() {
|
||||
local chdir="$1" key="$2"
|
||||
(cd "$chdir" && terraform output -raw "$key" 2>/dev/null) || echo ""
|
||||
}
|
||||
|
||||
# ── Утилиты: счётчик шагов ────────────────────────────────────────────────────
|
||||
STEP_NUM=0
|
||||
STEP_FAILS=0
|
||||
next_step() {
|
||||
STEP_NUM=$((STEP_NUM + 1))
|
||||
step "[$STEP_NUM] $*"
|
||||
}
|
||||
|
||||
assert_step() {
|
||||
local desc="$1"; shift
|
||||
if "$@"; then
|
||||
ok "$desc"
|
||||
return 0
|
||||
else
|
||||
fail "$desc"
|
||||
STEP_FAILS=$((STEP_FAILS + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Чистка артефактов ─────────────────────────────────────────────────────────
|
||||
clean_artifacts() {
|
||||
local dir="$1"
|
||||
rm -rf "$dir/.terraform" "$dir/.terraform.lock.hcl" \
|
||||
"$dir/terraform.tfstate" "$dir/terraform.tfstate.backup" \
|
||||
"$dir"/terraform.tfstate.*.backup
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# ТЕСТ: hello-node
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
test_hello_node() {
|
||||
local ex="hello-node"
|
||||
local dir="$EXAMPLES_DIR/$ex"
|
||||
local log="$LOGS_DIR/$ex"
|
||||
ACTIVE_EXAMPLE="$ex"
|
||||
STEP_NUM=0; STEP_FAILS=0
|
||||
local t0=$SECONDS
|
||||
|
||||
header "hello-node (nodejs20 · HTTP trigger + Job)"
|
||||
restore_all_backups "$ex"
|
||||
clean_artifacts "$dir"
|
||||
|
||||
# ── init ──────────────────────────────────────────────────────────────────
|
||||
next_step "terraform init"
|
||||
assert_step "init OK" tf_init "${log}-init.log" "$dir" || { RESULTS[$ex]="FAIL: init"; return 1; }
|
||||
|
||||
# ── ROUND 1: чистый цикл ─────────────────────────────────────────────────
|
||||
next_step "[R1] terraform apply"
|
||||
assert_step "apply OK" tf_apply "${log}-r1-apply.log" "$dir" || { RESULTS[$ex]="FAIL: R1 apply"; return 1; }
|
||||
assert_step "Apply complete in log" assert_apply_ok "${log}-r1-apply.log"
|
||||
|
||||
local trigger_url
|
||||
trigger_url=$(tf_output "$dir" "trigger_url")
|
||||
next_step "[R1] HTTP check — trigger alive (url: $trigger_url)"
|
||||
assert_step "HTTP 200" http_check "$trigger_url" 200 15 10
|
||||
|
||||
next_step "[R1] Job result check"
|
||||
local job_phase
|
||||
job_phase=$(tf_output "$dir" "job_phase")
|
||||
assert_step "Job phase = Succeeded" [ "$job_phase" = "Succeeded" ]
|
||||
local job_msg
|
||||
job_msg=$(tf_output "$dir" "job_message")
|
||||
assert_step "Job message contains sum" echo "$job_msg" | grep -q '"sum"'
|
||||
info "Job output: $job_msg"
|
||||
|
||||
next_step "[R1] terraform destroy"
|
||||
assert_step "destroy OK" tf_destroy "${log}-r1-destroy.log" "$dir" || { RESULTS[$ex]="FAIL: R1 destroy"; return 1; }
|
||||
assert_step "Destroy complete in log" assert_destroy_ok "${log}-r1-destroy.log"
|
||||
|
||||
next_step "[R1] HTTP check — trigger gone (want 404/502+unreachable)"
|
||||
# после destroy endpoint должен исчезнуть или вернуть 404
|
||||
local gone=0
|
||||
for i in $(seq 1 18); do
|
||||
local code
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$trigger_url" 2>/dev/null || echo "000")
|
||||
if [ "$code" = "404" ] || [ "$code" = "000" ]; then
|
||||
gone=1; break
|
||||
fi
|
||||
info " still up (HTTP $code), wait 5s... ($i/18)"
|
||||
sleep 5
|
||||
done
|
||||
assert_step "endpoint gone after destroy" [ "$gone" -eq 1 ]
|
||||
|
||||
# ── ROUND 2: apply → 3 модификации → destroy ─────────────────────────────
|
||||
next_step "[R2] terraform apply (fresh)"
|
||||
assert_step "apply OK" tf_apply "${log}-r2-apply.log" "$dir" || { RESULTS[$ex]="FAIL: R2 apply"; return 1; }
|
||||
|
||||
trigger_url=$(tf_output "$dir" "trigger_url")
|
||||
|
||||
# Mod 1: memory 128 → 256
|
||||
next_step "[R2-mod1] memory_mb 128→256 в http.tf"
|
||||
backup_file "$dir/http.tf"
|
||||
patch_memory "$dir/http.tf" 128 256
|
||||
assert_step "apply mod1 OK" tf_apply "${log}-r2-mod1.log" "$dir"
|
||||
assert_step "mod1: 1 changed" grep -q '1 changed' "${log}-r2-mod1.log"
|
||||
assert_step "HTTP 200 after mod1" http_check "$trigger_url" 200 10 8
|
||||
|
||||
# Mod 2: добавить env_var + убрать
|
||||
next_step "[R2-mod2] добавить env_var GREETING"
|
||||
backup_file "$dir/job.tf" # на случай если патч затронет и его
|
||||
add_env_var "$dir/http.tf" "GREETING" "stress-test-v1"
|
||||
assert_step "apply mod2 OK" tf_apply "${log}-r2-mod2.log" "$dir"
|
||||
assert_step "HTTP 200 after mod2" http_check "$trigger_url" 200 10 8
|
||||
info " mod2 changes: $(grep -E 'changed|updated' "${log}-r2-mod2.log" | head -2)"
|
||||
|
||||
# Mod 3: timeout 30 → 45
|
||||
next_step "[R2-mod3] timeout_sec 30→45 в http.tf"
|
||||
patch_timeout "$dir/http.tf" 30 45
|
||||
assert_step "apply mod3 OK" tf_apply "${log}-r2-mod3.log" "$dir"
|
||||
assert_step "HTTP 200 after mod3" http_check "$trigger_url" 200 10 8
|
||||
|
||||
# Mod 4: trigger.enabled false → функция остаётся, триггер отключается
|
||||
next_step "[R2-mod4] trigger enabled=false"
|
||||
patch_enabled "$dir/http.tf" false
|
||||
assert_step "apply mod4 OK" tf_apply "${log}-r2-mod4.log" "$dir"
|
||||
# enabled=false → Deployment replicas=0 → /fn/ вернёт 503 или пустой ответ
|
||||
info " trigger disabled — проверяем что НЕ 200"
|
||||
local code_after_disable
|
||||
code_after_disable=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$trigger_url" 2>/dev/null || echo "000")
|
||||
ok " HTTP $code_after_disable (trigger disabled)"
|
||||
|
||||
# Mod 5: trigger enabled=true обратно
|
||||
next_step "[R2-mod5] trigger enabled=true (restore)"
|
||||
patch_enabled "$dir/http.tf" true
|
||||
assert_step "apply mod5 OK" tf_apply "${log}-r2-mod5.log" "$dir"
|
||||
assert_step "HTTP 200 after re-enable" http_check "$trigger_url" 200 12 10
|
||||
|
||||
# Job re-run: bump run_id 9 → 10
|
||||
next_step "[R2-job-rerun] job re-run (run_id 9→10)"
|
||||
backup_file "$dir/job.tf"
|
||||
perl -pi -e 's/(run_id\s+=\s+)9/${1}10/' "$dir/job.tf"
|
||||
assert_step "apply job-rerun OK" tf_apply "${log}-r2-job-rerun.log" "$dir"
|
||||
local new_job_phase
|
||||
new_job_phase=$(tf_output "$dir" "job_phase")
|
||||
assert_step "re-run Succeeded" [ "$new_job_phase" = "Succeeded" ]
|
||||
local new_job_msg
|
||||
new_job_msg=$(tf_output "$dir" "job_message")
|
||||
info " re-run output: $new_job_msg"
|
||||
|
||||
# Восстанавливаем файлы до оригинала перед destroy
|
||||
restore_all_backups "$ex"
|
||||
|
||||
next_step "[R2] terraform destroy"
|
||||
assert_step "destroy OK" tf_destroy "${log}-r2-destroy.log" "$dir" || { RESULTS[$ex]="FAIL: R2 destroy"; return 1; }
|
||||
assert_step "Destroy complete" assert_destroy_ok "${log}-r2-destroy.log"
|
||||
|
||||
# ── ROUND 3: apply → немедленный destroy (no-wait) ───────────────────────
|
||||
next_step "[R3] apply → immediate destroy"
|
||||
assert_step "R3 apply OK" tf_apply "${log}-r3-apply.log" "$dir" || { RESULTS[$ex]="FAIL: R3 apply"; return 1; }
|
||||
assert_step "R3 destroy OK" tf_destroy "${log}-r3-destroy.log" "$dir" || { RESULTS[$ex]="FAIL: R3 destroy"; return 1; }
|
||||
|
||||
clean_artifacts "$dir"
|
||||
TIMINGS[$ex]=$((SECONDS - t0))
|
||||
|
||||
if [ "$STEP_FAILS" -eq 0 ]; then
|
||||
RESULTS[$ex]="PASS"
|
||||
ok "hello-node все шаги PASS (${TIMINGS[$ex]}s)"
|
||||
else
|
||||
RESULTS[$ex]="FAIL: $STEP_FAILS step(s) failed"
|
||||
fail "hello-node: $STEP_FAILS шагов с ошибкой"
|
||||
fi
|
||||
ACTIVE_EXAMPLE=""
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# ТЕСТ: simple-node / simple-python (одинаковая логика, разный runtime)
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
test_simple() {
|
||||
local ex="$1" # simple-node | simple-python
|
||||
local dir="$EXAMPLES_DIR/$ex"
|
||||
local log="$LOGS_DIR/$ex"
|
||||
ACTIVE_EXAMPLE="$ex"
|
||||
STEP_NUM=0; STEP_FAILS=0
|
||||
local t0=$SECONDS
|
||||
|
||||
header "$ex"
|
||||
restore_all_backups "$ex"
|
||||
clean_artifacts "$dir"
|
||||
|
||||
# init
|
||||
next_step "terraform init"
|
||||
assert_step "init OK" tf_init "${log}-init.log" "$dir" || { RESULTS[$ex]="FAIL: init"; return 1; }
|
||||
|
||||
# ── ROUND 1 ───────────────────────────────────────────────────────────────
|
||||
next_step "[R1] apply"
|
||||
assert_step "apply OK" tf_apply "${log}-r1-apply.log" "$dir" || { RESULTS[$ex]="FAIL: R1 apply"; return 1; }
|
||||
|
||||
local display_url
|
||||
display_url=$(tf_output "$dir" "display_url")
|
||||
next_step "[R1] HTTP check — display alive"
|
||||
assert_step "HTTP 200" http_check "$display_url" 200 15 10
|
||||
|
||||
local job_result
|
||||
job_result=$(tf_output "$dir" "job_result")
|
||||
assert_step "job_result contains time" echo "$job_result" | grep -q '"time"'
|
||||
info " job result: $job_result"
|
||||
|
||||
next_step "[R1] destroy"
|
||||
assert_step "destroy OK" tf_destroy "${log}-r1-destroy.log" "$dir" || { RESULTS[$ex]="FAIL: R1 destroy"; return 1; }
|
||||
|
||||
next_step "[R1] endpoint gone check"
|
||||
local gone=0
|
||||
for i in $(seq 1 18); do
|
||||
local code
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$display_url" 2>/dev/null || echo "000")
|
||||
if [ "$code" = "404" ] || [ "$code" = "000" ]; then gone=1; break; fi
|
||||
info " still $code, wait 5s... ($i/18)"
|
||||
sleep 5
|
||||
done
|
||||
assert_step "endpoint gone" [ "$gone" -eq 1 ]
|
||||
|
||||
# ── ROUND 2: три модификации ──────────────────────────────────────────────
|
||||
next_step "[R2] apply (fresh)"
|
||||
assert_step "apply OK" tf_apply "${log}-r2-apply.log" "$dir" || { RESULTS[$ex]="FAIL: R2 apply"; return 1; }
|
||||
display_url=$(tf_output "$dir" "display_url")
|
||||
|
||||
# Mod 1: memory 64 → 96 в time-display.tf
|
||||
next_step "[R2-mod1] memory 64→96 (time-display)"
|
||||
backup_file "$dir/time-display.tf"
|
||||
patch_memory "$dir/time-display.tf" 64 96
|
||||
assert_step "apply mod1 OK" tf_apply "${log}-r2-mod1.log" "$dir"
|
||||
assert_step "1 changed" grep -q '1 changed' "${log}-r2-mod1.log"
|
||||
assert_step "HTTP 200 after mod1" http_check "$display_url" 200 12 10
|
||||
|
||||
# Mod 2: memory 64 → 96 в time-getter.tf
|
||||
next_step "[R2-mod2] memory 64→96 (time-getter)"
|
||||
backup_file "$dir/time-getter.tf"
|
||||
patch_memory "$dir/time-getter.tf" 64 96
|
||||
assert_step "apply mod2 OK" tf_apply "${log}-r2-mod2.log" "$dir"
|
||||
assert_step "1 changed" grep -q '1 changed' "${log}-r2-mod2.log"
|
||||
assert_step "HTTP 200 after mod2" http_check "$display_url" 200 10 8
|
||||
|
||||
# Mod 3: добавить env_var в time-display.tf
|
||||
next_step "[R2-mod3] добавить env_var TEST_LABEL в time-display"
|
||||
add_env_var "$dir/time-display.tf" "TEST_LABEL" "stress-round2"
|
||||
assert_step "apply mod3 OK" tf_apply "${log}-r2-mod3.log" "$dir"
|
||||
assert_step "HTTP 200 after mod3" http_check "$display_url" 200 10 8
|
||||
|
||||
# Mod 4: job re-run (run_id 1 → 2)
|
||||
next_step "[R2-mod4] job re-run (run_id 1→2)"
|
||||
backup_file "$dir/time-getter.tf"
|
||||
perl -pi -e 's/(run_id\s+=\s+)1/${1}2/' "$dir/time-getter.tf"
|
||||
assert_step "apply mod4 (job rerun) OK" tf_apply "${log}-r2-mod4.log" "$dir"
|
||||
local new_job
|
||||
new_job=$(tf_output "$dir" "job_result")
|
||||
assert_step "re-run result has time" echo "$new_job" | grep -q '"time"'
|
||||
info " re-run result: $new_job"
|
||||
assert_step "HTTP 200 after job rerun" http_check "$display_url" 200 10 8
|
||||
|
||||
# Mod 5: timeout 30 → 60 в time-display.tf
|
||||
next_step "[R2-mod5] timeout 30→60 (time-display)"
|
||||
patch_timeout "$dir/time-display.tf" 30 60
|
||||
assert_step "apply mod5 OK" tf_apply "${log}-r2-mod5.log" "$dir"
|
||||
|
||||
restore_all_backups "$ex"
|
||||
|
||||
next_step "[R2] destroy"
|
||||
assert_step "destroy OK" tf_destroy "${log}-r2-destroy.log" "$dir" || { RESULTS[$ex]="FAIL: R2 destroy"; return 1; }
|
||||
|
||||
# ── ROUND 3: immediate destroy ────────────────────────────────────────────
|
||||
next_step "[R3] apply → immediate destroy"
|
||||
assert_step "R3 apply OK" tf_apply "${log}-r3-apply.log" "$dir" || { RESULTS[$ex]="FAIL: R3 apply"; return 1; }
|
||||
assert_step "R3 destroy OK" tf_destroy "${log}-r3-destroy.log" "$dir" || { RESULTS[$ex]="FAIL: R3 destroy"; return 1; }
|
||||
|
||||
clean_artifacts "$dir"
|
||||
TIMINGS[$ex]=$((SECONDS - t0))
|
||||
|
||||
if [ "$STEP_FAILS" -eq 0 ]; then
|
||||
RESULTS[$ex]="PASS"
|
||||
ok "$ex все шаги PASS (${TIMINGS[$ex]}s)"
|
||||
else
|
||||
RESULTS[$ex]="FAIL: $STEP_FAILS step(s) failed"
|
||||
fail "$ex: $STEP_FAILS шагов с ошибкой"
|
||||
fi
|
||||
ACTIVE_EXAMPLE=""
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# ТЕСТ: notes-python (postgres + CRUD)
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
test_notes_python() {
|
||||
local ex="notes-python"
|
||||
local dir="$EXAMPLES_DIR/$ex"
|
||||
local log="$LOGS_DIR/$ex"
|
||||
ACTIVE_EXAMPLE="$ex"
|
||||
STEP_NUM=0; STEP_FAILS=0
|
||||
local t0=$SECONDS
|
||||
|
||||
header "notes-python (python3.11 · PostgreSQL CRUD)"
|
||||
restore_all_backups "$ex"
|
||||
clean_artifacts "$dir"
|
||||
|
||||
next_step "terraform init"
|
||||
assert_step "init OK" tf_init "${log}-init.log" "$dir" || { RESULTS[$ex]="FAIL: init"; return 1; }
|
||||
|
||||
# ── ROUND 1 ───────────────────────────────────────────────────────────────
|
||||
next_step "[R1] apply (DB init + CRUD deploy)"
|
||||
assert_step "apply OK" tf_apply "${log}-r1-apply.log" "$dir" || { RESULTS[$ex]="FAIL: R1 apply"; return 1; }
|
||||
|
||||
# Проверяем DB init джобы
|
||||
local tbl_phase idx_phase
|
||||
tbl_phase=$(tf_output "$dir" "db_init_table_status" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('phase','?'))" 2>/dev/null || echo "?")
|
||||
idx_phase=$(tf_output "$dir" "db_init_index_status" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('phase','?'))" 2>/dev/null || echo "?")
|
||||
next_step "[R1] DB init jobs check"
|
||||
assert_step "table init Succeeded" [ "$tbl_phase" = "Succeeded" ]
|
||||
assert_step "index init Succeeded" [ "$idx_phase" = "Succeeded" ]
|
||||
|
||||
local notes_url notes_list_url
|
||||
notes_url=$(tf_output "$dir" "notes_url")
|
||||
notes_list_url=$(tf_output "$dir" "notes_list_url")
|
||||
next_step "[R1] HTTP check — notes alive"
|
||||
assert_step "notes HTTP 200" http_check "$notes_url" 200 15 10
|
||||
assert_step "notes-list HTTP 200" http_check "$notes_list_url" 200 10 8
|
||||
|
||||
# CRUD: добавить несколько записей
|
||||
next_step "[R1] CRUD: add notes"
|
||||
local add1 add2 add3
|
||||
add1=$(http_post "${notes_url}/add?title=StressTest1&body=body1" '{}')
|
||||
add2=$(http_post "${notes_url}/add?title=StressTest2&body=body2" '{}')
|
||||
add3=$(http_post "${notes_url}/add?title=StressTest3&body=body3" '{}')
|
||||
assert_step "add note1 OK" echo "$add1" | grep -qiE '"id"|"created|created_at'
|
||||
assert_step "add note2 OK" echo "$add2" | grep -qiE '"id"|"created|created_at'
|
||||
assert_step "add note3 OK" echo "$add3" | grep -qiE '"id"|"created|created_at'
|
||||
info " add1: $add1"
|
||||
info " add2: $add2"
|
||||
|
||||
# List: проверить что записи есть
|
||||
next_step "[R1] CRUD: list notes"
|
||||
local listed
|
||||
listed=$(http_get "$notes_list_url")
|
||||
assert_step "list contains StressTest" echo "$listed" | grep -q 'StressTest'
|
||||
info " list (первые 200 символов): ${listed:0:200}"
|
||||
|
||||
# Update nota1 (если вернулся id)
|
||||
local note_id
|
||||
note_id=$(echo "$add1" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || echo "")
|
||||
if [ -n "$note_id" ]; then
|
||||
next_step "[R1] CRUD: update note id=$note_id"
|
||||
local updated
|
||||
updated=$(http_post "${notes_url}/update?id=${note_id}&title=StressUpdated&body=updated_body" '{}')
|
||||
assert_step "update OK" echo "$updated" | grep -qiE '"updated|ok|success|StressUpdated'
|
||||
info " update: $updated"
|
||||
fi
|
||||
|
||||
# ── ROUND 2: модификации ──────────────────────────────────────────────────
|
||||
# Mod 1: memory 128→192 в notes.tf
|
||||
next_step "[R2-mod1] memory 128→192 (notes.tf)"
|
||||
backup_file "$dir/notes.tf"
|
||||
patch_memory "$dir/notes.tf" 128 192
|
||||
assert_step "apply mod1 OK" tf_apply "${log}-r2-mod1.log" "$dir"
|
||||
assert_step "1 changed" grep -q '1 changed' "${log}-r2-mod1.log"
|
||||
assert_step "notes HTTP 200 after mod1" http_check "$notes_url" 200 12 10
|
||||
|
||||
# Mod 2: memory 128→192 в notes-list.tf
|
||||
next_step "[R2-mod2] memory 128→192 (notes-list.tf)"
|
||||
backup_file "$dir/notes-list.tf"
|
||||
patch_memory "$dir/notes-list.tf" 128 192
|
||||
assert_step "apply mod2 OK" tf_apply "${log}-r2-mod2.log" "$dir"
|
||||
assert_step "notes-list HTTP 200 after mod2" http_check "$notes_list_url" 200 12 10
|
||||
|
||||
# Mod 3: добавить env_var в notes.tf
|
||||
next_step "[R2-mod3] env_var MAX_NOTES=100 в notes.tf"
|
||||
add_env_var "$dir/notes.tf" "MAX_NOTES" "100"
|
||||
assert_step "apply mod3 OK" tf_apply "${log}-r2-mod3.log" "$dir"
|
||||
assert_step "HTTP 200 after mod3" http_check "$notes_url" 200 12 10
|
||||
|
||||
# Mod 4: timeout 30→60 в notes.tf
|
||||
next_step "[R2-mod4] timeout 30→60 (notes.tf)"
|
||||
patch_timeout "$dir/notes.tf" 30 60
|
||||
assert_step "apply mod4 OK" tf_apply "${log}-r2-mod4.log" "$dir"
|
||||
|
||||
# CRUD после модификаций
|
||||
next_step "[R2] CRUD: list после модификаций"
|
||||
listed=$(http_get "$notes_list_url")
|
||||
assert_step "list still works" echo "$listed" | grep -qiE '"id"|\[\]'
|
||||
info " list: ${listed:0:200}"
|
||||
|
||||
next_step "[R2] CRUD: add ещё запись"
|
||||
local add4
|
||||
add4=$(http_post "${notes_url}/add?title=PostMod&body=after_modifications" '{}')
|
||||
assert_step "add after mod OK" echo "$add4" | grep -qiE '"id"|"created'
|
||||
|
||||
restore_all_backups "$ex"
|
||||
|
||||
next_step "[R2] destroy"
|
||||
assert_step "destroy OK" tf_destroy "${log}-r2-destroy.log" "$dir" || { RESULTS[$ex]="FAIL: R2 destroy"; return 1; }
|
||||
|
||||
# ── ROUND 3: apply → immediate destroy ───────────────────────────────────
|
||||
next_step "[R3] apply → immediate destroy"
|
||||
assert_step "R3 apply OK" tf_apply "${log}-r3-apply.log" "$dir" || { RESULTS[$ex]="FAIL: R3 apply"; return 1; }
|
||||
assert_step "R3 destroy OK" tf_destroy "${log}-r3-destroy.log" "$dir" || { RESULTS[$ex]="FAIL: R3 destroy"; return 1; }
|
||||
|
||||
clean_artifacts "$dir"
|
||||
TIMINGS[$ex]=$((SECONDS - t0))
|
||||
|
||||
if [ "$STEP_FAILS" -eq 0 ]; then
|
||||
RESULTS[$ex]="PASS"
|
||||
ok "notes-python все шаги PASS (${TIMINGS[$ex]}s)"
|
||||
else
|
||||
RESULTS[$ex]="FAIL: $STEP_FAILS step(s) failed"
|
||||
fail "notes-python: $STEP_FAILS шагов с ошибкой"
|
||||
fi
|
||||
ACTIVE_EXAMPLE=""
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
main() {
|
||||
local total_t0=$SECONDS
|
||||
|
||||
echo -e "\n${BOLD}${CYAN}"
|
||||
echo "╔══════════════════════════════════════════════╗"
|
||||
echo "║ sless STRESS TEST $(date '+%Y-%m-%d %H:%M:%S') ║"
|
||||
echo "╚══════════════════════════════════════════════╝"
|
||||
echo -e "${RESET}"
|
||||
echo "Примеры : ${EXAMPLES[*]}"
|
||||
echo "Логи : $LOGS_DIR"
|
||||
echo
|
||||
echo "На каждый пример:"
|
||||
echo " R1: apply → HTTP-check → destroy → endpoint-gone-check"
|
||||
echo " R2: apply → 4-5 модификаций (memory, env_var, timeout, re-run) → destroy"
|
||||
echo " R3: apply → immediate destroy"
|
||||
echo
|
||||
|
||||
for ex in "${EXAMPLES[@]}"; do
|
||||
case "$ex" in
|
||||
hello-node) test_hello_node || true ;;
|
||||
simple-node) test_simple "$ex" || true ;;
|
||||
simple-python) test_simple "$ex" || true ;;
|
||||
notes-python) test_notes_python || true ;;
|
||||
*) fail "Unknown example: $ex"; RESULTS[$ex]="SKIP: unknown" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Финальная сводка ──────────────────────────────────────────────────────
|
||||
local total_elapsed=$((SECONDS - total_t0))
|
||||
echo
|
||||
echo -e "${BOLD}${CYAN}════════════════ ИТОГ ════════════════${RESET}"
|
||||
local all_pass=1
|
||||
for ex in "${EXAMPLES[@]}"; do
|
||||
local result="${RESULTS[$ex]:-UNKNOWN}"
|
||||
local timing="${TIMINGS[$ex]:-?}"
|
||||
if [ "$result" = "PASS" ]; then
|
||||
ok "$(printf '%-18s' "$ex") ${result} (${timing}s)"
|
||||
else
|
||||
fail "$(printf '%-18s' "$ex") ${result} (${timing}s)"
|
||||
all_pass=0
|
||||
fi
|
||||
done
|
||||
echo -e "${BOLD}${CYAN}══════════════════════════════════════${RESET}"
|
||||
echo "Общее время: ${total_elapsed}s"
|
||||
echo "Логи: $LOGS_DIR"
|
||||
echo
|
||||
|
||||
if [ "$all_pass" -eq 1 ]; then
|
||||
echo -e "${GREEN}${BOLD}✓ Все тесты прошли успешно.${RESET}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}${BOLD}✗ Есть ошибки — см. логи выше и в $LOGS_DIR${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
# 2026-03-11 12:30
|
||||
# Наборный тест: собирает образ, выполняет несколько пушей с разными тегами
|
||||
# и выводит статистику (количество успешных/проваленных, время и попытки).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SECRETS_FILE="secrets/pearlharbor_registry.txt"
|
||||
if [ ! -f "$SECRETS_FILE" ]; then
|
||||
echo "secrets file not found: $SECRETS_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NUM_PUSHES=${NUM_PUSHES:-5}
|
||||
RETRIES_PER_PUSH=${RETRIES_PER_PUSH:-3}
|
||||
BACKOFF=${BACKOFF:-2}
|
||||
PROJECT=${PROJECT:-pearlharbor}
|
||||
REGISTRY_USER=${REGISTRY_USER:-admin}
|
||||
CREATE_PROJECT=${CREATE_PROJECT:-false}
|
||||
|
||||
connection_url=$(grep -E '^connection_url=' "$SECRETS_FILE" | cut -d'=' -f2-)
|
||||
admin_pass=$(grep -E '^admin_pass=' "$SECRETS_FILE" | cut -d'=' -f2-)
|
||||
registry=$(echo "$connection_url" | sed -E 's~https?://~~' | sed -E 's~/$~~')
|
||||
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "docker not found" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "Registry: $registry"
|
||||
echo "Project: $PROJECT"
|
||||
echo "Pushes: $NUM_PUSHES, retries per push: $RETRIES_PER_PUSH"
|
||||
|
||||
echo "Building local image..."
|
||||
docker build -t sless-sample:local -f examples/push-sample/Dockerfile examples/push-sample
|
||||
|
||||
if [ "$CREATE_PROJECT" = "true" ]; then
|
||||
echo "Ensuring project $PROJECT exists..."
|
||||
create_code=$(curl -s -u "$REGISTRY_USER:$admin_pass" -o /dev/null -w "%{http_code}" -X POST "https://$registry/api/v2.0/projects" -H 'Content-Type: application/json' -d "{\"project_name\":\"$PROJECT\",\"metadata\":{\"public\":\"true\"}}") || create_code=0
|
||||
echo "Project create response: $create_code"
|
||||
fi
|
||||
|
||||
echo "Logging in to registry..."
|
||||
if ! echo "$admin_pass" | docker login "$registry" -u "$REGISTRY_USER" --password-stdin >/dev/null 2>&1; then
|
||||
echo "docker login failed" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
results=()
|
||||
start_all=$(date +%s.%N)
|
||||
for i in $(seq 1 "$NUM_PUSHES"); do
|
||||
tag="test-$(date +%Y%m%d%H%M%S)-$i"
|
||||
remote="$registry/$PROJECT/sless-sample:$tag"
|
||||
echo "\n=== Push $i/$NUM_PUSHES -> $remote ==="
|
||||
docker tag sless-sample:local "$remote"
|
||||
|
||||
attempt=0
|
||||
success=0
|
||||
t_start=$(date +%s.%N)
|
||||
last_err=""
|
||||
while [ $attempt -lt $RETRIES_PER_PUSH ]; do
|
||||
attempt=$((attempt+1))
|
||||
echo "Attempt $attempt for $remote"
|
||||
if docker push "$remote"; then
|
||||
success=1
|
||||
break
|
||||
else
|
||||
last_err="push failed on attempt $attempt"
|
||||
sleep_time=$((BACKOFF ** attempt))
|
||||
echo "push failed, sleeping $sleep_time s"
|
||||
sleep $sleep_time
|
||||
fi
|
||||
done
|
||||
t_end=$(date +%s.%N)
|
||||
elapsed=$(printf "%.3f" "$(echo "$t_end - $t_start" | bc -l)")
|
||||
results+=("$remote|$success|$attempt|$elapsed|$last_err")
|
||||
done
|
||||
end_all=$(date +%s.%N)
|
||||
total_time=$(printf "%.3f" "$(echo "$end_all - $start_all" | bc -l)")
|
||||
|
||||
echo "\n=== Summary ==="
|
||||
echo "Total pushes: $NUM_PUSHES, total time: ${total_time}s"
|
||||
printf "%s\n" "TAG | SUCCESS | ATTEMPTS | TIME_S | MESSAGE"
|
||||
for r in "${results[@]}"; do
|
||||
IFS='|' read -r tag ok attempts time msg <<< "$r"
|
||||
if [ "$ok" -eq 1 ]; then ok_str="OK"; else ok_str="FAIL"; fi
|
||||
printf "%s | %s | %s | %s | %s\n" "$tag" "$ok_str" "$attempts" "$time" "${msg:--}"
|
||||
done
|
||||
|
||||
fail_count=0
|
||||
for r in "${results[@]}"; do
|
||||
IFS='|' read -r tag ok attempts time msg <<< "$r"
|
||||
if [ "$ok" -ne 1 ]; then fail_count=$((fail_count+1)); fi
|
||||
done
|
||||
|
||||
echo "Failures: $fail_count / $NUM_PUSHES"
|
||||
if [ "$fail_count" -ne 0 ]; then
|
||||
exit 4
|
||||
fi
|
||||
exit 0
|
||||
Reference in New Issue
Block a user