diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 7d53aa0..a49f98b 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -17,4 +17,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: - version: v2.1.6 + version: v2.12.2 diff --git a/.golangci.yml b/.golangci.yml index 63a13fc..93fc8a1 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,40 +1,42 @@ version: "2" +run: + tests: false linters: + default: all enable: - - asasalint - - asciicheck - - bidichk - - bodyclose - - contextcheck - - dogsled - - dupl - - durationcheck - - errchkjson - - errorlint - - exhaustive - - gocheckcompilerdirectives - - gochecksumtype - - gosec - - gosmopolitan - - loggercheck - - makezero - - musttag - - nilerr - - nilnesserr - - noctx - - protogetter - - reassign - - recvcheck - - rowserrcheck - - spancheck - - sqlclosecheck - - testifylint - - unparam - - whitespace - - wsl - - zerologlint + - wsl_v5 + - gomodguard_v2 disable: + - wsl + - gomodguard + - cyclop + - depguard + - err113 + - exhaustruct + - forbidigo + - forcetypeassert + - gochecknoglobals + - gochecknoinits + - godot + - godox - lll + - mnd + - musttag + - nakedret + - nlreturn + - nolintlint + - nonamedreturns + - tagliatelle + - varnamelen + - wrapcheck + - funlen + settings: + wsl_v5: + allow-first-in-block: true + allow-whole-block: true + branch-max-lines: 2 + disable: + - err exclusions: generated: lax presets: @@ -47,6 +49,9 @@ linters: - builtin$ - examples$ formatters: + enable: + - gofmt + - goimports exclusions: generated: lax paths: diff --git a/.goreleaser.yml b/.goreleaser.yml index 0adb236..ff1ba05 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -8,6 +8,7 @@ builds: - CGO_ENABLED=0 goarch: - amd64 + - arm64 goos: - linux ldflags: diff --git a/api.go b/api.go index 412f168..bb279d4 100644 --- a/api.go +++ b/api.go @@ -12,30 +12,47 @@ import ( "time" ) -type ApiClient struct { - Client *http.Client - Gateway string - Token string - Timeout time.Duration - UseTls bool +const DefaultTimeout = 5 + +type AuthTokenAnswer struct { + Token string `json:"jwt"` + ExpireAt uint `json:"expireAt"` } -const DefaultTimeout = 5 +type Recipient struct { + To string `json:"to"` + Target string `json:"target"` +} + +type Message struct { + Recipients []Recipient `json:"recipients"` + Text string `json:"text"` + Provider string `json:"provider"` + Type string `json:"type"` +} type Credentials struct { Username string `json:"username"` Password string `json:"password"` } -func NewApiClient(gateway string) *ApiClient { - return &ApiClient{ +type APIClient struct { + Client *http.Client + Gateway string + Token string + Timeout time.Duration + UseTLS bool +} + +func NewAPIClient(gateway string) *APIClient { + return &APIClient{ Client: &http.Client{}, Gateway: gateway, Timeout: DefaultTimeout * time.Second, } } -func (ac *ApiClient) Login(username, password string) (err error) { +func (ac *APIClient) Login(username, password string) (err error) { ac.Token = "" response, err := ac.DoRequest("signin", Credentials{username, password}) @@ -57,7 +74,7 @@ func (ac *ApiClient) Login(username, password string) (err error) { return } -func (ac *ApiClient) DoLegacyRequest(mode string, +func (ac *APIClient) DoLegacyRequest(mode string, to string, text string, username string, @@ -80,17 +97,17 @@ func (ac *ApiClient) DoLegacyRequest(mode string, schema := "http://" - if ac.UseTls { + if ac.UseTLS { schema = "https://" } - myUrl := schema + ac.Gateway + "/api.php" + "?" + params.Encode() + u := schema + ac.Gateway + "/api.php" + "?" + params.Encode() // Setup Timeout context ctx, cancel := context.WithTimeout(context.Background(), ac.Timeout) defer cancel() - req, err := http.NewRequestWithContext(ctx, "GET", myUrl, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { return err @@ -116,7 +133,7 @@ func (ac *ApiClient) DoLegacyRequest(mode string, resp.Body.Close() - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { err = fmt.Errorf("API request failed with status %d", resp.StatusCode) return err } @@ -124,7 +141,7 @@ func (ac *ApiClient) DoLegacyRequest(mode string, return nil } -func (ac *ApiClient) DoRequest(rawUrl string, body interface{}) (respBody []byte, err error) { +func (ac *APIClient) DoRequest(rawURL string, body any) (respBody []byte, err error) { // Build request body as JSON var buf bytes.Buffer @@ -142,14 +159,14 @@ func (ac *ApiClient) DoRequest(rawUrl string, body interface{}) (respBody []byte schema := "http://" - if ac.UseTls { + if ac.UseTLS { schema = "https://" } // Build Request - baseUrl := schema + ac.Gateway + "/api/" + b := schema + ac.Gateway + "/api/" - req, err := http.NewRequestWithContext(ctx, "POST", baseUrl+rawUrl, &buf) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, b+rawURL, &buf) if err != nil { return []byte(""), err @@ -181,7 +198,7 @@ func (ac *ApiClient) DoRequest(rawUrl string, body interface{}) (respBody []byte resp.Body.Close() - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { err = fmt.Errorf("API request failed with status %d", resp.StatusCode) return } diff --git a/api_test.go b/api_test.go index df9041b..91ea7aa 100644 --- a/api_test.go +++ b/api_test.go @@ -9,14 +9,14 @@ import ( "github.com/jarcoal/httpmock" ) -func TestApiClient_Login(t *testing.T) { +func TestAPIClient_Login(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() httpmock.RegisterResponder("POST", "http://brevisone.local/api/signin", httpmock.NewStringResponder(200, `{"jwt":"abc123","expireAt":0}`)) - ac := NewApiClient("brevisone.local") + ac := NewAPIClient("brevisone.local") err := ac.Login("admin", "password") if err != nil { @@ -43,7 +43,7 @@ func TestApiClient_LoginTimeout(t *testing.T) { }, ) - ac := NewApiClient("brevisone.local") + ac := NewAPIClient("brevisone.local") ac.Timeout = 1 * time.Second err := ac.Login("admin", "password") @@ -60,7 +60,7 @@ func TestApiClient_LoginErr(t *testing.T) { httpmock.RegisterResponder("POST", "https://brevisone.local/api/signin", httpmock.NewStringResponder(401, `{}`)) - ac := NewApiClient("brevisone.local") + ac := NewAPIClient("brevisone.local") err := ac.Login("admin", "password") if err == nil { @@ -75,7 +75,7 @@ func TestApiClient_UnmarshalErr(t *testing.T) { httpmock.RegisterResponder("POST", "https://brevisone.local/api/signin", httpmock.NewStringResponder(200, `{`)) - ac := NewApiClient("brevisone.local") + ac := NewAPIClient("brevisone.local") err := ac.Login("admin", "password") if err == nil { @@ -90,7 +90,7 @@ func TestApiClient_DoRequest(t *testing.T) { httpmock.RegisterResponder("POST", "http://brevisone.local/api/test", httpmock.NewStringResponder(200, `{"test":true}`)) - ac := NewApiClient("brevisone.local") + ac := NewAPIClient("brevisone.local") ac.Token = "abc1234" response, err := ac.DoRequest("test", nil) @@ -111,7 +111,7 @@ func TestApiClient_DoLegacyRequest(t *testing.T) { httpmock.RegisterResponder("GET", "http://brevisone.local/api.php?mode=number&password=password&text=text&to=to&username=username", httpmock.NewStringResponder(200, `{"test":true}`)) - ac := NewApiClient("brevisone.local") + ac := NewAPIClient("brevisone.local") ac.Token = "abc1234" err := ac.DoLegacyRequest("test", "to", "text", "username", "password") @@ -121,7 +121,7 @@ func TestApiClient_DoLegacyRequest(t *testing.T) { } func TestApiClient_DoRequestErr(t *testing.T) { - ac := NewApiClient("local") + ac := NewAPIClient("local") _, err := ac.DoRequest("test", nil) if err == nil { diff --git a/api_types.go b/api_types.go deleted file mode 100644 index 3c3efdb..0000000 --- a/api_types.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -type AuthTokenAnswer struct { - Token string `json:"jwt"` - ExpireAt uint `json:"expireAt"` -} - -type Recipient struct { - To string `json:"to"` - Target string `json:"target"` -} - -type Message struct { - Recipients []Recipient `json:"recipients"` - Text string `json:"text"` - Provider string `json:"provider"` - Type string `json:"type"` -} diff --git a/config.go b/config.go index f254b40..bbc0e7b 100644 --- a/config.go +++ b/config.go @@ -29,7 +29,7 @@ type Config struct { date string notificationType string doNotUseTLS bool - useLegacyHttpApi bool + useLegacyAPI bool } func (c *Config) BindArguments(fs *pflag.FlagSet) { @@ -40,7 +40,7 @@ func (c *Config) BindArguments(fs *pflag.FlagSet) { fs.BoolVar(&c.insecure, "insecure", false, "Skip verification of the TLS certificates (is needed for the default self signed certificate, default false)") fs.BoolVar(&c.doNotUseTLS, "no-tls", false, "Do NOT use TLS to connect to the gateway (default false)") - fs.BoolVar(&c.useLegacyHttpApi, "use-legacy-http-api", false, "Use old HTTP API (required on older firmware versions, default false)") + fs.BoolVar(&c.useLegacyAPI, "use-legacy-http-api", false, "Use old HTTP API (required on older firmware versions, default false)") // Where to send the message to fs.StringVarP(&c.target, "target", "T", "", "Target contact, group or phone number (required)") @@ -107,16 +107,16 @@ func (c *Config) FormatMessage() string { if c.serviceName != "" { // This is a service notification - msg.WriteString(fmt.Sprintf("%s @ %s - %s", c.serviceName, c.hostName, c.checkState)) + fmt.Fprintf(&msg, "%s @ %s - %s", c.serviceName, c.hostName, c.checkState) } else { - msg.WriteString(fmt.Sprintf("%s - %s", c.hostName, c.checkState)) + fmt.Fprintf(&msg, "%s - %s", c.hostName, c.checkState) } if c.comment != "" { - msg.WriteString(fmt.Sprintf("\r\n\"%s\"", c.comment)) + fmt.Fprintf(&msg, "\r\n\"%s\"", c.comment) if c.author != "" { - msg.WriteString(fmt.Sprintf(` by %s`, c.author)) + msg.WriteString(" by " + c.author) } } @@ -134,7 +134,7 @@ func (c *Config) FormatMessage() string { func (c *Config) Run() (err error) { // Setup API client - api := NewApiClient(c.gateway) + api := NewAPIClient(c.gateway) // Update client to allow insecure when requested if c.insecure { @@ -145,9 +145,10 @@ func (c *Config) Run() (err error) { } } - api.UseTls = !c.doNotUseTLS + api.UseTLS = !c.doNotUseTLS - if c.useLegacyHttpApi { + //nolint: nestif + if c.useLegacyAPI { err := api.DoLegacyRequest(c.targetType, c.target, c.FormatMessage(), diff --git a/examples/icinga2/commands.conf b/contrib/icinga2/commands.conf similarity index 100% rename from examples/icinga2/commands.conf rename to contrib/icinga2/commands.conf diff --git a/examples/icinga2/templates.conf b/contrib/icinga2/templates.conf similarity index 100% rename from examples/icinga2/templates.conf rename to contrib/icinga2/templates.conf diff --git a/examples/icinga2/users.conf b/contrib/icinga2/users.conf similarity index 100% rename from examples/icinga2/users.conf rename to contrib/icinga2/users.conf diff --git a/go.mod b/go.mod index 9058a24..eb5ab47 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/NETWAYS/notify-brevisone go 1.24 require ( - github.com/NETWAYS/go-check v0.6.4 + github.com/NETWAYS/go-check v1.0.0 github.com/jarcoal/httpmock v1.4.1 github.com/spf13/pflag v1.0.10 ) diff --git a/go.sum b/go.sum index 945c06d..44414d8 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/NETWAYS/go-check v0.6.4 h1:4WETSVNZNEP0Yudcp5xlvxq6RGn920cmUKq4fz/P1GQ= -github.com/NETWAYS/go-check v0.6.4/go.mod h1:8/GWnq8SirreAixgRmcp82JG16NnEl38rHq9phICy9s= +github.com/NETWAYS/go-check v1.0.0 h1:YkzTwFfGR+Z+mK3Wsqpnu8wibzsB30im19iPNfCOsMQ= +github.com/NETWAYS/go-check v1.0.0/go.mod h1:8/GWnq8SirreAixgRmcp82JG16NnEl38rHq9phICy9s= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A= diff --git a/main.go b/main.go index db32814..7f07ac8 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "github.com/NETWAYS/go-check" @@ -11,6 +12,13 @@ Sends SMS or rings at a given number https://github.com/NETWAYS/notify-brevisone` +var ( + // These get filled at build time with the proper values + version = "development" + commit = "HEAD" + date = "latest" +) + func main() { defer check.CatchPanic() @@ -27,7 +35,7 @@ func main() { if len(os.Args) <= 1 { plugin.FlagSet.Usage() - check.ExitRaw(check.Unknown, "No arguments given") + check.Exit(check.Unknown, "No arguments given") } err := config.Validate() @@ -40,5 +48,19 @@ func main() { check.ExitError(err) } - check.ExitRaw(check.OK, "done") + check.Exit(check.OK, "done") +} + +func buildVersion() string { + result := version + + if commit != "" { + result = fmt.Sprintf("%s\ncommit: %s", result, commit) + } + + if date != "" { + result = fmt.Sprintf("%s\ndate: %s", result, date) + } + + return result } diff --git a/integration_test.go b/main_test.go similarity index 84% rename from integration_test.go rename to main_test.go index f8082a0..dbeb413 100644 --- a/integration_test.go +++ b/main_test.go @@ -5,6 +5,14 @@ import ( "testing" ) +func TestBuildVersion(t *testing.T) { + expected := "development\ncommit: HEAD\ndate: latest" + + if expected != buildVersion() { + t.Error("\nActual: ", buildVersion(), "\nExpected: ", expected) + } +} + func TestConfig_Run_Integration(t *testing.T) { var ( gateway = os.Getenv("BREVIS_INTEGRATION_GATEWAY") diff --git a/version.go b/version.go deleted file mode 100644 index b10595f..0000000 --- a/version.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" -) - -// nolint: gochecknoglobals -var ( - // These get filled at build time with the proper vaules - version = "development" - commit = "HEAD" - date = "latest" -) - -//goland:noinspection GoBoolExpressions -func buildVersion() string { - result := version - - if commit != "" { - result = fmt.Sprintf("%s\ncommit: %s", result, commit) - } - - if date != "" { - result = fmt.Sprintf("%s\ndate: %s", result, date) - } - - return result -} diff --git a/version_test.go b/version_test.go deleted file mode 100644 index fc0f8f7..0000000 --- a/version_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import ( - "testing" -) - -func TestBuildVersion(t *testing.T) { - expected := "development\ncommit: HEAD\ndate: latest" - - if expected != buildVersion() { - t.Error("\nActual: ", buildVersion(), "\nExpected: ", expected) - } -}