diff --git a/.gitignore b/.gitignore index c051262d..1b538ded 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ certs/ .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml +.idea/ ### Go template # If you prefer the allow list template instead of the deny list, see community template: diff --git a/cmd/upswake/root_test.go b/cmd/upswake/root_test.go index 0c91eaa3..d8826ebc 100644 --- a/cmd/upswake/root_test.go +++ b/cmd/upswake/root_test.go @@ -183,7 +183,7 @@ func Test_Execute(t *testing.T) { args: []string{"upswake", "serve"}, filesystem: func() afero.Fs { fs := afero.NewMemMapFs() - configYaml := ` + const configYaml = ` nut_servers: - name: test-nut-server host: 127.0.0.1 @@ -206,7 +206,7 @@ nut_servers: exitCode: 0, timeout: 5 * time.Second, wantOutput: []string{ - "http server started on", + "http(s) server started", "Gracefully stopping worker", }, notWantOutput: []string{"ERROR", "error"}, diff --git a/cmd/upswake/serve.go b/cmd/upswake/serve.go index 4ad12d99..1d25e2c1 100644 --- a/cmd/upswake/serve.go +++ b/cmd/upswake/serve.go @@ -124,6 +124,7 @@ func (j *serveCMD) serveCmdRunE(cmd *cobra.Command, _ []string) error { }(ctx, &wg) err = server.Start( + j.fs, cliArgs.ListenAddress(), cliArgs.UseSSL, cliArgs.CertFile, diff --git a/cmd/upswake/serve_test.go b/cmd/upswake/serve_test.go index 1e9cbe51..44c2d1c4 100644 --- a/cmd/upswake/serve_test.go +++ b/cmd/upswake/serve_test.go @@ -70,7 +70,7 @@ func Test_serveCmdRunE(t *testing.T) { args: []string{"serve", "--config", "upswake.yaml", "--port", "8081"}, }, err: ErrTimeout, // expect a timeout error, as the command will run indefinitely otherwise - wantOutputs: []string{"http server started on [::]:8081"}, + wantOutputs: []string{`"msg":"http(s) server started","address":"[::]:8081`}, notWantOutputs: []string{`"level":"ERROR"`, `"level":"error"`, `"level":"Error"`}, }, { @@ -104,7 +104,7 @@ nut_servers: }, err: ErrTimeout, // expect a timeout error, as the command will run indefinitely otherwise wantOutputs: []string{ - "http server started on [::]:8082", + `"msg":"http(s) server started","address":"[::]:8082"`, `"status":200`, `{"level":"info","ts":`, `"msg":"REQUEST","remote_ip":"127.0.0.1","host":"127.0.0.1:8082","method":"POST","uri":"/api/upswake","user_agent":"Go-http-client/1.1","status":200}`, diff --git a/go.mod b/go.mod index 981ecad1..5ca1663b 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/go-playground/validator/v10 v10.30.1 github.com/google/uuid v1.6.0 github.com/ka-weihe/fast-levenshtein v0.0.0-20201227151214-4c99ee36a1ba - github.com/labstack/echo/v4 v4.15.0 + github.com/labstack/echo/v5 v5.0.0 github.com/open-policy-agent/opa v1.13.1 github.com/robbiet480/go.nut v0.0.0-20240622015809-60e196249c53 github.com/sabhiram/go-wol v0.0.0-20250815165103-eaddd4c17972 @@ -15,7 +15,7 @@ require ( github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 - github.com/swaggo/echo-swagger v1.4.1 + github.com/swaggo/echo-swagger v1.5.0 github.com/swaggo/swag v1.16.6 go.uber.org/zap v1.27.1 golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541 @@ -30,7 +30,6 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect - github.com/ghodss/yaml v1.0.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect @@ -44,7 +43,6 @@ require ( github.com/goccy/go-json v0.10.5 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/labstack/gommon v0.4.2 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect github.com/lestrrat-go/dsig v1.0.0 // indirect @@ -54,10 +52,9 @@ require ( github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect github.com/lestrrat-go/option/v2 v2.0.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -70,11 +67,11 @@ require ( github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/swaggo/files/v2 v2.0.2 // indirect + github.com/sv-tools/openapi v0.2.1 // indirect + github.com/swaggo/files/v2 v2.0.0 // indirect + github.com/swaggo/swag/v2 v2.0.0-rc4 // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.7 // indirect - github.com/valyala/fasttemplate v1.2.2 // indirect github.com/vektah/gqlparser/v2 v2.5.31 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -89,7 +86,6 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.47.0 // indirect golang.org/x/mod v0.31.0 // indirect - golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect diff --git a/go.sum b/go.sum index 19496df9..0e77decf 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,6 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -95,10 +93,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo/v4 v4.15.0 h1:hoRTKWcnR5STXZFe9BmYun9AMTNeSbjHi2vtDuADJ24= -github.com/labstack/echo/v4 v4.15.0/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c= -github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= -github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/labstack/echo/v5 v5.0.0 h1:JHKGrI0cbNsNMyKvranuY0C94O4hSM7yc/HtwcV3Na4= +github.com/labstack/echo/v5 v5.0.0/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= @@ -118,12 +114,8 @@ github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuh github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -132,6 +124,8 @@ github.com/open-policy-agent/opa v1.13.1 h1:2odxAcL3L0GNTlsuDcoguxViGxQxlpGL6zR8 github.com/open-policy-agent/opa v1.13.1/go.mod h1:M3Asy9yp1YTusUU5VQuENDe92GLmamIuceqjw+C8PHY= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -186,20 +180,20 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk= -github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc= -github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU= -github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0= +github.com/sv-tools/openapi v0.2.1 h1:ES1tMQMJFGibWndMagvdoo34T1Vllxr1Nlm5wz6b1aA= +github.com/sv-tools/openapi v0.2.1/go.mod h1:k5VuZamTw1HuiS9p2Wl5YIDWzYnHG6/FgPOSFXLAhGg= +github.com/swaggo/echo-swagger v1.5.0 h1:nkHxOaBy0SkbJMtMeXZC64KHSa0mJdZFQhVqwEcMres= +github.com/swaggo/echo-swagger v1.5.0/go.mod h1:TzO363X1ZG/MSbjrG2IX6m65Yd3/zpqh5KM6lPctAhk= +github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= +github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= +github.com/swaggo/swag/v2 v2.0.0-rc4 h1:SZ8cK68gcV6cslwrJMIOqPkJELRwq4gmjvk77MrvHvY= +github.com/swaggo/swag/v2 v2.0.0-rc4/go.mod h1:Ow7Y8gF16BTCDn8YxZbyKn8FkMLRUHekv1kROJZpbvE= github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= -github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k= github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= @@ -254,7 +248,6 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= diff --git a/internal/api/docs/docs.go b/internal/api/docs/docs.go index 9202a715..3bdfbb34 100644 --- a/internal/api/docs/docs.go +++ b/internal/api/docs/docs.go @@ -33,7 +33,7 @@ const docTemplate = `{ }, "/api/servers/broadcastwake": { "post": { - "description": "Wake a server using Wake on LAN by using the mac and enumerating all available broadcast addresses", + "description": "Wake a server using Wake on LAN by using the MAC and enumerating all available broadcast addresses", "consumes": [ "application/json" ], @@ -43,7 +43,7 @@ const docTemplate = `{ "tags": [ "servers" ], - "summary": "Wake a server using just a mac", + "summary": "Wake a server using just a MAC", "parameters": [ { "description": "Broadcast wake request", @@ -79,7 +79,7 @@ const docTemplate = `{ }, "/api/servers/wake": { "post": { - "description": "Wake a server using Wake on LAN using the mac and broadcast address provided", + "description": "Wake a server using Wake on LAN using the MAC and broadcast address provided", "consumes": [ "application/json" ], @@ -89,7 +89,7 @@ const docTemplate = `{ "tags": [ "servers" ], - "summary": "Wake a server using a mac and a broadcast address", + "summary": "Wake a server using a MAC and a broadcast address", "parameters": [ { "description": "Wake server request", diff --git a/internal/api/docs/swagger.json b/internal/api/docs/swagger.json index 6bbe4b59..e8cec1a8 100644 --- a/internal/api/docs/swagger.json +++ b/internal/api/docs/swagger.json @@ -22,7 +22,7 @@ }, "/api/servers/broadcastwake": { "post": { - "description": "Wake a server using Wake on LAN by using the mac and enumerating all available broadcast addresses", + "description": "Wake a server using Wake on LAN by using the MAC and enumerating all available broadcast addresses", "consumes": [ "application/json" ], @@ -32,7 +32,7 @@ "tags": [ "servers" ], - "summary": "Wake a server using just a mac", + "summary": "Wake a server using just a MAC", "parameters": [ { "description": "Broadcast wake request", @@ -68,7 +68,7 @@ }, "/api/servers/wake": { "post": { - "description": "Wake a server using Wake on LAN using the mac and broadcast address provided", + "description": "Wake a server using Wake on LAN using the MAC and broadcast address provided", "consumes": [ "application/json" ], @@ -78,7 +78,7 @@ "tags": [ "servers" ], - "summary": "Wake a server using a mac and a broadcast address", + "summary": "Wake a server using a MAC and a broadcast address", "parameters": [ { "description": "Wake server request", diff --git a/internal/api/docs/swagger.yaml b/internal/api/docs/swagger.yaml index 05b0a059..7cbd451d 100644 --- a/internal/api/docs/swagger.yaml +++ b/internal/api/docs/swagger.yaml @@ -94,7 +94,7 @@ paths: post: consumes: - application/json - description: Wake a server using Wake on LAN by using the mac and enumerating + description: Wake a server using Wake on LAN by using the MAC and enumerating all available broadcast addresses parameters: - description: Broadcast wake request @@ -119,14 +119,14 @@ paths: description: Wake on LAN packet failed to send schema: $ref: '#/definitions/handlers.Response' - summary: Wake a server using just a mac + summary: Wake a server using just a MAC tags: - servers /api/servers/wake: post: consumes: - application/json - description: Wake a server using Wake on LAN using the mac and broadcast address + description: Wake a server using Wake on LAN using the MAC and broadcast address provided parameters: - description: Wake server request @@ -150,7 +150,7 @@ paths: description: Wake on LAN packet failed to send schema: $ref: '#/definitions/handlers.Response' - summary: Wake a server using a mac and a broadcast address + summary: Wake a server using a MAC and a broadcast address tags: - servers /api/upswake: diff --git a/internal/api/handlers/root.go b/internal/api/handlers/root.go index 81f2df31..8401ffb3 100644 --- a/internal/api/handlers/root.go +++ b/internal/api/handlers/root.go @@ -8,7 +8,7 @@ import ( "github.com/TheDarthMole/UPSWake/internal/domain/entity" "github.com/TheDarthMole/UPSWake/internal/network" "github.com/TheDarthMole/UPSWake/internal/ups" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/spf13/afero" echoSwagger "github.com/swaggo/echo-swagger" ) @@ -52,7 +52,7 @@ func (h *RootHandler) Register(g *echo.Group) { // @Accept plain // @Produce html // @Router / [get] -func (*RootHandler) Root(c echo.Context) error { +func (*RootHandler) Root(c *echo.Context) error { return c.Redirect(http.StatusMovedPermanently, "/swagger/index.html") } @@ -67,24 +67,24 @@ func (*RootHandler) Root(c echo.Context) error { // @Success 200 {object} Response "OK" // @Failure 500 {object} Response // @Router /health [get] -func (h *RootHandler) Health(c echo.Context) error { +func (h *RootHandler) Health(c *echo.Context) error { if err := h.cfg.Validate(); err != nil { return c.JSON(http.StatusInternalServerError, Response{Message: err.Error()}) } if _, err := network.GetAllBroadcastAddresses(); err != nil { - c.Logger().Errorf("Error getting broadcast addresses: %s", err) + c.Logger().Error("Error getting broadcast addresses: " + err.Error()) return c.JSON(http.StatusInternalServerError, Response{Message: err.Error()}) } // TODO: Speed this up by running in parallel for _, server := range h.cfg.NutServers { if _, err := ups.GetJSON(&server); err != nil { - c.Logger().Errorf("Error getting NUT server status: %s", err) + c.Logger().Error("Error getting NUT server status: " + err.Error()) return c.JSON(http.StatusInternalServerError, Response{Message: err.Error()}) } } - c.Logger().Debugf("Health check OK") + c.Logger().Debug("Health check OK") return c.JSON(http.StatusOK, Response{Message: "OK"}) } diff --git a/internal/api/handlers/root_test.go b/internal/api/handlers/root_test.go index 109f41ec..2c4657bf 100644 --- a/internal/api/handlers/root_test.go +++ b/internal/api/handlers/root_test.go @@ -7,9 +7,10 @@ import ( "github.com/TheDarthMole/UPSWake/internal/api" "github.com/TheDarthMole/UPSWake/internal/domain/entity" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/spf13/afero" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var cfg = &entity.Config{ @@ -34,6 +35,28 @@ var cfg = &entity.Config{ }, } +func assertRoutes(t *testing.T, e *echo.Echo, expectedRoutes []string) { + require.NotNil(t, e) + require.NotNil(t, e.Router()) + require.NotNil(t, e.Router().Routes()) + + lenExpectedRoutes := len(expectedRoutes) + + // Remove found routes from expected routes to find missing routes + missingRoutes := expectedRoutes + for _, route := range e.Router().Routes() { + for i, expected := range missingRoutes { + if expected == route.Path { + missingRoutes = append(missingRoutes[:i], missingRoutes[i+1:]...) + break + } + } + } + + assert.Lenf(t, e.Router().Routes(), lenExpectedRoutes, "Expected %d routes to be registered", lenExpectedRoutes) + assert.Equalf(t, []string{}, missingRoutes, "The following expected routes are missing: %v", missingRoutes) +} + func newMemFS(t *testing.T, data map[string][]byte) afero.Fs { t.Helper() memfs := afero.NewMemMapFs() @@ -150,18 +173,7 @@ func TestRootHandler_Register(t *testing.T) { h.Register(g) expectedRoutes := []string{"/", "/health", "/swagger/*"} - lenExpectedRoutes := len(expectedRoutes) - for _, route := range e.Routes() { - for i, expected := range expectedRoutes { - if expected == route.Path { - expectedRoutes = append(expectedRoutes[:i], expectedRoutes[i+1:]...) - break - } - } - } - - assert.Len(t, e.Routes(), lenExpectedRoutes, "Expected 2 routes to be registered") - assert.Equalf(t, []string{}, expectedRoutes, "The following expected routes are missing: %v", expectedRoutes) + assertRoutes(t, e, expectedRoutes) } func TestNewRootHandler(t *testing.T) { diff --git a/internal/api/handlers/servers.go b/internal/api/handlers/servers.go index a48680f9..eee61b63 100644 --- a/internal/api/handlers/servers.go +++ b/internal/api/handlers/servers.go @@ -2,14 +2,14 @@ package handlers import ( "errors" + "log/slog" "net" "net/http" - "strconv" "github.com/TheDarthMole/UPSWake/internal/domain/entity" "github.com/TheDarthMole/UPSWake/internal/network" "github.com/TheDarthMole/UPSWake/internal/wol" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" ) var ( @@ -67,8 +67,8 @@ func (s *ServerHandler) Register(g *echo.Group) { // WakeServer godoc // -// @Summary Wake a server using a mac and a broadcast address -// @Description Wake a server using Wake on LAN using the mac and broadcast address provided +// @Summary Wake a server using a MAC and a broadcast address +// @Description Wake a server using Wake on LAN using the MAC and broadcast address provided // @Tags servers // @Accept json // @Produce json @@ -77,15 +77,15 @@ func (s *ServerHandler) Register(g *echo.Group) { // @Failure 400 {object} Response "Input validation failed" // @Failure 500 {object} Response "Wake on LAN packet failed to send" // @Router /api/servers/wake [post] -func (s *ServerHandler) WakeServer(c echo.Context) error { +func (s *ServerHandler) WakeServer(c *echo.Context) error { wsRequest := NewWakeServerRequest() if err := c.Bind(wsRequest); err != nil { - c.Logger().Errorf("failed to bind wake server request %s", err) + c.Logger().Error("failed to bind wake server request", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, Response{Message: ErrorBindingRequest.Error()}) } if err := c.Validate(wsRequest); err != nil { - c.Logger().Errorf("failed to validate wake server request %s", err) + c.Logger().Error("failed to validate wake server request", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, Response{Message: ErrorValidatingRequest.Error()}) } @@ -98,25 +98,25 @@ func (s *ServerHandler) WakeServer(c echo.Context) error { []string{}, ) if err != nil { - c.Logger().Errorf("failed to create target server: %s", err) + c.Logger().Error("failed to create target server", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorCreatingTargetServer.Error()}) } wolClient := wol.NewWoLClient(ts) if err = wolClient.Wake(); err != nil { - c.Logger().Errorf("failed to send wake on lan %s", err) + c.Logger().Error("failed to send wake on lan", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorSendingWoLPacket.Error()}) } - c.Logger().Infof("wake on lan packet sent to %s", sanitizeString(wsRequest.Mac)) + c.Logger().Info("wake on lan packet sent", slog.String("mac", sanitizeString(wsRequest.Mac))) return c.JSON(http.StatusCreated, Response{Message: WoLSentMessage}) } // BroadcastWakeServer godoc // -// @Summary Wake a server using just a mac -// @Description Wake a server using Wake on LAN by using the mac and enumerating all available broadcast addresses +// @Summary Wake a server using just a MAC +// @Description Wake a server using Wake on LAN by using the MAC and enumerating all available broadcast addresses // @Tags servers // @Accept json // @Produce json @@ -125,31 +125,31 @@ func (s *ServerHandler) WakeServer(c echo.Context) error { // @Failure 400 {object} Response "Input validation failed" // @Failure 500 {object} Response "Wake on LAN packet failed to send" // @Router /api/servers/broadcastwake [post] -func (s *ServerHandler) BroadcastWakeServer(c echo.Context) error { +func (s *ServerHandler) BroadcastWakeServer(c *echo.Context) error { wsRequest := NewBroadcastWakeRequest() if err := c.Bind(wsRequest); err != nil { - c.Logger().Errorf("failed to bind wake server request: %s", err) + c.Logger().Error("failed to bind wake server request", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, Response{Message: ErrorBindingRequest.Error()}) } if err := c.Validate(wsRequest); err != nil { - c.Logger().Errorf("failed to validate wake server request: %s", err) + c.Logger().Error("failed to validate wake server request", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, Response{Message: ErrorValidatingRequest.Error()}) } broadcasts, err := s.broadcastAddresses() if err != nil { - c.Logger().Errorf("failed to get broadcast addresses, %s", err) + c.Logger().Error("failed to get broadcast addresses", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorBroadcastAddress.Error()}) } if len(broadcasts) == 0 { - c.Logger().Errorf("no broadcast addresses available, got %v", broadcasts) + c.Logger().Error("no broadcast addresses available", slog.Any("broadcasts", broadcasts)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorBroadcastAddress.Error()}) } for _, broadcast := range broadcasts { if broadcast == nil { - c.Logger().Errorf("invalid broadcast address, got %v", broadcast) + c.Logger().Error("invalid broadcast address") return c.JSON(http.StatusInternalServerError, Response{Message: ErrorBroadcastAddress.Error()}) } @@ -162,16 +162,19 @@ func (s *ServerHandler) BroadcastWakeServer(c echo.Context) error { []string{}, ) if err != nil { - c.Logger().Errorf("failed to create new target server %s", err) + c.Logger().Error("failed to create new target server", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorCreatingTargetServer.Error()}) } wolClient := wol.NewWoLClient(ts) if err = wolClient.Wake(); err != nil { - c.Logger().Errorf("failed to send wake on lan %s", err) + c.Logger().Error("failed to send wake on lan", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorSendingWoLPacket.Error()}) } - c.Logger().Infof("sent wake on lan to %s:%s with mac %s", sanitizeString(broadcast.String()), strconv.Itoa(wsRequest.Port), sanitizeString(wsRequest.Mac)) + c.Logger().Info("sent wake on lan", + slog.String("mac", sanitizeString(wsRequest.Mac)), + slog.Int("port", wsRequest.Port), + slog.String("broadcast", broadcast.String())) } return c.JSON(http.StatusCreated, Response{Message: BroadcastWoLSentMessage}) } diff --git a/internal/api/handlers/servers_test.go b/internal/api/handlers/servers_test.go index 6d075c6c..7e46f933 100644 --- a/internal/api/handlers/servers_test.go +++ b/internal/api/handlers/servers_test.go @@ -10,7 +10,7 @@ import ( "github.com/TheDarthMole/UPSWake/internal/api" "github.com/TheDarthMole/UPSWake/internal/domain/entity" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/stretchr/testify/assert" ) @@ -28,17 +28,7 @@ func TestServerHandler_Register(t *testing.T) { h.Register(g) expectedRoutes := []string{"/wake", "/broadcastwake"} - for _, route := range e.Routes() { - for i, expected := range expectedRoutes { - if expected == route.Path { - expectedRoutes = append(expectedRoutes[:i], expectedRoutes[i+1:]...) - break - } - } - } - - assert.Len(t, e.Routes(), 2, "Expected 2 routes to be registered") - assert.Equalf(t, []string{}, expectedRoutes, "The following expected routes are missing: %v", expectedRoutes) + assertRoutes(t, e, expectedRoutes) } func TestNewWakeServerRequest(t *testing.T) { diff --git a/internal/api/handlers/upswake.go b/internal/api/handlers/upswake.go index 3e0d6779..deca5a7c 100644 --- a/internal/api/handlers/upswake.go +++ b/internal/api/handlers/upswake.go @@ -2,12 +2,13 @@ package handlers import ( "fmt" + "log/slog" "net/http" "github.com/TheDarthMole/UPSWake/internal/domain/entity" "github.com/TheDarthMole/UPSWake/internal/evaluator" "github.com/TheDarthMole/UPSWake/internal/wol" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/spf13/afero" ) @@ -46,7 +47,7 @@ func (h *UPSWakeHandler) Register(g *echo.Group) { // @Produce json // @Success 200 {object} []entity.NutServer // @Router /api/upswake [get] -func (h *UPSWakeHandler) ListNutServerMappings(c echo.Context) error { +func (h *UPSWakeHandler) ListNutServerMappings(c *echo.Context) error { nutServers := h.cfg.NutServers // Don't leak passwords for i, nutServer := range nutServers { @@ -70,10 +71,10 @@ func (h *UPSWakeHandler) ListNutServerMappings(c echo.Context) error { // @Failure 404 {object} Response "MAC address not found in the config" // @Failure 500 {object} Response "Internal server error" // @Router /api/upswake [post] -func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { +func (h *UPSWakeHandler) RunWakeEvaluation(c *echo.Context) error { mac := &macAddress{} if err := c.Bind(mac); err != nil { - c.Logger().Errorf("failed to bind mac address: %s", err) + c.Logger().Error("failed to bind mac address", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, upsWakeResponse{ Message: ErrorBindingRequest.Error(), Woken: false, @@ -82,7 +83,7 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { eval := evaluator.NewRegoEvaluator(h.cfg, mac.Mac, h.rulesFS) result, err := eval.EvaluateExpressions() if err != nil { - c.Logger().Errorf("Failed to evaluate expressions: %s", err) + c.Logger().Error("Failed to evaluate expressions", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, upsWakeResponse{ Message: err.Error(), Woken: false, @@ -90,7 +91,7 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { } if !result.Found { - c.Logger().Errorf("mac address not found in the config: %s", sanitizeString(mac.Mac)) + c.Logger().Error("mac address not found in the config", slog.String("mac", sanitizeString(mac.Mac))) return c.JSON(http.StatusConflict, upsWakeResponse{ Message: "MAC address not found in the config", Woken: false, @@ -98,7 +99,7 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { } if !result.Allowed { - c.Logger().Debugf("no rule evaluated to true: %s", sanitizeString(mac.Mac)) + c.Logger().Debug("no rule evaluated to true", slog.String("mac", sanitizeString(mac.Mac))) return c.JSON(http.StatusOK, upsWakeResponse{ Message: "No rule evaluated to true", Woken: false, @@ -114,7 +115,7 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { []string{}, ) if err != nil { - c.Logger().Errorf("Failed to create target server: %s", err) + c.Logger().Error("Failed to create target server", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, upsWakeResponse{ Message: fmt.Sprintf("Failed to create target server: %s", err), Woken: false, @@ -124,14 +125,14 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { wolClient := wol.NewWoLClient(ts) if err = wolClient.Wake(); err != nil { - c.Logger().Errorf("Failed to send wake on lan %s", err) + c.Logger().Error("Failed to send wake on lan", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, upsWakeResponse{ Message: fmt.Sprintf("Failed to send wake on LAN: %s", err), Woken: false, }) } - c.Logger().Debugf("Wake on LAN sent to %s", sanitizeString(mac.Mac)) + c.Logger().Debug("Wake on LAN sent", slog.String("mac", sanitizeString(mac.Mac))) return c.JSON(http.StatusOK, upsWakeResponse{ Message: "Wake on LAN sent", Woken: true, diff --git a/internal/api/handlers/upswake_test.go b/internal/api/handlers/upswake_test.go index 94ff5f16..26950b47 100644 --- a/internal/api/handlers/upswake_test.go +++ b/internal/api/handlers/upswake_test.go @@ -8,7 +8,7 @@ import ( "github.com/TheDarthMole/UPSWake/internal/api" "github.com/TheDarthMole/UPSWake/internal/domain/entity" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/spf13/afero" "github.com/stretchr/testify/assert" ) diff --git a/internal/api/server.go b/internal/api/server.go index 4a72f366..1f017533 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -2,19 +2,22 @@ package api import ( "context" + "time" _ "github.com/TheDarthMole/UPSWake/internal/api/docs" // swaggo docs "github.com/go-playground/validator/v10" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" + "github.com/labstack/echo/v5" + "github.com/labstack/echo/v5/middleware" + "github.com/spf13/afero" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) type Server struct { - ctx context.Context - echo *echo.Echo - sugar *zap.SugaredLogger + ctx context.Context + cancel context.CancelFunc + echo *echo.Echo + sugar *zap.SugaredLogger } type CustomValidator struct { @@ -37,15 +40,14 @@ func (cv *CustomValidator) Validate(i any) error { } func NewServer(ctx context.Context, s *zap.SugaredLogger) *Server { + newCtx, cancel := context.WithCancel(ctx) app := echo.New() - app.Validator = NewCustomValidator(ctx) + app.Validator = NewCustomValidator(newCtx) app.Pre(middleware.RemoveTrailingSlash()) app.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ - LogStatus: true, - LogURI: true, - LogError: true, - HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code - LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { + LogStatus: true, + LogURI: true, + LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error { if v.Error == nil { s.Logw(zapcore.InfoLevel, "REQUEST", @@ -73,22 +75,33 @@ func NewServer(ctx context.Context, s *zap.SugaredLogger) *Server { })) return &Server{ - ctx: ctx, - echo: app, - sugar: s, + ctx: newCtx, + cancel: cancel, + echo: app, + sugar: s, } } -func (s *Server) Start(address string, useSSL bool, certFile, keyFile string) error { +func (s *Server) Start(fs afero.Fs, address string, useSSL bool, certFile, keyFile string) error { + fsFileSystem := afero.NewIOFS(fs) + start := echo.StartConfig{ + Address: address, + HideBanner: true, + HidePort: false, + CertFilesystem: fsFileSystem, + GracefulTimeout: 5 * time.Second, + } if useSSL { s.echo.Pre(middleware.HTTPSRedirect()) - return s.echo.StartTLS(address, certFile, keyFile) + return start.StartTLS(s.ctx, s.echo, certFile, keyFile) } - return s.echo.Start(address) + + return start.Start(s.ctx, s.echo) } func (s *Server) Stop() error { - return s.echo.Shutdown(s.ctx) + s.cancel() + return nil } func (s *Server) Root() *echo.Group { diff --git a/internal/api/server_test.go b/internal/api/server_test.go index cfbfedff..97774944 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -1,23 +1,35 @@ package api import ( + "bytes" "context" + "crypto/ecdsa" + "crypto/elliptic" + cryptRand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" "errors" "fmt" + "math/big" "math/rand/v2" "net/http" "net/http/httptest" + "os" "testing" "time" config "github.com/TheDarthMole/UPSWake/internal/domain/entity" "github.com/go-playground/validator/v10" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.uber.org/zap" ) -func pingHandler(c echo.Context) error { +func pingHandler(c *echo.Context) error { return c.String(http.StatusOK, "pong") } @@ -151,14 +163,14 @@ func TestNewServer(t *testing.T) { got := NewServer(tt.args.ctx, tt.args.s) assert.NotNil(t, got) - assert.Equal(t, tt.want.ctx, got.ctx) assert.Equal(t, tt.want.sugar, got.sugar) // echo instance and validator assert.NotNil(t, got.echo) cv, ok := got.echo.Validator.(*CustomValidator) assert.True(t, ok, "echo.Validator should be *CustomValidator") - assert.Equal(t, tt.want.ctx, cv.ctx) + assert.NotNil(t, cv.ctx) + assert.NotNil(t, cv.validator) req := httptest.NewRequest(http.MethodGet, "/ping", http.NoBody) rec := httptest.NewRecorder() @@ -186,11 +198,92 @@ func TestServer_Root(t *testing.T) { assert.Equal(t, expected, e.Root()) } +func certificateTemplate(t *testing.T) *x509.Certificate { + notBefore := time.Now() + notAfter := notBefore.Add(365 * 24 * time.Hour) + + serialNumber, err := cryptRand.Int(cryptRand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + require.NoError(t, err) + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"My Organization"}, //nolint: misspell + }, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + return &template +} + +func generateEncodedRSAKeys(t *testing.T) ([]byte, []byte) { + privateKey, err := rsa.GenerateKey(cryptRand.Reader, 2048) + require.NoError(t, err) + + template := certificateTemplate(t) + + derBytes, err := x509.CreateCertificate(cryptRand.Reader, template, template, &privateKey.PublicKey, privateKey) + require.NoError(t, err) + + var pemKey bytes.Buffer + var pemCert bytes.Buffer + + err = pem.Encode(&pemCert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + require.NoError(t, err) + + b := x509.MarshalPKCS1PrivateKey(privateKey) + + err = pem.Encode(&pemKey, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: b}) + require.NoError(t, err) + + return pemKey.Bytes(), pemCert.Bytes() +} + +func generateEncodedECCKeys(t *testing.T) ([]byte, []byte) { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptRand.Reader) + require.NoError(t, err) + + template := certificateTemplate(t) + + derBytes, err := x509.CreateCertificate(cryptRand.Reader, template, template, &privateKey.PublicKey, privateKey) + require.NoError(t, err) + + var pemKey bytes.Buffer + var pemCert bytes.Buffer + + err = pem.Encode(&pemCert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + require.NoError(t, err) + + b, err := x509.MarshalECPrivateKey(privateKey) + require.NoError(t, err) + + err = pem.Encode(&pemKey, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) + require.NoError(t, err) + + return pemKey.Bytes(), pemCert.Bytes() +} + func TestServer_Start_Stop(t *testing.T) { type fields struct { ctx context.Context sugar *zap.SugaredLogger } + + certFs := afero.NewMemMapFs() + + privateRSAKey, publicRSAKey := generateEncodedRSAKeys(t) + + require.NoError(t, afero.WriteFile(certFs, "rsa.key", privateRSAKey, os.ModePerm)) + require.NoError(t, afero.WriteFile(certFs, "rsa.cert", publicRSAKey, os.ModePerm)) + + privateECCKey, publicECCKey := generateEncodedECCKeys(t) + + require.NoError(t, afero.WriteFile(certFs, "ecc.key", privateECCKey, os.ModePerm)) + require.NoError(t, afero.WriteFile(certFs, "ecc.cert", publicECCKey, os.ModePerm)) + type args struct { address string useSSL bool @@ -211,7 +304,7 @@ func TestServer_Start_Stop(t *testing.T) { sugar: zap.NewExample().Sugar(), }, args: args{ - address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), + address: "127.0.0.1:0", useSSL: false, certFile: "", keyFile: "", @@ -226,10 +319,10 @@ func TestServer_Start_Stop(t *testing.T) { sugar: zap.NewExample().Sugar(), }, args: args{ - address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), + address: "127.0.0.1:0", useSSL: true, - certFile: "../../certs/rsa.cert", - keyFile: "../../certs/rsa.key", + certFile: "rsa.cert", + keyFile: "rsa.key", }, wantStartErr: false, wantStopErr: false, @@ -241,10 +334,10 @@ func TestServer_Start_Stop(t *testing.T) { sugar: zap.NewExample().Sugar(), }, args: args{ - address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), + address: "127.0.0.1:0", useSSL: true, - certFile: "../../certs/ecc.cert", - keyFile: "../../certs/ecc.key", + certFile: "ecc.cert", + keyFile: "ecc.key", }, wantStartErr: false, wantStopErr: false, @@ -256,7 +349,7 @@ func TestServer_Start_Stop(t *testing.T) { sugar: zap.NewExample().Sugar(), }, args: args{ - address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), + address: "127.0.0.1:0", useSSL: true, certFile: "", keyFile: "", @@ -309,7 +402,7 @@ func TestServer_Start_Stop(t *testing.T) { } }() - err := srv.Start(tt.args.address, tt.args.useSSL, tt.args.certFile, tt.args.keyFile) + err := srv.Start(certFs, tt.args.address, tt.args.useSSL, tt.args.certFile, tt.args.keyFile) // http.ErrServerClosed is returned when the server is shut down normally if (err != nil && !errors.Is(err, http.ErrServerClosed)) != tt.wantStartErr { t.Errorf("Start() error = %v, wantErr %v", err, tt.wantStartErr)