refactor: reorganize project in order to include automation #1

Merged
Emmanuel BENOîT merged 19 commits from :master into master 2024-07-19 22:01:35 +02:00
13 changed files with 295 additions and 52 deletions
Showing only changes of commit 96637019c1 - Show all commits

View file

@ -25,5 +25,8 @@ jobs:
- name: Build plugins
run: |
go mod download
./build.sh
make build-cross
- name: Run tests
run: |
make test

58
Makefile Normal file
View file

@ -0,0 +1,58 @@
BINNAME = go-monitoring
BINDIR = $(CURDIR)/bin
TARGETS = linux/amd64 linux/386 linux/arm linux/arm64
SRC := $(shell find . -type f -name '*.go' -print) go.mod go.sum
GOHOSTOS = $(shell go env GOHOSTOS)
GOHOSTARCH ?= $(shell go env GOHOSTARCH)
GIT_TAG = $(shell git describe --tags --abbrev=0 --exact-match 2>/dev/null)
GIT_COMMIT = $(shell git rev-parse --short HEAD 2>/dev/null)
GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean")
ifneq ($(VERSION),)
BINARY_VERSION = $(VERSION)
endif
BINARY_VERSION ?= $(GIT_TAG)
LDFLAGS += -w -s
ifneq ($(BINARY_VERSION),)
LDFLAGS += -X nocternity.net/go-monitoring/pkg/version.version=${BINARY_VERSION}
endif
LDFLAGS += -X nocternity.net/go-monitoring/pkg/version.commit=${GIT_COMMIT}
LDFLAGS += -X nocternity.net/go-monitoring/pkg/version.status=${GIT_STATUS}
.PHONY: all
all: build
.PHONY: build
build: GOOS=$(GOHOSTOS)
build: GOARCH=$(GOHOSTARCH)
build: build-target symlink
.PHONY: symlink
symlink:
@ln -sf $(BINDIR)/$(GOOS)/$(GOARCH)/$(BINNAME) $(BINDIR)/$(BINNAME)
.PHONY: build-target
build-target: LDFLAGS += -X nocternity.net/go-monitoring/pkg/version.target=${GOOS}/${GOARCH}
build-target: $(BINDIR)/$(GOOS)/$(GOARCH)/$(BINNAME)
$(BINDIR)/$(GOOS)/$(GOARCH)/$(BINNAME): $(SRC)
@mkdir -p $(BINDIR)/$(GOOS)/$(GOARCH)
@CGO_ENABLED=0 go build -ldflags "$(LDFLAGS)" -o $(BINDIR)/$(GOOS)/$(GOARCH)/$(BINNAME) .
.PHONY: test
test: build
@go test -v ./...
.PHONY: build-cross
build-cross:
@for target in $(TARGETS); do \
$(MAKE) build-target GOOS=`echo $$target | cut -d / -f 1` GOARCH=`echo $$target | cut -d / -f 2`; \
done
.PHONY: clean
clean:
@rm -rf $(BINDIR)

View file

@ -1,15 +0,0 @@
#!/bin/bash
set -e
cd "$(dirname $0)"
for arch in 386 amd64; do
mkdir -p bin/$arch
for d in $(find cmd -mindepth 1 -maxdepth 1 -type d); do
pushd $d >/dev/null
xn="$(basename "$d")"
GOARCH=$arch go get .
GOARCH=$arch go build
/bin/mv "$xn" ../../bin/$arch
popd >/dev/null
done
done

View file

@ -1,4 +1,4 @@
package main
package sslcert // import nocternity.net/go-monitoring/cmd/sslcert
import (
"bufio"
@ -12,8 +12,9 @@ import (
"strings"
"time"
"nocternity.net/go/monitoring/perfdata"
"nocternity.net/go/monitoring/plugin"
"nocternity.net/go-monitoring/pkg/perfdata"
"nocternity.net/go-monitoring/pkg/plugin"
"nocternity.net/go-monitoring/pkg/program"
"github.com/karrick/golf"
)
@ -198,7 +199,7 @@ func (flags *programFlags) parseArguments() {
}
// Initialise the monitoring check program.
func newProgram() *checkProgram {
func NewProgram() program.Program {
program := &checkProgram{
plugin: plugin.New("Certificate check"),
}
@ -207,13 +208,13 @@ func newProgram() *checkProgram {
}
// Terminate the monitoring check program.
func (program *checkProgram) close() {
func (program *checkProgram) Done() {
program.plugin.Done()
}
// Check the values that were specified from the command line. Returns true
// if the arguments made sense.
func (program *checkProgram) checkFlags() bool {
func (program *checkProgram) CheckArguments() bool {
if program.hostname == "" {
program.plugin.SetState(plugin.UNKNOWN, "no hostname specified")
return false
@ -332,7 +333,7 @@ func (program *checkProgram) setPerfData(tlDays int) {
// Run the check: fetch the certificate, check its names then check its time
// to expiry and update the plugin's performance data.
func (program *checkProgram) runCheck() {
func (program *checkProgram) RunCheck() {
err := program.getCertificate()
if err != nil {
program.plugin.SetState(plugin.UNKNOWN, err.Error())
@ -343,11 +344,3 @@ func (program *checkProgram) runCheck() {
program.setPerfData(tlDays)
}
}
func main() {
program := newProgram()
defer program.close()
if program.checkFlags() {
program.runCheck()
}
}

View file

@ -1,4 +1,4 @@
package main
package zoneserial // import nocternity.net/go-monitoring/cmd/zoneserial
import (
"fmt"
@ -8,8 +8,9 @@ import (
"strings"
"time"
"nocternity.net/go/monitoring/perfdata"
"nocternity.net/go/monitoring/plugin"
"nocternity.net/go-monitoring/pkg/perfdata"
"nocternity.net/go-monitoring/pkg/plugin"
"nocternity.net/go-monitoring/pkg/program"
"github.com/karrick/golf"
"github.com/miekg/dns"
@ -75,7 +76,7 @@ func (flags *programFlags) parseArguments() {
}
// Initialise the monitoring check program.
func newProgram() *checkProgram {
func NewProgram() program.Program {
program := &checkProgram{
plugin: plugin.New("DNS zone serial match check"),
}
@ -84,7 +85,7 @@ func newProgram() *checkProgram {
}
// Terminate the monitoring check program.
func (program *checkProgram) close() {
func (program *checkProgram) Done() {
if r := recover(); r != nil {
program.plugin.SetState(plugin.UNKNOWN, "Internal error")
program.plugin.AddLine("Error info: %v", r)
@ -93,7 +94,7 @@ func (program *checkProgram) close() {
}
// Check the values that were specified from the command line. Returns true if the arguments made sense.
func (program *checkProgram) checkFlags() bool {
func (program *checkProgram) CheckArguments() bool {
if program.hostname == "" {
program.plugin.SetState(plugin.UNKNOWN, "no DNS hostname specified")
return false
@ -176,7 +177,7 @@ func (program *checkProgram) getSerial(server string, response queryResponse) (o
// Run the monitoring check. This implies querying both servers, extracting the serial from
// their responses, then comparing the serials.
func (program *checkProgram) runCheck() {
func (program *checkProgram) RunCheck() {
checkResponse, refResponse := program.queryServers()
cOk, cSerial := program.getSerial("checked", checkResponse)
rOk, rSerial := program.getSerial("reference", refResponse)
@ -190,11 +191,3 @@ func (program *checkProgram) runCheck() {
program.plugin.SetState(plugin.CRITICAL, "serials mismatch")
}
}
func main() {
program := newProgram()
defer program.close()
if program.checkFlags() {
program.runCheck()
}
}

10
go.mod
View file

@ -1,8 +1,14 @@
module nocternity.net/go/monitoring
module nocternity.net/go-monitoring
go 1.15
go 1.22
require (
github.com/karrick/golf v1.4.0
github.com/miekg/dns v1.1.40
)
require (
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 // indirect
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe // indirect
)

6
go.sum
View file

@ -1,14 +1,20 @@
github.com/karrick/golf v1.4.0 h1:9i9HnUh7uCyUFJhIqg311HBibw4f2pbGldi0ZM2FhaQ=
github.com/karrick/golf v1.4.0/go.mod h1:qGN0IhcEL+IEgCXp00RvH32UP59vtwc8w5YcIdArNRk=
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=

55
main.go Normal file
View file

@ -0,0 +1,55 @@
package main
import (
"fmt"
"os"
"path/filepath"
"nocternity.net/go-monitoring/cmd/sslcert"
"nocternity.net/go-monitoring/cmd/zoneserial"
"nocternity.net/go-monitoring/pkg/program"
"nocternity.net/go-monitoring/pkg/version"
)
var (
programs map[string]program.ProgramBuilder = map[string]program.ProgramBuilder{
"check_ssl_certificate": sslcert.NewProgram,
"check_zone_serial": zoneserial.NewProgram,
}
)
func exitError(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1)
}
func main() {
ownName := filepath.Base(os.Args[0])
var program program.Program
if builder, ok := programs[ownName]; ok {
program = builder()
} else if len(os.Args) < 2 {
exitError("Syntax: %s <program> [arguments]\n %s programs", os.Args[0], os.Args[0])
} else if os.Args[1] == "programs" {
fmt.Println("Available programs:")
for name := range programs {
fmt.Printf(" %s\n", name)
}
os.Exit(0)
} else if os.Args[1] == "--version" || os.Args[1] == "-v" {
fmt.Printf("%s %s\n", ownName, version.Version())
os.Exit(0)
} else if builder, ok := programs[os.Args[1]]; ok {
os.Args = os.Args[1:]
program = builder()
} else {
exitError("Unknown program: %s", os.Args[1])
}
defer program.Done()
if program.CheckArguments() {
program.RunCheck()
}
}

View file

@ -1,6 +1,6 @@
// Package `perfdata` provides representations for a monitoring plugin's
// performance data.
package perfdata
package perfdata // import nocternity.net/go-monitoring/pkg/perfdata
import (
"fmt"

View file

@ -0,0 +1,108 @@
package perfdata // import nocternity.net/go-monitoring/pkg/perfdata
import (
"fmt"
"testing"
)
func assert(t *testing.T, check bool, msg string) {
if !check {
t.Errorf(msg)
}
}
func assertPanic(t *testing.T, f func(), msg string) {
defer func() {
if r := recover(); r == nil {
t.Errorf(msg)
}
}()
f()
}
func TestValueCheckValid(t *testing.T) {
validValues := []string{
"0", "0.", "0.952", "1", "123", "123.", "123.45", ".1",
"-0", "-0.", "-0.952", "-1", "-123", "-123.", "-123.45", "-.1",
}
for _, value := range validValues {
if !valueCheck.MatchString(value) {
t.Errorf("'%s' is a valid value string", value)
}
}
}
func TestValueCheckInvalid(t *testing.T) {
invalidValues := []string{".", "-.", "a", " ", ""}
for _, value := range invalidValues {
if valueCheck.MatchString(value) {
t.Errorf("'%s' is an invalid value string", value)
}
}
}
func TestPdrMaxInvalid(t *testing.T) {
assertPanic(
t, func() { PDRMax("") },
"Created PerfDataRange with invalid max value",
)
}
func TestPdrMax(t *testing.T) {
value := "123"
pdr := PDRMax(value)
assert(t, pdr.start == "0", "Min value should be '0'")
assert(t, pdr.end == value, "Max value not copied to PerfDataRange")
assert(t, !pdr.inside, "Inside flag should not be set")
}
func TestPdrMinMaxInvalid(t *testing.T) {
assertPanic(
t, func() { PDRMinMax("", "123") },
"Created PerfDataRange with invalid min value",
)
assertPanic(
t, func() { PDRMinMax("123", "") },
"Created PerfDataRange with invalid max value",
)
}
func TestPdrMinMax(t *testing.T) {
min, max := "123", "456"
pdr := PDRMinMax(min, max)
assert(t, pdr.start == min, "Min value not copied to PerfDataRange")
assert(t, pdr.end == max, "Max value not copied to PerfDataRange")
assert(t, !pdr.inside, "Inside flag should not be set")
}
func TestPdrInside(t *testing.T) {
pdr := &PerfDataRange{}
pdr = pdr.Inside()
assert(t, pdr.inside, "Inside flag should be set")
pdr = pdr.Inside()
assert(t, pdr.inside, "Inside flag should still be set")
}
func TestPdrString(t *testing.T) {
type Test struct {
pdr PerfDataRange
out string
}
tests := []Test{
{pdr: PerfDataRange{start: "Y", end: "X"}, out: "Y:X"},
{pdr: PerfDataRange{end: "X"}, out: "~:X"},
{pdr: PerfDataRange{start: "0", end: "X"}, out: ":X"},
{pdr: PerfDataRange{inside: true, start: "Y", end: "X"}, out: "@Y:X"},
}
for _, test := range tests {
result := test.pdr.String()
assert(
t,
result == test.out,
fmt.Sprintf("Expected '%s', got '%s'", test.out, result),
)
}
}

View file

@ -1,6 +1,6 @@
// Package plugin implements a helper that can be used to implement a Nagios,
// Centreon, Icinga... service monitoring plugin.
package plugin
package plugin // import nocternity.net/go-monitoring/pkg/perfdata
import (
"container/list"
@ -8,7 +8,7 @@ import (
"os"
"strings"
"nocternity.net/go/monitoring/perfdata"
"nocternity.net/go-monitoring/pkg/perfdata"
)
// Status represents the return status of the monitoring plugin. The

9
pkg/program/program.go Normal file
View file

@ -0,0 +1,9 @@
package program // import nocternity.net/go-monitoring/pkg/program
type Program interface {
CheckArguments() bool
RunCheck()
Done()
}
type ProgramBuilder func() Program

27
pkg/version/version.go Normal file
View file

@ -0,0 +1,27 @@
package version // import nocternity.net/go-monitoring/pkg/version
import "fmt"
var (
version = ""
commit = "unknown"
status = ""
target = "unknown/unknown"
)
func Version() string {
var schar, ver string
if status == "dirty" {
schar = "*"
} else {
schar = ""
}
if version == "" {
ver = "development version"
} else {
ver = version
}
return fmt.Sprintf("%s (%s%s) %s", ver, commit, schar, target)
}