-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial version of kubectl plugin
add an initial version of a kubectl-garm plugin to visualize the pools in an opinionated manner
- Loading branch information
1 parent
ad14380
commit 1a97f25
Showing
8 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package main | ||
|
||
import "github.com/mercedes-benz/garm-operator/pkg/command" | ||
|
||
func main() { | ||
if err := command.RootCommand.Execute(); err != nil { | ||
panic(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package command | ||
|
||
import "github.com/spf13/cobra" | ||
|
||
var describeCmd = &cobra.Command{ | ||
Use: "pools", | ||
Aliases: []string{"pool", "p"}, | ||
Short: "analyze and prepare captured traces", | ||
Long: "analyze and prepare previous captured traces for further processing", | ||
} | ||
|
||
func init() { | ||
RootCommand.AddCommand(describeCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package command | ||
|
||
import ( | ||
"k8s.io/client-go/dynamic" | ||
"k8s.io/client-go/rest" | ||
"k8s.io/client-go/tools/clientcmd" | ||
|
||
"k8s.io/client-go/kubernetes/scheme" | ||
|
||
garmoperatorv1alpha1 "github.com/mercedes-benz/garm-operator/api/v1alpha1" | ||
) | ||
|
||
func generateKubeClient() (*dynamic.DynamicClient, error) { | ||
|
||
config, err := clientcmd.BuildConfigFromFlags("", opt.kubeConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return dynamic.NewForConfig(config) | ||
// cliSet, err := dynamic.NewForConfig(config) | ||
//if err != nil { | ||
// return nil, err | ||
//} | ||
// | ||
//// add the garm-operator CRD scheme | ||
//garmoperatorv1alpha1.AddToScheme(scheme.Scheme) | ||
// | ||
//return kubernetes.NewForConfig(config) | ||
} | ||
|
||
func newRestClient() (*rest.RESTClient, error) { | ||
config, err := clientcmd.BuildConfigFromFlags("", opt.kubeConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
config.APIPath = "/apis" | ||
config.ContentConfig.GroupVersion = &garmoperatorv1alpha1.GroupVersion | ||
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() | ||
config.UserAgent = rest.DefaultKubernetesUserAgent() | ||
|
||
return rest.RESTClientFor(config) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package command | ||
|
||
import ( | ||
"context" | ||
"os" | ||
"slices" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/jedib0t/go-pretty/v6/table" | ||
|
||
garmoperatorv1alpha1 "github.com/mercedes-benz/garm-operator/api/v1alpha1" | ||
) | ||
|
||
var overviewCmd = &cobra.Command{ | ||
Use: "overview", | ||
Aliases: []string{"o"}, | ||
Short: "analyze and prepare captured traces", | ||
Long: "analyze and prepare previous captured traces for further processing", | ||
RunE: generateOverview, | ||
} | ||
|
||
func init() { | ||
describeCmd.AddCommand(overviewCmd) | ||
|
||
describeCmd.PersistentFlags().StringSliceVar(&opt.sortBy, "sort-by", []string{"region", "flavor"}, "sorted") | ||
describeCmd.PersistentFlags().BoolVar(&opt.markdown, "markdown", false, "output as markdown") | ||
} | ||
|
||
type providerSummary struct { | ||
name string | ||
flavors []flavor | ||
} | ||
|
||
type flavor struct { | ||
name string | ||
maxRunner uint | ||
minRunner uint | ||
} | ||
|
||
func generateOverview(cmd *cobra.Command, args []string) error { | ||
|
||
kubeClient, err := newRestClient() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
poolList := &garmoperatorv1alpha1.PoolList{} | ||
pools := kubeClient.Get(). | ||
Namespace("garm-infra-stage-prod"). | ||
Resource("pools"). | ||
Do(context.Background()) | ||
|
||
pools.Into(poolList) | ||
|
||
summary := []providerSummary{} | ||
|
||
for _, pool := range poolList.Items { | ||
|
||
providerName := pool.Spec.ProviderName | ||
maxRunners := pool.Spec.MaxRunners | ||
minIdleRunners := pool.Spec.MinIdleRunners | ||
|
||
providerIndex := slices.IndexFunc(summary, func(provider providerSummary) bool { return provider.name == providerName }) | ||
if providerIndex == -1 { | ||
summary = append(summary, providerSummary{ | ||
name: providerName, | ||
flavors: []flavor{ | ||
{ | ||
name: pool.Spec.Flavor, | ||
maxRunner: pool.Spec.MaxRunners, | ||
minRunner: pool.Spec.MinIdleRunners, | ||
}, | ||
}, | ||
}) | ||
} else { | ||
|
||
flavorIndex := slices.IndexFunc(summary[providerIndex].flavors, func(flavor flavor) bool { return flavor.name == pool.Spec.Flavor }) | ||
if flavorIndex == -1 { | ||
summary[providerIndex].flavors = append(summary[providerIndex].flavors, flavor{ | ||
name: pool.Spec.Flavor, | ||
maxRunner: pool.Spec.MaxRunners, | ||
minRunner: pool.Spec.MinIdleRunners, | ||
}) | ||
} else { | ||
|
||
summary[providerIndex].flavors[flavorIndex].maxRunner += maxRunners // calculate the runners on top | ||
summary[providerIndex].flavors[flavorIndex].minRunner += minIdleRunners | ||
} | ||
} | ||
|
||
} | ||
|
||
printOverview(summary) | ||
|
||
return nil | ||
} | ||
|
||
func printOverview(summary []providerSummary) { | ||
|
||
t := table.NewWriter() | ||
t.SetOutputMirror(os.Stdout) | ||
t.AppendHeader(table.Row{"region", "flavor", "max runners", "Min Idle Runners"}) | ||
|
||
for _, s := range summary { | ||
for _, f := range s.flavors { | ||
t.AppendRow([]interface{}{s.name, f.name, f.maxRunner, f.minRunner}) | ||
} | ||
} | ||
|
||
if len(opt.sortBy) > 0 { | ||
for _, sortOption := range opt.sortBy { | ||
t.SortBy([]table.SortBy{ | ||
{Name: sortOption, Mode: table.Asc}, | ||
}) | ||
} | ||
|
||
} | ||
|
||
t.SetStyle(table.StyleColoredBlackOnRedWhite) | ||
|
||
if opt.markdown { | ||
t.RenderMarkdown() | ||
} else { | ||
t.Render() | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package command | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
type options struct { | ||
kubeConfig string | ||
kubeConfigContext string | ||
namespace string | ||
sortBy []string | ||
markdown bool | ||
} | ||
|
||
var opt = &options{} | ||
|
||
var RootCommand = &cobra.Command{ | ||
Use: "kubectl-garm", | ||
Short: "opinionated cli plugin to visualize pools and other garm resources", | ||
Long: "this is the long help - define later", | ||
} | ||
|
||
func Root() *cobra.Command { | ||
return RootCommand | ||
} | ||
|
||
func init() { | ||
|
||
RootCommand.PersistentFlags().StringVar(&opt.kubeConfig, "kubeconfig", "", "path to the kubeconfig file to use for CLI requests") | ||
RootCommand.PersistentFlags().StringVar(&opt.kubeConfigContext, "context", "", "name of the kubeconfig context to use") | ||
RootCommand.PersistentFlags().StringVarP(&opt.namespace, "namespace", "n", "", "namespace to use for CLI requests") | ||
|
||
RootCommand.AddGroup( | ||
&cobra.Group{ | ||
ID: "pools", | ||
Title: "all the pools stuff", | ||
}, | ||
) | ||
} |