Skip to content

Commit

Permalink
Added cluster name in header
Browse files Browse the repository at this point in the history
  • Loading branch information
bonnefoa committed Aug 27, 2022
1 parent d567a4f commit eccf94f
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 77 deletions.
7 changes: 4 additions & 3 deletions cmd/kubectl-fzf-completion/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
)

func completeFun(cmd *cobra.Command, args []string) {
header, comps, err := completion.ProcessCommandArgs(cmd.Use, args)
completionResults, err := completion.ProcessCommandArgs(cmd.Use, args)
if e, ok := err.(resources.UnknownResourceError); ok {
logrus.Warnf("Unknown resource type: %s", e)
os.Exit(6)
Expand All @@ -32,12 +32,13 @@ func completeFun(cmd *cobra.Command, args []string) {
if err != nil {
logrus.Fatalf("Completion error: %s", err)
}
if len(comps) == 0 {
if len(completionResults.Completions) == 0 {
logrus.Warn("No completion found")
os.Exit(5)
}
formattedComps := util.FormatCompletion(header, comps)
formattedComps := completionResults.GetFormattedOutput()

// TODO pass query
fzfResult, err := fzf.CallFzf(formattedComps, "")
if err != nil {
logrus.Fatalf("Call fzf error: %s", err)
Expand Down
53 changes: 21 additions & 32 deletions pkg/completion/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,15 @@ import (
"kubectlfzf/pkg/fetcher"
"kubectlfzf/pkg/k8s/resources"
"kubectlfzf/pkg/parse"
"kubectlfzf/pkg/util"
"sort"
"strings"
"time"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

func getNamespace(args []string) *string {
for k, arg := range args {
if (arg == "-n" || arg == "--namespace") && len(args) > k+1 {
return &args[k+1]
}
if strings.HasPrefix(arg, "--namespace=") {
return &strings.Split(arg, "=")[1]
}
}
return nil
}
const labelHeader = "Namespace\tLabel\tOccurrences"
const fieldSelectorHeader = "Namespace\tFieldSelector\tOccurrences"

func getResourceCompletion(ctx context.Context, r resources.ResourceType, namespace *string,
fetchConfig *fetcher.Fetcher) ([]string, error) {
Expand All @@ -43,44 +32,44 @@ func getResourceCompletion(ctx context.Context, r resources.ResourceType, namesp
}

func processCommandArgsWithFetchConfig(ctx context.Context, fetchConfig *fetcher.Fetcher,
cmdVerb string, args []string) ([]string, []string, error) {
var comps []string
cmdVerb string, args []string) (*CompletionResult, error) {
var err error
resourceType, flagCompletion, err := parse.ParseFlagAndResources(cmdVerb, args)
if err != nil {
return nil, nil, err
return nil, err
}
logrus.Debugf("Call Get Fun with %+v, resource type detected %s, flag detected %s", args, resourceType, flagCompletion)
labelHeader := []string{"Namespace", "Label", "Occurrences"}
fieldSelectorHeader := []string{"Namespace", "FieldSelector", "Occurrences"}

namespace := getNamespace(args)
completionResult := &CompletionResult{Cluster: fetchConfig.GetContext()}
namespace := parse.ParseNamespaceFromArgs(args)
if flagCompletion == parse.FlagLabel {
comps, err := GetTagResourceCompletion(ctx, resourceType, namespace, fetchConfig, TagTypeLabel)
return labelHeader, comps, err
completionResult.Completions, err = GetTagResourceCompletion(ctx, resourceType, namespace, fetchConfig, TagTypeLabel)
completionResult.Header = labelHeader
return completionResult, err
} else if flagCompletion == parse.FlagFieldSelector {
comps, err := GetTagResourceCompletion(ctx, resourceType, namespace, fetchConfig, TagTypeFieldSelector)
return fieldSelectorHeader, comps, err
completionResult.Completions, err = GetTagResourceCompletion(ctx, resourceType, namespace, fetchConfig, TagTypeFieldSelector)
completionResult.Header = fieldSelectorHeader
return completionResult, err
}

header := resources.ResourceToHeader(resourceType)
comps, err = getResourceCompletion(ctx, resourceType, namespace, fetchConfig)
completionResult.Header = resources.ResourceToHeader(resourceType)
completionResult.Completions, err = getResourceCompletion(ctx, resourceType, namespace, fetchConfig)
if err != nil {
return header, comps, errors.Wrap(err, "error getting resource completion")
return completionResult, errors.Wrap(err, "error getting resource completion")
}
sort.Strings(comps)
return header, comps, err
sort.Strings(completionResult.Completions)
return completionResult, err
}

func ProcessCommandArgs(cmdVerb string, args []string) (string, []string, error) {
func ProcessCommandArgs(cmdVerb string, args []string) (*CompletionResult, error) {
fetchConfigCli := fetcher.GetFetchConfigCli()
f := fetcher.NewFetcher(&fetchConfigCli)
err := f.SetClusterNameFromCurrentContext()
if err != nil {
return "", nil, err
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
header, comps, err := processCommandArgsWithFetchConfig(ctx, f, cmdVerb, args)
completionResult, err := processCommandArgsWithFetchConfig(ctx, f, cmdVerb, args)
cancel()
return util.DumpLine(header), comps, err
return completionResult, err
}
18 changes: 18 additions & 0 deletions pkg/completion/completion_results.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package completion

import (
"fmt"
"kubectlfzf/pkg/util"
)

type CompletionResult struct {
Cluster string
Header string
Completions []string
}

func (c *CompletionResult) GetFormattedOutput() string {
lines := []string{fmt.Sprintf("Cluster: %s", c.Cluster), c.Header}
lines = append(lines, c.Completions...)
return util.FormatCompletion(lines)
}
28 changes: 14 additions & 14 deletions pkg/completion/completion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ func TestProcessResourceName(t *testing.T) {
{"exec", []string{"-ti", ""}},
}
for _, cmdArg := range cmdArgs {
_, comps, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
completionResults, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
require.NoError(t, err)
require.Greater(t, len(comps), 0)
require.Contains(t, comps[0], "kube-system\tcoredns-6d4b75cb6d-m6m4q\t172.17.0.3\t192.168.49.2\tminikube\tRunning\tBurstable\tcoredns\tCriticalAddonsOnly:,node-role.kubernetes.io/master:NoSchedule,node-role.kubernetes.io/control-plane:NoSchedule\tNone")
require.Greater(t, len(completionResults.Completions), 0)
require.Contains(t, completionResults.Completions[0], "kube-system\tcoredns-6d4b75cb6d-m6m4q\t172.17.0.3\t192.168.49.2\tminikube\tRunning\tBurstable\tcoredns\tCriticalAddonsOnly:,node-role.kubernetes.io/master:NoSchedule,node-role.kubernetes.io/control-plane:NoSchedule\tNone")
}
}

Expand All @@ -44,10 +44,10 @@ func TestProcessNamespace(t *testing.T) {
{"logs", []string{"--namespace="}},
}
for _, cmdArg := range cmdArgs {
_, comps, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
completionResults, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
require.NoError(t, err)
require.Greater(t, len(comps), 0)
require.Contains(t, comps[0], "default\t")
require.Greater(t, len(completionResults.Completions), 0)
require.Contains(t, completionResults.Completions[0], "default\t")
}
}

Expand All @@ -61,10 +61,10 @@ func TestProcessLabelCompletion(t *testing.T) {
{"get", []string{"pods", "--selector="}},
}
for _, cmdArg := range cmdArgs {
_, comps, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
completionResults, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
require.NoError(t, err)
require.Equal(t, "kube-system\ttier=control-plane\t4", comps[0])
require.Len(t, comps, 12)
require.Equal(t, "kube-system\ttier=control-plane\t4", completionResults.Completions[0])
require.Len(t, completionResults.Completions, 12)
}
}

Expand All @@ -75,9 +75,9 @@ func TestProcessFieldSelectorCompletion(t *testing.T) {
{"get", []string{"pods", "--field-selector="}},
}
for _, cmdArg := range cmdArgs {
_, comps, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
completionResults, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
require.NoError(t, err)
assert.Equal(t, "kube-system\tspec.nodeName=minikube\t7", comps[0])
assert.Equal(t, "kube-system\tspec.nodeName=minikube\t7", completionResults.Completions[0])
}
}

Expand All @@ -91,7 +91,7 @@ func TestUnmanagedCompletion(t *testing.T) {
{"get", []string{"--all-namespaces"}},
}
for _, cmdArg := range cmdArgs {
_, _, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
_, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
require.Error(t, err)
require.IsType(t, parse.UnmanagedFlagError(""), err)
}
Expand All @@ -113,9 +113,9 @@ func TestManagedCompletion(t *testing.T) {
{"get", []string{"-n", ""}},
}
for _, cmdArg := range cmdArgs {
_, comps, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
completionResults, err := processCommandArgsWithFetchConfig(context.Background(), fetchConfig, cmdArg.verb, cmdArg.args)
require.NoError(t, err)
require.NotNil(t, comps)
require.NotNil(t, completionResults)
}
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/fzf/call_fzf.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ func setCompsInStdin(cmd *exec.Cmd, comps string) error {

func CallFzf(comps string, query string) (string, error) {
var result strings.Builder
header := strings.Split(comps, "\n")[0]
header := strings.Split(comps, "\n")[1]
numFields := len(strings.Fields(header))
logrus.Debugf("header: %s, numFields: %d", header, numFields)
previewWindow := fmt.Sprintf("--preview-window=down:%d", numFields)
previewCmd := fmt.Sprintf("echo -e \"%s\n{}\" | sed -e \"s/'//g\" | awk '(NR==1){for (i=1; i<=NF; i++) a[i]=$i} (NR==2){for (i in a) {printf a[i] \": \" $i \"\\n\"} }' | column -t | fold -w $COLUMNS", header)

// TODO Make fzf options configurable
fzfArgs := []string{"-1", "--header-lines=1", "--layout", "reverse", "-e", "--no-hscroll", "--no-sort", "--cycle", "-q", query, previewWindow, "--preview", previewCmd}
fzfArgs := []string{"-1", "--header-lines=2", "--layout", "reverse", "-e", "--no-hscroll", "--no-sort", "--cycle", "-q", query, previewWindow, "--preview", previewCmd}
logrus.Infof("fzf args: %+v", fzfArgs)
cmd := exec.Command("fzf", fzfArgs...)
cmd.Stdout = &result
Expand Down
42 changes: 21 additions & 21 deletions pkg/k8s/resources/k8s_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,26 @@ func (r *ResourceMeta) labelsString() string {
return util.JoinSlicesOrNone(els, ",")
}

func ResourceToHeader(r ResourceType) []string {
replicaSetHeader := []string{"Namespace", "Name", "Replicas", "AvailableReplicas", "ReadyReplicas", "Selector", "Age", "Labels"}
apiResourceHeader := []string{"Name", "Shortnames", "ApiVersion", "Namespaced", "Kind"}
configMapHeader := []string{"Namespace", "Name", "Age", "Labels"}
cronJobHeader := []string{"Namespace", "Name", "Schedule", "LastSchedule", "Containers", "Age", "Labels"}
daemonSetHeader := []string{"Namespace", "Name", "Desired", "Current", "Ready", "LabelSelector", "Containers", "Age", "Labels"}
deploymentHeader := []string{"Namespace", "Name", "Desired", "Current", "Up-to-date", "Available", "Age", "Labels"}
endpointsHeader := []string{"Namespace", "Name", "Age", "ReadyIps", "ReadyPods", "NotReadyIps", "NotReadyPods", "Labels"}
horizontalPodAutoscalerHeader := []string{"Namespace", "Name", "Reference", "Targets", "MinPods", "MaxPods", "Replicas", "Age", "Labels"}
ingressHeader := []string{"Namespace", "Name", "Address", "Age", "Labels"}
jobHeader := []string{"Namespace", "Name", "Completions", "Containers", "Age", "Labels"}
namespaceHeader := []string{"Name", "Age", "Labels"}
nodeHeader := []string{"Name", "Roles", "Status", "InstanceType", "Zone", "InternalIp", "Taints", "InstanceID", "Age", "Labels"}
podHeader := []string{"Namespace", "Name", "PodIp", "HostIp", "NodeName", "Phase", "QOSClass", "Containers", "Tolerations", "Claims", "Age", "Labels"}
persistentVolumeHeader := []string{"Name", "Status", "StorageClass", "Zone", "Claim", "Volume", "Affinities", "Age", "Labels"}
persistentVolumeClaimHeader := []string{"Namespace", "Name", "Status", "Capacity", "VolumeName", "StorageClass", "Age", "Labels"}
secretHeader := []string{"Namespace", "Name", "Type", "Data", "Age", "Labels"}
serviceHeader := []string{"Namespace", "Name", "Type", "ClusterIp", "Ports", "Selector", "Age", "Labels"}
serviceAccountHeader := []string{"Namespace", "Name", "Secrets", "Age", "Labels"}
statefulSetHeader := []string{"Namespace", "Name", "Replicas", "Selector", "Age", "Labels"}
func ResourceToHeader(r ResourceType) string {
replicaSetHeader := "Namespace\tName\tReplicas\tAvailableReplicas\tReadyReplicas\tSelector\tAge\tLabels"
apiResourceHeader := "Name\tShortnames\tApiVersion\tNamespaced\tKind"
configMapHeader := "Namespace\tName\tAge\tLabels"
cronJobHeader := "Namespace\tName\tSchedule\tLastSchedule\tContainers\tAge\tLabels"
daemonSetHeader := "Namespace\tName\tDesired\tCurrent\tReady\tLabelSelector\tContainers\tAge\tLabels"
deploymentHeader := "Namespace\tName\tDesired\tCurrent\tUp-to-date\tAvailable\tAge\tLabels"
endpointsHeader := "Namespace\tName\tAge\tReadyIps\tReadyPods\tNotReadyIps\tNotReadyPods\tLabels"
horizontalPodAutoscalerHeader := "Namespace\tName\tReference\tTargets\tMinPods\tMaxPods\tReplicas\tAge\tLabels"
ingressHeader := "Namespace\tName\tAddress\tAge\tLabels"
jobHeader := "Namespace\tName\tCompletions\tContainers\tAge\tLabels"
namespaceHeader := "Name\tAge\tLabels"
nodeHeader := "Name\tRoles\tStatus\tInstanceType\tZone\tInternalIp\tTaints\tInstanceID\tAge\tLabels"
podHeader := "Namespace\tName\tPodIp\tHostIp\tNodeName\tPhase\tQOSClass\tContainers\tTolerations\tClaims\tAge\tLabels"
persistentVolumeHeader := "Name\tStatus\tStorageClass\tZone\tClaim\tVolume\tAffinities\tAge\tLabels"
persistentVolumeClaimHeader := "Namespace\tName\tStatus\tCapacity\tVolumeName\tStorageClass\tAge\tLabels"
secretHeader := "Namespace\tName\tType\tData\tAge\tLabels"
serviceHeader := "Namespace\tName\tType\tClusterIp\tPorts\tSelector\tAge\tLabels"
serviceAccountHeader := "Namespace\tName\tSecrets\tAge\tLabels"
statefulSetHeader := "Namespace\tName\tReplicas\tSelector\tAge\tLabels"
switch r {
case ResourceTypeApiResource:
return apiResourceHeader
Expand Down Expand Up @@ -146,6 +146,6 @@ func ResourceToHeader(r ResourceType) []string {
case ResourceTypeStatefulSet:
return statefulSetHeader
default:
return []string{"Unknown"}
return "Unknown"
}
}
12 changes: 12 additions & 0 deletions pkg/parse/parse_args.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,15 @@ func ParseFlagAndResources(cmdVerb string, cmdArgs []string) (resourceType resou
}
return
}

func ParseNamespaceFromArgs(args []string) *string {
for k, arg := range args {
if (arg == "-n" || arg == "--namespace") && len(args) > k+1 {
return &args[k+1]
}
if strings.HasPrefix(arg, "--namespace=") {
return &strings.Split(arg, "=")[1]
}
}
return nil
}
7 changes: 3 additions & 4 deletions pkg/util/formatting.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ import (
"github.com/sirupsen/logrus"
)

func FormatCompletion(header string, comps []string) string {
func FormatCompletion(lines []string) string {
logrus.Info("formating completion")
b := new(strings.Builder)
w := tabwriter.NewWriter(b, 0, 0, 1, ' ', tabwriter.StripEscape)
fmt.Fprintln(w, header)
for _, c := range comps {
fmt.Fprintln(w, c)
for _, line := range lines {
fmt.Fprintln(w, line)
}
w.Flush()
return b.String()
Expand Down
2 changes: 1 addition & 1 deletion pkg/util/formatting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

func TestFormatCompletion(t *testing.T) {
res := FormatCompletion("header1\thead2", []string{"comp1\tc1", "c2\tc22"})
res := FormatCompletion([]string{"header1\thead2", "comp1\tc1", "c2\tc22"})
expected := `header1 head2
comp1 c1
c2 c22
Expand Down

0 comments on commit eccf94f

Please sign in to comment.