bergerx/kubectl-status

View on GitHub
pkg/plugin/plugin.go

Summary

Maintainability
A
1 hr
Test Coverage
package plugin

import (
    "context"
    "fmt"
    "io"
    _ "unsafe" // required for using go:linkname in the file

    "github.com/fatih/color"
    "github.com/spf13/viper"
    "k8s.io/apimachinery/pkg/api/meta"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/watch"
    "k8s.io/cli-runtime/pkg/genericiooptions"
    "k8s.io/cli-runtime/pkg/resource"
    watchtools "k8s.io/client-go/tools/watch"
    "k8s.io/klog/v2"
    "k8s.io/kubectl/pkg/cmd/util"
    "k8s.io/kubectl/pkg/util/interrupt"

    "github.com/bergerx/kubectl-status/pkg/input"
)

func errorPrintf(wr io.Writer, format string, a ...interface{}) {
    _, _ = color.New(color.BgRed, color.FgHiWhite).Printf(format, a...)
    _, _ = fmt.Fprintln(wr)
}

func Run(f util.Factory, streams genericiooptions.IOStreams, args []string) error {
    klog.V(5).InfoS("All config settings", "settings", viper.AllSettings())
    if viper.Get("color") == "always" {
        color.NoColor = false
    } else if viper.Get("color") == "never" {
        color.NoColor = true
    }
    repo, err := input.NewResourceRepo(f)
    if err != nil {
        klog.V(2).ErrorS(err, "Error creating repo")
        return err
    }
    engine, err := newRenderEngine(streams)
    if err != nil {
        klog.V(2).ErrorS(err, "Error creating engine")
        return err
    }
    klog.V(5).InfoS("Created engine", "engine", engine)
    results := repo.CLIQueryResults(args)
    count := 0
    err = results.Visit(func(resourceInfo *resource.Info, err error) error {
        count += 1
        klog.V(5).InfoS("Processing resource", "item", count, "resource", resourceInfo)
        processObj(resourceInfo.Object, engine, repo)
        return err
    })
    klog.V(5).InfoS("Processed matching resources", "count", count)
    if err != nil {
        klog.V(1).ErrorS(err, "Error querying resources")
        return err
    }
    isWatch := viper.GetBool("watch")
    if !isWatch && count == 0 {
        return fmt.Errorf("no resources found")
    }
    if viper.GetBool("watch") {
        return runWatch(results, engine, repo)
    }
    return nil
}

func runWatch(results *resource.Result, engine *renderEngine, repo *input.ResourceRepo) error {
    color.HiYellow("\nPrinted all existing resource statuses, starting to watch. Switching to shallow mode during watch!\n\n")
    viper.Set("shallow", true)
    viper.Set("watching", true)
    klog.V(5).InfoS("Will run watch")
    obj, err := results.Object()
    if err != nil {
        klog.V(1).ErrorS(err, "Failed to get results object")
        return err
    }
    rv, err := meta.NewAccessor().ResourceVersion(obj)
    if err != nil {
        klog.V(1).ErrorS(err, "Watch failed to obtain resource version for list")
        return err
    }
    klog.V(5).InfoS("Starting watch with a specific resource version", "rv", rv)
    w, err := results.Watch(rv)
    if err != nil {
        klog.V(1).ErrorS(err, "Can't start watch")
        return err
    }
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    intr := interrupt.New(nil, cancel)
    _ = intr.Run(func() error {
        _, err := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
            klog.V(5).InfoS("Processing watch event", "e", e)
            processObj(e.Object, engine, repo)
            return false, nil
        })
        klog.V(1).ErrorS(err, "Watch failed", "obj", obj)
        return err
    })
    return nil
}

func processObj(obj runtime.Object, engine *renderEngine, repo *input.ResourceRepo) {
    streams := engine.ioStreams
    _, _ = fmt.Fprintf(streams.Out, "\n")
    out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
    if err != nil {
        errorPrintf(streams.ErrOut, "Failed to decode obj=%s: %s", obj, err)
        return
    }
    r := newRenderableObject(out, engine, repo)
    err = r.render(streams.Out)
    if err != nil {
        _, _ = fmt.Fprintf(streams.ErrOut, "\n")
        errorPrintf(streams.ErrOut, "Failed to render: %s", err)
        return
    }
    _, _ = fmt.Fprintf(streams.Out, "\n")
}