Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
4623ebd9
Commit
4623ebd9
authored
Jul 25, 2018
by
David Eads
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add gonum graph representation of GC graph
parent
f95a5768
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
291 additions
and
1 deletion
+291
-1
BUILD
pkg/controller/garbagecollector/BUILD
+12
-1
dump.go
pkg/controller/garbagecollector/dump.go
+279
-0
dump_test.go
pkg/controller/garbagecollector/dump_test.go
+0
-0
No files found.
pkg/controller/garbagecollector/BUILD
View file @
4623ebd9
...
@@ -9,6 +9,7 @@ load(
...
@@ -9,6 +9,7 @@ load(
go_library(
go_library(
name = "go_default_library",
name = "go_default_library",
srcs = [
srcs = [
"dump.go",
"errors.go",
"errors.go",
"garbagecollector.go",
"garbagecollector.go",
"graph.go",
"graph.go",
...
@@ -44,12 +45,19 @@ go_library(
...
@@ -44,12 +45,19 @@ go_library(
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/golang/groupcache/lru:go_default_library",
"//vendor/github.com/golang/groupcache/lru:go_default_library",
"//vendor/gonum.org/v1/gonum/graph:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/encoding:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/encoding/dot:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/simple:go_default_library",
],
],
)
)
go_test(
go_test(
name = "go_default_test",
name = "go_default_test",
srcs = ["garbagecollector_test.go"],
srcs = [
"dump_test.go",
"garbagecollector_test.go",
],
embed = [":go_default_library"],
embed = [":go_default_library"],
deps = [
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/api/legacyscheme:go_default_library",
...
@@ -70,7 +78,10 @@ go_test(
...
@@ -70,7 +78,10 @@ go_test(
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/gonum.org/v1/gonum/graph:go_default_library",
"//vendor/gonum.org/v1/gonum/graph/simple:go_default_library",
],
],
)
)
...
...
pkg/controller/garbagecollector/dump.go
0 → 100644
View file @
4623ebd9
/*
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"
{
w
.
WriteHeader
(
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
{
w
.
Write
([]
byte
(
err
.
Error
()))
w
.
WriteHeader
(
http
.
StatusInternalServerError
)
return
}
w
.
Write
(
data
)
w
.
WriteHeader
(
http
.
StatusOK
)
}
pkg/controller/garbagecollector/dump_test.go
0 → 100644
View file @
4623ebd9
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment