Skip to content

Commit

Permalink
Merge pull request #64 from jlojosnegros/pod-info-command
Browse files Browse the repository at this point in the history
Add new `pod-info` command.
  • Loading branch information
openshift-ci[bot] authored Jun 20, 2022
2 parents d511789 + 25d2d82 commit 440dc57
Show file tree
Hide file tree
Showing 5 changed files with 366 additions and 3 deletions.
1 change: 1 addition & 0 deletions cmd/knit/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
func main() {
root := cmd.NewRootCommand(
k8s.NewPodResourcesCommand,
k8s.NewPodInfoCommand,
ghw.NewLscpuCommand,
ghw.NewLspciCommand,
ghw.NewLstopoCommand,
Expand Down
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ require (
github.com/safchain/ethtool v0.2.0
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
k8s.io/api v0.23.0
k8s.io/apimachinery v0.23.0
k8s.io/client-go v0.23.0
k8s.io/klog/v2 v2.30.0
k8s.io/kubelet v0.23.0
k8s.io/kubernetes v0.23.0
Expand All @@ -23,6 +26,7 @@ require (
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-logr/logr v1.2.0 // indirect
Expand All @@ -32,6 +36,7 @@ require (
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/imdario/mergo v0.3.5 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jaypipes/pcidb v0.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
Expand Down Expand Up @@ -62,10 +67,7 @@ require (
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/api v0.23.0 // indirect
k8s.io/apimachinery v0.23.0 // indirect
k8s.io/apiserver v0.23.0 // indirect
k8s.io/client-go v0.23.0 // indirect
k8s.io/component-base v0.23.0 // indirect
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
Expand Down Expand Up @@ -347,6 +348,7 @@ github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7U
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
Expand Down
206 changes: 206 additions & 0 deletions pkg/knit/cmd/k8s/podinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* 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.
*
* Copyright 2022 Red Hat, Inc.
*/
package k8s

import (
"context"
"fmt"
"io"
"os"
"os/user"
"path/filepath"
"text/template"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

"github.com/openshift-kni/debug-tools/pkg/knit/cmd"
"github.com/spf13/cobra"
)

type podInfoOptions struct {
nodeName string
}

//Only need some info about the pod.
// Right now is:
// - pod name
// - pod namespace
// - node name
// - status.qosClass
// - containers
// - requests cpu
// - limits cpu
// Note this output format could change but it would be parsed on insight rules
// so the change should be sync with it.
// Caution: We filter the data from pods to avoid exposing sensible information
// (like environment variables or input parameters which can contain passwords)
// so take care of that when changing this template.
const defaultTemplate string = `
[
{{- range $idx, $item := .Items}}
{{- if (ne $idx 0)}},{{end}}
{
"namespace":"{{.ObjectMeta.Namespace}}",
"name":"{{.ObjectMeta.Name}}",
"nodeName":"{{.Spec.NodeName}}",
"qosClass": "{{.Status.QOSClass}}",
{{- if .Spec.Containers }}
"containers": [
{{- range $cdx, $cont := .Spec.Containers -}}
{{- if (ne $cdx 0) }},{{ end }}
{
"name":"{{.Name}}"
{{- if or .Resources.Requests .Resources.Limits -}}
,
"resources": {
{{- if .Resources.Limits}} {{if .Resources.Limits.Cpu}}
"limits": {
"cpu": "{{.Resources.Limits.Cpu}}"
}
{{- end }}{{end}}
{{- if .Resources.Requests}}{{if .Resources.Requests.Cpu -}}
,
"requests": {
"cpu": "{{.Resources.Requests.Cpu}}"
}
{{- end }}{{end}}
}
{{- end }}
}
{{- end }}
]
{{- end }}
}
{{- end }}
]`

func NewPodInfoCommand(knitOpts *cmd.KnitOptions) *cobra.Command {

opts := &podInfoOptions{}
podInfo := &cobra.Command{
Use: "podinfo",
Short: "get pod information complementing podresources data",
RunE: func(cmd *cobra.Command, args []string) error {

clientset, err := getClientSetFromClusterConfig()
if err != nil {
return fmt.Errorf("unable to get clientset: %w", err)
}

podInfoTemplate, err := createOutputTemplate("pod_info", defaultTemplate)
if err != nil {
return fmt.Errorf("unable to get output template: %w", err)
}

nodeFieldSelector := buildNodeFieldSelector(opts.nodeName)

return showPodInfo(nodeFieldSelector, clientset, podInfoTemplate, os.Stdout)
},
}

podInfo.Flags().StringVar(&opts.nodeName, "node-name", "", "node name to get pod info from.")

return podInfo
}

// GetConfig creates a *rest.Config for talking to a Kubernetes apiserver.
//
// Config precedence
//
// - KUBECONFIG environment variable pointing at a file
// - $HOME/.kube/config if exists
// - In-cluster config if running in cluster
func getKubeConfig() (*rest.Config, error) {
kubeconfigFromFilePath := func(kubeConfigFilePath string) (*rest.Config, error) {
if _, err := os.Stat(kubeConfigFilePath); err != nil {
return nil, fmt.Errorf("cannot stat kubeconfig '%s'", kubeConfigFilePath)
}
return clientcmd.BuildConfigFromFlags("", kubeConfigFilePath)
}

// If an env variable is specified with the config location, use that
kubeConfig := os.Getenv("KUBECONFIG")
if len(kubeConfig) > 0 {
return kubeconfigFromFilePath(kubeConfig)
}

// try the default location in the user's home directory
if usr, err := user.Current(); err == nil {
kubeConfig := filepath.Join(usr.HomeDir, ".kube", "config")
return kubeconfigFromFilePath(kubeConfig)
}

// try the in-cluster config
if c, err := rest.InClusterConfig(); err == nil {
return c, nil
}

return nil, fmt.Errorf("could not locate a kubeconfig")
}

func getClientSetFromClusterConfig() (kubernetes.Interface, error) {

config, err := getKubeConfig()
if err != nil {
return nil, err
}
// creates the clientset
return kubernetes.NewForConfig(config)
}

func createOutputTemplate(name string, tmplStr string) (*template.Template, error) {
podInfoTemplate, err := template.New(name).Parse(tmplStr)
if err != nil {
return nil, err
}
return podInfoTemplate, nil
}

func buildNodeFieldSelector(nodeName string) string {
fieldSelector := ""
if len(nodeName) != 0 {
fieldSelector = fmt.Sprintf("spec.nodeName=%s,", nodeName)
}
fieldSelector += "status.phase=Running"

return fieldSelector
}

func showPodInfo(nodeFieldSelector string, clientset kubernetes.Interface, podInfoTemplate *template.Template, output io.Writer) error {

if nil == podInfoTemplate {
return fmt.Errorf("wrong incoming params: need an output template")
}

listOptions := metav1.ListOptions{
FieldSelector: nodeFieldSelector,
}
// get pods in all the namespaces by omitting namespace
// Or specify namespace to get pods in particular namespace
pods, err := clientset.CoreV1().Pods("").List(context.TODO(), listOptions)
if err != nil {
return fmt.Errorf("error while getting pods list: %w", err)
}

if err := podInfoTemplate.Execute(output, pods); err != nil {
return fmt.Errorf("error while trying to format output: %w", err)
}

return nil
}
Loading

0 comments on commit 440dc57

Please sign in to comment.