From d88819119924a6ac5c1f65746ff54ba79dabc272 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 16:07:46 +0300 Subject: [PATCH 01/18] Initial commit of the Print graph feature --- endure.go | 12 +++++ go.mod | 4 +- go.sum | 14 ++++++ structures/service_graph.go | 49 +++++++++++++++++++++ tests/happy_scenarios/happyScenario_test.go | 2 +- 5 files changed, 79 insertions(+), 2 deletions(-) diff --git a/endure.go b/endure.go index 491c916..1f7af96 100644 --- a/endure.go +++ b/endure.go @@ -54,6 +54,8 @@ type Endure struct { retry bool maxInterval time.Duration initialInterval time.Duration + // option to print resulted (before init) graph + print bool mutex *sync.RWMutex @@ -168,6 +170,12 @@ func SetBackoffTimes(initialInterval time.Duration, maxInterval time.Duration) O } } +func PrintGraph(print bool) Options { + return func(endure *Endure) { + endure.print = print + } +} + // Depender depends the dependencies // name is a name of the dependency, for example - S2 // vertex is a value -> pointer to the structure @@ -216,6 +224,10 @@ func (e *Endure) Init() error { return errors.E(op, errors.Init, err) } + if e.print { + e.graph.Print() + } + // we should build init list in the reverse order sorted, err := structures.TopologicalSort(e.graph.Vertices) if err != nil { diff --git a/go.mod b/go.mod index a8144d6..3c15de9 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.15 require ( github.com/cenkalti/backoff/v4 v4.1.0 + github.com/goccy/go-graphviz v0.0.8 github.com/stretchr/testify v1.6.1 go.uber.org/zap v1.16.0 -) \ No newline at end of file + golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect +) diff --git a/go.sum b/go.sum index f8cc907..e49e868 100644 --- a/go.sum +++ b/go.sum @@ -2,17 +2,26 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc= github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/goccy/go-graphviz v0.0.8 h1:hYQikvj368s8+rmfzFOZeiCXvSocGH7rfAyLTOy/7AM= +github.com/goccy/go-graphviz v0.0.8/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -35,6 +44,11 @@ go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= +golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= diff --git a/structures/service_graph.go b/structures/service_graph.go index 3f99a6c..d67d74d 100644 --- a/structures/service_graph.go +++ b/structures/service_graph.go @@ -1,9 +1,12 @@ package structures import ( + "bytes" "errors" "fmt" "reflect" + + "github.com/goccy/go-graphviz" ) type Kind int @@ -332,6 +335,52 @@ func (g *Graph) FindProviders(depID string) []*Vertex { return ret } +func (g *Graph) Print() { + gr := graphviz.New() + graph, err := gr.Graph() + if err != nil { + panic(err) + } + + for i := 0; i < len(g.Vertices); i++ { + if len(g.Vertices[i].Dependencies) > 0 { + for j := 0; j < len(g.Vertices[i].Dependencies); j++ { + n, err := graph.CreateNode(g.Vertices[i].ID) + if err != nil { + panic(err) + } + + m, err := graph.CreateNode(g.Vertices[i].Dependencies[j].ID) + if err != nil { + panic(err) + } + + e, err := graph.CreateEdge("", n, m) + if err != nil { + panic(err) + } + e.SetLabel("") + } + } + } + + var buf bytes.Buffer + if err := gr.Render(graph, graphviz.PNG, &buf); err != nil { + panic(err) + } + + // write to file directly + if err := gr.RenderFilename(graph, graphviz.PNG, "./graph.png"); err != nil { + panic(err) + } +} + +func (g *Graph) print(deps []*Vertex) { + for i := 0; i < len(deps); i++ { + + } +} + type Vertices []*Vertex func (v Vertices) Len() int { diff --git a/tests/happy_scenarios/happyScenario_test.go b/tests/happy_scenarios/happyScenario_test.go index a0e671f..df9c361 100644 --- a/tests/happy_scenarios/happyScenario_test.go +++ b/tests/happy_scenarios/happyScenario_test.go @@ -57,7 +57,7 @@ func testLog(t *testing.T, level endure.Level) { } func TestEndure_Init_OK(t *testing.T) { - c, err := endure.NewContainer(endure.DebugLevel) + c, err := endure.NewContainer(endure.DebugLevel, endure.PrintGraph(true)) assert.NoError(t, err) assert.NoError(t, c.Register(&plugin4.S4{})) From a7c9730628e31836f797169c9c1054c1a77df505 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 16:09:04 +0300 Subject: [PATCH 02/18] Remove debug code --- tests/disabled_vertices/disabled_vertices_test.go | 2 +- tests/happy_scenarios/happyScenario_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/disabled_vertices/disabled_vertices_test.go b/tests/disabled_vertices/disabled_vertices_test.go index 5d54b67..5af2c8d 100644 --- a/tests/disabled_vertices/disabled_vertices_test.go +++ b/tests/disabled_vertices/disabled_vertices_test.go @@ -86,7 +86,7 @@ func TestDisabledViaInterface(t *testing.T) { } func TestDisabledRoot(t *testing.T) { - cont, err := endure.NewContainer(endure.DebugLevel, endure.RetryOnFail(true)) + cont, err := endure.NewContainer(endure.DebugLevel, endure.PrintGraph(true)) if err != nil { t.Fatal(err) } diff --git a/tests/happy_scenarios/happyScenario_test.go b/tests/happy_scenarios/happyScenario_test.go index df9c361..a0e671f 100644 --- a/tests/happy_scenarios/happyScenario_test.go +++ b/tests/happy_scenarios/happyScenario_test.go @@ -57,7 +57,7 @@ func testLog(t *testing.T, level endure.Level) { } func TestEndure_Init_OK(t *testing.T) { - c, err := endure.NewContainer(endure.DebugLevel, endure.PrintGraph(true)) + c, err := endure.NewContainer(endure.DebugLevel) assert.NoError(t, err) assert.NoError(t, c.Register(&plugin4.S4{})) From 32d5afe5b86d437a2de775cede51b6e9573bd933 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 16:24:09 +0300 Subject: [PATCH 03/18] Remove print_graph options from the tests --- tests/disabled_vertices/disabled_vertices_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/disabled_vertices/disabled_vertices_test.go b/tests/disabled_vertices/disabled_vertices_test.go index 5af2c8d..f0fd9b0 100644 --- a/tests/disabled_vertices/disabled_vertices_test.go +++ b/tests/disabled_vertices/disabled_vertices_test.go @@ -86,7 +86,7 @@ func TestDisabledViaInterface(t *testing.T) { } func TestDisabledRoot(t *testing.T) { - cont, err := endure.NewContainer(endure.DebugLevel, endure.PrintGraph(true)) + cont, err := endure.NewContainer(endure.DebugLevel) if err != nil { t.Fatal(err) } From 144e971f45925d0384434f91648be22a61bac2f7 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 16:28:09 +0300 Subject: [PATCH 04/18] Remove panics, add warning to the log in case of failed print --- endure.go | 10 ++++++++-- structures/service_graph.go | 13 +++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/endure.go b/endure.go index 1f7af96..8be2909 100644 --- a/endure.go +++ b/endure.go @@ -220,12 +220,18 @@ func (e *Endure) Register(vertex interface{}) error { func (e *Endure) Init() error { const op = errors.Op("Init") // traverse the graph - if err := e.addEdges(); err != nil { + err := e.addEdges() + if err != nil { return errors.E(op, errors.Init, err) } + // if failed - continue, just send warning to a user + // print is not critical if e.print { - e.graph.Print() + err = e.graph.Print() + if err != nil { + e.logger.Warn("failed to print the graph", zap.Error(err)) + } } // we should build init list in the reverse order diff --git a/structures/service_graph.go b/structures/service_graph.go index d67d74d..6844716 100644 --- a/structures/service_graph.go +++ b/structures/service_graph.go @@ -335,7 +335,7 @@ func (g *Graph) FindProviders(depID string) []*Vertex { return ret } -func (g *Graph) Print() { +func (g *Graph) Print() error { gr := graphviz.New() graph, err := gr.Graph() if err != nil { @@ -347,17 +347,17 @@ func (g *Graph) Print() { for j := 0; j < len(g.Vertices[i].Dependencies); j++ { n, err := graph.CreateNode(g.Vertices[i].ID) if err != nil { - panic(err) + return err } m, err := graph.CreateNode(g.Vertices[i].Dependencies[j].ID) if err != nil { - panic(err) + return err } e, err := graph.CreateEdge("", n, m) if err != nil { - panic(err) + return err } e.SetLabel("") } @@ -366,13 +366,14 @@ func (g *Graph) Print() { var buf bytes.Buffer if err := gr.Render(graph, graphviz.PNG, &buf); err != nil { - panic(err) + return err } // write to file directly if err := gr.RenderFilename(graph, graphviz.PNG, "./graph.png"); err != nil { - panic(err) + return err } + return nil } func (g *Graph) print(deps []*Vertex) { From 47b7b241e96b2a3642bb7056439b93f8d75ec4b9 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 17:08:23 +0300 Subject: [PATCH 05/18] Eliminate Configure and Stop from tests --- container.go | 13 --- endure.go | 21 +--- examples/sample_1/modules/db/db_layer.go | 11 +- internal.go | 109 +----------------- tests/backoff/plugins/plugin1/plugin1.go | 11 -- tests/backoff/plugins/plugin3/plugin3.go | 8 -- tests/backoff/plugins/plugin4/plugin4.go | 8 -- tests/happy_scenarios/plugin2/plugin2.go | 8 -- tests/happy_scenarios/plugin4/plugin4.go | 8 -- tests/happy_scenarios/plugin5/plugin5.go | 8 -- tests/happy_scenarios/plugin6/plugin6.go | 8 -- tests/happy_scenarios/plugin7/plugin7.go | 8 -- .../plugin1/foo2_value.go | 8 -- .../plugin2/foo4_value.go | 8 -- tests/interfaces/plugins/plugin1/plugin1.go | 8 -- tests/interfaces/plugins/plugin2/plugin2.go | 8 -- tests/stress/InitErr/foo2_init_err.go | 8 -- tests/stress/ServeErr/foo4ServeError.go | 8 -- tests/stress/ServeErr/plugin2.go | 8 -- tests/stress/ServeErr/plugin5.go | 8 -- tests/stress/ServeRetryErr/plugin2.go | 8 -- tests/stress/ServeRetryErr/plugin4.go | 8 -- tests/stress/ServeRetryErr/plugin5.go | 8 -- tests/stress/ServeRetryErr/plugin6.go | 8 -- 24 files changed, 8 insertions(+), 309 deletions(-) diff --git a/container.go b/container.go index 6649ab3..36f1a22 100644 --- a/container.go +++ b/container.go @@ -3,12 +3,6 @@ package endure // InitMethodName is the function name for the reflection const InitMethodName = "Init" -// ConfigureMethodName -const ConfigureMethodName = "Configure" - -// CloseMethodName -const CloseMethodName = "Close" - // ServeMethodName const ServeMethodName = "Serve" @@ -37,13 +31,6 @@ type result struct { } type ( - // used to gracefully stop and configure the plugins - graceful interface { - // Configure is used when we need to make preparation and wait for all services till Serve - Configure() error - // Close frees resources allocated by the service - Close() error - } // this is the main service interface with should implement every plugin Service interface { // Serve diff --git a/endure.go b/endure.go index 8be2909..bf6a7ca 100644 --- a/endure.go +++ b/endure.go @@ -269,33 +269,16 @@ func (e *Endure) Serve() (<-chan *Result, error) { const op = errors.Op("Serve") e.startMainThread() + // simple check that we have at least one vertex in the graph to Serve atLeastOne := false - // call configure - nCopy := e.runList.Head - // DEPRECATED TODO + nCopy := e.runList.Head for nCopy != nil { if nCopy.Vertex.IsDisabled { nCopy = nCopy.Next continue } atLeastOne = true - // deprecated - err := e.configure(nCopy) - if err != nil { - e.logger.Error("backoff failed", zap.String("vertex id", nCopy.Vertex.ID), zap.Error(err)) - return nil, errors.E(op, errors.Serve, err) - } - - nCopy = nCopy.Next - } - - nCopy = e.runList.Head - for nCopy != nil { - if nCopy.Vertex.IsDisabled { - nCopy = nCopy.Next - continue - } err := e.serve(nCopy) if err != nil { return nil, errors.E(op, errors.Serve, err) diff --git a/examples/sample_1/modules/db/db_layer.go b/examples/sample_1/modules/db/db_layer.go index 1430b18..eb340de 100644 --- a/examples/sample_1/modules/db/db_layer.go +++ b/examples/sample_1/modules/db/db_layer.go @@ -37,17 +37,8 @@ func (db *DB) Serve() chan error { return errCh } -func (db *DB) Configure() error { - db.logger.SuperLogToStdOut("configuring DB") - return nil -} - -func (db *DB) Close() error { - return db.boltdb.Close() -} - func (db *DB) Stop() error { - return nil + return db.boltdb.Close() } func (db *DB) Name() string { diff --git a/internal.go b/internal.go index f579be4..1c5675b 100644 --- a/internal.go +++ b/internal.go @@ -19,7 +19,7 @@ import ( func (e *Endure) init(vertex *structures.Vertex) error { const op = errors.Op("internal_init") if vertex.IsDisabled { - e.logger.Warn("vertex is disable due to error.Disabled in the Init func or due to Endure decision (Disabled dependency)", zap.String("vertex id", vertex.ID)) + e.logger.Warn("vertex is disabled due to error.Disabled in the Init func or due to Endure decision (Disabled dependency)", zap.String("vertex id", vertex.ID)) return nil } // we already checked the Interface satisfaction @@ -420,9 +420,8 @@ func (e *Endure) traverseCallProvider(vertex *structures.Vertex, in []reflect.Va /* Algorithm is the following (all steps executing in the topological order): -1. Call Configure() on all services -- OPTIONAL -2. Call Serve() on all services -- MUST -3. Call Stop() on all services -- MUST +2. Call Serve() on all services -- OPTIONAL +3. Call Stop() on all services -- OPTIONAL 4. Call Clear() on a services, which implements this interface -- OPTIONAL */ // call configure on the node @@ -446,23 +445,6 @@ func (e *Endure) callServeFn(vertex *structures.Vertex, in []reflect.Value) *res return nil } -/* -callConfigureFn invoke Configure() error method -*/ -func (e *Endure) callConfigureFn(vertex *structures.Vertex, in []reflect.Value) error { - const op = errors.Op("internal_call_configure_function") - m, _ := reflect.TypeOf(vertex.Iface).MethodByName(ConfigureMethodName) - ret := m.Func.Call(in) - res := ret[0].Interface() - if res != nil { - if e, ok := res.(error); ok && e != nil { - return errors.E(op, errors.FunctionCall, e) - } - return errors.E(op, errors.FunctionCall, errors.Str("unknown error occurred during the function call")) - } - return nil -} - func (e *Endure) stop(vID string) error { const op = errors.Op("internal_stop") vertex := e.graph.GetVertex(vID) @@ -476,14 +458,6 @@ func (e *Endure) stop(vID string) error { e.logger.Error("error occurred during the callStopFn", zap.String("vertex id", vertex.ID)) return errors.E(op, errors.FunctionCall, err) } - - if reflect.TypeOf(vertex.Iface).Implements(reflect.TypeOf((*graceful)(nil)).Elem()) { - err = e.callCloseFn(vertex.ID, in) - if err != nil { - e.logger.Error("error occurred during the callCloseFn", zap.String("vertex id", vertex.ID)) - return errors.E(op, errors.FunctionCall, err) - } - } } return nil @@ -505,23 +479,6 @@ func (e *Endure) callStopFn(vertex *structures.Vertex, in []reflect.Value) error return nil } -// TODO add stack to the all of the log events -func (e *Endure) callCloseFn(vID string, in []reflect.Value) error { - const op = errors.Op("internal_call_close_function") - v := e.graph.GetVertex(vID) - // Call Close() method, which returns only error (or nil) - m, _ := reflect.TypeOf(v.Iface).MethodByName(CloseMethodName) - ret := m.Func.Call(in) - rErr := ret[0].Interface() - if rErr != nil { - if e, ok := rErr.(error); ok && e != nil { - return errors.E(op, errors.FunctionCall, e) - } - return errors.E(op, errors.FunctionCall, errors.Str("unknown error occurred during the function call")) - } - return nil -} - func (e *Endure) sendStopSignal(sorted []*structures.Vertex) { for _, v := range sorted { // get result by vertex ID @@ -602,12 +559,11 @@ func (e *Endure) forceExitHandler(ctx context.Context, data chan *structures.Dll } } -// serve run configure (if exist) and callServeFn for each node and put the results in the map +// serve run calls callServeFn for each node and put the results in the map func (e *Endure) serve(n *structures.DllNode) error { const op = errors.Op("internal_serve") // check if type implements serve, if implements, call serve if reflect.TypeOf(n.Vertex.Iface).Implements(reflect.TypeOf((*Service)(nil)).Elem()) { - // handle all configure in := make([]reflect.Value, 0, 1) // add service itself in = append(in, reflect.ValueOf(n.Vertex.Iface)) @@ -719,22 +675,6 @@ func (e *Endure) retryHandler(res *result) { headCopy = headCopy.Next } - // call configure - headCopy = affectedRunList.Head - for headCopy != nil { - berr := backoff.Retry(e.backoffConfigure(headCopy), b) - if berr != nil { - e.userResultsCh <- &Result{ - Error: errors.E(op, errors.FunctionCall, errors.Errorf("error during the Init function call")), - VertexID: headCopy.Vertex.ID, - } - e.logger.Error("backoff failed", zap.String("vertex id", headCopy.Vertex.ID), zap.Error(berr)) - return - } - - headCopy = headCopy.Next - } - // call serve headCopy = affectedRunList.Head for headCopy != nil { @@ -824,43 +764,4 @@ func (e *Endure) backoffInit(v *structures.Vertex) func() error { return nil } -} - -func (e *Endure) configure(n *structures.DllNode) error { - const op = errors.Op("internal_configure") - if n.Vertex.IsDisabled { - return nil - } - // handle all configure - in := make([]reflect.Value, 0, 1) - // add service itself - in = append(in, reflect.ValueOf(n.Vertex.Iface)) - - if reflect.TypeOf(n.Vertex.Iface).Implements(reflect.TypeOf((*graceful)(nil)).Elem()) { - err := e.callConfigureFn(n.Vertex, in) - if err != nil { - return errors.E(op, errors.FunctionCall, err) - } - } - - return nil -} - -func (e *Endure) backoffConfigure(n *structures.DllNode) func() error { - return func() error { - // handle all configure - in := make([]reflect.Value, 0, 1) - // add service itself - in = append(in, reflect.ValueOf(n.Vertex.Iface)) - - if reflect.TypeOf(n.Vertex.Iface).Implements(reflect.TypeOf((*graceful)(nil)).Elem()) { - err := e.callConfigureFn(n.Vertex, in) - if err != nil { - e.logger.Error("error configuring the vertex", zap.String("vertex id", n.Vertex.ID), zap.Error(err)) - return err - } - } - - return nil - } -} +} \ No newline at end of file diff --git a/tests/backoff/plugins/plugin1/plugin1.go b/tests/backoff/plugins/plugin1/plugin1.go index 8884a16..d222a86 100644 --- a/tests/backoff/plugins/plugin1/plugin1.go +++ b/tests/backoff/plugins/plugin1/plugin1.go @@ -14,17 +14,6 @@ func (f *Plugin1) Init() error { return nil } -func (f *Plugin1) Configure() error { - if number > 1 { - return errors.New("test error when num > 1") - } - return nil -} - -func (f *Plugin1) Close() error { - return nil -} - func (f *Plugin1) Serve() chan error { errCh := make(chan error, 1) go func() { diff --git a/tests/backoff/plugins/plugin3/plugin3.go b/tests/backoff/plugins/plugin3/plugin3.go index b868d67..cc440f7 100644 --- a/tests/backoff/plugins/plugin3/plugin3.go +++ b/tests/backoff/plugins/plugin3/plugin3.go @@ -14,14 +14,6 @@ func (f *Plugin3) Init() error { return nil } -func (f *Plugin3) Configure() error { - return nil -} - -func (f *Plugin3) Close() error { - return nil -} - func (f *Plugin3) Serve() chan error { errCh := make(chan error, 1) number2 += 1 diff --git a/tests/backoff/plugins/plugin4/plugin4.go b/tests/backoff/plugins/plugin4/plugin4.go index 8437c67..aa2215f 100644 --- a/tests/backoff/plugins/plugin4/plugin4.go +++ b/tests/backoff/plugins/plugin4/plugin4.go @@ -14,14 +14,6 @@ func (f *Plugin4) Init() error { return nil } -func (f *Plugin4) Configure() error { - return nil -} - -func (f *Plugin4) Close() error { - return nil -} - func (f *Plugin4) Serve() chan error { errCh := make(chan error, 1) if number3 == 0 { diff --git a/tests/happy_scenarios/plugin2/plugin2.go b/tests/happy_scenarios/plugin2/plugin2.go index 2eeb55a..0869dab 100644 --- a/tests/happy_scenarios/plugin2/plugin2.go +++ b/tests/happy_scenarios/plugin2/plugin2.go @@ -20,14 +20,6 @@ func (s2 *S2) CreateDB() (DB, error) { return DB{}, nil } -func (s2 *S2) Close() error { - return nil -} - -func (s2 *S2) Configure() error { - return nil -} - func (s2 *S2) Serve() chan error { errCh := make(chan error, 1) return errCh diff --git a/tests/happy_scenarios/plugin4/plugin4.go b/tests/happy_scenarios/plugin4/plugin4.go index dc863a2..3643131 100644 --- a/tests/happy_scenarios/plugin4/plugin4.go +++ b/tests/happy_scenarios/plugin4/plugin4.go @@ -43,10 +43,6 @@ func (s *S4) AddService(svc plugin5.S5) error { return nil } -func (s *S4) Configure() error { - return nil -} - func (s *S4) Serve() chan error { errCh := make(chan error, 1) @@ -55,10 +51,6 @@ func (s *S4) Serve() chan error { return errCh } -func (s *S4) Close() error { - return nil -} - func (s *S4) Stop() error { return nil } diff --git a/tests/happy_scenarios/plugin5/plugin5.go b/tests/happy_scenarios/plugin5/plugin5.go index e5c0173..aedd01e 100644 --- a/tests/happy_scenarios/plugin5/plugin5.go +++ b/tests/happy_scenarios/plugin5/plugin5.go @@ -12,19 +12,11 @@ func (s *S5) Init() error { return nil } -func (s *S5) Configure() error { - return nil -} - func (s *S5) Serve() chan error { errCh := make(chan error, 1) return errCh } -func (s *S5) Close() error { - return nil -} - func (s *S5) Stop() error { return nil } diff --git a/tests/happy_scenarios/plugin6/plugin6.go b/tests/happy_scenarios/plugin6/plugin6.go index 35346b8..9b824de 100644 --- a/tests/happy_scenarios/plugin6/plugin6.go +++ b/tests/happy_scenarios/plugin6/plugin6.go @@ -16,19 +16,11 @@ func (s *S6Interface) Init() error { return nil } -func (s *S6Interface) Configure() error { - return nil -} - func (s *S6Interface) Serve() chan error { errCh := make(chan error, 1) return errCh } -func (s *S6Interface) Close() error { - return nil -} - func (s *S6Interface) Stop() error { return nil } diff --git a/tests/happy_scenarios/plugin7/plugin7.go b/tests/happy_scenarios/plugin7/plugin7.go index e6bcd6f..456989f 100644 --- a/tests/happy_scenarios/plugin7/plugin7.go +++ b/tests/happy_scenarios/plugin7/plugin7.go @@ -12,14 +12,6 @@ func (s1 *Plugin7) Serve() chan error { return errCh } -func (s1 *Plugin7) Configure() error { - return nil -} - -func (s1 *Plugin7) Close() error { - return nil -} - func (s1 *Plugin7) Stop() error { return nil } diff --git a/tests/happy_scenarios/provided_value_but_need_pointer/plugin1/foo2_value.go b/tests/happy_scenarios/provided_value_but_need_pointer/plugin1/foo2_value.go index 8ff18bd..6bdac17 100644 --- a/tests/happy_scenarios/provided_value_but_need_pointer/plugin1/foo2_value.go +++ b/tests/happy_scenarios/provided_value_but_need_pointer/plugin1/foo2_value.go @@ -9,14 +9,6 @@ func (s2 *Plugin1) Init(db *plugin2.DBV) error { return nil } -func (s2 *Plugin1) Close() error { - return nil -} - -func (s2 *Plugin1) Configure() error { - return nil -} - func (s2 *Plugin1) Serve() chan error { errCh := make(chan error, 1) return errCh diff --git a/tests/happy_scenarios/provided_value_but_need_pointer/plugin2/foo4_value.go b/tests/happy_scenarios/provided_value_but_need_pointer/plugin2/foo4_value.go index e3c72cc..860703e 100644 --- a/tests/happy_scenarios/provided_value_but_need_pointer/plugin2/foo4_value.go +++ b/tests/happy_scenarios/provided_value_but_need_pointer/plugin2/foo4_value.go @@ -26,19 +26,11 @@ func (s *Plugin2) CreateAnotherDB() (DBV, error) { }, nil } -func (s *Plugin2) Configure() error { - return nil -} - func (s *Plugin2) Serve() chan error { errCh := make(chan error, 1) return errCh } -func (s *Plugin2) Close() error { - return nil -} - func (s *Plugin2) Stop() error { return nil } diff --git a/tests/interfaces/plugins/plugin1/plugin1.go b/tests/interfaces/plugins/plugin1/plugin1.go index 56fae37..6dce639 100644 --- a/tests/interfaces/plugins/plugin1/plugin1.go +++ b/tests/interfaces/plugins/plugin1/plugin1.go @@ -13,19 +13,11 @@ func (s *Plugin1) Init(foow plugin6.FooWriter) error { return nil } -func (s *Plugin1) Configure() error { - return nil -} - func (s *Plugin1) Serve() chan error { errCh := make(chan error, 1) return errCh } -func (s *Plugin1) Close() error { - return nil -} - func (s *Plugin1) Stop() error { return nil } diff --git a/tests/interfaces/plugins/plugin2/plugin2.go b/tests/interfaces/plugins/plugin2/plugin2.go index bf13155..7b77cdd 100644 --- a/tests/interfaces/plugins/plugin2/plugin2.go +++ b/tests/interfaces/plugins/plugin2/plugin2.go @@ -16,19 +16,11 @@ func (s *Plugin2) Init() error { return nil } -func (s *Plugin2) Configure() error { - return nil -} - func (s *Plugin2) Serve() chan error { errCh := make(chan error, 1) return errCh } -func (s *Plugin2) Close() error { - return nil -} - func (s *Plugin2) Stop() error { return nil } diff --git a/tests/stress/InitErr/foo2_init_err.go b/tests/stress/InitErr/foo2_init_err.go index 01fe376..c638129 100644 --- a/tests/stress/InitErr/foo2_init_err.go +++ b/tests/stress/InitErr/foo2_init_err.go @@ -7,14 +7,6 @@ func (s2 *S2Err) Init() error { return nil } -func (s2 *S2Err) Close() error { - return nil -} - -func (s2 *S2Err) Configure() error { - return nil -} - func (s2 *S2Err) Serve() chan error { errCh := make(chan error, 1) return errCh diff --git a/tests/stress/ServeErr/foo4ServeError.go b/tests/stress/ServeErr/foo4ServeError.go index d5e3dbb..51f8d70 100644 --- a/tests/stress/ServeErr/foo4ServeError.go +++ b/tests/stress/ServeErr/foo4ServeError.go @@ -28,10 +28,6 @@ func (s *S4ServeError) CreateAnotherDB() (*FOO4DB, error) { }, nil } -func (s *S4ServeError) Configure() error { - return nil -} - func (s *S4ServeError) Serve() chan error { errCh := make(chan error, 1) go func() { @@ -40,10 +36,6 @@ func (s *S4ServeError) Serve() chan error { return errCh } -func (s *S4ServeError) Close() error { - return nil -} - func (s *S4ServeError) Stop() error { return nil } diff --git a/tests/stress/ServeErr/plugin2.go b/tests/stress/ServeErr/plugin2.go index c6402a8..47a3417 100644 --- a/tests/stress/ServeErr/plugin2.go +++ b/tests/stress/ServeErr/plugin2.go @@ -18,14 +18,6 @@ func (s2 *S2) CreateDB() (DB, error) { return DB{}, nil } -func (s2 *S2) Close() error { - return nil -} - -func (s2 *S2) Configure() error { - return nil -} - func (s2 *S2) Serve() chan error { errCh := make(chan error, 1) return errCh diff --git a/tests/stress/ServeErr/plugin5.go b/tests/stress/ServeErr/plugin5.go index 9d3221a..a0bcf48 100644 --- a/tests/stress/ServeErr/plugin5.go +++ b/tests/stress/ServeErr/plugin5.go @@ -12,19 +12,11 @@ func (s *S5) Init() error { return nil } -func (s *S5) Configure() error { - return nil -} - func (s *S5) Serve() chan error { errCh := make(chan error, 1) return errCh } -func (s *S5) Close() error { - return nil -} - func (s *S5) Stop() error { return nil } diff --git a/tests/stress/ServeRetryErr/plugin2.go b/tests/stress/ServeRetryErr/plugin2.go index 4262179..4e29587 100644 --- a/tests/stress/ServeRetryErr/plugin2.go +++ b/tests/stress/ServeRetryErr/plugin2.go @@ -10,14 +10,6 @@ func (s2 *S2) Init() error { return nil } -func (s2 *S2) Close() error { - return nil -} - -func (s2 *S2) Configure() error { - return nil -} - func (s2 *S2) Serve() chan error { errCh := make(chan error, 1) return errCh diff --git a/tests/stress/ServeRetryErr/plugin4.go b/tests/stress/ServeRetryErr/plugin4.go index d458103..4cb9f6c 100644 --- a/tests/stress/ServeRetryErr/plugin4.go +++ b/tests/stress/ServeRetryErr/plugin4.go @@ -8,19 +8,11 @@ func (s *S4) Init(foo5 S5) error { return nil } -func (s *S4) Configure() error { - return nil -} - func (s *S4) Serve() chan error { errCh := make(chan error, 1) return errCh } -func (s *S4) Close() error { - return nil -} - func (s *S4) Stop() error { return nil } diff --git a/tests/stress/ServeRetryErr/plugin5.go b/tests/stress/ServeRetryErr/plugin5.go index b8b0a91..a47607e 100644 --- a/tests/stress/ServeRetryErr/plugin5.go +++ b/tests/stress/ServeRetryErr/plugin5.go @@ -12,19 +12,11 @@ func (s *S5) Init() error { return nil } -func (s *S5) Configure() error { - return nil -} - func (s *S5) Serve() chan error { errCh := make(chan error, 1) return errCh } -func (s *S5) Close() error { - return nil -} - func (s *S5) Stop() error { return nil } diff --git a/tests/stress/ServeRetryErr/plugin6.go b/tests/stress/ServeRetryErr/plugin6.go index 070f468..4629212 100644 --- a/tests/stress/ServeRetryErr/plugin6.go +++ b/tests/stress/ServeRetryErr/plugin6.go @@ -21,14 +21,6 @@ func (s2 *S2ServeErr) Init(svc S4) error { return nil } -func (s2 *S2ServeErr) Close() error { - return nil -} - -func (s2 *S2ServeErr) Configure() error { - return nil -} - func (s2 *S2ServeErr) Serve() chan error { errCh := make(chan error, 1) go func() { From 6325351e101c564b07d8ce0e161adbe338b46e57 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 17:20:14 +0300 Subject: [PATCH 06/18] Format internal.go --- internal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal.go b/internal.go index 1c5675b..03133b0 100644 --- a/internal.go +++ b/internal.go @@ -764,4 +764,4 @@ func (e *Endure) backoffInit(v *structures.Vertex) func() error { return nil } -} \ No newline at end of file +} From 570a07ccbb00970e1bdf1df36de3a8a148bed9bb Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 17:28:32 +0300 Subject: [PATCH 07/18] Fix failing test --- tests/backoff/backoff_test.go | 1 + tests/backoff/plugins/plugin1/plugin1.go | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/backoff/backoff_test.go b/tests/backoff/backoff_test.go index a8cbead..2eed2a3 100644 --- a/tests/backoff/backoff_test.go +++ b/tests/backoff/backoff_test.go @@ -85,6 +85,7 @@ func TestEndure_MainThread_Backoff(t *testing.T) { if r.Error != nil { assert.NoError(t, c.Stop()) wg.Done() + return } } }() diff --git a/tests/backoff/plugins/plugin1/plugin1.go b/tests/backoff/plugins/plugin1/plugin1.go index d222a86..4ae115b 100644 --- a/tests/backoff/plugins/plugin1/plugin1.go +++ b/tests/backoff/plugins/plugin1/plugin1.go @@ -4,13 +4,10 @@ import ( "errors" ) -var number int = 0 - type Plugin1 struct { } func (f *Plugin1) Init() error { - number += 1 return nil } From cd2bb32e92c70b51a271f43ce372937bedff4a48 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 17:41:50 +0300 Subject: [PATCH 08/18] Remove Backoff test for configure --- tests/backoff/backoff_test.go | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/tests/backoff/backoff_test.go b/tests/backoff/backoff_test.go index 2eed2a3..5e5d7c9 100644 --- a/tests/backoff/backoff_test.go +++ b/tests/backoff/backoff_test.go @@ -66,36 +66,6 @@ func TestEndure_MainThread_Init_Backoff(t *testing.T) { assert.Greater(t, 11, after-now) } -func TestEndure_MainThread_Backoff(t *testing.T) { - c, err := endure.NewContainer(endure.DebugLevel, endure.RetryOnFail(true), endure.SetBackoffTimes(time.Second, time.Second*10)) - assert.NoError(t, err) - - assert.NoError(t, c.Register(&plugin1.Plugin1{})) - assert.NoError(t, c.Init()) - - res, err := c.Serve() - assert.NoError(t, err) - - wg := &sync.WaitGroup{} - - now := time.Now().Second() - wg.Add(1) - go func() { - for r := range res { - if r.Error != nil { - assert.NoError(t, c.Stop()) - wg.Done() - return - } - } - }() - wg.Wait() - - after := time.Now().Second() - // after - now should not be more than 15 as we set in NewContainer - assert.Greater(t, 15, after-now, "time") -} - func TestEndure_BackoffTimers(t *testing.T) { c, err := endure.NewContainer(endure.DebugLevel, endure.RetryOnFail(true), endure.SetBackoffTimes(time.Second, time.Second*5)) assert.NoError(t, err) From 758f0f88cf6c15feeb09573d90dc22d628ed29e1 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 17:42:14 +0300 Subject: [PATCH 09/18] Remove unused import --- tests/backoff/backoff_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/backoff/backoff_test.go b/tests/backoff/backoff_test.go index 5e5d7c9..bed09ee 100644 --- a/tests/backoff/backoff_test.go +++ b/tests/backoff/backoff_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/spiral/endure" - "github.com/spiral/endure/tests/backoff/plugins/plugin1" "github.com/spiral/endure/tests/backoff/plugins/plugin2" "github.com/spiral/endure/tests/backoff/plugins/plugin3" "github.com/spiral/endure/tests/backoff/plugins/plugin4" From 430a7058cc3218918202fba6fb9a7f0a08111052 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 17:48:28 +0300 Subject: [PATCH 10/18] Update codecov badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5160fa6..95fb6e4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

- + From 7aa9851d6d799290a2feab57c7fe26c6710666ca Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 17:49:50 +0300 Subject: [PATCH 11/18] Remove old codecov --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 95fb6e4..3712438 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ -

From c8cb85cef98801544463658c8cc12cdb4d5ff58d Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 19:48:53 +0300 Subject: [PATCH 12/18] Separate print_graph function in windows and linux packages --- endure.go | 2 +- structures/print_graph.go | 50 +++++++++++++++++++++++++++++++ structures/print_graph_windows.go | 49 ++++++++++++++++++++++++++++++ structures/service_graph.go | 50 ------------------------------- 4 files changed, 100 insertions(+), 51 deletions(-) create mode 100644 structures/print_graph.go create mode 100644 structures/print_graph_windows.go diff --git a/endure.go b/endure.go index bf6a7ca..928d0de 100644 --- a/endure.go +++ b/endure.go @@ -228,7 +228,7 @@ func (e *Endure) Init() error { // if failed - continue, just send warning to a user // print is not critical if e.print { - err = e.graph.Print() + err = structures.PrintGraph(e.graph.Vertices) if err != nil { e.logger.Warn("failed to print the graph", zap.Error(err)) } diff --git a/structures/print_graph.go b/structures/print_graph.go new file mode 100644 index 0000000..49005db --- /dev/null +++ b/structures/print_graph.go @@ -0,0 +1,50 @@ +// +build !windows + +package structures + +import ( + "bytes" + + "github.com/goccy/go-graphviz" +) + +func PrintGraph(vertices []*Vertex) error { + gr := graphviz.New() + graph, err := gr.Graph() + if err != nil { + panic(err) + } + + for i := 0; i < len(vertices); i++ { + if len(vertices[i].Dependencies) > 0 { + for j := 0; j < len(vertices[i].Dependencies); j++ { + n, err := graph.CreateNode(vertices[i].ID) + if err != nil { + return err + } + + m, err := graph.CreateNode(vertices[i].Dependencies[j].ID) + if err != nil { + return err + } + + e, err := graph.CreateEdge("", n, m) + if err != nil { + return err + } + e.SetLabel("") + } + } + } + + var buf bytes.Buffer + if err := gr.Render(graph, graphviz.PNG, &buf); err != nil { + return err + } + + // write to file directly + if err := gr.RenderFilename(graph, graphviz.PNG, "./graph.png"); err != nil { + return err + } + return nil +} diff --git a/structures/print_graph_windows.go b/structures/print_graph_windows.go new file mode 100644 index 0000000..434dfc9 --- /dev/null +++ b/structures/print_graph_windows.go @@ -0,0 +1,49 @@ +// +build windows +package structures + +import ( + "bytes" + + "github.com/goccy/go-graphviz" +) + +func PrintGraph(vertices *Vertices) error { + gr := graphviz.New() + graph, err := gr.Graph() + if err != nil { + panic(err) + } + + for i := 0; i < len(g.Vertices); i++ { + if len(g.Vertices[i].Dependencies) > 0 { + for j := 0; j < len(g.Vertices[i].Dependencies); j++ { + n, err := graph.CreateNode(g.Vertices[i].ID) + if err != nil { + return err + } + + m, err := graph.CreateNode(g.Vertices[i].Dependencies[j].ID) + if err != nil { + return err + } + + e, err := graph.CreateEdge("", n, m) + if err != nil { + return err + } + e.SetLabel("") + } + } + } + + var buf bytes.Buffer + if err := gr.Render(graph, graphviz.PNG, &buf); err != nil { + return err + } + + // write to file directly + if err := gr.RenderFilename(graph, graphviz.PNG, "./graph.png"); err != nil { + return err + } + return nil +} diff --git a/structures/service_graph.go b/structures/service_graph.go index 6844716..3f99a6c 100644 --- a/structures/service_graph.go +++ b/structures/service_graph.go @@ -1,12 +1,9 @@ package structures import ( - "bytes" "errors" "fmt" "reflect" - - "github.com/goccy/go-graphviz" ) type Kind int @@ -335,53 +332,6 @@ func (g *Graph) FindProviders(depID string) []*Vertex { return ret } -func (g *Graph) Print() error { - gr := graphviz.New() - graph, err := gr.Graph() - if err != nil { - panic(err) - } - - for i := 0; i < len(g.Vertices); i++ { - if len(g.Vertices[i].Dependencies) > 0 { - for j := 0; j < len(g.Vertices[i].Dependencies); j++ { - n, err := graph.CreateNode(g.Vertices[i].ID) - if err != nil { - return err - } - - m, err := graph.CreateNode(g.Vertices[i].Dependencies[j].ID) - if err != nil { - return err - } - - e, err := graph.CreateEdge("", n, m) - if err != nil { - return err - } - e.SetLabel("") - } - } - } - - var buf bytes.Buffer - if err := gr.Render(graph, graphviz.PNG, &buf); err != nil { - return err - } - - // write to file directly - if err := gr.RenderFilename(graph, graphviz.PNG, "./graph.png"); err != nil { - return err - } - return nil -} - -func (g *Graph) print(deps []*Vertex) { - for i := 0; i < len(deps); i++ { - - } -} - type Vertices []*Vertex func (v Vertices) Len() int { From 32062ade5829ff914cba89cfbe7c295fcb3e6fd7 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 19:50:50 +0300 Subject: [PATCH 13/18] Correct windows package --- structures/print_graph_windows.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/structures/print_graph_windows.go b/structures/print_graph_windows.go index 434dfc9..895b883 100644 --- a/structures/print_graph_windows.go +++ b/structures/print_graph_windows.go @@ -7,22 +7,22 @@ import ( "github.com/goccy/go-graphviz" ) -func PrintGraph(vertices *Vertices) error { +func PrintGraph(vertices []*Vertex) error { gr := graphviz.New() graph, err := gr.Graph() if err != nil { panic(err) } - for i := 0; i < len(g.Vertices); i++ { - if len(g.Vertices[i].Dependencies) > 0 { - for j := 0; j < len(g.Vertices[i].Dependencies); j++ { - n, err := graph.CreateNode(g.Vertices[i].ID) + for i := 0; i < len(vertices); i++ { + if len(vertices[i].Dependencies) > 0 { + for j := 0; j < len(vertices[i].Dependencies); j++ { + n, err := graph.CreateNode(vertices[i].ID) if err != nil { return err } - m, err := graph.CreateNode(g.Vertices[i].Dependencies[j].ID) + m, err := graph.CreateNode(vertices[i].Dependencies[j].ID) if err != nil { return err } From ac0cb28f55e880c43bafea64222de3abe36ac32c Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 19:57:32 +0300 Subject: [PATCH 14/18] Update graph printing for windows --- errors/errors.go | 3 +++ structures/print_graph.go | 14 +++++----- structures/print_graph_windows.go | 45 +++---------------------------- 3 files changed, 15 insertions(+), 47 deletions(-) diff --git a/errors/errors.go b/errors/errors.go index 3dafdc5..e80bef7 100755 --- a/errors/errors.go +++ b/errors/errors.go @@ -45,6 +45,7 @@ const ( ArgType Init Serve + Unsupported Disabled Traverse @@ -73,6 +74,8 @@ func (k Kind) String() string { return "Traverse error" case FunctionCall: return "Function call error" + case Unsupported: + return "Unsupported" default: return "UNDEF" } diff --git a/structures/print_graph.go b/structures/print_graph.go index 49005db..87d2ca0 100644 --- a/structures/print_graph.go +++ b/structures/print_graph.go @@ -6,13 +6,15 @@ import ( "bytes" "github.com/goccy/go-graphviz" + "github.com/spiral/endure/errors" ) func PrintGraph(vertices []*Vertex) error { + const op = errors.Op("print_graph") gr := graphviz.New() graph, err := gr.Graph() if err != nil { - panic(err) + return errors.E(op, err) } for i := 0; i < len(vertices); i++ { @@ -20,17 +22,17 @@ func PrintGraph(vertices []*Vertex) error { for j := 0; j < len(vertices[i].Dependencies); j++ { n, err := graph.CreateNode(vertices[i].ID) if err != nil { - return err + return errors.E(op, err) } m, err := graph.CreateNode(vertices[i].Dependencies[j].ID) if err != nil { - return err + return errors.E(op, err) } e, err := graph.CreateEdge("", n, m) if err != nil { - return err + return errors.E(op, err) } e.SetLabel("") } @@ -39,12 +41,12 @@ func PrintGraph(vertices []*Vertex) error { var buf bytes.Buffer if err := gr.Render(graph, graphviz.PNG, &buf); err != nil { - return err + return errors.E(op, err) } // write to file directly if err := gr.RenderFilename(graph, graphviz.PNG, "./graph.png"); err != nil { - return err + return errors.E(op, err) } return nil } diff --git a/structures/print_graph_windows.go b/structures/print_graph_windows.go index 895b883..c7c2e36 100644 --- a/structures/print_graph_windows.go +++ b/structures/print_graph_windows.go @@ -1,49 +1,12 @@ // +build windows + package structures import ( - "bytes" - - "github.com/goccy/go-graphviz" + "github.com/spiral/endure/errors" ) func PrintGraph(vertices []*Vertex) error { - gr := graphviz.New() - graph, err := gr.Graph() - if err != nil { - panic(err) - } - - for i := 0; i < len(vertices); i++ { - if len(vertices[i].Dependencies) > 0 { - for j := 0; j < len(vertices[i].Dependencies); j++ { - n, err := graph.CreateNode(vertices[i].ID) - if err != nil { - return err - } - - m, err := graph.CreateNode(vertices[i].Dependencies[j].ID) - if err != nil { - return err - } - - e, err := graph.CreateEdge("", n, m) - if err != nil { - return err - } - e.SetLabel("") - } - } - } - - var buf bytes.Buffer - if err := gr.Render(graph, graphviz.PNG, &buf); err != nil { - return err - } - - // write to file directly - if err := gr.RenderFilename(graph, graphviz.PNG, "./graph.png"); err != nil { - return err - } - return nil + const op = errors.Op("print_graph") + return errors.E(op, errors.Unsupported, errors.Str("windows currently not supported for this feature")) } From bda8ef209561cec97030ffdaa0a64cfd6df5efbf Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 20:28:06 +0300 Subject: [PATCH 15/18] Remove render graph in the buffer --- structures/print_graph.go | 7 ------- tests/disabled_vertices/graph.png | Bin 0 -> 16879 bytes 2 files changed, 7 deletions(-) create mode 100644 tests/disabled_vertices/graph.png diff --git a/structures/print_graph.go b/structures/print_graph.go index 87d2ca0..45f8343 100644 --- a/structures/print_graph.go +++ b/structures/print_graph.go @@ -3,8 +3,6 @@ package structures import ( - "bytes" - "github.com/goccy/go-graphviz" "github.com/spiral/endure/errors" ) @@ -39,11 +37,6 @@ func PrintGraph(vertices []*Vertex) error { } } - var buf bytes.Buffer - if err := gr.Render(graph, graphviz.PNG, &buf); err != nil { - return errors.E(op, err) - } - // write to file directly if err := gr.RenderFilename(graph, graphviz.PNG, "./graph.png"); err != nil { return errors.E(op, err) diff --git a/tests/disabled_vertices/graph.png b/tests/disabled_vertices/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..f54318dbc102b2c89e4efbad575ecfd94dd122ca GIT binary patch literal 16879 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 AfB*mh literal 0 HcmV?d00001 From e12a34d542f4fa9609c5c44fafeff9368819ab2d Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 21:18:23 +0300 Subject: [PATCH 16/18] Remove redundant check for the Disabled vertex --- internal.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal.go b/internal.go index 03133b0..8b53da5 100644 --- a/internal.go +++ b/internal.go @@ -41,10 +41,6 @@ Here we also track the Disabled vertices. If the vertex is disabled we should re */ func (e *Endure) callInitFn(init reflect.Method, vertex *structures.Vertex) error { const op = errors.Op("internal_call_init_function") - if vertex.IsDisabled { - e.logger.Warn("vertex disabled", zap.String("vertex id", vertex.ID)) - return nil - } in, err := e.findInitParameters(vertex) if err != nil { return errors.E(op, errors.FunctionCall, err) From a39a8957fe23c50e0b0b46cb8318415ead6435ee Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sat, 24 Oct 2020 21:29:35 +0300 Subject: [PATCH 17/18] Fix sample_1. Add graph.png representing sample_1 graph --- examples/sample_1/.gitignore | 2 + examples/sample_1/go.mod | 4 +- examples/sample_1/go.sum | 26 ++++++-- examples/sample_1/graph.png | Bin 0 -> 27076 bytes examples/sample_1/main.go | 9 +-- examples/sample_1/modules/db/db_layer.go | 2 +- examples/sample_1/modules/http/http_layer.go | 62 +++++++++---------- 7 files changed, 58 insertions(+), 47 deletions(-) create mode 100644 examples/sample_1/.gitignore create mode 100644 examples/sample_1/graph.png diff --git a/examples/sample_1/.gitignore b/examples/sample_1/.gitignore new file mode 100644 index 0000000..30526e3 --- /dev/null +++ b/examples/sample_1/.gitignore @@ -0,0 +1,2 @@ +# Created by .ignore support plugin (hsz.mobi) +examples_bolt_db \ No newline at end of file diff --git a/examples/sample_1/go.mod b/examples/sample_1/go.mod index 9c51d3d..ff5ffd9 100644 --- a/examples/sample_1/go.mod +++ b/examples/sample_1/go.mod @@ -1,11 +1,11 @@ module github.com/spiral/endure/examples/db_http_logger -go 1.14 +go 1.15 require ( github.com/NYTimes/gziphandler v1.1.1 github.com/gorilla/mux v1.7.4 github.com/rs/cors v1.7.0 - github.com/spiral/endure v1.0.0-beta6 + github.com/spiral/endure v1.0.0-beta10 go.etcd.io/bbolt v1.3.5 ) diff --git a/examples/sample_1/go.sum b/examples/sample_1/go.sum index ec6b307..eddc2bf 100644 --- a/examples/sample_1/go.sum +++ b/examples/sample_1/go.sum @@ -2,21 +2,32 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4eamEDs= -github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= +github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc= +github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/corona10/goimagehash v1.0.2 h1:pUfB0LnsJASMPGEZLj7tGY251vF+qLGqOgEP4rUs6kA= +github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/goccy/go-graphviz v0.0.8 h1:hYQikvj368s8+rmfzFOZeiCXvSocGH7rfAyLTOy/7AM= +github.com/goccy/go-graphviz v0.0.8/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY= +github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -25,8 +36,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/spiral/endure v1.0.0-beta6 h1:Kl8xHM/7sSCfpaBndJnJ1zaHVcEeug05OeiemauczwA= -github.com/spiral/endure v1.0.0-beta6/go.mod h1:ZqOMVUfVNlUKzhO3WpbqTqV4q4fKOJG8qCyqBYvmL6g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -41,10 +50,14 @@ go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= +golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -67,6 +80,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/sample_1/graph.png b/examples/sample_1/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..f9b71f67af7b5bbc7ef8a9e0c7f61f650f47db2b GIT binary patch literal 27076 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 literal 0 HcmV?d00001 diff --git a/examples/sample_1/main.go b/examples/sample_1/main.go index fdfb616..1e1acae 100644 --- a/examples/sample_1/main.go +++ b/examples/sample_1/main.go @@ -3,6 +3,7 @@ package main import ( "os" "os/signal" + "syscall" "github.com/spiral/endure" "github.com/spiral/endure/examples/db_http_logger/modules/db" @@ -13,7 +14,7 @@ import ( ) func main() { - container, err := endure.NewContainer(endure.DebugLevel, endure.RetryOnFail(true)) + container, err := endure.NewContainer(endure.DebugLevel, endure.RetryOnFail(true), endure.PrintGraph(true)) if err != nil { panic(err) } @@ -49,13 +50,13 @@ func main() { } // stop by CTRL+C - c := make(chan os.Signal) - signal.Notify(c, os.Interrupt) + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGKILL, syscall.SIGINT) for { select { case e := <-errCh: - println(e.Error.Err.Error()) + println(e.Error.Error()) er := container.Stop() if er != nil { panic(er) diff --git a/examples/sample_1/modules/db/db_layer.go b/examples/sample_1/modules/db/db_layer.go index eb340de..55f0187 100644 --- a/examples/sample_1/modules/db/db_layer.go +++ b/examples/sample_1/modules/db/db_layer.go @@ -21,7 +21,7 @@ type Repository interface { func (db *DB) Init(logger logger.SuperLogger) error { logger.SuperLogToStdOut("initializing DB") db.logger = logger - db.path = "./examples" + db.path = "./examples_bolt_db" bdb, err := bolt.Open(db.path, 0666, nil) if err != nil { return err diff --git a/examples/sample_1/modules/http/http_layer.go b/examples/sample_1/modules/http/http_layer.go index cf4f15d..cbb725b 100644 --- a/examples/sample_1/modules/http/http_layer.go +++ b/examples/sample_1/modules/http/http_layer.go @@ -38,6 +38,33 @@ func (h *Http) Init(db db.Repository, logger logger.SuperLogger) error { h.client = client h.db = db h.logger = logger + + h.logger.SuperLogToStdOut("configuring http") + r := mux.NewRouter() + + c := cors.New(cors.Options{ + AllowedOrigins: []string{"*"}, + AllowCredentials: true, + AllowedMethods: []string{"GET", "HEAD", "POST", "PUT", "OPTIONS"}, + AllowedHeaders: []string{"*"}, + }) + + r.Methods("POST").HandlerFunc(h.update).Path("/update") + r.Methods("POST").HandlerFunc(h.ddelete).Path("/delete") + r.Methods("GET").HandlerFunc(h.sselect).Path("/select") + r.Methods("POST").HandlerFunc(h.insert).Path("/insert") + + // just as sample, we put server here + server := &http.Server{ + Addr: ":8080", + Handler: c.Handler(r), + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + + h.server = server + return nil } @@ -73,36 +100,6 @@ func (h *Http) Stop() error { return nil } -func (h *Http) Configure() error { - h.logger.SuperLogToStdOut("configuring http") - r := mux.NewRouter() - - c := cors.New(cors.Options{ - AllowedOrigins: []string{"*"}, - AllowCredentials: true, - AllowedMethods: []string{"GET", "HEAD", "POST", "PUT", "OPTIONS"}, - AllowedHeaders: []string{"*"}, - }) - - r.Methods("POST").HandlerFunc(h.update).Path("/update") - r.Methods("POST").HandlerFunc(h.ddelete).Path("/delete") - r.Methods("GET").HandlerFunc(h.sselect).Path("/select") - r.Methods("POST").HandlerFunc(h.insert).Path("/insert") - - // just as sample, we put server here - server := &http.Server{ - Addr: ":8080", - Handler: c.Handler(r), - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - MaxHeaderBytes: 1 << 20, - } - - h.server = server - - return nil -} - func (h *Http) Depends() []interface{} { return []interface{}{ h.AddMiddleware, @@ -114,9 +111,6 @@ func (h *Http) AddMiddleware(m Middleware) error { return nil } -func (h *Http) Close() error { - return nil -} ///////////////// INFRA HANDLERS ////////////////////////////// @@ -137,7 +131,7 @@ func (h *Http) sselect(writer http.ResponseWriter, request *http.Request) { writer.WriteHeader(http.StatusOK) for i := 0; i < 10000; i++ { - writer.Write([]byte("TEST_GZIP_HEADERS")) + _, _ = writer.Write([]byte("TEST_GZIP_HEADERS")) } } From bc9d67c4e412d10109a387ec0e7d9cd3eadae429 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Sun, 25 Oct 2020 03:58:12 +0300 Subject: [PATCH 18/18] Update names --- endure.go | 16 ++++++++-------- examples/sample_1/go.mod | 4 +++- examples/sample_1/main.go | 2 +- internal.go | 2 +- structures/{service_graph.go => graph.go} | 0 .../{print_graph.go => visualize_graph.go} | 2 +- ...ph_windows.go => visualize_graph_windows.go} | 2 +- tests/happy_scenarios/graph.png | Bin 0 -> 34264 bytes tests/happy_scenarios/happyScenario_test.go | 2 +- 9 files changed, 16 insertions(+), 14 deletions(-) rename structures/{service_graph.go => graph.go} (100%) rename structures/{print_graph.go => visualize_graph.go} (95%) rename structures/{print_graph_windows.go => visualize_graph_windows.go} (83%) create mode 100644 tests/happy_scenarios/graph.png diff --git a/endure.go b/endure.go index 928d0de..bc783b6 100644 --- a/endure.go +++ b/endure.go @@ -54,8 +54,8 @@ type Endure struct { retry bool maxInterval time.Duration initialInterval time.Duration - // option to print resulted (before init) graph - print bool + // option to visualize resulted (before init) graph + visualize bool mutex *sync.RWMutex @@ -170,9 +170,9 @@ func SetBackoffTimes(initialInterval time.Duration, maxInterval time.Duration) O } } -func PrintGraph(print bool) Options { +func Visualize(print bool) Options { return func(endure *Endure) { - endure.print = print + endure.visualize = print } } @@ -226,11 +226,11 @@ func (e *Endure) Init() error { } // if failed - continue, just send warning to a user - // print is not critical - if e.print { - err = structures.PrintGraph(e.graph.Vertices) + // visualize is not critical + if e.visualize { + err = structures.Visualize(e.graph.Vertices) if err != nil { - e.logger.Warn("failed to print the graph", zap.Error(err)) + e.logger.Warn("failed to visualize the graph", zap.Error(err)) } } diff --git a/examples/sample_1/go.mod b/examples/sample_1/go.mod index ff5ffd9..b2b4146 100644 --- a/examples/sample_1/go.mod +++ b/examples/sample_1/go.mod @@ -6,6 +6,8 @@ require ( github.com/NYTimes/gziphandler v1.1.1 github.com/gorilla/mux v1.7.4 github.com/rs/cors v1.7.0 - github.com/spiral/endure v1.0.0-beta10 + github.com/spiral/endure v1.0.0-beta9 go.etcd.io/bbolt v1.3.5 ) + +replace github.com/spiral/endure v1.0.0-beta9 => /home/valery/Projects/opensource/spiral/endure diff --git a/examples/sample_1/main.go b/examples/sample_1/main.go index 1e1acae..f763c7d 100644 --- a/examples/sample_1/main.go +++ b/examples/sample_1/main.go @@ -14,7 +14,7 @@ import ( ) func main() { - container, err := endure.NewContainer(endure.DebugLevel, endure.RetryOnFail(true), endure.PrintGraph(true)) + container, err := endure.NewContainer(endure.DebugLevel, endure.RetryOnFail(true), endure.Visualize(true)) if err != nil { panic(err) } diff --git a/internal.go b/internal.go index 8b53da5..3467ef8 100644 --- a/internal.go +++ b/internal.go @@ -534,7 +534,7 @@ func (e *Endure) forceExitHandler(ctx context.Context, data chan *structures.Dll if err != nil { // TODO do not return until finished // just log the errors - // stack it in slice and if slice is not empty, print it ?? + // stack it in slice and if slice is not empty, visualize it ?? e.logger.Error("error occurred during the services stopping", zap.String("vertex id", node.Vertex.ID), zap.Error(err)) } // exit from vertex poller diff --git a/structures/service_graph.go b/structures/graph.go similarity index 100% rename from structures/service_graph.go rename to structures/graph.go diff --git a/structures/print_graph.go b/structures/visualize_graph.go similarity index 95% rename from structures/print_graph.go rename to structures/visualize_graph.go index 45f8343..a18c053 100644 --- a/structures/print_graph.go +++ b/structures/visualize_graph.go @@ -7,7 +7,7 @@ import ( "github.com/spiral/endure/errors" ) -func PrintGraph(vertices []*Vertex) error { +func Visualize(vertices []*Vertex) error { const op = errors.Op("print_graph") gr := graphviz.New() graph, err := gr.Graph() diff --git a/structures/print_graph_windows.go b/structures/visualize_graph_windows.go similarity index 83% rename from structures/print_graph_windows.go rename to structures/visualize_graph_windows.go index c7c2e36..afa0ffd 100644 --- a/structures/print_graph_windows.go +++ b/structures/visualize_graph_windows.go @@ -6,7 +6,7 @@ import ( "github.com/spiral/endure/errors" ) -func PrintGraph(vertices []*Vertex) error { +func Visualize(vertices []*Vertex) error { const op = errors.Op("print_graph") return errors.E(op, errors.Unsupported, errors.Str("windows currently not supported for this feature")) } diff --git a/tests/happy_scenarios/graph.png b/tests/happy_scenarios/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..0b30b68f9b8d5bed65ef7724c3e736f4ddfba13a GIT binary patch literal 34264 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 pV5?Dd4f$_#9q)q+Obm>Ri^te3l8FjT;~4ODQ$