mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
add functions/vendor files
This commit is contained in:
1
vendor/github.com/ccirello/supervisor/.gitignore
generated
vendored
Normal file
1
vendor/github.com/ccirello/supervisor/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
supervisor.test
|
||||
6
vendor/github.com/ccirello/supervisor/.travis.yml
generated
vendored
Normal file
6
vendor/github.com/ccirello/supervisor/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
language: go
|
||||
go_import_path: cirello.io/supervisor
|
||||
go:
|
||||
- 1.7.5
|
||||
- 1.8
|
||||
- tip
|
||||
19
vendor/github.com/ccirello/supervisor/LICENSE
generated
vendored
Normal file
19
vendor/github.com/ccirello/supervisor/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016 Ulderico Cirello
|
||||
|
||||
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
|
||||
5
vendor/github.com/ccirello/supervisor/README.md
generated
vendored
Normal file
5
vendor/github.com/ccirello/supervisor/README.md
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[](https://travis-ci.org/ucirello/supervisor)
|
||||
|
||||
go get [-u] cirello.io/supervisor
|
||||
|
||||
http://godoc.org/cirello.io/supervisor
|
||||
40
vendor/github.com/ccirello/supervisor/anon.go
generated
vendored
Normal file
40
vendor/github.com/ccirello/supervisor/anon.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
universalAnonSvcMu sync.Mutex
|
||||
universalAnonSvc uint64
|
||||
)
|
||||
|
||||
func getUniversalAnonSvc() uint64 {
|
||||
universalAnonSvcMu.Lock()
|
||||
universalAnonSvc++
|
||||
v := universalAnonSvc
|
||||
universalAnonSvcMu.Unlock()
|
||||
return v
|
||||
}
|
||||
|
||||
type anonymousService struct {
|
||||
id uint64
|
||||
f func(context.Context)
|
||||
}
|
||||
|
||||
func newAnonymousService(f func(context.Context)) *anonymousService {
|
||||
return &anonymousService{
|
||||
id: getUniversalAnonSvc(),
|
||||
f: f,
|
||||
}
|
||||
}
|
||||
|
||||
func (a anonymousService) Serve(ctx context.Context) {
|
||||
a.f(ctx)
|
||||
}
|
||||
|
||||
func (a anonymousService) String() string {
|
||||
return fmt.Sprintf("anonymous service %d", a.id)
|
||||
}
|
||||
78
vendor/github.com/ccirello/supervisor/doc.go
generated
vendored
Normal file
78
vendor/github.com/ccirello/supervisor/doc.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Package supervisor provides supervisor trees for Go applications.
|
||||
|
||||
This package implements supervisor trees, similar to what Erlang runtime offers.
|
||||
It is built on top of context package, with all of its advantages, namely the
|
||||
possibility trickle down context-related values and cancelation signals.
|
||||
|
||||
A supervisor tree can be composed either of services or other supervisors - each
|
||||
supervisor can have its own set of configurations. Any instance of
|
||||
supervisor.Service can be added to a tree.
|
||||
|
||||
Supervisor
|
||||
├─▶ Supervisor (if one service dies, only one is restarted)
|
||||
│ ├─▶ Service
|
||||
│ └─▶ Service
|
||||
├─▶ Group (if one service dies, all others are restarted too)
|
||||
│ └─▶ Service
|
||||
│ Service
|
||||
│ Service
|
||||
└─▶ Service
|
||||
|
||||
Example:
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"cirello.io/supervisor"
|
||||
"context"
|
||||
)
|
||||
|
||||
type Simpleservice int
|
||||
|
||||
func (s *Simpleservice) String() string {
|
||||
return fmt.Sprintf("simple service %d", int(*s))
|
||||
}
|
||||
|
||||
func (s *Simpleservice) Serve(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
fmt.Println("do something...")
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main(){
|
||||
svc := Simpleservice(1)
|
||||
supervisor.Add(&svc)
|
||||
|
||||
// Simply, if not special context is needed:
|
||||
// supervisor.Serve()
|
||||
// Or, using context.Context to propagate behavior:
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func(){
|
||||
<-c
|
||||
fmt.Println("halting supervisor...")
|
||||
cancel()
|
||||
}()
|
||||
supervisor.ServeContext(ctx)
|
||||
}
|
||||
|
||||
TheJerf's blog post about Suture is a very good and helpful read to understand
|
||||
how this package has been implemented.
|
||||
|
||||
This is package is inspired by github.com/thejerf/suture
|
||||
|
||||
http://www.jerf.org/iri/post/2930
|
||||
*/
|
||||
package supervisor // import "cirello.io/supervisor"
|
||||
69
vendor/github.com/ccirello/supervisor/group.go
generated
vendored
Normal file
69
vendor/github.com/ccirello/supervisor/group.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Group is a superset of Supervisor datastructure responsible for offering a
|
||||
// supervisor tree whose all services are restarted whenever one of them fail or
|
||||
// is restarted. It assumes that all services rely on each other. It implements
|
||||
// Service, therefore it can be nested if necessary either with other Group or
|
||||
// Supervisor. When passing the Group around, remind to do it as reference
|
||||
// (&group).
|
||||
type Group struct {
|
||||
*Supervisor
|
||||
}
|
||||
|
||||
// Serve starts the Group tree. It can be started only once at a time. If
|
||||
// stopped (canceled), it can be restarted. In case of concurrent calls, it will
|
||||
// hang until the current call is completed.
|
||||
func (g *Group) Serve(ctx context.Context) {
|
||||
if g.Supervisor == nil {
|
||||
panic("Supervisor missing for this Group.")
|
||||
}
|
||||
g.Supervisor.prepare()
|
||||
restartCtx, cancel := context.WithCancel(ctx)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
processingFailure bool
|
||||
)
|
||||
processFailure := func() {
|
||||
mu.Lock()
|
||||
if processingFailure {
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
processingFailure = true
|
||||
mu.Unlock()
|
||||
|
||||
if !g.shouldRestart() {
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
|
||||
g.mu.Lock()
|
||||
g.log("halting all services after failure")
|
||||
for _, c := range g.terminations {
|
||||
c()
|
||||
}
|
||||
|
||||
g.cancelations = make(map[string]context.CancelFunc)
|
||||
g.mu.Unlock()
|
||||
|
||||
go func() {
|
||||
g.log("waiting for all services termination")
|
||||
g.runningServices.Wait()
|
||||
g.log("waiting for all services termination - completed")
|
||||
|
||||
mu.Lock()
|
||||
processingFailure = false
|
||||
mu.Unlock()
|
||||
|
||||
g.log("triggering group restart")
|
||||
g.added <- struct{}{}
|
||||
}()
|
||||
}
|
||||
serve(g.Supervisor, restartCtx, processFailure)
|
||||
}
|
||||
101
vendor/github.com/ccirello/supervisor/helpers.go
generated
vendored
Normal file
101
vendor/github.com/ccirello/supervisor/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func serve(s *Supervisor, ctx context.Context, processFailure processFailure) {
|
||||
s.running.Lock()
|
||||
defer s.running.Unlock()
|
||||
|
||||
startServices(s, ctx, processFailure)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-s.added:
|
||||
startServices(s, ctx, processFailure)
|
||||
|
||||
case <-ctx.Done():
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
<-ctx.Done()
|
||||
|
||||
wg.Wait()
|
||||
s.runningServices.Wait()
|
||||
|
||||
s.mu.Lock()
|
||||
s.cancelations = make(map[string]context.CancelFunc)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func startServices(s *Supervisor, supervisorCtx context.Context, processFailure processFailure) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, name := range s.svcorder {
|
||||
svc := s.services[name]
|
||||
if _, ok := s.cancelations[name]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
terminateCtx, terminate := context.WithCancel(supervisorCtx)
|
||||
s.cancelations[name] = terminate
|
||||
s.terminations[name] = terminate
|
||||
|
||||
go func(name string, svc service) {
|
||||
s.runningServices.Add(1)
|
||||
defer s.runningServices.Done()
|
||||
wg.Done()
|
||||
retry := true
|
||||
for retry {
|
||||
retry = svc.svctype == Permanent
|
||||
s.log(fmt.Sprintf("%s starting", name))
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
s.log(fmt.Sprintf("%s panic: %v", name, r))
|
||||
retry = svc.svctype == Permanent || svc.svctype == Transient
|
||||
}
|
||||
}()
|
||||
ctx, cancel := context.WithCancel(terminateCtx)
|
||||
s.mu.Lock()
|
||||
s.cancelations[name] = cancel
|
||||
s.mu.Unlock()
|
||||
svc.svc.Serve(ctx)
|
||||
}()
|
||||
if retry {
|
||||
processFailure()
|
||||
}
|
||||
select {
|
||||
case <-terminateCtx.Done():
|
||||
s.log(fmt.Sprintf("%s restart aborted (terminated)", name))
|
||||
return
|
||||
case <-supervisorCtx.Done():
|
||||
s.log(fmt.Sprintf("%s restart aborted (supervisor halted)", name))
|
||||
return
|
||||
default:
|
||||
}
|
||||
switch svc.svctype {
|
||||
case Temporary:
|
||||
s.log(fmt.Sprintf("%s exited (temporary)", name))
|
||||
return
|
||||
case Transient:
|
||||
s.log(fmt.Sprintf("%s exited (transient)", name))
|
||||
default:
|
||||
s.log(fmt.Sprintf("%s exited (permanent)", name))
|
||||
}
|
||||
}
|
||||
}(name, svc)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
108
vendor/github.com/ccirello/supervisor/stdsupervisor.go
generated
vendored
Normal file
108
vendor/github.com/ccirello/supervisor/stdsupervisor.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultSupervisor Supervisor
|
||||
running sync.Mutex
|
||||
defaultContext = context.Background()
|
||||
)
|
||||
|
||||
func init() {
|
||||
defaultSupervisor.Name = "default supervisor"
|
||||
}
|
||||
|
||||
// Add inserts new service into the default supervisor. If it is already
|
||||
// started, it will launch it automatically.
|
||||
func Add(service Service) {
|
||||
defaultSupervisor.Add(service)
|
||||
}
|
||||
|
||||
// AddFunc inserts new anonymous service into the default supervisor. If it is
|
||||
// already started, it will launch it automatically.
|
||||
func AddFunc(f func(context.Context)) {
|
||||
defaultSupervisor.AddFunc(f)
|
||||
}
|
||||
|
||||
// Cancelations return a list of services names of default supervisor and their
|
||||
// cancelation calls. These calls be used to force a service restart.
|
||||
func Cancelations() map[string]context.CancelFunc {
|
||||
return defaultSupervisor.Cancelations()
|
||||
}
|
||||
|
||||
// Remove stops the service in the default supervisor tree and remove from it.
|
||||
func Remove(name string) {
|
||||
defaultSupervisor.Remove(name)
|
||||
}
|
||||
|
||||
// Serve starts the default supervisor tree. It can be started only once at a
|
||||
// time. If stopped (canceled), it can be restarted. In case of concurrent
|
||||
// calls, it will hang until the current call is completed. It can run only one
|
||||
// per package-level. If you need many, use
|
||||
// supervisor.Supervisor/supervisor.Group instead of supervisor.Serve{,Group}.
|
||||
// After its conclusion, its internal state is reset.
|
||||
func Serve() {
|
||||
running.Lock()
|
||||
defaultSupervisor.Serve(defaultContext)
|
||||
defaultSupervisor.reset()
|
||||
defaultContext = context.Background()
|
||||
running.Unlock()
|
||||
}
|
||||
|
||||
// ServeContext starts the default upervisor tree with a custom context.Context.
|
||||
// It can be started only once at a time. If stopped (canceled), it can be
|
||||
// restarted. In case of concurrent calls, it will hang until the current call
|
||||
// is completed. After its conclusion, its internal state is reset.
|
||||
func ServeContext(ctx context.Context) {
|
||||
running.Lock()
|
||||
defaultSupervisor.Serve(ctx)
|
||||
defaultSupervisor.reset()
|
||||
running.Unlock()
|
||||
}
|
||||
|
||||
// ServeGroup starts the default supervisor tree within a Group. It can be
|
||||
// started only once at a time. If stopped (canceled), it can be restarted.
|
||||
// In case of concurrent calls, it will hang until the current call is
|
||||
// completed. It can run only one per package-level. If you need many, use
|
||||
// supervisor.ServeContext/supervisor.ServeGroupContext instead of
|
||||
// supervisor.Serve/supervisor.ServeGroup. After its conclusion, its internal
|
||||
// state is reset.
|
||||
func ServeGroup() {
|
||||
running.Lock()
|
||||
var group Group
|
||||
group.Supervisor = &defaultSupervisor
|
||||
group.Serve(defaultContext)
|
||||
defaultSupervisor.reset()
|
||||
defaultContext = context.Background()
|
||||
running.Unlock()
|
||||
}
|
||||
|
||||
// ServeGroupContext starts the defaultSupervisor tree with a custom
|
||||
// context.Context. It can be started only once at a time. If stopped
|
||||
// (canceled), it can be restarted. In case of concurrent calls, it will hang
|
||||
// until the current call is completed. After its conclusion, its internal
|
||||
// state is reset.
|
||||
func ServeGroupContext(ctx context.Context) {
|
||||
running.Lock()
|
||||
var group Group
|
||||
group.Supervisor = &defaultSupervisor
|
||||
group.Serve(ctx)
|
||||
defaultSupervisor.reset()
|
||||
running.Unlock()
|
||||
}
|
||||
|
||||
// Services return a list of services of default supervisor.
|
||||
func Services() map[string]Service {
|
||||
return defaultSupervisor.Services()
|
||||
}
|
||||
|
||||
// SetDefaultContext allows to change the context used for supervisor.Serve()
|
||||
// and supervisor.ServeGroup().
|
||||
func SetDefaultContext(ctx context.Context) {
|
||||
running.Lock()
|
||||
defaultContext = ctx
|
||||
running.Unlock()
|
||||
}
|
||||
147
vendor/github.com/ccirello/supervisor/stdsupervisor_example_test.go
generated
vendored
Normal file
147
vendor/github.com/ccirello/supervisor/stdsupervisor_example_test.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package supervisor_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"cirello.io/supervisor"
|
||||
)
|
||||
|
||||
type Simpleservice struct {
|
||||
id int
|
||||
sync.WaitGroup
|
||||
}
|
||||
|
||||
func (s *Simpleservice) Serve(ctx context.Context) {
|
||||
fmt.Println(s.String())
|
||||
s.Done()
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func (s *Simpleservice) String() string {
|
||||
return fmt.Sprintf("simple service %d", s.id)
|
||||
}
|
||||
|
||||
func ExampleAddFunc() {
|
||||
var svc sync.WaitGroup
|
||||
|
||||
svc.Add(1)
|
||||
supervisor.AddFunc(func(ctx context.Context) {
|
||||
fmt.Println("anonymous service")
|
||||
svc.Done()
|
||||
<-ctx.Done()
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
supervisor.ServeContext(ctx)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
svc.Wait()
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
// output:
|
||||
// anonymous service
|
||||
}
|
||||
|
||||
func ExampleServeContext() {
|
||||
svc := &Simpleservice{id: 1}
|
||||
svc.Add(1)
|
||||
supervisor.Add(svc)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
supervisor.ServeContext(ctx)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
svc.Wait()
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
// output:
|
||||
// simple service 1
|
||||
}
|
||||
|
||||
func ExampleServeGroupContext() {
|
||||
svc1 := &Simpleservice{id: 1}
|
||||
svc1.Add(1)
|
||||
supervisor.Add(svc1)
|
||||
svc2 := &Simpleservice{id: 2}
|
||||
svc2.Add(1)
|
||||
supervisor.Add(svc2)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
supervisor.ServeGroupContext(ctx)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
svc1.Wait()
|
||||
svc2.Wait()
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
// unordered output:
|
||||
// simple service 1
|
||||
// simple service 2
|
||||
}
|
||||
|
||||
func ExampleServe() {
|
||||
svc := &Simpleservice{id: 1}
|
||||
svc.Add(1)
|
||||
supervisor.Add(svc)
|
||||
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
supervisor.SetDefaultContext(ctx)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
supervisor.Serve()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
svc.Wait()
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
// output:
|
||||
// simple service 1
|
||||
}
|
||||
|
||||
func ExampleServeGroup() {
|
||||
svc1 := &Simpleservice{id: 1}
|
||||
svc1.Add(1)
|
||||
supervisor.Add(svc1)
|
||||
svc2 := &Simpleservice{id: 2}
|
||||
svc2.Add(1)
|
||||
supervisor.Add(svc2)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
supervisor.SetDefaultContext(ctx)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
supervisor.ServeGroup()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
svc1.Wait()
|
||||
svc2.Wait()
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
// unordered output:
|
||||
// simple service 1
|
||||
// simple service 2
|
||||
}
|
||||
109
vendor/github.com/ccirello/supervisor/stdsupervisor_test.go
generated
vendored
Normal file
109
vendor/github.com/ccirello/supervisor/stdsupervisor_test.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddFunc(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
universalAnonSvcMu.Lock()
|
||||
oldCount := universalAnonSvc
|
||||
universalAnonSvc = 0
|
||||
universalAnonSvcMu.Unlock()
|
||||
defer func() {
|
||||
universalAnonSvcMu.Lock()
|
||||
universalAnonSvc = oldCount
|
||||
universalAnonSvcMu.Unlock()
|
||||
}()
|
||||
|
||||
var (
|
||||
runCount int
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
wg.Add(1)
|
||||
AddFunc(func(ctx context.Context) {
|
||||
runCount++
|
||||
wg.Done()
|
||||
<-ctx.Done()
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go ServeContext(ctx)
|
||||
|
||||
svcs := Services()
|
||||
if _, ok := svcs["anonymous service 1"]; !ok {
|
||||
t.Errorf("anonymous service was not found in service list")
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
cancel()
|
||||
|
||||
if runCount == 0 {
|
||||
t.Errorf("anonymous service should have been started")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultSupevisorAndGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := &holdingservice{id: 1}
|
||||
svc.Add(1)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
SetDefaultContext(ctx)
|
||||
Add(svc)
|
||||
if len(defaultSupervisor.services) != 1 {
|
||||
t.Errorf("%s should have been added", svc.String())
|
||||
}
|
||||
|
||||
Remove(svc.String())
|
||||
if len(defaultSupervisor.services) != 0 {
|
||||
t.Errorf("%s should have been removed. services: %#v", svc.String(), defaultSupervisor.services)
|
||||
}
|
||||
|
||||
Add(svc)
|
||||
|
||||
svcs := Services()
|
||||
if _, ok := svcs[svc.String()]; !ok {
|
||||
t.Errorf("%s should have been found", svc.String())
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
Serve()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
svc.Wait()
|
||||
|
||||
cs := Cancelations()
|
||||
if _, ok := cs[svc.String()]; !ok {
|
||||
t.Errorf("%s's cancelation should have been found. %#v", svc.String(), cs)
|
||||
}
|
||||
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
SetDefaultContext(ctx)
|
||||
svc.Add(1)
|
||||
Add(svc)
|
||||
if len(defaultSupervisor.services) != 1 {
|
||||
t.Errorf("%s should have been added", svc.String())
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
ServeGroup()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
svc.Wait()
|
||||
cancel()
|
||||
wg.Wait()
|
||||
}
|
||||
245
vendor/github.com/ccirello/supervisor/supervisor.go
generated
vendored
Normal file
245
vendor/github.com/ccirello/supervisor/supervisor.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
svc Service
|
||||
svctype ServiceType
|
||||
}
|
||||
|
||||
type processFailure func()
|
||||
|
||||
// AlwaysRestart adjusts the supervisor to never halt in face of failures.
|
||||
const AlwaysRestart = -1
|
||||
|
||||
// ServiceType defines the restart strategy for a service.
|
||||
type ServiceType int
|
||||
|
||||
const (
|
||||
// Permanent services are always restarted
|
||||
Permanent ServiceType = iota
|
||||
// Transient services are restarted only when panic.
|
||||
Transient
|
||||
// Temporary services are never restarted.
|
||||
Temporary
|
||||
)
|
||||
|
||||
// Service is the public interface expected by a Supervisor.
|
||||
//
|
||||
// This will be internally named after the result of fmt.Stringer, if available.
|
||||
// Otherwise it is going to use an internal representation for the service
|
||||
// name.
|
||||
type Service interface {
|
||||
// Serve is called by a Supervisor to start the service. It expects the
|
||||
// service to honor the passed context and its lifetime. Observe
|
||||
// <-ctx.Done() and ctx.Err(). If the service is stopped by anything
|
||||
// but the Supervisor, it will get started again. Be careful with shared
|
||||
// state among restarts.
|
||||
Serve(ctx context.Context)
|
||||
}
|
||||
|
||||
// Supervisor is the basic datastructure responsible for offering a supervisor
|
||||
// tree. It implements Service, therefore it can be nested if necessary. When
|
||||
// passing the Supervisor around, remind to do it as reference (&supervisor).
|
||||
// Once the supervisor is started, its attributes are frozen.
|
||||
type Supervisor struct {
|
||||
// Name for this supervisor tree, used for logging.
|
||||
Name string
|
||||
name string
|
||||
|
||||
// MaxRestarts is the number of maximum restarts given MaxTime. If more
|
||||
// than MaxRestarts occur in the last MaxTime, then the supervisor
|
||||
// stops all services and halts. Set this to AlwaysRestart to prevent
|
||||
// supervisor halt.
|
||||
MaxRestarts int
|
||||
maxrestarts int
|
||||
|
||||
|
||||
// MaxTime is the time period on which the internal restart count will
|
||||
// be reset.
|
||||
MaxTime time.Duration
|
||||
maxtime time.Duration
|
||||
|
||||
// Log is a replaceable function used for overall logging.
|
||||
// Default: log.Printf.
|
||||
Log func(interface{})
|
||||
log func(interface{})
|
||||
|
||||
// indicates that supervisor is ready for use.
|
||||
prepared sync.Once
|
||||
|
||||
// signals that a new service has just been added, so the started
|
||||
// supervisor picks it up.
|
||||
added chan struct{}
|
||||
|
||||
// indicates that supervisor has running services.
|
||||
running sync.Mutex
|
||||
runningServices sync.WaitGroup
|
||||
|
||||
mu sync.Mutex
|
||||
svcorder []string // order in which services must be started
|
||||
services map[string]service // added services
|
||||
cancelations map[string]context.CancelFunc // each service cancelation
|
||||
terminations map[string]context.CancelFunc // each service termination call
|
||||
lastRestart time.Time
|
||||
restarts int
|
||||
}
|
||||
|
||||
func (s *Supervisor) prepare() {
|
||||
s.prepared.Do(s.reset)
|
||||
}
|
||||
|
||||
func (s *Supervisor) reset() {
|
||||
s.mu.Lock()
|
||||
if s.Name == "" {
|
||||
s.Name = "supervisor"
|
||||
}
|
||||
if s.MaxRestarts == 0 {
|
||||
s.MaxRestarts = 5
|
||||
}
|
||||
if s.MaxTime == 0 {
|
||||
s.MaxTime = 15 * time.Second
|
||||
}
|
||||
if s.Log == nil {
|
||||
s.Log = func(msg interface{}) {
|
||||
log.Printf("%s: %v", s.Name, msg)
|
||||
}
|
||||
}
|
||||
|
||||
s.name = s.Name
|
||||
s.maxrestarts = s.MaxRestarts
|
||||
s.maxtime = s.MaxTime
|
||||
s.log = s.Log
|
||||
|
||||
s.added = make(chan struct{})
|
||||
s.cancelations = make(map[string]context.CancelFunc)
|
||||
s.services = make(map[string]service)
|
||||
s.terminations = make(map[string]context.CancelFunc)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Supervisor) shouldRestart() bool {
|
||||
if s.maxrestarts == AlwaysRestart {
|
||||
return true
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if time.Since(s.lastRestart) > s.maxtime {
|
||||
s.restarts = 0
|
||||
}
|
||||
s.lastRestart = time.Now()
|
||||
s.restarts++
|
||||
return s.restarts < s.maxrestarts
|
||||
}
|
||||
|
||||
// Cancelations return a list of services names and their cancelation calls.
|
||||
// These calls be used to force a service restart.
|
||||
func (s *Supervisor) Cancelations() map[string]context.CancelFunc {
|
||||
svclist := make(map[string]context.CancelFunc)
|
||||
s.mu.Lock()
|
||||
for k, v := range s.cancelations {
|
||||
svclist[k] = v
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return svclist
|
||||
}
|
||||
|
||||
// Add inserts into the Supervisor tree a new permanent service. If the
|
||||
// Supervisor is already started, it will start it automatically.
|
||||
func (s *Supervisor) Add(service Service) {
|
||||
s.AddService(service, Permanent)
|
||||
}
|
||||
|
||||
// AddFunc inserts into the Supervisor tree a new permanent anonymous service.
|
||||
// If the Supervisor is already started, it will start it automatically.
|
||||
func (s *Supervisor) AddFunc(f func(context.Context)) {
|
||||
s.AddService(newAnonymousService(f), Permanent)
|
||||
}
|
||||
|
||||
// AddService inserts into the Supervisor tree a new service of ServiceType. If
|
||||
// the Supervisor is already started, it will start it automatically. If the
|
||||
// same service is added more than once, it will reset its backoff mechanism and
|
||||
// force a service restart.
|
||||
func (s *Supervisor) AddService(svc Service, svctype ServiceType) {
|
||||
s.prepare()
|
||||
|
||||
name := fmt.Sprintf("%s", svc)
|
||||
s.mu.Lock()
|
||||
s.services[name] = service{
|
||||
svc: svc,
|
||||
svctype: svctype,
|
||||
}
|
||||
s.svcorder = append(s.svcorder, name)
|
||||
s.mu.Unlock()
|
||||
|
||||
go func() {
|
||||
s.added <- struct{}{}
|
||||
}()
|
||||
}
|
||||
|
||||
// Remove stops the service in the Supervisor tree and remove from it.
|
||||
func (s *Supervisor) Remove(name string) {
|
||||
s.prepare()
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if _, ok := s.services[name]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
delete(s.services, name)
|
||||
|
||||
for i, n := range s.svcorder {
|
||||
if name == n {
|
||||
s.svcorder = append(s.svcorder[:i], s.svcorder[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if c, ok := s.terminations[name]; ok {
|
||||
delete(s.terminations, name)
|
||||
c()
|
||||
}
|
||||
|
||||
if _, ok := s.cancelations[name]; ok {
|
||||
delete(s.cancelations, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve starts the Supervisor tree. It can be started only once at a time. If
|
||||
// stopped (canceled), it can be restarted. In case of concurrent calls, it will
|
||||
// hang until the current call is completed.
|
||||
func (s *Supervisor) Serve(ctx context.Context) {
|
||||
s.prepare()
|
||||
restartCtx, cancel := context.WithCancel(ctx)
|
||||
processFailure := func() {
|
||||
restart := s.shouldRestart()
|
||||
if !restart {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
serve(s, restartCtx, processFailure)
|
||||
}
|
||||
|
||||
// Services return a list of services
|
||||
func (s *Supervisor) Services() map[string]Service {
|
||||
svclist := make(map[string]Service)
|
||||
s.mu.Lock()
|
||||
for k, v := range s.services {
|
||||
svclist[k] = v.svc
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return svclist
|
||||
}
|
||||
|
||||
func (s *Supervisor) String() string {
|
||||
s.prepare()
|
||||
return s.name
|
||||
}
|
||||
42
vendor/github.com/ccirello/supervisor/supervisor_example_test.go
generated
vendored
Normal file
42
vendor/github.com/ccirello/supervisor/supervisor_example_test.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package supervisor_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"cirello.io/supervisor"
|
||||
)
|
||||
|
||||
func ExampleSupervisor() {
|
||||
var supervisor supervisor.Supervisor
|
||||
|
||||
svc := &Simpleservice{id: 1}
|
||||
svc.Add(1)
|
||||
supervisor.Add(svc)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
go supervisor.Serve(ctx)
|
||||
|
||||
svc.Wait()
|
||||
cancel()
|
||||
}
|
||||
|
||||
func ExampleGroup() {
|
||||
supervisor := supervisor.Group{
|
||||
Supervisor: &supervisor.Supervisor{},
|
||||
}
|
||||
|
||||
svc1 := &Simpleservice{id: 1}
|
||||
svc1.Add(1)
|
||||
supervisor.Add(svc1)
|
||||
svc2 := &Simpleservice{id: 2}
|
||||
svc2.Add(1)
|
||||
supervisor.Add(svc2)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
go supervisor.Serve(ctx)
|
||||
|
||||
svc1.Wait()
|
||||
svc2.Wait()
|
||||
cancel()
|
||||
}
|
||||
1072
vendor/github.com/ccirello/supervisor/supervisor_test.go
generated
vendored
Normal file
1072
vendor/github.com/ccirello/supervisor/supervisor_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user