Integrate BACKBEAT SDK and resolve KACHING license validation
Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
34
vendor/go.uber.org/dig/internal/digerror/errors.go
generated
vendored
Normal file
34
vendor/go.uber.org/dig/internal/digerror/errors.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2021 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package digerror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// BugPanicf panics with the provided message directing users to GitHub issues
|
||||
// creation page.
|
||||
func BugPanicf(msg string, args ...interface{}) {
|
||||
panic(fmt.Sprintf("It looks like you have found a bug in dig. "+
|
||||
"Please file an issue at https://github.com/uber-go/dig/issues/new "+
|
||||
"and provide the following message: "+
|
||||
msg, args...))
|
||||
}
|
||||
125
vendor/go.uber.org/dig/internal/digreflect/func.go
generated
vendored
Normal file
125
vendor/go.uber.org/dig/internal/digreflect/func.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package digreflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Func contains runtime information about a function.
|
||||
type Func struct {
|
||||
// Name of the function.
|
||||
Name string
|
||||
|
||||
// Name of the package in which this function is defined.
|
||||
Package string
|
||||
|
||||
// Path to the file in which this function is defined.
|
||||
File string
|
||||
|
||||
// Line number in the file at which this function is defined.
|
||||
Line int
|
||||
}
|
||||
|
||||
// String returns a string representation of the function.
|
||||
func (f *Func) String() string {
|
||||
return fmt.Sprint(f)
|
||||
}
|
||||
|
||||
// Format implements fmt.Formatter for Func, printing a single-line
|
||||
// representation for %v and a multi-line one for %+v.
|
||||
func (f *Func) Format(w fmt.State, c rune) {
|
||||
if w.Flag('+') && c == 'v' {
|
||||
// "path/to/package".MyFunction
|
||||
// path/to/file.go:42
|
||||
fmt.Fprintf(w, "%q.%v", f.Package, f.Name)
|
||||
fmt.Fprintf(w, "\n\t%v:%v", f.File, f.Line)
|
||||
} else {
|
||||
// "path/to/package".MyFunction (path/to/file.go:42)
|
||||
fmt.Fprintf(w, "%q.%v (%v:%v)", f.Package, f.Name, f.File, f.Line)
|
||||
}
|
||||
}
|
||||
|
||||
// InspectFunc inspects and returns runtime information about the given
|
||||
// function.
|
||||
func InspectFunc(function interface{}) *Func {
|
||||
fptr := reflect.ValueOf(function).Pointer()
|
||||
return InspectFuncPC(fptr)
|
||||
}
|
||||
|
||||
// InspectFuncPC inspects and returns runtime information about the function
|
||||
// at the given program counter address.
|
||||
func InspectFuncPC(pc uintptr) *Func {
|
||||
f := runtime.FuncForPC(pc)
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
pkgName, funcName := splitFuncName(f.Name())
|
||||
fileName, lineNum := f.FileLine(pc)
|
||||
return &Func{
|
||||
Name: funcName,
|
||||
Package: pkgName,
|
||||
File: fileName,
|
||||
Line: lineNum,
|
||||
}
|
||||
}
|
||||
|
||||
const _vendor = "/vendor/"
|
||||
|
||||
func splitFuncName(function string) (pname string, fname string) {
|
||||
if len(function) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// We have something like "path.to/my/pkg.MyFunction". If the function is
|
||||
// a closure, it is something like, "path.to/my/pkg.MyFunction.func1".
|
||||
|
||||
idx := 0
|
||||
|
||||
// Everything up to the first "." after the last "/" is the package name.
|
||||
// Everything after the "." is the full function name.
|
||||
if i := strings.LastIndex(function, "/"); i >= 0 {
|
||||
idx = i
|
||||
}
|
||||
if i := strings.Index(function[idx:], "."); i >= 0 {
|
||||
idx += i
|
||||
}
|
||||
pname, fname = function[:idx], function[idx+1:]
|
||||
|
||||
// The package may be vendored.
|
||||
if i := strings.Index(pname, _vendor); i > 0 {
|
||||
pname = pname[i+len(_vendor):]
|
||||
}
|
||||
|
||||
// Package names are URL-encoded to avoid ambiguity in the case where the
|
||||
// package name contains ".git". Otherwise, "foo/bar.git.MyFunction" would
|
||||
// mean that "git" is the top-level function and "MyFunction" is embedded
|
||||
// inside it.
|
||||
if unescaped, err := url.QueryUnescape(pname); err == nil {
|
||||
pname = unescaped
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
61
vendor/go.uber.org/dig/internal/dot/README.md
generated
vendored
Normal file
61
vendor/go.uber.org/dig/internal/dot/README.md
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# Dot
|
||||
|
||||
The dot module generates a DOT file representation of a dependency graph.
|
||||
|
||||
## Interpreting the graph
|
||||
|
||||
The graph should be read from left to right. The leftmost node in the graph (the root node) depends
|
||||
on its dependency tree to the right. An arrow from node_a to node_b in the graph means that node_b
|
||||
is consumed by node_a and that node_b is a parameter of node_a. The rendered graph holds the
|
||||
following kinds of nodes,
|
||||
|
||||
**Nodes:**
|
||||
|
||||
- *Constructors* [Rectangles]: Takes parameters and produces results.
|
||||
- *Results* [Ovals]: Results inside a constructor are produced by that constructor. Results are consumed
|
||||
directly by other constructors and/or part of a group of results.
|
||||
- *Groups* [Diamonds]: Represent value groups in [fx](https://godoc.org/go.uber.org/fx). Multiple results can form a group. Any
|
||||
result linked to a group by an edge are members of that group. A group is a collection of results.
|
||||
Groups can also be parameters of constructors.
|
||||
|
||||
**Edges:**
|
||||
|
||||
- *Solid Arrows*: An arrow from node_a to node_b means that node_b is a parameter of node_a and that
|
||||
node_a depends on node_b.
|
||||
- *Dashed Arrows*: A dashed arrow from node_a to node_b represents an optional dependency that node_a
|
||||
has on node_b.
|
||||
|
||||
**Graph Colors:**
|
||||
|
||||
- *Red*: Graph nodes are the root cause failures.
|
||||
- *Orange*: Graph nodes are the transitive failures.
|
||||
|
||||
## Testing and verifying changes
|
||||
|
||||
Unit tests and visualize golden tests are run with
|
||||
|
||||
```shell
|
||||
$ make test
|
||||
```
|
||||
|
||||
You can visualize the effect of your code changes by visualizing generated test graphs as pngs.
|
||||
|
||||
In the dig root directory, generate the graph DOT files with respect to your latest code changes.
|
||||
|
||||
```shell
|
||||
$ go test -generate
|
||||
```
|
||||
|
||||
Assuming that you have [graphviz](https://www.graphviz.org/) installed and are in the testdata directory,
|
||||
generate a png image representation of a graph for viewing.
|
||||
|
||||
```shell
|
||||
$ dot -Tpng ${name_of_dot_file_in_testdata}.dot -o ${name_of_dot_file_in_testdata}.png
|
||||
$ open ${name_of_dot_file_in_testdata}.png
|
||||
```
|
||||
|
||||
## Graph Pruning
|
||||
|
||||
If dot.Visualize is used to visualize an error graph, non-failing nodes are pruned out of the graph
|
||||
to make the error graph more readable to the user. Pruning increases readability since successful
|
||||
nodes clutter the graph and do not help the user debug errors.
|
||||
466
vendor/go.uber.org/dig/internal/dot/graph.go
generated
vendored
Normal file
466
vendor/go.uber.org/dig/internal/dot/graph.go
generated
vendored
Normal file
@@ -0,0 +1,466 @@
|
||||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package dot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ErrorType of a constructor or group is updated when they fail to build.
|
||||
type ErrorType int
|
||||
|
||||
const (
|
||||
noError ErrorType = iota
|
||||
rootCause
|
||||
transitiveFailure
|
||||
)
|
||||
|
||||
// CtorID is a unique numeric identifier for constructors.
|
||||
type CtorID uintptr
|
||||
|
||||
// Ctor encodes a constructor provided to the container for the DOT graph.
|
||||
type Ctor struct {
|
||||
Name string
|
||||
Package string
|
||||
File string
|
||||
Line int
|
||||
ID CtorID
|
||||
Params []*Param
|
||||
GroupParams []*Group
|
||||
Results []*Result
|
||||
ErrorType ErrorType
|
||||
}
|
||||
|
||||
// removeParam deletes the dependency on the provided result's nodeKey.
|
||||
// This is used to prune links to results of deleted constructors.
|
||||
func (c *Ctor) removeParam(k nodeKey) {
|
||||
var pruned []*Param
|
||||
for _, p := range c.Params {
|
||||
if k != p.nodeKey() {
|
||||
pruned = append(pruned, p)
|
||||
}
|
||||
}
|
||||
c.Params = pruned
|
||||
}
|
||||
|
||||
type nodeKey struct {
|
||||
t reflect.Type
|
||||
name string
|
||||
group string
|
||||
}
|
||||
|
||||
// Node is a single node in a graph and is embedded into Params and Results.
|
||||
type Node struct {
|
||||
Type reflect.Type
|
||||
Name string
|
||||
Group string
|
||||
}
|
||||
|
||||
func (n *Node) nodeKey() nodeKey {
|
||||
return nodeKey{t: n.Type, name: n.Name, group: n.Group}
|
||||
}
|
||||
|
||||
// Param is a parameter node in the graph. Parameters are the input to constructors.
|
||||
type Param struct {
|
||||
*Node
|
||||
|
||||
Optional bool
|
||||
}
|
||||
|
||||
// Result is a result node in the graph. Results are the output of constructors.
|
||||
type Result struct {
|
||||
*Node
|
||||
|
||||
// GroupIndex is added to differentiate grouped values from one another.
|
||||
// Since grouped values have the same type and group, their Node / string
|
||||
// representations are the same so we need indices to uniquely identify
|
||||
// the values.
|
||||
GroupIndex int
|
||||
}
|
||||
|
||||
// Group is a group node in the graph. Group represents an fx value group.
|
||||
type Group struct {
|
||||
// Type is the type of values in the group.
|
||||
Type reflect.Type
|
||||
Name string
|
||||
Results []*Result
|
||||
ErrorType ErrorType
|
||||
}
|
||||
|
||||
func (g *Group) nodeKey() nodeKey {
|
||||
return nodeKey{t: g.Type, group: g.Name}
|
||||
}
|
||||
|
||||
// TODO(rhang): Avoid linear search to discover group results that should be pruned.
|
||||
func (g *Group) removeResult(r *Result) {
|
||||
var pruned []*Result
|
||||
for _, rg := range g.Results {
|
||||
if r.GroupIndex != rg.GroupIndex {
|
||||
pruned = append(pruned, rg)
|
||||
}
|
||||
}
|
||||
g.Results = pruned
|
||||
}
|
||||
|
||||
// Graph is the DOT-format graph in a Container.
|
||||
type Graph struct {
|
||||
Ctors []*Ctor
|
||||
ctorMap map[CtorID]*Ctor
|
||||
|
||||
Groups []*Group
|
||||
groupMap map[nodeKey]*Group
|
||||
|
||||
consumers map[nodeKey][]*Ctor
|
||||
|
||||
Failed *FailedNodes
|
||||
}
|
||||
|
||||
// FailedNodes is the nodes that failed in the graph.
|
||||
type FailedNodes struct {
|
||||
// RootCauses is a list of the point of failures. They are the root causes
|
||||
// of failed invokes and can be either missing types (not provided) or
|
||||
// error types (error providing).
|
||||
RootCauses []*Result
|
||||
|
||||
// TransitiveFailures is the list of nodes that failed to build due to
|
||||
// missing/failed dependencies.
|
||||
TransitiveFailures []*Result
|
||||
|
||||
// ctors is a collection of failed constructors IDs that are populated as the graph is
|
||||
// traversed for errors.
|
||||
ctors map[CtorID]struct{}
|
||||
|
||||
// Groups is a collection of failed groupKeys that is populated as the graph is traversed
|
||||
// for errors.
|
||||
groups map[nodeKey]struct{}
|
||||
}
|
||||
|
||||
// NewGraph creates an empty graph.
|
||||
func NewGraph() *Graph {
|
||||
return &Graph{
|
||||
ctorMap: make(map[CtorID]*Ctor),
|
||||
groupMap: make(map[nodeKey]*Group),
|
||||
consumers: make(map[nodeKey][]*Ctor),
|
||||
Failed: &FailedNodes{
|
||||
ctors: make(map[CtorID]struct{}),
|
||||
groups: make(map[nodeKey]struct{}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewGroup creates a new group with information in the groupKey.
|
||||
func NewGroup(k nodeKey) *Group {
|
||||
return &Group{
|
||||
Type: k.t,
|
||||
Name: k.group,
|
||||
}
|
||||
}
|
||||
|
||||
// AddCtor adds the constructor with paramList and resultList into the graph.
|
||||
func (dg *Graph) AddCtor(c *Ctor, paramList []*Param, resultList []*Result) {
|
||||
var (
|
||||
params []*Param
|
||||
groupParams []*Group
|
||||
)
|
||||
|
||||
// Loop through the paramList to separate them into regular params and
|
||||
// grouped params. For grouped params, we use getGroup to find the actual
|
||||
// group.
|
||||
for _, param := range paramList {
|
||||
if param.Group == "" {
|
||||
// Not a value group.
|
||||
params = append(params, param)
|
||||
continue
|
||||
}
|
||||
|
||||
k := nodeKey{t: param.Type.Elem(), group: param.Group}
|
||||
group := dg.getGroup(k)
|
||||
groupParams = append(groupParams, group)
|
||||
}
|
||||
|
||||
for _, result := range resultList {
|
||||
// If the result is a grouped value, we want to update its GroupIndex
|
||||
// and add it to the Group.
|
||||
if result.Group != "" {
|
||||
dg.addToGroup(result, c.ID)
|
||||
}
|
||||
}
|
||||
|
||||
c.Params = params
|
||||
c.GroupParams = groupParams
|
||||
c.Results = resultList
|
||||
|
||||
// Track which constructors consume a parameter.
|
||||
for _, p := range paramList {
|
||||
k := p.nodeKey()
|
||||
dg.consumers[k] = append(dg.consumers[k], c)
|
||||
}
|
||||
|
||||
dg.Ctors = append(dg.Ctors, c)
|
||||
dg.ctorMap[c.ID] = c
|
||||
}
|
||||
|
||||
func (dg *Graph) failNode(r *Result, isRootCause bool) {
|
||||
if isRootCause {
|
||||
dg.addRootCause(r)
|
||||
} else {
|
||||
dg.addTransitiveFailure(r)
|
||||
}
|
||||
}
|
||||
|
||||
// AddMissingNodes adds missing nodes to the list of failed Results in the graph.
|
||||
func (dg *Graph) AddMissingNodes(results []*Result) {
|
||||
// The failure(s) are root causes if there are no other failures.
|
||||
isRootCause := len(dg.Failed.RootCauses) == 0
|
||||
|
||||
for _, r := range results {
|
||||
dg.failNode(r, isRootCause)
|
||||
}
|
||||
}
|
||||
|
||||
// FailNodes adds results to the list of failed Results in the graph, and
|
||||
// updates the state of the constructor with the given id accordingly.
|
||||
func (dg *Graph) FailNodes(results []*Result, id CtorID) {
|
||||
// This failure is the root cause if there are no other failures.
|
||||
isRootCause := len(dg.Failed.RootCauses) == 0
|
||||
dg.Failed.ctors[id] = struct{}{}
|
||||
|
||||
for _, r := range results {
|
||||
dg.failNode(r, isRootCause)
|
||||
}
|
||||
|
||||
if c, ok := dg.ctorMap[id]; ok {
|
||||
if isRootCause {
|
||||
c.ErrorType = rootCause
|
||||
} else {
|
||||
c.ErrorType = transitiveFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FailGroupNodes finds and adds the failed grouped nodes to the list of failed
|
||||
// Results in the graph, and updates the state of the group and constructor
|
||||
// with the given id accordingly.
|
||||
func (dg *Graph) FailGroupNodes(name string, t reflect.Type, id CtorID) {
|
||||
// This failure is the root cause if there are no other failures.
|
||||
isRootCause := len(dg.Failed.RootCauses) == 0
|
||||
|
||||
k := nodeKey{t: t, group: name}
|
||||
group := dg.getGroup(k)
|
||||
|
||||
// If the ctor does not exist it cannot be failed.
|
||||
if _, ok := dg.ctorMap[id]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Track which constructors and groups have failed.
|
||||
dg.Failed.ctors[id] = struct{}{}
|
||||
dg.Failed.groups[k] = struct{}{}
|
||||
|
||||
for _, r := range dg.ctorMap[id].Results {
|
||||
if r.Type == t && r.Group == name {
|
||||
dg.failNode(r, isRootCause)
|
||||
}
|
||||
}
|
||||
|
||||
if c, ok := dg.ctorMap[id]; ok {
|
||||
if isRootCause {
|
||||
group.ErrorType = rootCause
|
||||
c.ErrorType = rootCause
|
||||
} else {
|
||||
group.ErrorType = transitiveFailure
|
||||
c.ErrorType = transitiveFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getGroup finds the group by nodeKey from the graph. If it is not available,
|
||||
// a new group is created and returned.
|
||||
func (dg *Graph) getGroup(k nodeKey) *Group {
|
||||
g, ok := dg.groupMap[k]
|
||||
if !ok {
|
||||
g = NewGroup(k)
|
||||
dg.groupMap[k] = g
|
||||
dg.Groups = append(dg.Groups, g)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
// addToGroup adds a newly provided grouped result to the appropriate group.
|
||||
func (dg *Graph) addToGroup(r *Result, id CtorID) {
|
||||
k := nodeKey{t: r.Type, group: r.Group}
|
||||
group := dg.getGroup(k)
|
||||
|
||||
r.GroupIndex = len(group.Results)
|
||||
group.Results = append(group.Results, r)
|
||||
}
|
||||
|
||||
// PruneSuccess removes elements from the graph that do not have failed results.
|
||||
// Removing elements that do not have failing results makes the graph easier to debug,
|
||||
// since non-failing nodes and edges can clutter the graph and don't help the user debug.
|
||||
func (dg *Graph) PruneSuccess() {
|
||||
dg.pruneCtors(dg.Failed.ctors)
|
||||
dg.pruneGroups(dg.Failed.groups)
|
||||
}
|
||||
|
||||
// pruneCtors removes constructors from the graph that do not have failing Results.
|
||||
func (dg *Graph) pruneCtors(failed map[CtorID]struct{}) {
|
||||
var pruned []*Ctor
|
||||
for _, c := range dg.Ctors {
|
||||
if _, ok := failed[c.ID]; ok {
|
||||
pruned = append(pruned, c)
|
||||
continue
|
||||
}
|
||||
// If a constructor is deleted, the constructor's stale result references need to
|
||||
// be removed from that result's Group and/or consuming constructor.
|
||||
dg.pruneCtorParams(c, dg.consumers)
|
||||
dg.pruneGroupResults(c, dg.groupMap)
|
||||
delete(dg.ctorMap, c.ID)
|
||||
}
|
||||
|
||||
dg.Ctors = pruned
|
||||
}
|
||||
|
||||
// pruneGroups removes groups from the graph that do not have failing results.
|
||||
func (dg *Graph) pruneGroups(failed map[nodeKey]struct{}) {
|
||||
var pruned []*Group
|
||||
for _, g := range dg.Groups {
|
||||
k := g.nodeKey()
|
||||
if _, ok := failed[k]; ok {
|
||||
pruned = append(pruned, g)
|
||||
continue
|
||||
}
|
||||
delete(dg.groupMap, k)
|
||||
}
|
||||
dg.Groups = pruned
|
||||
|
||||
dg.pruneCtorGroupParams(dg.groupMap)
|
||||
}
|
||||
|
||||
// pruneCtorParams removes results of the constructor argument that are still referenced in the
|
||||
// Params of constructors that consume those results. If the results in the constructor are found
|
||||
// in the params of a consuming constructor that result should be removed.
|
||||
func (dg *Graph) pruneCtorParams(c *Ctor, consumers map[nodeKey][]*Ctor) {
|
||||
for _, r := range c.Results {
|
||||
for _, ctor := range consumers[r.nodeKey()] {
|
||||
ctor.removeParam(r.nodeKey())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pruneCtorGroupParams removes constructor results that are still referenced in the GroupParams of
|
||||
// constructors that consume those results.
|
||||
func (dg *Graph) pruneCtorGroupParams(groups map[nodeKey]*Group) {
|
||||
for _, c := range dg.Ctors {
|
||||
var pruned []*Group
|
||||
for _, gp := range c.GroupParams {
|
||||
k := gp.nodeKey()
|
||||
if _, ok := groups[k]; ok {
|
||||
pruned = append(pruned, gp)
|
||||
}
|
||||
}
|
||||
c.GroupParams = pruned
|
||||
}
|
||||
}
|
||||
|
||||
// pruneGroupResults removes results of the constructor argument that are still referenced in
|
||||
// the Group object that contains that result. If a group no longer exists references to that
|
||||
// should should be removed.
|
||||
func (dg *Graph) pruneGroupResults(c *Ctor, groups map[nodeKey]*Group) {
|
||||
for _, r := range c.Results {
|
||||
k := r.nodeKey()
|
||||
if k.group == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
g, ok := groups[k]
|
||||
if ok {
|
||||
g.removeResult(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer for Param.
|
||||
func (p *Param) String() string {
|
||||
if p.Name != "" {
|
||||
return fmt.Sprintf("%v[name=%v]", p.Type.String(), p.Name)
|
||||
}
|
||||
return p.Type.String()
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer for Result.
|
||||
func (r *Result) String() string {
|
||||
switch {
|
||||
case r.Name != "":
|
||||
return fmt.Sprintf("%v[name=%v]", r.Type.String(), r.Name)
|
||||
case r.Group != "":
|
||||
return fmt.Sprintf("%v[group=%v]%v", r.Type.String(), r.Group, r.GroupIndex)
|
||||
default:
|
||||
return r.Type.String()
|
||||
}
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer for Group.
|
||||
func (g *Group) String() string {
|
||||
return fmt.Sprintf("[type=%v group=%v]", g.Type.String(), g.Name)
|
||||
}
|
||||
|
||||
// Attributes composes and returns a string of the Result node's attributes.
|
||||
func (r *Result) Attributes() string {
|
||||
switch {
|
||||
case r.Name != "":
|
||||
return fmt.Sprintf(`label=<%v<BR /><FONT POINT-SIZE="10">Name: %v</FONT>>`, r.Type, r.Name)
|
||||
case r.Group != "":
|
||||
return fmt.Sprintf(`label=<%v<BR /><FONT POINT-SIZE="10">Group: %v</FONT>>`, r.Type, r.Group)
|
||||
default:
|
||||
return fmt.Sprintf(`label=<%v>`, r.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes composes and returns a string of the Group node's attributes.
|
||||
func (g *Group) Attributes() string {
|
||||
attr := fmt.Sprintf(`shape=diamond label=<%v<BR /><FONT POINT-SIZE="10">Group: %v</FONT>>`, g.Type, g.Name)
|
||||
if g.ErrorType != noError {
|
||||
attr += " color=" + g.ErrorType.Color()
|
||||
}
|
||||
return attr
|
||||
}
|
||||
|
||||
// Color returns the color representation of each ErrorType.
|
||||
func (s ErrorType) Color() string {
|
||||
switch s {
|
||||
case rootCause:
|
||||
return "red"
|
||||
case transitiveFailure:
|
||||
return "orange"
|
||||
default:
|
||||
return "black"
|
||||
}
|
||||
}
|
||||
|
||||
func (dg *Graph) addRootCause(r *Result) {
|
||||
dg.Failed.RootCauses = append(dg.Failed.RootCauses, r)
|
||||
}
|
||||
|
||||
func (dg *Graph) addTransitiveFailure(r *Result) {
|
||||
dg.Failed.TransitiveFailures = append(dg.Failed.TransitiveFailures, r)
|
||||
}
|
||||
118
vendor/go.uber.org/dig/internal/graph/graph.go
generated
vendored
Normal file
118
vendor/go.uber.org/dig/internal/graph/graph.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2021 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package graph
|
||||
|
||||
// Graph represents a simple interface for representation
|
||||
// of a directed graph.
|
||||
// It is assumed that each node in the graph is uniquely
|
||||
// identified with an incremental positive integer (i.e. 1, 2, 3...).
|
||||
// A value of 0 for a node represents a sentinel error value.
|
||||
type Graph interface {
|
||||
// Order returns the total number of nodes in the graph
|
||||
Order() int
|
||||
|
||||
// EdgesFrom returns a list of integers that each
|
||||
// represents a node that has an edge from node u.
|
||||
EdgesFrom(u int) []int
|
||||
}
|
||||
|
||||
// IsAcyclic uses depth-first search to find cycles
|
||||
// in a generic graph represented by Graph interface.
|
||||
// If a cycle is found, it returns a list of nodes that
|
||||
// are in the cyclic path, identified by their orders.
|
||||
func IsAcyclic(g Graph) (bool, []int) {
|
||||
// cycleStart is a node that introduces a cycle in
|
||||
// the graph. Values in the range [1, g.Order()) mean
|
||||
// that there exists a cycle in g.
|
||||
info := newCycleInfo(g.Order())
|
||||
|
||||
for i := 0; i < g.Order(); i++ {
|
||||
info.Reset()
|
||||
|
||||
cycle := isAcyclic(g, i, info, nil /* cycle path */)
|
||||
if len(cycle) > 0 {
|
||||
return false, cycle
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// isAcyclic traverses the given graph starting from a specific node
|
||||
// using depth-first search using recursion. If a cycle is detected,
|
||||
// it returns the node that contains the "last" edge that introduces
|
||||
// a cycle.
|
||||
// For example, running isAcyclic starting from 1 on the following
|
||||
// graph will return 3.
|
||||
//
|
||||
// 1 -> 2 -> 3 -> 1
|
||||
func isAcyclic(g Graph, u int, info cycleInfo, path []int) []int {
|
||||
// We've already verified that there are no cycles from this node.
|
||||
if info[u].Visited {
|
||||
return nil
|
||||
}
|
||||
info[u].Visited = true
|
||||
info[u].OnStack = true
|
||||
|
||||
path = append(path, u)
|
||||
for _, v := range g.EdgesFrom(u) {
|
||||
if !info[v].Visited {
|
||||
if cycle := isAcyclic(g, v, info, path); len(cycle) > 0 {
|
||||
return cycle
|
||||
}
|
||||
} else if info[v].OnStack {
|
||||
// We've found a cycle, and we have a full path back.
|
||||
// Prune it down to just the cyclic nodes.
|
||||
cycle := path
|
||||
for i := len(cycle) - 1; i >= 0; i-- {
|
||||
if cycle[i] == v {
|
||||
cycle = cycle[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Complete the cycle by adding this node to it.
|
||||
return append(cycle, v)
|
||||
}
|
||||
}
|
||||
info[u].OnStack = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// cycleNode keeps track of a single node's info for cycle detection.
|
||||
type cycleNode struct {
|
||||
Visited bool
|
||||
OnStack bool
|
||||
}
|
||||
|
||||
// cycleInfo contains information about each node while we're trying to find
|
||||
// cycles.
|
||||
type cycleInfo []cycleNode
|
||||
|
||||
func newCycleInfo(order int) cycleInfo {
|
||||
return make(cycleInfo, order)
|
||||
}
|
||||
|
||||
func (info cycleInfo) Reset() {
|
||||
for i := range info {
|
||||
info[i].OnStack = false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user