Skip to content

Commit 08430df

Browse files
committed
refactor: update tools to support graceful shutdown and periodic tasks
1 parent c157706 commit 08430df

15 files changed

Lines changed: 1019 additions & 175 deletions

File tree

.github/workflows/ci.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ on:
1919
- "context/**"
2020
- "TODO.md"
2121
- "examples/**/README.md"
22+
schedule:
23+
# Run security scan weekly on Mondays at 00:00 UTC
24+
- cron: "0 0 * * 1"
2225

2326
jobs:
2427
test:
@@ -93,3 +96,53 @@ jobs:
9396
gofmt -s -d .
9497
exit 1
9598
fi
99+
100+
trivy:
101+
name: Vulnerability Scan (Trivy)
102+
runs-on: ubuntu-latest
103+
104+
steps:
105+
- name: Checkout code
106+
uses: actions/checkout@v4
107+
108+
- name: Run Trivy vulnerability scanner
109+
uses: aquasecurity/trivy-action@master
110+
with:
111+
scan-type: "fs"
112+
scan-ref: "."
113+
format: "sarif"
114+
output: "trivy-results.sarif"
115+
severity: "CRITICAL,HIGH,MEDIUM"
116+
117+
dependency-review:
118+
name: Dependency Review
119+
runs-on: ubuntu-latest
120+
if: github.event_name == 'pull_request'
121+
122+
steps:
123+
- name: Checkout code
124+
uses: actions/checkout@v4
125+
126+
- name: Dependency Review
127+
uses: actions/dependency-review-action@v4
128+
with:
129+
fail-on-severity: moderate
130+
131+
govulncheck:
132+
name: Go Vulnerability Check
133+
runs-on: ubuntu-latest
134+
135+
steps:
136+
- name: Checkout code
137+
uses: actions/checkout@v4
138+
139+
- name: Set up Go
140+
uses: actions/setup-go@v5
141+
with:
142+
go-version: "1.25"
143+
144+
- name: Install govulncheck
145+
run: go install golang.org/x/vuln/cmd/govulncheck@latest
146+
147+
- name: Run govulncheck
148+
run: govulncheck $(go list ./... | grep -v '/examples/' | grep -v '/test/')

.github/workflows/security.yml

Lines changed: 0 additions & 93 deletions
This file was deleted.

coaptool/coaptool.go

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
coaptcp "github.com/plgd-dev/go-coap/v3/tcp"
1919
coapudp "github.com/plgd-dev/go-coap/v3/udp"
2020

21+
"github.com/sandrolain/eventkit/pkg/common"
2122
testpayload "github.com/sandrolain/eventkit/pkg/testpayload"
2223
toolutil "github.com/sandrolain/eventkit/pkg/toolutil"
2324
)
@@ -43,15 +44,11 @@ func main() {
4344
Use: "send",
4445
Short: "Send periodic CoAP POST requests",
4546
RunE: func(cmd *cobra.Command, args []string) error {
46-
logger := toolutil.Logger()
47-
dur, err := time.ParseDuration(sendInterval)
48-
if err != nil {
49-
return fmt.Errorf("invalid interval: %w", err)
50-
}
51-
ticker := time.NewTicker(dur)
52-
defer ticker.Stop()
47+
ctx, cancel := common.SetupGracefulShutdown()
48+
defer cancel()
5349

54-
logger.Info("Sending CoAP POST periodically", "proto", sendProto, "addr", sendAddress, "path", sendPath, "every", dur)
50+
logger := toolutil.Logger()
51+
logger.Info("Sending CoAP POST periodically", "proto", sendProto, "addr", sendAddress, "path", sendPath, "interval", sendInterval)
5552

5653
sendOnce := func() {
5754
var body []byte
@@ -123,10 +120,10 @@ func main() {
123120
}
124121
}
125122

126-
for range ticker.C {
127-
go sendOnce()
128-
}
129-
select {}
123+
return common.StartPeriodicTask(ctx, sendInterval, func() error {
124+
sendOnce()
125+
return nil
126+
})
130127
},
131128
}
132129

@@ -145,14 +142,31 @@ func main() {
145142
Use: "serve",
146143
Short: "Run a CoAP server that logs requests",
147144
RunE: func(cmd *cobra.Command, args []string) error {
145+
ctx, cancel := common.SetupGracefulShutdown()
146+
defer cancel()
147+
148148
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
149149
logger.Info("Starting coapdbg", "proto", serveProto, "addr", serveAddr)
150150

151151
router := coapmux.NewRouter()
152152
if err := router.Handle("/", SimpleOKHandler(serveProto)); err != nil {
153153
return err
154154
}
155-
return Serve(serveProto, serveAddr, router)
155+
156+
// Start server in goroutine
157+
errChan := make(chan error, 1)
158+
go func() {
159+
errChan <- Serve(serveProto, serveAddr, router)
160+
}()
161+
162+
// Wait for shutdown or error
163+
select {
164+
case <-ctx.Done():
165+
logger.Info("Shutting down gracefully")
166+
return nil
167+
case err := <-errChan:
168+
return err
169+
}
156170
},
157171
}
158172
serveCmd.Flags().StringVar(&serveAddr, "address", ":5683", "Listen address (e.g.: :5683)")

gittool/gittool.go

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"context"
54
"fmt"
65
"os"
76
"path/filepath"
@@ -12,6 +11,7 @@ import (
1211
"github.com/go-git/go-git/v5/plumbing"
1312
"github.com/go-git/go-git/v5/plumbing/object"
1413
"github.com/go-git/go-git/v5/plumbing/transport/http"
14+
"github.com/sandrolain/eventkit/pkg/common"
1515
"github.com/spf13/cobra"
1616
)
1717

@@ -62,10 +62,8 @@ func main() {
6262
}
6363

6464
func runGitSend(remote, branch, interval, filename, message, username, password string) error {
65-
ctx := context.Background()
66-
_ = ctx // reserved for future use
67-
68-
dur, _ := time.ParseDuration(interval)
65+
ctx, cancel := common.SetupGracefulShutdown()
66+
defer cancel()
6967
// working dir
7068
tmpDir, err := os.MkdirTemp("", "gittool-*")
7169
if err != nil {
@@ -82,17 +80,16 @@ func runGitSend(remote, branch, interval, filename, message, username, password
8280
return err
8381
}
8482

85-
fmt.Printf("Ready. Remote: %s, branch: %s, file: %s. Interval: %s\n", remote, branch, filename, dur)
86-
ticker := time.NewTicker(dur)
87-
defer ticker.Stop()
88-
for range ticker.C {
83+
fmt.Printf("Ready. Remote: %s, branch: %s, file: %s. Interval: %s\n", remote, branch, filename, interval)
84+
85+
return common.StartPeriodicTask(ctx, interval, func() error {
8986
if err := doCommit(repo, tmpDir, branch, filename, message, username, password, remote); err != nil {
9087
fmt.Fprintf(os.Stderr, "Commit error: %v\n", err)
91-
} else {
92-
fmt.Printf("Committed to %s/%s at %s\n", remote, branch, time.Now().Format(time.RFC3339))
88+
return err
9389
}
94-
}
95-
return nil
90+
fmt.Printf("Committed to %s/%s at %s\n", remote, branch, time.Now().Format(time.RFC3339))
91+
return nil
92+
})
9693
}
9794

9895
func cloneOrInitRepo(tmpDir, remote, branch, username, password string) (*git.Repository, error) {

httptool/httptool.go

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"fmt"
55
"log/slog"
66
"os"
7-
"time"
87

8+
"github.com/sandrolain/eventkit/pkg/common"
99
toolutil "github.com/sandrolain/eventkit/pkg/toolutil"
1010
"github.com/spf13/cobra"
1111
"github.com/valyala/fasthttp"
@@ -42,16 +42,12 @@ type sendOptions struct {
4242
}
4343

4444
func (o *sendOptions) run() error {
45-
url := o.address + o.path
45+
ctx, cancel := common.SetupGracefulShutdown()
46+
defer cancel()
4647

47-
dur, err := time.ParseDuration(o.interval)
48-
if err != nil {
49-
return fmt.Errorf("invalid interval: %w", err)
50-
}
51-
ticker := time.NewTicker(dur)
52-
defer ticker.Stop()
48+
url := o.address + o.path
5349

54-
fmt.Printf("Sending %s requests to %s every %s\n", o.method, url, dur)
50+
fmt.Printf("Sending %s requests to %s every %s\n", o.method, url, o.interval)
5551

5652
sendRequest := func() {
5753
reqBody, contentType, err := toolutil.BuildPayload(o.payload, o.mime)
@@ -85,11 +81,10 @@ func (o *sendOptions) run() error {
8581
printHTTPResponse(o.method, url, w)
8682
}
8783

88-
for range ticker.C {
89-
go sendRequest()
90-
}
91-
92-
select {}
84+
return common.StartPeriodicTask(ctx, o.interval, func() error {
85+
sendRequest()
86+
return nil
87+
})
9388
}
9489

9590
func main() {
@@ -136,6 +131,9 @@ func newServeCommand() *cobra.Command {
136131
Use: "serve",
137132
Short: "Run an HTTP server that logs requests",
138133
RunE: func(cmd *cobra.Command, args []string) error {
134+
ctx, cancel := common.SetupGracefulShutdown()
135+
defer cancel()
136+
139137
slog.Info("Starting httpdbg", "addr", serveAddr)
140138

141139
handler := func(ctx *fasthttp.RequestCtx) {
@@ -157,11 +155,23 @@ func newServeCommand() *cobra.Command {
157155
toolutil.PrintColoredMessage("HTTP", sections, ctx.Request.Body(), ct)
158156
}
159157

160-
if err := fasthttp.ListenAndServe(serveAddr, handler); err != nil {
161-
slog.Error("error serving httpdbg", "err", err)
158+
// Start server in goroutine
159+
errChan := make(chan error, 1)
160+
go func() {
161+
if err := fasthttp.ListenAndServe(serveAddr, handler); err != nil {
162+
slog.Error("error serving httpdbg", "err", err)
163+
errChan <- err
164+
}
165+
}()
166+
167+
// Wait for shutdown or error
168+
select {
169+
case <-ctx.Done():
170+
slog.Info("Shutting down gracefully")
171+
return nil
172+
case err := <-errChan:
162173
return err
163174
}
164-
return nil
165175
},
166176
}
167177

0 commit comments

Comments
 (0)