mirror of
https://github.com/nabbar/golib.git
synced 2025-09-26 20:01:15 +08:00
Package Errors:
- Add interface Errors to expose func for collection of errors Package Server: - Add runner packages for start/stop & ticker process launch in goroutines - Runner Start/Stop : register a start and a stop function called into a managment instance of goroutines, expose package Server interface - Runner Ticker : register a func periodycly called into a managment instance of goroutine with a time ticker, expose package server interface - Add tools function to run x times a function or periodicly with a check function Package HttpServer: - Remove old run managment instance and use new Start&Stop Runner instead self runner - Replace the poller in server start / stop with package server tools function - Adjust code following this change - Add in healthcheck a test of dialing to server binding network to perform the healthcheck - Remove WaitNotify funct (cannot having multiple of waitnotify in same app) : keep waitnotify function in package config Package Monitor: - Remove old running system to use package server/runner/ticker - Adjust code following Package Cobra : - Fix minor bug with writing configure - Optimize code Bump dependencies
This commit is contained in:
@@ -27,10 +27,10 @@
|
||||
package cobra
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -87,46 +87,49 @@ func (c *cobra) ConfigureWriteConfig(basename string, defaultConfig func() io.Re
|
||||
}
|
||||
|
||||
var (
|
||||
fs *os.File
|
||||
fs *os.File
|
||||
ext string
|
||||
buf io.Reader
|
||||
nbr int64
|
||||
err error
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if fs != nil {
|
||||
_ = fs.Close()
|
||||
}
|
||||
}()
|
||||
ext = strings.ToLower(filepath.Ext(cfgFile))
|
||||
|
||||
buf, err := ioutil.ReadAll(defaultConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(filepath.Ext(cfgFile)) > 0 && strings.ToLower(filepath.Ext(cfgFile)) != ".json" {
|
||||
var mod = make(map[string]interface{}, 0)
|
||||
|
||||
err = json.Unmarshal(buf, &mod)
|
||||
if err != nil {
|
||||
switch ext {
|
||||
case ".toml", ".tml":
|
||||
if buf, err = c.jsonToToml(defaultConfig()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch strings.ToLower(filepath.Ext(cfgFile)) {
|
||||
case ".toml":
|
||||
buf, err = toml.Marshal(mod)
|
||||
case ".yml", ".yaml":
|
||||
buf, err = yaml.Marshal(mod)
|
||||
default:
|
||||
return fmt.Errorf("extension file '%s' not compatible", filepath.Ext(cfgFile))
|
||||
case ".yaml", ".yml":
|
||||
if buf, err = c.jsonToYaml(defaultConfig()); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
buf = defaultConfig()
|
||||
cfgFile = strings.TrimRight(cfgFile, ext) + ".json"
|
||||
}
|
||||
|
||||
fs, err = os.OpenFile(cfgFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer func() {
|
||||
if fs != nil {
|
||||
_ = fs.Close()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
_, err = fs.Write(buf)
|
||||
if err != nil {
|
||||
if nbr, err = io.Copy(fs, buf); err != nil {
|
||||
return err
|
||||
} else if nbr < 1 {
|
||||
return fmt.Errorf("error wrting 0 byte to config file")
|
||||
} else if err = fs.Close(); err != nil {
|
||||
fs = nil
|
||||
return err
|
||||
} else {
|
||||
fs = nil
|
||||
}
|
||||
|
||||
err = os.Chmod(cfgFile, 0600)
|
||||
@@ -140,6 +143,46 @@ func (c *cobra) ConfigureWriteConfig(basename string, defaultConfig func() io.Re
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cobra) jsonToToml(r io.Reader) (io.Reader, error) {
|
||||
var (
|
||||
e error
|
||||
p = make([]byte, 0)
|
||||
buf = bytes.NewBuffer(p)
|
||||
mod = make(map[string]interface{}, 0)
|
||||
)
|
||||
|
||||
if p, e = io.ReadAll(r); e != nil {
|
||||
return nil, e
|
||||
} else if e = json.Unmarshal(p, &mod); e != nil {
|
||||
return nil, e
|
||||
} else if p, e = toml.Marshal(mod); e != nil {
|
||||
return nil, e
|
||||
} else {
|
||||
buf.Write(p)
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cobra) jsonToYaml(r io.Reader) (io.Reader, error) {
|
||||
var (
|
||||
e error
|
||||
p = make([]byte, 0)
|
||||
buf = bytes.NewBuffer(p)
|
||||
mod = make(map[string]interface{}, 0)
|
||||
)
|
||||
|
||||
if p, e = io.ReadAll(r); e != nil {
|
||||
return nil, e
|
||||
} else if e = json.Unmarshal(p, &mod); e != nil {
|
||||
return nil, e
|
||||
} else if p, e = yaml.Marshal(mod); e != nil {
|
||||
return nil, e
|
||||
} else {
|
||||
buf.Write(p)
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cobra) AddCommandConfigure(basename string, defaultConfig func() io.Reader) {
|
||||
pkg := c.getPackageName()
|
||||
|
||||
|
@@ -175,6 +175,14 @@ type Error interface {
|
||||
ReturnParent(f ReturnError)
|
||||
}
|
||||
|
||||
type Errors interface {
|
||||
// ErrorsLast return the last registered error
|
||||
ErrorsLast() error
|
||||
|
||||
// ErrorsList return a slice of all registered errors
|
||||
ErrorsList() []error
|
||||
}
|
||||
|
||||
func MakeErrorIfError(err ...Error) Error {
|
||||
var e Error = nil
|
||||
|
||||
|
26
go.mod
26
go.mod
@@ -7,7 +7,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.19
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.19.8
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1
|
||||
github.com/bits-and-blooms/bitset v1.5.0
|
||||
github.com/c-bata/go-prompt v0.2.6
|
||||
github.com/fatih/color v1.15.0
|
||||
@@ -37,19 +37,19 @@ require (
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0
|
||||
github.com/spf13/viper v1.15.0
|
||||
github.com/vbauerster/mpb/v5 v5.4.0
|
||||
github.com/xanzy/go-gitlab v0.81.0
|
||||
github.com/xanzy/go-gitlab v0.82.0
|
||||
github.com/xhit/go-simple-mail v2.2.2+incompatible
|
||||
github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/net v0.8.0
|
||||
golang.org/x/oauth2 v0.6.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/sys v0.6.0
|
||||
golang.org/x/term v0.6.0
|
||||
golang.org/x/sys v0.7.0
|
||||
golang.org/x/term v0.7.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/clickhouse v0.5.0
|
||||
gorm.io/driver/mysql v1.4.7
|
||||
@@ -62,7 +62,7 @@ require (
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/ClickHouse/ch-go v0.54.0 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.8.1 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.8.3 // indirect
|
||||
github.com/DataDog/zstd v1.5.2 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
@@ -90,14 +90,14 @@ require (
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||
github.com/bytedance/sonic v1.8.6 // indirect
|
||||
github.com/bytedance/sonic v1.8.7 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/cockroachdb/errors v1.9.1 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/pebble v0.0.0-20210331181633-27fc006b8bfb // indirect
|
||||
github.com/cockroachdb/redact v1.1.3 // indirect
|
||||
github.com/getsentry/sentry-go v0.19.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.20.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-faster/city v1.0.1 // indirect
|
||||
@@ -117,7 +117,7 @@ require (
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect
|
||||
github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@@ -140,11 +140,11 @@ require (
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/juju/ratelimit v1.0.2 // indirect
|
||||
github.com/klauspost/compress v1.16.3 // indirect
|
||||
github.com/klauspost/compress v1.16.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.2 // indirect
|
||||
github.com/leodido/go-urn v1.2.3 // indirect
|
||||
github.com/lni/goutils v1.3.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
@@ -194,8 +194,8 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
@@ -32,7 +32,6 @@ import (
|
||||
liblog "github.com/nabbar/golib/logger"
|
||||
|
||||
libctx "github.com/nabbar/golib/context"
|
||||
srvrun "github.com/nabbar/golib/httpserver/run"
|
||||
srvtps "github.com/nabbar/golib/httpserver/types"
|
||||
montps "github.com/nabbar/golib/monitor/types"
|
||||
libsrv "github.com/nabbar/golib/server"
|
||||
@@ -50,7 +49,6 @@ type Info interface {
|
||||
|
||||
type Server interface {
|
||||
libsrv.Server
|
||||
libsrv.WaitNotify
|
||||
|
||||
Info
|
||||
|
||||
@@ -74,8 +72,6 @@ func New(cfg Config, defLog liblog.FuncLog) (Server, error) {
|
||||
|
||||
if e := s.SetConfig(cfg, defLog); e != nil {
|
||||
return nil, e
|
||||
} else {
|
||||
s.r = srvrun.New()
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
@@ -27,12 +27,13 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
libctx "github.com/nabbar/golib/context"
|
||||
srvrun "github.com/nabbar/golib/httpserver/run"
|
||||
srvtps "github.com/nabbar/golib/httpserver/types"
|
||||
liblog "github.com/nabbar/golib/logger"
|
||||
librun "github.com/nabbar/golib/server/runner/startStop"
|
||||
)
|
||||
|
||||
type srv struct {
|
||||
@@ -40,7 +41,8 @@ type srv struct {
|
||||
h srvtps.FuncHandler
|
||||
l liblog.FuncLog
|
||||
c libctx.Config[string]
|
||||
r srvrun.Run
|
||||
r librun.StartStop
|
||||
s *http.Server
|
||||
}
|
||||
|
||||
func (o *srv) Merge(s Server, def liblog.FuncLog) error {
|
||||
|
@@ -28,6 +28,7 @@ package httpserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
@@ -41,21 +42,38 @@ const (
|
||||
defaultNameMonitor = "HTTP Server"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotRunning = errors.New("server is not running")
|
||||
)
|
||||
|
||||
func (o *srv) HealthCheck(ctx context.Context) error {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.r == nil {
|
||||
return fmt.Errorf("server is not running")
|
||||
} else if o.r.IsRunning() {
|
||||
return nil
|
||||
} else if e := o.r.GetError(); e != nil {
|
||||
return errNotRunning
|
||||
} else if e := o.runAndHealthy(ctx); e != nil {
|
||||
return e
|
||||
} else if e = o.r.ErrorsLast(); e != nil {
|
||||
return e
|
||||
} else {
|
||||
return fmt.Errorf("server is not running")
|
||||
return errNotRunning
|
||||
}
|
||||
}
|
||||
|
||||
func (o *srv) runAndHealthy(ctx context.Context) error {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if !o.r.IsRunning() {
|
||||
return errNotRunning
|
||||
} else if e := o.PortNotUse(ctx, o.GetBindable()); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *srv) Monitor(vrs libver.Version) (montps.Monitor, error) {
|
||||
var (
|
||||
e error
|
||||
|
@@ -65,7 +65,6 @@ type Filter interface {
|
||||
|
||||
type Pool interface {
|
||||
libsrv.Server
|
||||
libsrv.WaitNotify
|
||||
|
||||
Manage
|
||||
Filter
|
||||
|
229
httpserver/run.go
Normal file
229
httpserver/run.go
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
srvtps "github.com/nabbar/golib/httpserver/types"
|
||||
liblog "github.com/nabbar/golib/logger"
|
||||
librun "github.com/nabbar/golib/server/runner/startStop"
|
||||
)
|
||||
|
||||
func (o *srv) newRun(ctx context.Context) error {
|
||||
if o == nil {
|
||||
return ErrorServerValidate.Error(nil)
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
if o.r != nil {
|
||||
if e := o.r.Stop(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
o.r = librun.New(o.runFuncStart, o.runFuncStop)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *srv) delRun(ctx context.Context) error {
|
||||
if o == nil {
|
||||
return ErrorServerValidate.Error(nil)
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
if o.r != nil {
|
||||
if e := o.r.Stop(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
o.r = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *srv) runStart(ctx context.Context) error {
|
||||
if o == nil {
|
||||
return ErrorServerValidate.Error(nil)
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.r == nil {
|
||||
return ErrorServerValidate.Error(nil)
|
||||
}
|
||||
|
||||
return o.r.Start(ctx)
|
||||
}
|
||||
|
||||
func (o *srv) runStop(ctx context.Context) error {
|
||||
if o == nil {
|
||||
return ErrorServerValidate.Error(nil)
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.r == nil {
|
||||
return ErrorServerValidate.Error(nil)
|
||||
}
|
||||
|
||||
return o.r.Stop(ctx)
|
||||
}
|
||||
|
||||
func (o *srv) runRestart(ctx context.Context) error {
|
||||
if o == nil {
|
||||
return ErrorServerValidate.Error(nil)
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.r == nil {
|
||||
return ErrorServerValidate.Error(nil)
|
||||
}
|
||||
|
||||
return o.r.Restart(ctx)
|
||||
}
|
||||
|
||||
func (o *srv) runIsRunning() bool {
|
||||
if o == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.r == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return o.r.IsRunning()
|
||||
}
|
||||
|
||||
func (o *srv) runFuncStart(ctx context.Context) (err error) {
|
||||
var (
|
||||
tls = false
|
||||
ser *http.Server
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if tls {
|
||||
ent := o.logger().Entry(liblog.InfoLevel, "TLS HTTP Server stopped")
|
||||
ent.ErrorAdd(true, err)
|
||||
ent.Log()
|
||||
} else {
|
||||
ent := o.logger().Entry(liblog.InfoLevel, "HTTP Server stopped")
|
||||
ent.ErrorAdd(true, err)
|
||||
ent.Log()
|
||||
}
|
||||
}()
|
||||
|
||||
if ser = o.getServer(); ser == nil {
|
||||
if err = o.setServer(ctx); err != nil {
|
||||
ent := o.logger().Entry(liblog.ErrorLevel, "starting http server")
|
||||
ent.ErrorAdd(true, err)
|
||||
ent.Log()
|
||||
return err
|
||||
} else if ser = o.getServer(); ser == nil {
|
||||
err = ErrorServerStart.ErrorParent(fmt.Errorf("cannot create new server, cannot retrieve server"))
|
||||
ent := o.logger().Entry(liblog.ErrorLevel, "starting http server")
|
||||
ent.ErrorAdd(true, err)
|
||||
ent.Log()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if ser.TLSConfig != nil && len(ser.TLSConfig.Certificates) > 0 {
|
||||
tls = true
|
||||
}
|
||||
|
||||
ser.BaseContext = func(listener net.Listener) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
if tls {
|
||||
o.logger().Entry(liblog.InfoLevel, "TLS HTTP Server is starting").Log()
|
||||
err = ser.ListenAndServeTLS("", "")
|
||||
} else {
|
||||
o.logger().Entry(liblog.InfoLevel, "HTTP Server is starting").Log()
|
||||
err = ser.ListenAndServe()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (o *srv) runFuncStop(ctx context.Context) (err error) {
|
||||
var x, n = context.WithTimeout(ctx, srvtps.TimeoutWaitingStop)
|
||||
defer n()
|
||||
|
||||
var (
|
||||
tls = false
|
||||
ser *http.Server
|
||||
)
|
||||
|
||||
defer func() {
|
||||
o.delServer()
|
||||
if tls {
|
||||
ent := o.logger().Entry(liblog.InfoLevel, "Shutdown of TLS HTTP Server has been called")
|
||||
ent.ErrorAdd(true, err)
|
||||
ent.Log()
|
||||
} else {
|
||||
ent := o.logger().Entry(liblog.InfoLevel, "Shutdown of HTTP Server has been called")
|
||||
ent.ErrorAdd(true, err)
|
||||
ent.Log()
|
||||
}
|
||||
}()
|
||||
|
||||
if ser = o.getServer(); ser == nil {
|
||||
err = ErrorServerStart.ErrorParent(fmt.Errorf("cannot retrieve server"))
|
||||
ent := o.logger().Entry(liblog.ErrorLevel, "starting http server")
|
||||
ent.ErrorAdd(true, err)
|
||||
ent.Log()
|
||||
return err
|
||||
} else if ser.TLSConfig != nil && len(ser.TLSConfig.Certificates) > 0 {
|
||||
tls = true
|
||||
}
|
||||
|
||||
if tls {
|
||||
o.logger().Entry(liblog.InfoLevel, "Calling TLS HTTP Server shutdown").Log()
|
||||
} else {
|
||||
o.logger().Entry(liblog.InfoLevel, "Calling HTTP Server shutdown").Log()
|
||||
}
|
||||
|
||||
err = ser.Shutdown(x)
|
||||
|
||||
return err
|
||||
}
|
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
liblog "github.com/nabbar/golib/logger"
|
||||
)
|
||||
|
||||
type sRun struct {
|
||||
m sync.RWMutex
|
||||
err error
|
||||
chn chan struct{}
|
||||
ctx context.Context
|
||||
cnl context.CancelFunc
|
||||
log liblog.FuncLog
|
||||
srv *http.Server
|
||||
run bool
|
||||
tls bool
|
||||
}
|
||||
|
||||
func (o *sRun) logger() liblog.Logger {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.log == nil {
|
||||
return liblog.GetDefault()
|
||||
} else if l := o.log(); l == nil {
|
||||
return liblog.GetDefault()
|
||||
} else {
|
||||
return l
|
||||
}
|
||||
}
|
||||
|
||||
func (o *sRun) SetServer(srv *http.Server, log liblog.FuncLog, tls bool) {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
srv.BaseContext = func(listener net.Listener) context.Context {
|
||||
return o.getContext()
|
||||
}
|
||||
|
||||
o.srv = srv
|
||||
o.log = log
|
||||
o.tls = tls
|
||||
}
|
||||
|
||||
func (o *sRun) getServer() *http.Server {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
return o.srv
|
||||
}
|
||||
|
||||
func (o *sRun) delServer() {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
if o.srv != nil {
|
||||
o.logger().Entry(liblog.ErrorLevel, "closing server").ErrorAdd(true, o.srv.Close()).Check(liblog.NilLevel)
|
||||
o.srv = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *sRun) getContext() context.Context {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
return o.ctx
|
||||
}
|
||||
|
||||
func (o *sRun) getCancel() context.CancelFunc {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
return o.cnl
|
||||
}
|
||||
|
||||
func (o *sRun) isTLS() bool {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
return o.tls
|
||||
}
|
||||
|
||||
func (o *sRun) GetError() error {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
return o.err
|
||||
}
|
||||
|
||||
func (o *sRun) setError(err error) {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
o.err = err
|
||||
}
|
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
srvtps "github.com/nabbar/golib/httpserver/types"
|
||||
liblog "github.com/nabbar/golib/logger"
|
||||
)
|
||||
|
||||
func (o *sRun) Start(ctx context.Context) error {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
if o == nil || o.srv == nil {
|
||||
return fmt.Errorf("invalid instance")
|
||||
}
|
||||
|
||||
o.ctx, o.cnl = context.WithCancel(ctx)
|
||||
go o.runServer()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *sRun) runServer() {
|
||||
defer func() {
|
||||
cnl := o.getCancel()
|
||||
if cnl != nil {
|
||||
cnl()
|
||||
}
|
||||
|
||||
o.setRunning(false)
|
||||
o.logger().Entry(liblog.InfoLevel, "server stopped").Log()
|
||||
}()
|
||||
|
||||
var err error
|
||||
o.logger().Entry(liblog.InfoLevel, "Server is starting").Log()
|
||||
o.setError(nil)
|
||||
o.setRunning(true)
|
||||
|
||||
if o.isTLS() {
|
||||
err = o.getServer().ListenAndServeTLS("", "")
|
||||
} else {
|
||||
err = o.getServer().ListenAndServe()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
x := o.getContext()
|
||||
if x != nil && x.Err() != nil && errors.Is(err, x.Err()) {
|
||||
err = nil
|
||||
} else if errors.Is(err, http.ErrServerClosed) {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
o.setError(err)
|
||||
o.logger().Entry(liblog.InfoLevel, "Server return an error").ErrorAdd(true, err).Check(liblog.NilLevel)
|
||||
}
|
||||
|
||||
func (o *sRun) Stop(ctx context.Context) error {
|
||||
var cnl context.CancelFunc
|
||||
|
||||
if ctx.Err() != nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
if t, ok := ctx.Deadline(); !ok || t.IsZero() {
|
||||
ctx, cnl = context.WithTimeout(ctx, srvtps.TimeoutWaitingStop)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if cnl != nil {
|
||||
cnl()
|
||||
}
|
||||
//o.delServer()
|
||||
}()
|
||||
|
||||
var err error
|
||||
if o.IsRunning() {
|
||||
o.StopWaitNotify()
|
||||
if srv := o.getServer(); srv != nil {
|
||||
err = srv.Shutdown(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
o.logger().Entry(liblog.ErrorLevel, "Shutting down server").ErrorAdd(true, err).Check(liblog.NilLevel)
|
||||
return err
|
||||
}
|
||||
|
||||
func (o *sRun) Restart(ctx context.Context) error {
|
||||
_ = o.Stop(ctx)
|
||||
return o.Start(ctx)
|
||||
}
|
||||
|
||||
func (o *sRun) IsRunning() bool {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
return o.run
|
||||
}
|
||||
|
||||
func (o *sRun) setRunning(flag bool) {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
o.run = flag
|
||||
}
|
@@ -28,109 +28,126 @@ package httpserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
liberr "github.com/nabbar/golib/errors"
|
||||
srvtps "github.com/nabbar/golib/httpserver/types"
|
||||
liblog "github.com/nabbar/golib/logger"
|
||||
libsrv "github.com/nabbar/golib/server"
|
||||
)
|
||||
|
||||
func (o *srv) Start(ctx context.Context) error {
|
||||
ssl := o.cfgGetTLS()
|
||||
var errInvalid = errors.New("invalid instance")
|
||||
|
||||
if o.IsTLS() && ssl == nil {
|
||||
return ErrorServerValidate.ErrorParent(fmt.Errorf("TLS Config is not well defined"))
|
||||
func (o *srv) getServer() *http.Server {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
bind := o.GetBindable()
|
||||
name := o.GetName()
|
||||
if name == "" {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
return o.s
|
||||
}
|
||||
|
||||
func (o *srv) delServer() {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
o.s = nil
|
||||
}
|
||||
|
||||
func (o *srv) setServer(ctx context.Context) error {
|
||||
if o == nil {
|
||||
return errInvalid
|
||||
}
|
||||
|
||||
var (
|
||||
ssl = o.cfgGetTLS()
|
||||
bind = o.GetBindable()
|
||||
name = o.GetName()
|
||||
|
||||
fctStop = func() {
|
||||
_ = o.Stop(ctx)
|
||||
}
|
||||
)
|
||||
|
||||
if o.IsTLS() && ssl == nil {
|
||||
err := ErrorServerValidate.ErrorParent(fmt.Errorf("TLS Config is not well defined"))
|
||||
ent := o.logger().Entry(liblog.ErrorLevel, "starting http server")
|
||||
ent.ErrorAdd(true, err)
|
||||
ent.Log()
|
||||
return err
|
||||
} else if name == "" {
|
||||
name = bind
|
||||
}
|
||||
|
||||
var stdlog = o.logger()
|
||||
|
||||
s := &http.Server{
|
||||
Addr: bind,
|
||||
ErrorLog: o.logger().GetStdLogger(liblog.ErrorLevel, log.LstdFlags|log.Lmicroseconds),
|
||||
Handler: o.HandlerLoadFct(),
|
||||
Addr: bind,
|
||||
Handler: o.HandlerLoadFct(),
|
||||
}
|
||||
|
||||
if ssl != nil && ssl.LenCertificatePair() > 0 {
|
||||
s.TLSConfig = ssl.TlsConfig("")
|
||||
}
|
||||
|
||||
if e := o.cfgGetServer().initServer(s); e != nil {
|
||||
stdlog.SetIOWriterFilter("http: TLS handshake error from 127.0.0.1")
|
||||
} else if e := o.cfgGetServer().initServer(s); e != nil {
|
||||
ent := o.logger().Entry(liblog.ErrorLevel, "init http2 server")
|
||||
ent.ErrorAdd(true, e)
|
||||
ent.Log()
|
||||
return e
|
||||
}
|
||||
|
||||
if o.IsRunning() {
|
||||
_ = o.Stop(ctx)
|
||||
}
|
||||
s.ErrorLog = stdlog.GetStdLogger(liblog.ErrorLevel, log.LstdFlags|log.Lmicroseconds)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if e := o.PortInUse(o.GetBindable()); e != nil {
|
||||
_ = o.Stop(ctx)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
o.r.SetServer(s, o.logger, ssl != nil)
|
||||
|
||||
if e := o.r.Start(ctx); e != nil {
|
||||
if e := o.RunIfPortInUse(ctx, o.GetBindable(), 5, fctStop); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
ok := false
|
||||
ts := time.Now()
|
||||
o.m.Lock()
|
||||
o.s = s
|
||||
o.m.Unlock()
|
||||
|
||||
for {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
ok = o.r.IsRunning()
|
||||
return nil
|
||||
}
|
||||
|
||||
if ok {
|
||||
break
|
||||
} else if time.Since(ts) > 10*time.Second {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
_ = o.r.Restart(ctx)
|
||||
} else {
|
||||
ts = time.Now()
|
||||
|
||||
for {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if e := o.PortInUse(o.GetBindable()); e != nil {
|
||||
return nil
|
||||
} else if time.Since(ts) > 10*time.Second {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_ = o.r.Restart(ctx)
|
||||
func (o *srv) Start(ctx context.Context) error {
|
||||
// Register Server to runner
|
||||
if o.getServer() != nil {
|
||||
if e := o.Stop(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
_ = o.r.Stop(ctx)
|
||||
return ErrorServerStart.Error(nil)
|
||||
if e := o.newRun(ctx); e != nil {
|
||||
return e
|
||||
} else if e = o.runStart(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *srv) Stop(ctx context.Context) error {
|
||||
if o == nil {
|
||||
return errInvalid
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return o.r.Stop(ctx)
|
||||
}
|
||||
|
||||
@@ -140,17 +157,25 @@ func (o *srv) Restart(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (o *srv) IsRunning() bool {
|
||||
if o == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.r == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return o.r.IsRunning()
|
||||
}
|
||||
|
||||
func (o *srv) PortInUse(listen string) liberr.Error {
|
||||
func (o *srv) PortInUse(ctx context.Context, listen string) liberr.Error {
|
||||
var (
|
||||
dia = net.Dialer{}
|
||||
con net.Conn
|
||||
err error
|
||||
ctx context.Context
|
||||
cnl context.CancelFunc
|
||||
)
|
||||
|
||||
@@ -163,7 +188,7 @@ func (o *srv) PortInUse(listen string) liberr.Error {
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, cnl = context.WithTimeout(context.TODO(), srvtps.TimeoutWaitingPortFreeing)
|
||||
ctx, cnl = context.WithTimeout(ctx, srvtps.TimeoutWaitingPortFreeing)
|
||||
con, err = dia.DialContext(ctx, "tcp", listen)
|
||||
|
||||
if con != nil {
|
||||
@@ -180,3 +205,37 @@ func (o *srv) PortInUse(listen string) liberr.Error {
|
||||
|
||||
return ErrorPortUse.Error(nil)
|
||||
}
|
||||
|
||||
func (o *srv) PortNotUse(ctx context.Context, listen string) error {
|
||||
var (
|
||||
err error
|
||||
|
||||
cnl context.CancelFunc
|
||||
con net.Conn
|
||||
dia = net.Dialer{}
|
||||
)
|
||||
|
||||
ctx, cnl = context.WithTimeout(context.TODO(), srvtps.TimeoutWaitingPortFreeing)
|
||||
defer cnl()
|
||||
|
||||
con, err = dia.DialContext(ctx, "tcp", listen)
|
||||
defer func() {
|
||||
if con != nil {
|
||||
_ = con.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (o *srv) RunIfPortInUse(ctx context.Context, listen string, nbr uint8, fct func()) liberr.Error {
|
||||
chk := func() bool {
|
||||
return o.PortInUse(ctx, listen) == nil
|
||||
}
|
||||
|
||||
if !libsrv.RunNbr(nbr, chk, fct) {
|
||||
return ErrorPortUse.Error(nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ package types
|
||||
import "time"
|
||||
|
||||
const (
|
||||
TimeoutWaitingPortFreeing = 500 * time.Microsecond
|
||||
TimeoutWaitingStop = 10 * time.Second
|
||||
TimeoutWaitingPortFreeing = 100 * time.Microsecond
|
||||
TimeoutWaitingStop = 5 * time.Second
|
||||
BadHandlerName = "no handler"
|
||||
)
|
||||
|
@@ -59,6 +59,9 @@ type Logger interface {
|
||||
//GetIOWriterLevel return the minimal level of log message for io.WriterCloser interface
|
||||
GetIOWriterLevel() Level
|
||||
|
||||
// SetIOWriterFilter allow to filter message that contained the given pattern. If the pattern is found, the log is drop.
|
||||
SetIOWriterFilter(pattern string)
|
||||
|
||||
//SetOptions allow to set or update the options for the logger
|
||||
SetOptions(opt *Options) error
|
||||
|
||||
|
@@ -27,7 +27,11 @@
|
||||
|
||||
package logger
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (o *logger) Close() error {
|
||||
if o == nil {
|
||||
@@ -49,7 +53,13 @@ func (o *logger) Write(p []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
o.newEntry(o.GetIOWriterLevel(), string(p), nil, o.GetFields(), nil).Log()
|
||||
val := strings.TrimSpace(string(o.IOWriterFilter(p)))
|
||||
|
||||
if len(val) < 1 {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
o.newEntry(o.GetIOWriterLevel(), val, nil, o.GetFields(), nil).Log()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -62,7 +72,6 @@ func (o *logger) SetIOWriterLevel(lvl Level) {
|
||||
|
||||
o.x.Store(keyWriter, lvl)
|
||||
}
|
||||
|
||||
func (o *logger) GetIOWriterLevel() Level {
|
||||
if o == nil {
|
||||
return NilLevel
|
||||
@@ -76,3 +85,29 @@ func (o *logger) GetIOWriterLevel() Level {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (o *logger) SetIOWriterFilter(pattern string) {
|
||||
if o == nil {
|
||||
return
|
||||
} else if o.x == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.x.Store(keyFilter, []byte(pattern))
|
||||
}
|
||||
|
||||
func (o *logger) IOWriterFilter(p []byte) []byte {
|
||||
if o == nil {
|
||||
return p
|
||||
} else if o.x == nil {
|
||||
return p
|
||||
} else if i, l := o.x.Load(keyFilter); !l {
|
||||
return p
|
||||
} else if v, k := i.([]byte); !k {
|
||||
return p
|
||||
} else if bytes.Contains(p, v) {
|
||||
return make([]byte, 0)
|
||||
} else {
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
@@ -51,6 +51,7 @@ const (
|
||||
keyOptions
|
||||
keyLogrus
|
||||
keyWriter
|
||||
keyFilter
|
||||
|
||||
_TraceFilterMod = "/pkg/mod/"
|
||||
_TraceFilterVendor = "/vendor/"
|
||||
|
@@ -38,6 +38,7 @@ const (
|
||||
ErrorValidatorError
|
||||
ErrorLoggerError
|
||||
ErrorTimeout
|
||||
ErrorInvalid
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -59,6 +60,8 @@ func getMessage(code liberr.CodeError) (message string) {
|
||||
return "cannot initialize logger"
|
||||
case ErrorTimeout:
|
||||
return "timeout error"
|
||||
case ErrorInvalid:
|
||||
return "invalid instance"
|
||||
}
|
||||
|
||||
return liberr.NullMessage
|
||||
|
@@ -50,6 +50,6 @@ func New(ctx libctx.FuncContext, info montps.Info) (montps.Monitor, error) {
|
||||
m: sync.RWMutex{},
|
||||
i: info,
|
||||
x: libctx.NewConfig[string](ctx),
|
||||
s: make(chan struct{}),
|
||||
r: nil,
|
||||
}, nil
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ import (
|
||||
libctx "github.com/nabbar/golib/context"
|
||||
liberr "github.com/nabbar/golib/errors"
|
||||
montps "github.com/nabbar/golib/monitor/types"
|
||||
librun "github.com/nabbar/golib/server/runner/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -64,7 +65,7 @@ type mon struct {
|
||||
m sync.RWMutex
|
||||
i montps.Info
|
||||
x libctx.Config[string]
|
||||
s chan struct{}
|
||||
r librun.Ticker
|
||||
}
|
||||
|
||||
func (o *mon) SetHealthCheck(fct montps.HealthCheck) {
|
||||
|
@@ -30,71 +30,58 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/nabbar/golib/monitor/types"
|
||||
montps "github.com/nabbar/golib/monitor/types"
|
||||
librun "github.com/nabbar/golib/server/runner/ticker"
|
||||
)
|
||||
|
||||
func (o *mon) setChan() {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
o.s = make(chan struct{})
|
||||
}
|
||||
|
||||
func (o *mon) getChan() <-chan struct{} {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
return o.s
|
||||
}
|
||||
|
||||
func (o *mon) sendChan() {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
o.s <- struct{}{}
|
||||
}
|
||||
const (
|
||||
MaxPoolStart = 15 * time.Second
|
||||
MaxTickPooler = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
func (o *mon) Start(ctx context.Context) error {
|
||||
if o.IsRunning() {
|
||||
_ = o.Stop(ctx)
|
||||
}
|
||||
|
||||
o.setChan()
|
||||
go o.ticker(ctx)
|
||||
|
||||
if o.IsRunning() {
|
||||
return nil
|
||||
if o == nil {
|
||||
return ErrorInvalid.Error(nil)
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
for {
|
||||
if time.Since(t) > 15*time.Second {
|
||||
return ErrorTimeout.Error(nil)
|
||||
} else if o.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
o.setRunner(ctx)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
if o.r == nil {
|
||||
return ErrorInvalid.Error(nil)
|
||||
}
|
||||
|
||||
if e := o.r.Start(ctx); e != nil {
|
||||
return e
|
||||
} else {
|
||||
return o.poolIsRunning(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *mon) Stop(ctx context.Context) error {
|
||||
if !o.IsRunning() {
|
||||
defer o.delRunner(ctx)
|
||||
|
||||
if o == nil {
|
||||
return ErrorInvalid.Error(nil)
|
||||
} else if !o.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
for {
|
||||
o.sendChan()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if time.Since(t) > 15*time.Second {
|
||||
return ErrorTimeout.Error(nil)
|
||||
} else if !o.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
if o.r == nil {
|
||||
return ErrorInvalid.Error(nil)
|
||||
} else if e := o.r.Stop(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *mon) Restart(ctx context.Context) error {
|
||||
@@ -108,63 +95,104 @@ func (o *mon) Restart(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (o *mon) IsRunning() bool {
|
||||
if i, l := o.x.Load(keyRun); !l {
|
||||
if o == nil {
|
||||
return false
|
||||
} else if v, k := i.(bool); !k {
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.r == nil {
|
||||
return false
|
||||
} else {
|
||||
return v
|
||||
return o.r.IsRunning()
|
||||
}
|
||||
}
|
||||
|
||||
func (o *mon) setRunning(state bool) {
|
||||
if state {
|
||||
o.x.Store(keyRun, state)
|
||||
} else {
|
||||
o.x.Delete(keyRun)
|
||||
func (o *mon) poolIsRunning(ctx context.Context) error {
|
||||
if o == nil {
|
||||
return ErrorInvalid.Error(nil)
|
||||
} else if o.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *mon) ticker(ctx context.Context) {
|
||||
var (
|
||||
cfg = o.getCfg()
|
||||
chg = false
|
||||
tck *time.Ticker
|
||||
tck = time.NewTicker(MaxTickPooler)
|
||||
tms = time.Now()
|
||||
)
|
||||
|
||||
tck = time.NewTicker(cfg.intervalCheck)
|
||||
defer tck.Stop()
|
||||
|
||||
o.setRunning(true)
|
||||
defer o.setRunning(false)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tck.C:
|
||||
o.check(ctx, cfg)
|
||||
|
||||
if o.IsRise() {
|
||||
tck.Reset(cfg.intervalRise)
|
||||
chg = true
|
||||
} else if o.IsFall() {
|
||||
tck.Reset(cfg.intervalFall)
|
||||
chg = true
|
||||
} else if chg {
|
||||
tck.Reset(cfg.intervalCheck)
|
||||
chg = false
|
||||
if time.Since(tms) >= MaxPoolStart {
|
||||
return ErrorTimeout.Error(nil)
|
||||
} else if o.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
case <-o.getChan():
|
||||
return
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *mon) setRunner(ctx context.Context) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
if o.r != nil {
|
||||
_ = o.r.Stop(ctx)
|
||||
}
|
||||
|
||||
var (
|
||||
cfg = o.getCfg()
|
||||
)
|
||||
|
||||
o.r = librun.New(cfg.intervalCheck, o.runFunc)
|
||||
}
|
||||
|
||||
func (o *mon) delRunner(ctx context.Context) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
if o.r != nil {
|
||||
_ = o.r.Stop(ctx)
|
||||
}
|
||||
|
||||
o.r = nil
|
||||
}
|
||||
|
||||
func (o *mon) runFunc(ctx context.Context, tck *time.Ticker) error {
|
||||
var (
|
||||
cfg = o.getCfg()
|
||||
chg = false
|
||||
)
|
||||
|
||||
o.check(ctx, cfg)
|
||||
|
||||
if o.IsRise() {
|
||||
tck.Reset(cfg.intervalRise)
|
||||
chg = true
|
||||
} else if o.IsFall() {
|
||||
tck.Reset(cfg.intervalFall)
|
||||
chg = true
|
||||
} else if chg {
|
||||
tck.Reset(cfg.intervalCheck)
|
||||
chg = false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *mon) check(ctx context.Context, cfg *runCfg) {
|
||||
var fct types.HealthCheck
|
||||
var fct montps.HealthCheck
|
||||
|
||||
if fct = o.getFct(); fct == nil {
|
||||
l := o.getLastCheck()
|
||||
|
@@ -115,6 +115,7 @@ type Request interface {
|
||||
|
||||
Do() (*http.Response, liberr.Error)
|
||||
DoParse(model interface{}, validStatus ...int) liberr.Error
|
||||
DoParseRetry(retry int, model interface{}, validStatus ...int) liberr.Error
|
||||
|
||||
Monitor(ctx context.Context, vrs libver.Version) (montps.Monitor, error)
|
||||
HealthCheck(ctx context.Context) error
|
||||
|
@@ -213,3 +213,19 @@ func (r *request) DoParse(model interface{}, validStatus ...int) liberr.Error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *request) DoParseRetry(retry int, model interface{}, validStatus ...int) liberr.Error {
|
||||
var e liberr.Error
|
||||
|
||||
for i := 0; i < retry; i++ {
|
||||
if e = r.DoParse(model, validStatus...); e != nil {
|
||||
continue
|
||||
} else if r.IsError() {
|
||||
continue
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
@@ -26,7 +26,11 @@
|
||||
|
||||
package server
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Action func(ctx context.Context) error
|
||||
|
||||
type Server interface {
|
||||
// Start is used to start the server
|
||||
|
@@ -24,71 +24,78 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package run
|
||||
package startStop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
var closeChan = make(chan struct{})
|
||||
|
||||
func (o *sRun) StartWaitNotify(ctx context.Context) {
|
||||
if !o.IsRunning() {
|
||||
func init() {
|
||||
close(closeChan)
|
||||
}
|
||||
|
||||
func (o *run) IsRunning() bool {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.c == nil || o.c == closeChan {
|
||||
return false
|
||||
}
|
||||
|
||||
select {
|
||||
case <-o.c:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (o *run) chanInit() {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for interrupt signal to gracefully shutdown the server with
|
||||
// a timeout of 5 seconds.
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT)
|
||||
signal.Notify(quit, syscall.SIGTERM)
|
||||
signal.Notify(quit, syscall.SIGQUIT)
|
||||
|
||||
o.initChan()
|
||||
defer o.delChan()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-quit:
|
||||
_ = o.Stop(ctx)
|
||||
return
|
||||
case <-o.getContext().Done():
|
||||
if o.IsRunning() {
|
||||
_ = o.Stop(ctx)
|
||||
}
|
||||
return
|
||||
case <-o.getChan():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *sRun) StopWaitNotify() {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
if o.chn != nil {
|
||||
o.chn <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *sRun) initChan() {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
o.chn = make(chan struct{})
|
||||
|
||||
o.c = make(chan struct{})
|
||||
}
|
||||
|
||||
func (o *sRun) getChan() <-chan struct{} {
|
||||
func (o *run) chanDone() <-chan struct{} {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
return o.chn
|
||||
|
||||
return o.c
|
||||
}
|
||||
|
||||
func (o *sRun) delChan() {
|
||||
func (o *run) chanSend() {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.c != nil && o.c != closeChan {
|
||||
o.c <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *run) chanClose() {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
if o.chn != nil {
|
||||
close(o.chn)
|
||||
|
||||
if o.c != nil && o.c != closeChan {
|
||||
o.c = closeChan
|
||||
}
|
||||
o.chn = nil
|
||||
}
|
82
server/runner/startStop/error.go
Normal file
82
server/runner/startStop/error.go
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package startStop
|
||||
|
||||
func (o *run) ErrorsLast() error {
|
||||
if o == nil {
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if len(o.e) > 0 {
|
||||
return o.e[len(o.e)-1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *run) ErrorsList() []error {
|
||||
var res = make([]error, 0)
|
||||
|
||||
if o == nil {
|
||||
res = append(res, ErrInvalid)
|
||||
return res
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if len(o.e) > 0 {
|
||||
return o.e
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (o *run) errorsAdd(e error) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
o.e = append(o.e, e)
|
||||
}
|
||||
|
||||
func (o *run) errorsClean() {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
o.e = make([]error, 0)
|
||||
}
|
@@ -24,30 +24,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package run
|
||||
package startStop
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
liblog "github.com/nabbar/golib/logger"
|
||||
liberr "github.com/nabbar/golib/errors"
|
||||
libsrv "github.com/nabbar/golib/server"
|
||||
)
|
||||
|
||||
type Run interface {
|
||||
type StartStop interface {
|
||||
libsrv.Server
|
||||
libsrv.WaitNotify
|
||||
|
||||
GetError() error
|
||||
SetServer(srv *http.Server, log liblog.FuncLog, tls bool)
|
||||
liberr.Errors
|
||||
}
|
||||
|
||||
func New() Run {
|
||||
return &sRun{
|
||||
m: sync.RWMutex{},
|
||||
ctx: nil,
|
||||
cnl: nil,
|
||||
log: nil,
|
||||
srv: nil,
|
||||
func New(start, stop func(ctx context.Context) error) StartStop {
|
||||
return &run{
|
||||
m: sync.RWMutex{},
|
||||
e: make([]error, 0),
|
||||
f: start,
|
||||
s: stop,
|
||||
c: nil,
|
||||
}
|
||||
}
|
168
server/runner/startStop/model.go
Normal file
168
server/runner/startStop/model.go
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package startStop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
libsrv "github.com/nabbar/golib/server"
|
||||
)
|
||||
|
||||
const (
|
||||
pollStop = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
var ErrInvalid = errors.New("invalid instance")
|
||||
|
||||
type run struct {
|
||||
m sync.RWMutex
|
||||
e []error
|
||||
f func(ctx context.Context) error
|
||||
s func(ctx context.Context) error
|
||||
c chan struct{}
|
||||
}
|
||||
|
||||
func (o *run) Restart(ctx context.Context) error {
|
||||
if e := o.Stop(ctx); e != nil {
|
||||
return e
|
||||
} else if e = o.Start(ctx); e != nil {
|
||||
_ = o.Stop(ctx)
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *run) Stop(ctx context.Context) error {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return e
|
||||
} else if !o.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
e error
|
||||
t = time.NewTicker(pollStop)
|
||||
)
|
||||
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if o.IsRunning() {
|
||||
o.chanSend()
|
||||
e = o.callStop(ctx)
|
||||
} else {
|
||||
return e
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *run) callStop(ctx context.Context) error {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return o.s(ctx)
|
||||
}
|
||||
|
||||
func (o *run) Start(ctx context.Context) error {
|
||||
if e := o.checkMeStart(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
var can context.CancelFunc
|
||||
ctx, can = context.WithCancel(ctx)
|
||||
|
||||
go func(x context.Context, n context.CancelFunc, fct libsrv.Action) {
|
||||
defer n()
|
||||
|
||||
o.chanInit()
|
||||
defer o.chanClose()
|
||||
|
||||
if e := fct(x); e != nil {
|
||||
o.errorsAdd(e)
|
||||
return
|
||||
}
|
||||
}(ctx, can, o.f)
|
||||
|
||||
go func(x context.Context, n context.CancelFunc) {
|
||||
defer n()
|
||||
defer o.chanClose()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-o.chanDone():
|
||||
return
|
||||
case <-x.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}(ctx, can)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *run) checkMe() error {
|
||||
if o == nil {
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.f == nil || o.s == nil {
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *run) checkMeStart(ctx context.Context) error {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return e
|
||||
} else if o.IsRunning() {
|
||||
if e = o.Stop(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
101
server/runner/ticker/chan.go
Normal file
101
server/runner/ticker/chan.go
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package ticker
|
||||
|
||||
var closeChan = make(chan struct{})
|
||||
|
||||
func init() {
|
||||
close(closeChan)
|
||||
}
|
||||
|
||||
func (o *run) IsRunning() bool {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.c == nil || o.c == closeChan {
|
||||
return false
|
||||
}
|
||||
|
||||
select {
|
||||
case <-o.c:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (o *run) chanInit() {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
o.c = make(chan struct{})
|
||||
}
|
||||
|
||||
func (o *run) chanDone() <-chan struct{} {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
return o.c
|
||||
}
|
||||
|
||||
func (o *run) chanSend() {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.c != nil && o.c != closeChan {
|
||||
o.c <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *run) chanClose() {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
if o.c != nil && o.c != closeChan {
|
||||
o.c = closeChan
|
||||
}
|
||||
}
|
@@ -24,18 +24,59 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package httpserver
|
||||
package ticker
|
||||
|
||||
import "context"
|
||||
func (o *run) ErrorsLast() error {
|
||||
if o == nil {
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
func (o *srv) StartWaitNotify(ctx context.Context) {
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
go o.r.StartWaitNotify(ctx)
|
||||
|
||||
if len(o.e) > 0 {
|
||||
return o.e[len(o.e)-1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *srv) StopWaitNotify() {
|
||||
func (o *run) ErrorsList() []error {
|
||||
var res = make([]error, 0)
|
||||
|
||||
if o == nil {
|
||||
res = append(res, ErrInvalid)
|
||||
return res
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
go o.r.StopWaitNotify()
|
||||
|
||||
if len(o.e) > 0 {
|
||||
return o.e
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (o *run) errorsAdd(e error) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
o.e = append(o.e, e)
|
||||
}
|
||||
|
||||
func (o *run) errorsClean() {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
|
||||
o.e = make([]error, 0)
|
||||
}
|
@@ -24,24 +24,28 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package pool
|
||||
package ticker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
libhtp "github.com/nabbar/golib/httpserver"
|
||||
liberr "github.com/nabbar/golib/errors"
|
||||
libsrv "github.com/nabbar/golib/server"
|
||||
)
|
||||
|
||||
func (o *pool) StartWaitNotify(ctx context.Context) {
|
||||
o.Walk(func(bindAddress string, srv libhtp.Server) bool {
|
||||
srv.StartWaitNotify(ctx)
|
||||
return true
|
||||
})
|
||||
type Ticker interface {
|
||||
libsrv.Server
|
||||
liberr.Errors
|
||||
}
|
||||
|
||||
func (o *pool) StopWaitNotify() {
|
||||
o.Walk(func(bindAddress string, srv libhtp.Server) bool {
|
||||
srv.StopWaitNotify()
|
||||
return true
|
||||
})
|
||||
func New(tick time.Duration, fct func(ctx context.Context, tck *time.Ticker) error) Ticker {
|
||||
return &run{
|
||||
m: sync.RWMutex{},
|
||||
e: make([]error, 0),
|
||||
f: fct,
|
||||
d: tick,
|
||||
c: nil,
|
||||
}
|
||||
}
|
155
server/runner/ticker/model.go
Normal file
155
server/runner/ticker/model.go
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package ticker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
pollStop = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
var ErrInvalid = errors.New("invalid instance")
|
||||
|
||||
type run struct {
|
||||
m sync.RWMutex
|
||||
e []error
|
||||
f func(ctx context.Context, tck *time.Ticker) error
|
||||
d time.Duration
|
||||
c chan struct{}
|
||||
}
|
||||
|
||||
func (o *run) Restart(ctx context.Context) error {
|
||||
if e := o.Stop(ctx); e != nil {
|
||||
return e
|
||||
} else if e = o.Start(ctx); e != nil {
|
||||
_ = o.Stop(ctx)
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *run) Stop(ctx context.Context) error {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return e
|
||||
} else if !o.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var t = time.NewTicker(pollStop)
|
||||
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if o.IsRunning() {
|
||||
o.chanSend()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *run) Start(ctx context.Context) error {
|
||||
if e := o.checkMeStart(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
go func(con context.Context, dur time.Duration, fct func(ctx context.Context, tck *time.Ticker) error) {
|
||||
var (
|
||||
tck = time.NewTicker(dur)
|
||||
x, n = context.WithCancel(con)
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if n != nil {
|
||||
n()
|
||||
}
|
||||
if tck != nil {
|
||||
tck.Stop()
|
||||
}
|
||||
o.chanClose()
|
||||
}()
|
||||
|
||||
o.chanInit()
|
||||
o.errorsClean()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tck.C:
|
||||
if e := fct(x, tck); e != nil {
|
||||
o.errorsAdd(e)
|
||||
}
|
||||
case <-con.Done():
|
||||
return
|
||||
case <-o.chanDone():
|
||||
return
|
||||
}
|
||||
}
|
||||
}(ctx, o.d, o.f)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *run) checkMe() error {
|
||||
if o == nil {
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
o.m.RLock()
|
||||
defer o.m.RUnlock()
|
||||
|
||||
if o.f == nil || o.d == 0 {
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *run) checkMeStart(ctx context.Context) error {
|
||||
if e := o.checkMe(); e != nil {
|
||||
return e
|
||||
} else if o.IsRunning() {
|
||||
if e = o.Stop(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
76
server/tools.go
Normal file
76
server/tools.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FunCheck func() bool
|
||||
type FunRun func()
|
||||
|
||||
func RunNbr(max uint8, chk FunCheck, run FunRun) bool {
|
||||
var i uint8
|
||||
|
||||
for i = 0; i < max; i++ {
|
||||
if chk() {
|
||||
return true
|
||||
}
|
||||
|
||||
run()
|
||||
}
|
||||
|
||||
return chk()
|
||||
}
|
||||
|
||||
func RunTick(ctx context.Context, tick, max time.Duration, chk FunCheck, run FunRun) bool {
|
||||
var (
|
||||
s = time.Now()
|
||||
t = time.NewTicker(tick)
|
||||
)
|
||||
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
|
||||
case <-t.C:
|
||||
if chk() {
|
||||
return true
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
if time.Since(s) >= max {
|
||||
return chk()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user