From e0ea40c75a723d3aefa709aca9bc37c65165a4ee Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Mon, 17 Jun 2024 12:56:09 +0200 Subject: [PATCH] fix: properly return 503 status when shutdown was initiated Signed-off-by: Valery Piashchynski --- .github/dependabot.yml | 2 +- .github/workflows/linters.yml | 4 +- .golangci.yml | 13 ++--- go.work.sum | 52 ++++++++++++++++++ health.go | 10 +++- jobs.go | 10 +++- plugin.go | 49 +++++++++-------- ready.go | 16 ++++-- tests/configs/.rr-status-503.yaml | 21 ++++++++ tests/plugin_test.go | 88 +++++++++++++++++++++++++++++++ 10 files changed, 225 insertions(+), 40 deletions(-) create mode 100644 tests/configs/.rr-status-503.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6271660..309f020 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,7 +17,7 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: weekly + interval: daily reviewers: - "rustatian" assignees: diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index e134043..41a955b 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -16,8 +16,8 @@ jobs: go-version: stable - name: Run linter - uses: golangci/golangci-lint-action@v3.7.0 # Action page: + uses: golangci/golangci-lint-action@v6.0.1 # Action page: with: - version: v1.54 # without patch version + version: v1.59 # without patch version only-new-issues: false # show only new issues if it's a pull request args: --timeout=10m --build-tags=race ./... diff --git a/.golangci.yml b/.golangci.yml index 653be45..25d1e2d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,21 +2,11 @@ run: timeout: 1m - skip-dirs: - - .github - - .git allow-parallel-runners: true -output: - format: colored-line-number # colored-line-number|line-number|json|tab|checkstyle|code-climate - linters-settings: wsl: allow-assign-and-anything: true - govet: - check-shadowing: true - golint: - min-confidence: 0.1 gocyclo: min-complexity: 15 godot: @@ -78,6 +68,9 @@ linters: # All available linters list: = 500 { - // if there is 500 or 503 status code return immediately + // if there are 500 or 503 status codes return immediately w.WriteHeader(rd.unavailableStatusCode) _, _ = w.Write([]byte(fmt.Sprintf(template, html.EscapeString(pl[i]), rd.unavailableStatusCode))) return diff --git a/tests/configs/.rr-status-503.yaml b/tests/configs/.rr-status-503.yaml new file mode 100644 index 0000000..5d03654 --- /dev/null +++ b/tests/configs/.rr-status-503.yaml @@ -0,0 +1,21 @@ +version: '3' + +rpc: + listen: tcp://127.0.0.1:6005 + +server: + command: "php php_test_files/sleep.php" + relay: "pipes" + relay_timeout: "20s" + +status: + address: "127.0.0.1:34711" + +logs: + mode: development + level: debug + +http: + address: 127.0.0.1:11934 + pool: + num_workers: 1 diff --git a/tests/plugin_test.go b/tests/plugin_test.go index fbd4532..91609ec 100644 --- a/tests/plugin_test.go +++ b/tests/plugin_test.go @@ -1,6 +1,7 @@ package status import ( + "context" "io" "log/slog" "net" @@ -460,6 +461,93 @@ func TestJobsReadiness(t *testing.T) { wg.Wait() } +func TestShutdown503(t *testing.T) { + cont := endure.New(slog.LevelDebug, endure.GracefulShutdownTimeout(time.Second*10)) + + cfg := &config.Plugin{ + Version: "2024.1.0", + Path: "configs/.rr-status-503.yaml", + } + + err := cont.RegisterAll( + cfg, + &rpcPlugin.Plugin{}, + &logger.Plugin{}, + &server.Plugin{}, + &status.Plugin{}, + &jobs.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout, error here is OK, because in the PHP we are sleeping for the 300s + _ = cont.Stop() + return + } + } + }() + + time.Sleep(time.Second) + go func() { + httpClient := &http.Client{ + Timeout: time.Second * 10, + } + + req, err2 := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://127.0.0.1:11934", nil) + assert.NoError(t, err2) + _, _ = httpClient.Do(req) + }() + time.Sleep(time.Second) + stopCh <- struct{}{} + + httpClient := &http.Client{ + Timeout: time.Second * 10, + } + + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://127.0.0.1:34711/health", nil) + assert.NoError(t, err) + require.NotNil(t, req) + + rsp, err := httpClient.Do(req) + require.NoError(t, err) + + assert.Equal(t, http.StatusServiceUnavailable, rsp.StatusCode) + + wg.Wait() +} + func checkJobsReadiness(t *testing.T) { req, err := http.NewRequest("GET", "http://127.0.0.1:35544/ready?plugin=jobs", nil) assert.NoError(t, err)