Skip to content

Commit

Permalink
Initial crossplane top command setup
Browse files Browse the repository at this point in the history
Signed-off-by: Piotr Zaniewski <piotr@upbound.io>
  • Loading branch information
Piotr1215 committed Jan 15, 2024
1 parent c15b040 commit 99ba9a4
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cmd/crank/beta/beta.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package beta

import (
"github.com/crossplane/crossplane/cmd/crank/beta/render"
"github.com/crossplane/crossplane/cmd/crank/beta/top"
"github.com/crossplane/crossplane/cmd/crank/beta/trace"
"github.com/crossplane/crossplane/cmd/crank/beta/xpkg"
)
Expand All @@ -30,6 +31,7 @@ type Cmd struct {
// Subcommands and flags will appear in the CLI help output in the same
// order they're specified here. Keep them in alphabetical order.
Render render.Cmd `cmd:"" help:"Render a composite resource (XR)."`
Top top.Cmd `cmd:"" help:"Top provides a dynamic real-time view of resources consumption by Crossplane pods."`
Trace trace.Cmd `cmd:"" help:"Trace a Crossplane resource to get a detailed output of its relationships, helpful for troubleshooting."`
XPKG xpkg.Cmd `cmd:"" help:"Manage Crossplane packages."`
}
Expand Down
113 changes: 113 additions & 0 deletions cmd/crank/beta/top/top.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
Copyright 2023 The Crossplane 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 top contains the top command.
package top

import (
"context"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/alecthomas/kong"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/metrics/pkg/client/clientset/versioned"

"github.com/crossplane/crossplane-runtime/pkg/logging"
)

// TODO:(piotr1215) implement kubeconfig using ctrl "sigs.k8s.io/controller-runtime"
func getKubeConfig() (string, error) {
kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig != "" {
return kubeconfig, nil
}
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("could not get user home directory: %w", err)
}
return filepath.Join(home, ".kube", "config"), nil
}

// Cmd represents the top command.
type Cmd struct{}

// Help returns help instructions for the top command.
func (c *Cmd) Help() string {
return `Usage: crossplane top`
}

// Run runs the top command.
func (c *Cmd) Run(_ *kong.Context, logger logging.Logger) error { //nolint:gocyclo // TODO:(piotr1215) refactor to lower cyclomatic complexity
// TODO:(piotr1215) add logging in key places
logger = logger.WithValues("cmd", "top")

// Build the config from the kubeconfig path
kubeconfig, err := getKubeConfig()
if err != nil {
// TODO:(piotr1215) use crossplane-runtime error package
return fmt.Errorf("failed to get kubeconfig: %w", err)
}
logger.Debug("Found kubeconfig")

config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return fmt.Errorf("could not build config from flags: %w", err)
}

// Create the clientset for Kubernetes
k8sClientset, err := kubernetes.NewForConfig(config)
if err != nil {
return fmt.Errorf("could not create the clientset for Kubernetes: %w", err)
}

// Create the clientset for Metrics
metricsClientset, err := versioned.NewForConfig(config)
if err != nil {
return fmt.Errorf("could not create the clientset for Metrics: %w", err)
}

// Fetch all pods from all namespaces in case of Crossplane pods being installed elswhere
pods, err := k8sClientset.CoreV1().Pods(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return fmt.Errorf("could not fetch all pods from all namespaces: %w", err)
}

// Loop through pods and print metrics
for _, pod := range pods.Items {
for labelKey := range pod.GetLabels() {
if strings.HasPrefix(labelKey, "pkg.crossplane.io/provider") || strings.HasPrefix(labelKey, "pkg.crossplane.io/function") {
podMetrics, err := metricsClientset.MetricsV1beta1().PodMetricses(pod.Namespace).Get(context.TODO(), pod.Name, metav1.GetOptions{})
if err != nil {
fmt.Printf("Error getting metrics for pod %s: %v\n", pod.Name, err)
continue
}

for _, container := range podMetrics.Containers {
cpuUsage := container.Usage.Cpu().ScaledValue(resource.Milli)
memoryUsage := fmt.Sprintf("%dMi", container.Usage.Memory().ScaledValue(resource.Mega))
fmt.Printf("%-20s %-55s %-12d %-15s\n", pod.Namespace, pod.Name, cpuUsage, memoryUsage)
}
}
}
}
return nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ require (
k8s.io/client-go v0.28.3
k8s.io/code-generator v0.28.3
k8s.io/kubectl v0.28.2
k8s.io/metrics v0.28.2
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
sigs.k8s.io/controller-runtime v0.16.3
sigs.k8s.io/controller-tools v0.13.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,8 @@ k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5Ohx
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
k8s.io/kubectl v0.28.2 h1:fOWOtU6S0smdNjG1PB9WFbqEIMlkzU5ahyHkc7ESHgM=
k8s.io/kubectl v0.28.2/go.mod h1:6EQWTPySF1fn7yKoQZHYf9TPwIl2AygHEcJoxFekr64=
k8s.io/metrics v0.28.2 h1:Z/oMk5SmiT/Ji1SaWOPfW2l9W831BLO9/XxDq9iS3ak=
k8s.io/metrics v0.28.2/go.mod h1:QTIIdjMrq+KodO+rmp6R9Pr1LZO8kTArNtkWoQXw0sw=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
Expand Down

0 comments on commit 99ba9a4

Please sign in to comment.