Skip to content

Commit 51ff50d

Browse files
authored
fix: pr71 (#80)
1 parent f1668f9 commit 51ff50d

File tree

17 files changed

+210
-304
lines changed

17 files changed

+210
-304
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ name: CI
22

33
on:
44
push:
5-
branches: main
6-
tags: v*
5+
branches: [main]
6+
tags: [v*]
77
pull_request:
88

99
jobs:
@@ -16,7 +16,7 @@ jobs:
1616
- name: Install Go
1717
uses: actions/setup-go@v5
1818
with:
19-
go-version: "1.23.2"
19+
go-version: "1.24.2"
2020
- name: Install Task
2121
uses: arduino/setup-task@v2
2222
- name: Install dependencies

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Install Go
2020
uses: actions/setup-go@v5
2121
with:
22-
go-version: "1.23.2"
22+
go-version: "1.24.2"
2323
- name: Release
2424
uses: goreleaser/goreleaser-action@v6
2525
env:

CONTRIBUTING.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ task build
3131

3232
## Release
3333

34-
TODO(#32)
34+
We use [GoReleaser](https://goreleaser.com/) and GitHub workflows to automate
35+
the binary publishing process. Setup files:
36+
37+
- [GoReleaser](./.goreleaser.yml)
38+
- [GitHub Workflows](./.github/workflows/release.yml)

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ against a randomly selected [public server](internal/servers.go) for each one.
3131

3232
```sh
3333
up
34+
up -p http
35+
up -p http -c 3
36+
up -p http -tg example.com
37+
cat testdata/stdin-urls.txt | go run . -p http
3438
```
3539

3640
[doc-img]: https://pkg.go.dev/badge/github.com/jesusprubio/up

Taskfile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@ tasks:
3939

4040
test:
4141
summary: "Run tests"
42-
cmd: go test -v ./...
42+
cmd: go test ./...

examples/tcp.go

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

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/jesusprubio/up
22

3-
go 1.22.4
3+
go 1.23.5
44

55
require github.com/fatih/color v1.18.0
66

internal/options.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@
22
package internal
33

44
import (
5+
"errors"
56
"flag"
67
"time"
78
)
89

10+
const targetDesc = "Protocol is required because the format is dependent: URL for HTTP, host:port for TCP, domain for DNS"
11+
912
// Options are the flags supported by the command line application.
1013
type Options struct {
11-
// Input flags.
1214
// Protocol to use. Example: 'http'.
1315
Protocol string
16+
// Where to point the probe.
17+
// URL (HTTP), host/port string (TCP) or domain (DNS).
18+
Target string
1419
// Number of iterations. Zero means infinite.
1520
Count uint
1621
// Time to wait for a response.
@@ -32,11 +37,14 @@ type Options struct {
3237
Debug bool
3338
// Show app documentation.
3439
Help bool
40+
// Disable stardard input target reading.
41+
NoStdin bool
3542
}
3643

3744
// Parse fulfills the command line flags provided by the user.
38-
func (opts *Options) Parse() {
45+
func (opts *Options) Parse() error {
3946
flag.StringVar(&opts.Protocol, "p", "", "Test only one protocol")
47+
flag.StringVar(&opts.Target, "tg", "", targetDesc)
4048
flag.UintVar(&opts.Count, "c", 0, "Number of iterations")
4149
flag.DurationVar(
4250
&opts.Timeout, "t", 5*time.Second, "Time to wait for a response",
@@ -51,7 +59,22 @@ func (opts *Options) Parse() {
5159
flag.BoolVar(&opts.JSONOutput, "j", false, "Output in JSON format")
5260
flag.BoolVar(&opts.GrepOutput, "g", false, "Output in grepable format")
5361
flag.BoolVar(&opts.NoColor, "nc", false, "Disable color output")
54-
flag.BoolVar(&opts.Debug, "dbg", false, "Verbose output")
62+
flag.BoolVar(&opts.Debug, "vv", false, "Verbose output")
5563
flag.BoolVar(&opts.Help, "h", false, "Show app documentation")
64+
flag.BoolVar(
65+
&opts.NoStdin,
66+
"nstd",
67+
false,
68+
"Disable standard input target reading",
69+
)
5670
flag.Parse()
71+
return opts.validate()
72+
}
73+
74+
// Ensures the setup is correct.
75+
func (opts *Options) validate() error {
76+
if opts.Target != "" && opts.Protocol == "" {
77+
return errors.New("protocol is required if target is set")
78+
}
79+
return nil
5780
}

internal/probe.go

100644100755
Lines changed: 38 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import (
77
"time"
88
)
99

10-
// Probe is an experiment to measure the connectivity of a network using
11-
// different protocols and public servers.
10+
// Probe is an experiment to measure the connectivity of a network.
1211
type Probe struct {
13-
// Protocols to use.
14-
Protocols []Protocol
12+
// Protocol to use.
13+
Proto Protocol
1514
// Number of iterations. Zero means infinite.
1615
Count uint
1716
// Delay between requests.
@@ -20,14 +19,15 @@ type Probe struct {
2019
Logger *slog.Logger
2120
// Channel to send back partial results.
2221
ReportCh chan *Report
23-
// URLs (HTTP), host/port strings (TCP) or domains (DNS).
24-
Input []string
22+
// Optional. Where to point the probe.
23+
// URL (HTTP), host/port string (TCP) or domain (DNS).
24+
Target string
2525
}
2626

2727
// Ensures the probe setup is correct.
2828
func (p Probe) validate() error {
29-
if p.Protocols == nil {
30-
return newErrorReqProp("Protocols")
29+
if p.Proto == nil {
30+
return newErrorReqProp("Proto")
3131
}
3232
// 'Delay' could be zero.
3333
if p.Logger == nil {
@@ -58,71 +58,39 @@ func (p Probe) Do(ctx context.Context) error {
5858
for {
5959
select {
6060
case <-ctx.Done():
61-
p.Logger.Debug(
62-
"Context cancelled between iterations",
63-
"count", count,
64-
)
61+
p.Logger.Debug("Context cancelled", "count", count)
6562
return nil
6663
default:
67-
p.Logger.Debug("New iteration", "count", count)
68-
for _, proto := range p.Protocols {
69-
select {
70-
case <-ctx.Done():
71-
p.Logger.Debug(
72-
"Context cancelled between protocols",
73-
"count", count, "protocol", proto,
74-
)
75-
return nil
76-
default:
77-
start := time.Now()
78-
p.Logger.Debug(
79-
"New protocol", "count", count, "protocol", proto,
80-
)
81-
if len(p.Input) == 0 {
82-
// Probe default list of urls
83-
rhost, extra, err := proto.Probe("")
84-
report := Report{
85-
ProtocolID: proto.String(),
86-
Time: time.Since(start),
87-
Error: err,
88-
RHost: rhost,
89-
Extra: extra,
90-
}
91-
p.Logger.Debug(
92-
"Sending report back",
93-
"count", count, "report", report,
94-
)
95-
p.ReportCh <- &report
96-
time.Sleep(p.Delay)
97-
continue
98-
}
99-
100-
// iterate over User provided inputs
101-
for _, addr := range p.Input {
102-
rhost, extra, err := proto.Probe(addr)
103-
report := Report{
104-
ProtocolID: proto.String(),
105-
Time: time.Since(start),
106-
Error: err,
107-
RHost: rhost,
108-
Extra: extra,
109-
}
110-
p.Logger.Debug(
111-
"Sending report back for address",
112-
"count", count, "address", addr, "report", report,
113-
)
114-
p.ReportCh <- &report
115-
time.Sleep(p.Delay)
116-
}
117-
}
118-
time.Sleep(p.Delay)
64+
p.Logger.Debug(
65+
"New iteration",
66+
"count",
67+
count,
68+
"protocol",
69+
p.Proto,
70+
"target",
71+
p.Target,
72+
)
73+
start := time.Now()
74+
target, extra, err := p.Proto.Probe(p.Target)
75+
var errMsg string
76+
if err != nil {
77+
errMsg = err.Error()
78+
}
79+
report := Report{
80+
ProtocolID: p.Proto.String(),
81+
Time: time.Since(start),
82+
Error: errMsg,
83+
Target: target,
84+
Extra: extra,
85+
}
86+
p.Logger.Debug("Sending report back", "report", report)
87+
p.ReportCh <- &report
88+
time.Sleep(p.Delay)
89+
count++
90+
if p.Count > 0 && count >= p.Count {
91+
p.Logger.Debug("Count limit reached", "count", count)
92+
return nil
11993
}
120-
}
121-
p.Logger.Debug("Iteration finished", "count", count, "p.Count", p.Count)
122-
count++
123-
if count == p.Count {
124-
p.Logger.Debug("Count limit reached", "count", count)
125-
return nil
12694
}
12795
}
12896
}

internal/probe_test.go

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,35 @@ func (p *testProtocol) Probe(target string) (string, string, error) {
1818
}
1919

2020
func TestProbeValidate(t *testing.T) {
21-
protocols := []Protocol{&testProtocol{}}
21+
proto := &testProtocol{}
22+
logger := slog.Default()
23+
reportCh := make(chan *Report)
24+
defer close(reportCh)
2225
t.Run("returns nil with valid setup", func(t *testing.T) {
23-
reportCh := make(chan *Report)
24-
defer close(reportCh)
25-
p := Probe{
26-
Protocols: protocols, Logger: slog.Default(), ReportCh: reportCh,
27-
}
26+
p := Probe{Proto: proto, Logger: logger, ReportCh: reportCh}
2827
err := p.validate()
2928
if err != nil {
3029
t.Fatalf("got %q, want nil", err)
3130
}
3231
})
33-
t.Run("returns an error if 'Protocols' is nil", func(t *testing.T) {
32+
t.Run("returns an error if 'Proto' is nil", func(t *testing.T) {
3433
p := Probe{}
3534
err := p.validate()
36-
want := "required property: Protocols"
35+
want := "required property: Proto"
3736
if err.Error() != want {
3837
t.Fatalf("got %q, want %q", err, want)
3938
}
4039
})
4140
t.Run("returns an error if 'Logger' is nil", func(t *testing.T) {
42-
p := Probe{Protocols: protocols}
41+
p := Probe{Proto: proto}
4342
err := p.validate()
4443
want := "required property: Logger"
4544
if err.Error() != want {
4645
t.Fatalf("got %q, want %q", err, want)
4746
}
4847
})
4948
t.Run("returns an error if 'ReportCh' is nil", func(t *testing.T) {
50-
p := Probe{Protocols: protocols, Logger: slog.Default()}
49+
p := Probe{Proto: proto, Logger: logger}
5150
err := p.validate()
5251
want := "required property: ReportCh"
5352
if err.Error() != want {
@@ -60,29 +59,29 @@ func TestProbeDo(t *testing.T) {
6059
t.Run("returns an error if the setup is invalid", func(t *testing.T) {
6160
p := Probe{}
6261
err := p.Do(context.Background())
63-
want := "invalid setup: required property: Protocols"
62+
want := "invalid setup: required property: Proto"
6463
if err.Error() != want {
6564
t.Fatalf("got %q, want %q", err, want)
6665
}
6766
})
68-
protocols := []Protocol{&testProtocol{}}
67+
protocol := &testProtocol{}
6968
t.Run("sends back the report in the channel", func(t *testing.T) {
7069
reportCh := make(chan *Report)
7170
defer close(reportCh)
7271
p := Probe{
73-
Protocols: protocols,
74-
Count: 2,
75-
Logger: slog.Default(),
76-
ReportCh: reportCh,
72+
Proto: &testProtocol{},
73+
Count: 2,
74+
Logger: slog.Default(),
75+
ReportCh: reportCh,
7776
}
78-
protoID := protocols[0].String()
77+
protoID := protocol.String()
7978
go func(t *testing.T) {
8079
for report := range p.ReportCh {
8180
if report.ProtocolID != protoID {
8281
t.Errorf("got %q, want %q", report.ProtocolID, protoID)
8382
}
84-
if report.RHost != testHostPort {
85-
t.Errorf("got %q, want %q", report.RHost, testHostPort)
83+
if report.Target != testHostPort {
84+
t.Errorf("got %q, want %q", report.Target, testHostPort)
8685
}
8786
if report.Time == 0 {
8887
t.Errorf("got %q, want > 0", report.Time)
@@ -101,7 +100,7 @@ func TestProbeDo(t *testing.T) {
101100
reportCh := make(chan *Report)
102101
defer close(reportCh)
103102
p := Probe{
104-
Protocols: protocols, Logger: slog.Default(), ReportCh: reportCh,
103+
Proto: protocol, Logger: slog.Default(), ReportCh: reportCh,
105104
}
106105
ctx, cancel := context.WithCancel(context.Background())
107106
cancel()

0 commit comments

Comments
 (0)