Skip to content

Commit

Permalink
Add oadm top command for analyzing image and imagestream usage.
Browse files Browse the repository at this point in the history
  • Loading branch information
soltysh committed Jul 20, 2016
1 parent 5fd1268 commit b43dfec
Show file tree
Hide file tree
Showing 4 changed files with 459 additions and 0 deletions.
2 changes: 2 additions & 0 deletions pkg/cmd/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/openshift/origin/pkg/cmd/admin/prune"
"github.com/openshift/origin/pkg/cmd/admin/registry"
"github.com/openshift/origin/pkg/cmd/admin/router"
"github.com/openshift/origin/pkg/cmd/admin/top"
"github.com/openshift/origin/pkg/cmd/cli/cmd"
"github.com/openshift/origin/pkg/cmd/experimental/buildchain"
exipfailover "github.com/openshift/origin/pkg/cmd/experimental/ipfailover"
Expand Down Expand Up @@ -82,6 +83,7 @@ func NewCommandAdmin(name, fullName string, out io.Writer, errout io.Writer) *co
diagnostics.NewCmdDiagnostics(diagnostics.DiagnosticsRecommendedName, fullName+" "+diagnostics.DiagnosticsRecommendedName, out),
prune.NewCommandPrune(prune.PruneRecommendedName, fullName+" "+prune.PruneRecommendedName, f, out),
buildchain.NewCmdBuildChain(name, fullName+" "+buildchain.BuildChainRecommendedCommandName, f, out),
top.NewCommandTop(top.TopRecommendedName, fullName+" "+top.TopRecommendedName, f, out),
},
},
{
Expand Down
111 changes: 111 additions & 0 deletions pkg/cmd/admin/top/image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package top

import (
"fmt"

kapi "k8s.io/kubernetes/pkg/api"

"github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
buildapi "github.com/openshift/origin/pkg/build/api"
deployapi "github.com/openshift/origin/pkg/deploy/api"
imageapi "github.com/openshift/origin/pkg/image/api"
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
)

// ImageInfo contains statistic information about Image usage.
type ImageInfo struct {
Image string
ImageStreamTag string
Parents []string
Usage []string
Metadata bool
Storage int64
}

// ImageInfo generates Image information from a graph and returns this as a list
// of ImageInfo array.
func ImageTop(g graph.Graph) []ImageInfo {
infos := []ImageInfo{}

imageNodes := getImageNodes(g.Nodes())
for _, in := range imageNodes {
image := in.Image
istag := getImageStreamTag(g, in)
parents := getImageParents(g, in)
usage := getImageUsage(g, in)
infos = append(infos, ImageInfo{
Image: image.Name,
ImageStreamTag: istag,
Parents: parents,
Usage: usage,
Metadata: len(image.DockerImageManifest) != 0,
Storage: image.DockerImageMetadata.Size,
})
}

return infos
}

func getImageStreamTag(g graph.Graph, node *imagegraph.ImageNode) string {
for _, e := range g.InboundEdges(node, ImageStreamEdgeKind) {
streamNode, ok := e.From().(*imagegraph.ImageStreamNode)
if !ok {
continue
}
return getTag(streamNode.ImageStream, node.Image)
}
return ""
}

func getTag(stream *imageapi.ImageStream, image *imageapi.Image) string {
for tag, history := range stream.Status.Tags {
if history.Items[0].Image == image.Name {
return fmt.Sprintf("%s/%s:%s", stream.Namespace, stream.Name, tag)
}
}
return ""
}

func getImageParents(g graph.Graph, node *imagegraph.ImageNode) []string {
parents := []string{}
for _, e := range g.InboundEdges(node, ParentImageEdgeKind) {
imageNode, ok := e.From().(*imagegraph.ImageNode)
if !ok {
continue
}
parents = append(parents, imageNode.Image.Name)
}
return parents
}

func getImageUsage(g graph.Graph, node *imagegraph.ImageNode) []string {
usage := []string{}
for _, e := range g.InboundEdges(node, PodImageEdgeKind) {
podNode, ok := e.From().(*kubegraph.PodNode)
if !ok {
continue
}
usage = append(usage, getController(podNode.Pod))
}
return usage
}

func getController(pod *kapi.Pod) string {
controller := "<none>"
if pod.Annotations == nil {
return controller
}

if bc, ok := pod.Annotations[buildapi.BuildAnnotation]; ok {
return fmt.Sprintf("Build: %s/%s", bc, pod.Namespace)
}
if dc, ok := pod.Annotations[deployapi.DeploymentAnnotation]; ok {
return fmt.Sprintf("Deployment: %s/%s", dc, pod.Namespace)
}
if dc, ok := pod.Annotations[deployapi.DeploymentPodAnnotation]; ok {
return fmt.Sprintf("Deployer: %s/%s", dc, pod.Namespace)
}

return controller
}
64 changes: 64 additions & 0 deletions pkg/cmd/admin/top/imagestream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package top

import (
gonum "github.com/gonum/graph"

"github.com/openshift/origin/pkg/api/graph"
imageapi "github.com/openshift/origin/pkg/image/api"
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
)

// ImageStreamInfo contains contains statistic information about ImageStream usage.
type ImageStreamInfo struct {
ImageStream *imageapi.ImageStream
Storage int64
Images int
Layers int
}

// ImageStreamInfo generates ImageStream information from a graph and
// returns this as a list of ImageStreamInfo array.
func ImageStreamTop(g graph.Graph) []ImageStreamInfo {
infos := []ImageStreamInfo{}

streamNodes := getImageStreamNodes(g.Nodes())
for _, sn := range streamNodes {
storage, images, layers := getImageStreamSize(g, sn)
infos = append(infos, ImageStreamInfo{
ImageStream: sn.ImageStream,
Storage: storage,
Images: images,
Layers: layers,
})
}

return infos
}

func getImageStreamSize(g graph.Graph, node *imagegraph.ImageStreamNode) (int64, int, int) {
imageEdges := g.OutboundEdges(node, ImageStreamEdgeKind)
storage := int64(0)
images := len(imageEdges)
layers := 0
for _, e := range imageEdges {
imageNode, ok := e.To().(*imagegraph.ImageNode)
if !ok {
continue
}
layerEdges := g.OutboundEdges(imageNode, ImageLayerEdgeKind, ImageTopLayerEdgeKind)
layers += len(layerEdges)
storage += imageNode.Image.DockerImageMetadata.Size
}

return storage, images, layers
}

func getImageStreamNodes(nodes []gonum.Node) []*imagegraph.ImageStreamNode {
ret := []*imagegraph.ImageStreamNode{}
for i := range nodes {
if node, ok := nodes[i].(*imagegraph.ImageStreamNode); ok {
ret = append(ret, node)
}
}
return ret
}
Loading

0 comments on commit b43dfec

Please sign in to comment.