代码拉取完成,页面将自动刷新
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package garbagecollector
import (
"fmt"
"net/http"
"strings"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/encoding"
"gonum.org/v1/gonum/graph/encoding/dot"
"gonum.org/v1/gonum/graph/simple"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
type gonumVertex struct {
uid types.UID
gvk schema.GroupVersionKind
namespace string
name string
missingFromGraph bool
beingDeleted bool
deletingDependents bool
virtual bool
vertexID int64
}
func (v *gonumVertex) ID() int64 {
return v.vertexID
}
func (v *gonumVertex) String() string {
kind := v.gvk.Kind + "." + v.gvk.Version
if len(v.gvk.Group) > 0 {
kind = kind + "." + v.gvk.Group
}
missing := ""
if v.missingFromGraph {
missing = "(missing)"
}
deleting := ""
if v.beingDeleted {
deleting = "(deleting)"
}
deletingDependents := ""
if v.deletingDependents {
deleting = "(deletingDependents)"
}
virtual := ""
if v.virtual {
virtual = "(virtual)"
}
return fmt.Sprintf(`%s/%s[%s]-%v%s%s%s%s`, kind, v.name, v.namespace, v.uid, missing, deleting, deletingDependents, virtual)
}
func (v *gonumVertex) Attributes() []encoding.Attribute {
kubectlString := v.gvk.Kind + "." + v.gvk.Version
if len(v.gvk.Group) > 0 {
kubectlString = kubectlString + "." + v.gvk.Group
}
kubectlString = kubectlString + "/" + v.name
label := fmt.Sprintf(`uid=%v
namespace=%v
%v
`,
v.uid,
v.namespace,
kubectlString,
)
conditionStrings := []string{}
if v.beingDeleted {
conditionStrings = append(conditionStrings, "beingDeleted")
}
if v.deletingDependents {
conditionStrings = append(conditionStrings, "deletingDependents")
}
if v.virtual {
conditionStrings = append(conditionStrings, "virtual")
}
if v.missingFromGraph {
conditionStrings = append(conditionStrings, "missingFromGraph")
}
conditionString := strings.Join(conditionStrings, ",")
if len(conditionString) > 0 {
label = label + conditionString + "\n"
}
return []encoding.Attribute{
{Key: "label", Value: fmt.Sprintf(`"%v"`, label)},
// these place metadata in the correct location, but don't conform to any normal attribute for rendering
{Key: "group", Value: fmt.Sprintf(`"%v"`, v.gvk.Group)},
{Key: "version", Value: fmt.Sprintf(`"%v"`, v.gvk.Version)},
{Key: "kind", Value: fmt.Sprintf(`"%v"`, v.gvk.Kind)},
{Key: "namespace", Value: fmt.Sprintf(`"%v"`, v.namespace)},
{Key: "name", Value: fmt.Sprintf(`"%v"`, v.name)},
{Key: "uid", Value: fmt.Sprintf(`"%v"`, v.uid)},
{Key: "missing", Value: fmt.Sprintf(`"%v"`, v.missingFromGraph)},
{Key: "beingDeleted", Value: fmt.Sprintf(`"%v"`, v.beingDeleted)},
{Key: "deletingDependents", Value: fmt.Sprintf(`"%v"`, v.deletingDependents)},
{Key: "virtual", Value: fmt.Sprintf(`"%v"`, v.virtual)},
}
}
func NewGonumVertex(node *node, nodeID int64) *gonumVertex {
gv, err := schema.ParseGroupVersion(node.identity.APIVersion)
if err != nil {
// this indicates a bad data serialization that should be prevented during storage of the API
utilruntime.HandleError(err)
}
return &gonumVertex{
uid: node.identity.UID,
gvk: gv.WithKind(node.identity.Kind),
namespace: node.identity.Namespace,
name: node.identity.Name,
beingDeleted: node.beingDeleted,
deletingDependents: node.deletingDependents,
virtual: node.virtual,
vertexID: nodeID,
}
}
func NewMissingGonumVertex(ownerRef metav1.OwnerReference, nodeID int64) *gonumVertex {
gv, err := schema.ParseGroupVersion(ownerRef.APIVersion)
if err != nil {
// this indicates a bad data serialization that should be prevented during storage of the API
utilruntime.HandleError(err)
}
return &gonumVertex{
uid: ownerRef.UID,
gvk: gv.WithKind(ownerRef.Kind),
name: ownerRef.Name,
missingFromGraph: true,
vertexID: nodeID,
}
}
func (m *concurrentUIDToNode) ToGonumGraph() graph.Directed {
m.uidToNodeLock.Lock()
defer m.uidToNodeLock.Unlock()
return toGonumGraph(m.uidToNode)
}
func toGonumGraph(uidToNode map[types.UID]*node) graph.Directed {
uidToVertex := map[types.UID]*gonumVertex{}
graphBuilder := simple.NewDirectedGraph()
// add the vertices first, then edges. That avoids having to deal with missing refs.
for _, node := range uidToNode {
// skip adding objects that don't have owner references and aren't referred to.
if len(node.dependents) == 0 && len(node.owners) == 0 {
continue
}
vertex := NewGonumVertex(node, graphBuilder.NewNode().ID())
uidToVertex[node.identity.UID] = vertex
graphBuilder.AddNode(vertex)
}
for _, node := range uidToNode {
currVertex := uidToVertex[node.identity.UID]
for _, ownerRef := range node.owners {
currOwnerVertex, ok := uidToVertex[ownerRef.UID]
if !ok {
currOwnerVertex = NewMissingGonumVertex(ownerRef, graphBuilder.NewNode().ID())
uidToVertex[node.identity.UID] = currOwnerVertex
graphBuilder.AddNode(currOwnerVertex)
}
graphBuilder.SetEdge(simple.Edge{
F: currVertex,
T: currOwnerVertex,
})
}
}
return graphBuilder
}
func (m *concurrentUIDToNode) ToGonumGraphForObj(uids ...types.UID) graph.Directed {
m.uidToNodeLock.Lock()
defer m.uidToNodeLock.Unlock()
return toGonumGraphForObj(m.uidToNode, uids...)
}
func toGonumGraphForObj(uidToNode map[types.UID]*node, uids ...types.UID) graph.Directed {
uidsToCheck := append([]types.UID{}, uids...)
interestingNodes := map[types.UID]*node{}
// build the set of nodes to inspect first, then use the normal construction on the subset
for i := 0; i < len(uidsToCheck); i++ {
uid := uidsToCheck[i]
// if we've already been observed, there was a bug, but skip it so we don't loop forever
if _, ok := interestingNodes[uid]; ok {
continue
}
node, ok := uidToNode[uid]
// if there is no node for the UID, skip over it. We may add it to the list multiple times
// but we won't loop forever and hopefully the condition doesn't happen very often
if !ok {
continue
}
interestingNodes[node.identity.UID] = node
for _, ownerRef := range node.owners {
// if we've already inspected this UID, don't add it to be inspected again
if _, ok := interestingNodes[ownerRef.UID]; ok {
continue
}
uidsToCheck = append(uidsToCheck, ownerRef.UID)
}
for dependent := range node.dependents {
// if we've already inspected this UID, don't add it to be inspected again
if _, ok := interestingNodes[dependent.identity.UID]; ok {
continue
}
uidsToCheck = append(uidsToCheck, dependent.identity.UID)
}
}
return toGonumGraph(interestingNodes)
}
func NewDebugHandler(controller *GarbageCollector) http.Handler {
return &debugHTTPHandler{controller: controller}
}
type debugHTTPHandler struct {
controller *GarbageCollector
}
func (h *debugHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/graph" {
http.Error(w, "", http.StatusNotFound)
return
}
var graph graph.Directed
if uidStrings := req.URL.Query()["uid"]; len(uidStrings) > 0 {
uids := []types.UID{}
for _, uidString := range uidStrings {
uids = append(uids, types.UID(uidString))
}
graph = h.controller.dependencyGraphBuilder.uidToNode.ToGonumGraphForObj(uids...)
} else {
graph = h.controller.dependencyGraphBuilder.uidToNode.ToGonumGraph()
}
data, err := dot.Marshal(graph, "full", "", " ", false)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(data)
w.WriteHeader(http.StatusOK)
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。