internal
config
monitors
run
whoami
···
1
1
+
# This is an example .goreleaser.yml file with some sensible defaults.
2
2
+
# Make sure to check the documentation at https://goreleaser.com
3
3
+
4
4
+
# The lines below are called `modelines`. See `:help modeline`
5
5
+
# Feel free to remove those if you don't want/need to use them.
6
6
+
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
7
7
+
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
8
8
+
9
9
+
version: 2
10
10
+
11
11
+
before:
12
12
+
hooks:
13
13
+
# You may remove this if you don't use go modules.
14
14
+
- go mod tidy
15
15
+
# you may remove this if you don't need go generate
16
16
+
- go generate ./...
17
17
+
18
18
+
builds:
19
19
+
- env:
20
20
+
- CGO_ENABLED=0
21
21
+
goos:
22
22
+
- linux
23
23
+
- windows
24
24
+
- darwin
25
25
+
- main: ./cmd/openstatus
26
26
+
27
27
+
archives:
28
28
+
- format: tar.gz
29
29
+
# this name template makes the OS and Arch compatible with the results of `uname`.
30
30
+
name_template: >-
31
31
+
{{ .ProjectName }}_
32
32
+
{{- title .Os }}_
33
33
+
{{- if eq .Arch "amd64" }}x86_64
34
34
+
{{- else if eq .Arch "386" }}i386
35
35
+
{{- else }}{{ .Arch }}{{ end }}
36
36
+
{{- if .Arm }}v{{ .Arm }}{{ end }}
37
37
+
# use zip for windows archives
38
38
+
format_overrides:
39
39
+
- goos: windows
40
40
+
format: zip
41
41
+
42
42
+
changelog:
43
43
+
sort: asc
44
44
+
filters:
45
45
+
exclude:
46
46
+
- "^docs:"
47
47
+
- "^test:"
···
1
1
+
tests:
2
2
+
ids:
3
3
+
- 2260
4
4
+
- 2264
5
5
+
···
1
1
-
tests:
2
2
-
- id:
3
3
-
- 1
4
4
-
- 660
5
5
-
···
6
6
7
7
require (
8
8
github.com/fatih/color v1.18.0
9
9
+
github.com/knadh/koanf/parsers/yaml v0.1.0
10
10
+
github.com/knadh/koanf/providers/file v1.1.2
9
11
github.com/knadh/koanf/v2 v2.1.1
10
12
github.com/rodaine/table v1.3.0
11
13
)
12
14
13
15
require (
16
16
+
github.com/fsnotify/fsnotify v1.7.0 // indirect
14
17
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
15
18
github.com/knadh/koanf/maps v0.1.1 // indirect
16
19
github.com/mattn/go-colorable v0.1.13 // indirect
···
18
21
github.com/mitchellh/copystructure v1.2.0 // indirect
19
22
github.com/mitchellh/reflectwalk v1.0.2 // indirect
20
23
golang.org/x/sys v0.26.0 // indirect
24
24
+
gopkg.in/yaml.v3 v3.0.1 // indirect
21
25
)
···
3
3
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4
4
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
5
5
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
6
6
+
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
7
7
+
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
6
8
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
7
9
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
8
10
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
9
11
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
10
12
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
11
13
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
14
14
+
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
15
15
+
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
16
16
+
github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w=
17
17
+
github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
12
18
github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
13
19
github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es=
14
20
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
···
43
49
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
44
50
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
45
51
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
52
52
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
46
53
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
47
54
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
48
55
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
···
1
1
package config
2
2
3
3
-
import "github.com/knadh/koanf/v2"
3
3
+
import (
4
4
+
"github.com/knadh/koanf/parsers/yaml"
5
5
+
"github.com/knadh/koanf/providers/file"
6
6
+
7
7
+
"github.com/knadh/koanf/v2"
8
8
+
)
4
9
5
10
var k = koanf.New(".")
6
11
···
11
16
type Config struct {
12
17
Tests TestsConfig `koanf:"tests"`
13
18
}
19
19
+
20
20
+
func ReadConfig(path string) (*Config, error) {
21
21
+
k.Load(file.Provider(path), yaml.Parser())
22
22
+
23
23
+
var out Config
24
24
+
25
25
+
err := k.Unmarshal("", &out)
26
26
+
if err != nil {
27
27
+
return nil, err
28
28
+
}
29
29
+
30
30
+
return &out, nil
31
31
+
32
32
+
}
···
51
51
fmt.Println("Monitor information")
52
52
monitorId := cmd.Args().Get(0)
53
53
err := getMonitorInfo(http.DefaultClient, cmd.String("access-token"), monitorId)
54
54
-
return err
54
54
+
if err != nil {
55
55
+
return cli.Exit("Failed to get monitor information", 1)
56
56
+
}
57
57
+
return nil
55
58
},
56
59
Flags: []cli.Flag{
57
60
&cli.StringFlag{
···
100
100
monitorId := cmd.Args().Get(0)
101
101
err := monitorTrigger(http.DefaultClient, cmd.String("access-token"), monitorId)
102
102
if err != nil {
103
103
-
return err
103
103
+
return cli.Exit("Failed to trigger monitor", 1)
104
104
}
105
105
-
// fmt.Println("Triggering monitor test", r)
106
105
return nil
107
106
},
108
107
}
···
72
72
},
73
73
Action: func(ctx context.Context, cmd *cli.Command) error {
74
74
fmt.Println("List of all monitors")
75
75
-
listMonitors(http.DefaultClient, cmd.String("access-token"))
75
75
+
err := listMonitors(http.DefaultClient, cmd.String("access-token"))
76
76
+
if err != nil {
77
77
+
return cli.Exit("Failed to list monitors", 1)
78
78
+
}
76
79
return nil
77
80
},
78
81
}
···
2
2
3
3
import (
4
4
"context"
5
5
+
"encoding/json"
5
6
"fmt"
7
7
+
"io"
8
8
+
"net/http"
9
9
+
"sync"
6
10
11
11
+
"github.com/fatih/color"
12
12
+
"github.com/openstatusHQ/cli/internal/config"
13
13
+
"github.com/openstatusHQ/cli/internal/monitors"
14
14
+
"github.com/rodaine/table"
7
15
"github.com/urfave/cli/v3"
8
16
)
9
17
18
18
+
func monitorTrigger(httpClient *http.Client, apiKey string, monitorId string) error {
19
19
+
20
20
+
if monitorId == "" {
21
21
+
return fmt.Errorf("Monitor ID is required")
22
22
+
}
23
23
+
24
24
+
url := fmt.Sprintf("https://api.openstatus.dev/v1/monitor/%s/run", monitorId)
25
25
+
26
26
+
req, err := http.NewRequest("POST", url, nil)
27
27
+
if err != nil {
28
28
+
return err
29
29
+
}
30
30
+
req.Header.Add("x-openstatus-key", apiKey)
31
31
+
res, err := httpClient.Do(req)
32
32
+
if err != nil {
33
33
+
return err
34
34
+
}
35
35
+
36
36
+
if res.StatusCode != http.StatusOK {
37
37
+
return fmt.Errorf("Failed to trigger monitor test")
38
38
+
}
39
39
+
40
40
+
defer res.Body.Close()
41
41
+
body, _ := io.ReadAll(res.Body)
42
42
+
43
43
+
var result []monitors.RunResult
44
44
+
err = json.Unmarshal(body, &result)
45
45
+
if err != nil {
46
46
+
return err
47
47
+
}
48
48
+
fmt.Println("Results for monitor:", monitorId)
49
49
+
headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()
50
50
+
columnFmt := color.New(color.FgYellow).SprintfFunc()
51
51
+
52
52
+
tbl := table.New("Region", "Latency (ms)", "Status")
53
53
+
tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)
54
54
+
55
55
+
var inError bool
56
56
+
for _, r := range result {
57
57
+
if r.Error != "" {
58
58
+
inError = true
59
59
+
tbl.AddRow(r.Region, r.Latency, color.RedString("❌"))
60
60
+
} else {
61
61
+
tbl.AddRow(r.Region, r.Latency, color.GreenString("✔"))
62
62
+
}
63
63
+
64
64
+
}
65
65
+
tbl.Print()
66
66
+
67
67
+
if inError {
68
68
+
fmt.Println(color.RedString("Some regions failed"))
69
69
+
return fmt.Errorf("Some regions failed")
70
70
+
} else {
71
71
+
fmt.Println(color.GreenString("All regions passed"))
72
72
+
}
73
73
+
return nil
74
74
+
}
75
75
+
10
76
func RunCmd() *cli.Command {
11
77
runCmd := cli.Command{
12
78
Name: "run",
···
14
80
Usage: "Run your synthetics tests defined in your configuration file",
15
81
Action: func(ctx context.Context, cmd *cli.Command) error {
16
82
17
17
-
fmt.Println(cmd.String("config"))
18
18
-
fmt.Println("Test ran 🔥")
83
83
+
path := cmd.String("config")
84
84
+
conf, err := config.ReadConfig(path)
85
85
+
if err != nil {
86
86
+
return err
87
87
+
}
88
88
+
size := len(conf.Tests.Ids)
89
89
+
ch := make(chan error, size)
90
90
+
91
91
+
fmt.Println("Tests are running")
92
92
+
93
93
+
var wg sync.WaitGroup
94
94
+
95
95
+
for _, id := range conf.Tests.Ids {
96
96
+
wg.Add(1)
97
97
+
go func(id int) {
98
98
+
defer wg.Done()
99
99
+
if err := monitorTrigger(http.DefaultClient, cmd.String("access-token"), fmt.Sprintf("%d", id)); err != nil {
100
100
+
ch <- err
101
101
+
}
102
102
+
103
103
+
}(id)
104
104
+
}
105
105
+
wg.Wait()
106
106
+
close(ch) // Close the channel when all workers have finished
107
107
+
108
108
+
if len(ch) > 0 {
109
109
+
return cli.Exit("Some tests failed", 1)
110
110
+
}
111
111
+
fmt.Println("Test ran succesfully🔥")
19
112
return nil
20
113
},
21
114
Flags: []cli.Flag{
···
27
27
}
28
28
29
29
defer res.Body.Close()
30
30
+
if res.StatusCode != http.StatusOK {
31
31
+
return fmt.Errorf("Failed to get workspace information")
32
32
+
}
30
33
body, _ := io.ReadAll(res.Body)
31
34
var whoami Whoami
32
35
err = json.Unmarshal(body, &whoami)
···
47
50
Usage: "Get your current workspace information",
48
51
Action: func(ctx context.Context, cmd *cli.Command) error {
49
52
fmt.Println("Your current workspace information")
50
50
-
getWhoamiCmd(http.DefaultClient, cmd.String("access-token"))
53
53
+
err := getWhoamiCmd(http.DefaultClient, cmd.String("access-token"))
54
54
+
if err != nil {
55
55
+
return cli.Exit("Failed to get workspace information", 1)
56
56
+
}
51
57
return nil
52
58
},
53
59
Flags: []cli.Flag{
···
1
1
-
tests:
2
2
-
- id: 1