Skip to content

Instantly share code, notes, and snippets.

@bryanl
Created July 11, 2019 15:28
Show Gist options
  • Save bryanl/bd910b247b069d8932200e48464bea49 to your computer and use it in GitHub Desktop.
Save bryanl/bd910b247b069d8932200e48464bea49 to your computer and use it in GitHub Desktop.
/*
Copyright (c) 2019 VMware, Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"context"
"fmt"
"log"
"sync"
"time"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/vmware/octant/pkg/action"
"github.com/vmware/octant/pkg/plugin"
"github.com/vmware/octant/pkg/plugin/api"
"github.com/vmware/octant/pkg/store"
"github.com/vmware/octant/pkg/view/component"
"github.com/vmware/octant/pkg/view/flexlayout"
)
func main() {
podGVK := schema.GroupVersionKind{Version: "v1", Kind: "Pod"}
capabilities := plugin.Capabilities{
SupportsPrinterConfig: []schema.GroupVersionKind{podGVK},
SupportsTab: []schema.GroupVersionKind{podGVK},
}
tabHandler := HandlePrintTab(handleTab)
printHandler := HandlePrint(handlePrint)
objectStatusHandler := HandleObjectStatus(handleObjectStatus)
p, err := NewPlugin("plugin-name", "a description", capabilities, tabHandler, printHandler, objectStatusHandler)
if err != nil {
log.Fatal(err)
}
p.Serve()
}
func handleTab(dashboardClient Dashboard, object runtime.Object) (*component.Tab, error) {
if object == nil {
return nil, errors.New("object is nil")
}
layout := flexlayout.New()
section := layout.AddSection()
err := section.Add(component.NewText("content from a plugin"), component.WidthHalf)
if err != nil {
return nil, err
}
tab := component.Tab{
Name: "PluginStub",
Contents: *layout.ToComponent("Plugin"),
}
return &tab, nil
}
func handlePrint(dashboardClient Dashboard, object runtime.Object) (plugin.PrintResponse, error) {
if object == nil {
return plugin.PrintResponse{}, errors.Errorf("object is nil")
}
ctx := context.Background()
key, err := store.KeyFromObject(object)
if err != nil {
return plugin.PrintResponse{}, err
}
u, err := dashboardClient.Get(ctx, key)
if err != nil {
return plugin.PrintResponse{}, err
}
log.Printf("loaded object from objectstore: %v", u)
msg := fmt.Sprintf("update from plugin at %s", time.Now().Format(time.RFC3339))
return plugin.PrintResponse{
Config: []component.SummarySection{
{Header: "from-plugin", Content: component.NewText(msg)},
},
Status: []component.SummarySection{
{Header: "from-plugin", Content: component.NewText(msg)},
},
Items: []component.FlexLayoutItem{
{
Width: component.WidthHalf,
View: component.NewText("item 1 from plugin"),
},
{
Width: component.WidthFull,
View: component.NewText("item 2 from plugin"),
},
},
}, nil
}
func handleObjectStatus(dashboardClient Dashboard, object runtime.Object) (plugin.ObjectStatusResponse, error) {
if object == nil {
return plugin.ObjectStatusResponse{}, errors.New("object is nil")
}
status := component.PodSummary{
Status: component.NodeStatusError,
Details: []component.Component{component.NewText(fmt.Sprintf("Timestamp: %s", time.Now().Format(time.RFC850)))},
}
return plugin.ObjectStatusResponse{
ObjectStatus: status,
}, nil
}
type PluginOption func(p *Plugin) error
type HandleActionFunc func(dashboardClient Dashboard, payload action.Payload) error
func HandleAction(fn HandleActionFunc) PluginOption {
return func(p *Plugin) error {
p.pluginHandler.handleAction = fn
return nil
}
}
type HandlePrintFunc func(dashboardClient Dashboard, object runtime.Object) (plugin.PrintResponse, error)
func HandlePrint(fn HandlePrintFunc) PluginOption {
return func(p *Plugin) error {
p.pluginHandler.print = fn
return nil
}
}
type HandleObjectStatusFunc func(dashboardClient Dashboard, object runtime.Object) (plugin.ObjectStatusResponse, error)
func HandleObjectStatus(fn HandleObjectStatusFunc) PluginOption {
return func(p *Plugin) error {
p.pluginHandler.objectStatus = fn
return nil
}
}
type HandlePrintTabFunc func(dashboardClient Dashboard, object runtime.Object) (*component.Tab, error)
func HandlePrintTab(fn HandlePrintTabFunc) PluginOption {
return func(p *Plugin) error {
p.pluginHandler.printTab = fn
return nil
}
}
type Plugin struct {
pluginHandler *pluginHandler
}
func NewPlugin(name, description string, capabilities plugin.Capabilities, options ...PluginOption) (*Plugin, error) {
p := &Plugin{
pluginHandler: &pluginHandler{
name: name,
description: description,
capabilities: capabilities,
},
}
for _, option := range options {
if err := option(p); err != nil {
return nil, err
}
}
if err := p.Validate(); err != nil {
return nil, err
}
return p, nil
}
func (p *Plugin) Validate() error {
if p.pluginHandler.name == "" {
return errors.New("plugin requires a name")
}
if p.pluginHandler.description == "" {
return errors.New("plugin requires a description")
}
return nil
}
func (p *Plugin) Serve() {
ph := &pluginHandler{}
plugin.Serve(ph)
}
type pluginHandler struct {
mu sync.Mutex
name string
description string
capabilities plugin.Capabilities
print func(dashboardClient Dashboard, object runtime.Object) (plugin.PrintResponse, error)
printTab func(dashboardClient Dashboard, object runtime.Object) (*component.Tab, error)
objectStatus func(dashboardClient Dashboard, object runtime.Object) (plugin.ObjectStatusResponse, error)
handleAction func(dashboardClient Dashboard, payload action.Payload) error
dashboardClient Dashboard
}
func (p *pluginHandler) Register(dashboardAPIAddress string) (plugin.Metadata, error) {
p.mu.Lock()
defer p.mu.Unlock()
client, err := NewDashboard(dashboardAPIAddress)
if err != nil {
return plugin.Metadata{}, errors.Wrap(err, "create api client")
}
p.dashboardClient = client
return plugin.Metadata{
Name: p.name,
Description: p.description,
Capabilities: p.capabilities,
}, nil
}
func (p *pluginHandler) Print(object runtime.Object) (plugin.PrintResponse, error) {
if p.print == nil {
return plugin.PrintResponse{}, nil
}
return p.print(p.dashboardClient, object)
}
func (p *pluginHandler) PrintTab(object runtime.Object) (*component.Tab, error) {
if p.printTab == nil {
return &component.Tab{}, nil
}
return p.printTab(p.dashboardClient, object)
}
func (p *pluginHandler) ObjectStatus(object runtime.Object) (plugin.ObjectStatusResponse, error) {
if p.objectStatus == nil {
return plugin.ObjectStatusResponse{}, nil
}
return p.objectStatus(p.dashboardClient, object)
}
func (p *pluginHandler) HandleAction(payload action.Payload) error {
if p.handleAction == nil {
return nil
}
return p.handleAction(p.dashboardClient, payload)
}
var _ plugin.Service = (*pluginHandler)(nil)
type Dashboard interface {
Close() error
List(ctx context.Context, key store.Key) ([]*unstructured.Unstructured, error)
Get(ctx context.Context, key store.Key) (*unstructured.Unstructured, error)
Update(ctx context.Context, object *unstructured.Unstructured) error
PortForward(ctx context.Context, req api.PortForwardRequest) (api.PortForwardResponse, error)
CancelPortForward(ctx context.Context, id string)
ForceFrontendUpdate(ctx context.Context) error
}
func NewDashboard(dashboardAPIAddress string) (Dashboard, error) {
return api.NewClient(dashboardAPIAddress)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment