refactor pf and benchmarks

mine
derailed 2020-02-14 14:28:52 -07:00
parent 2822ac702e
commit 7a977f31c7
111 changed files with 683 additions and 540 deletions

1
go.mod
View File

@ -29,6 +29,7 @@ replace (
require (
fyne.io/fyne v1.2.2 // indirect
github.com/GeertJohan/gomatrix v0.0.0-20190924221747-74328b69a02f // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/alexellis/go-execute v0.0.0-20200124154445-8697e4e28c5e // indirect
github.com/alexellis/hmac v0.0.0-20180624211220-5c52ab81c0de // indirect

4
go.sum
View File

@ -29,6 +29,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/GeertJohan/gomatrix v0.0.0-20190924221747-74328b69a02f h1:O4XncXE6+qNjZIvermf2/Z4esEl8K1zFVPbl3l14mjM=
github.com/GeertJohan/gomatrix v0.0.0-20190924221747-74328b69a02f/go.mod h1:HqtsgfzGADJzbZ+MbYAJ+PJnxIxBwBvYjyqd2wWw0j0=
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
@ -389,6 +391,8 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=

View File

@ -18,7 +18,7 @@ func init() {
}
func TestConfigCurrentContext(t *testing.T) {
name, kubeConfig := "blee", "./assets/config"
name, kubeConfig := "blee", "./testdata/config"
uu := []struct {
flags *genericclioptions.ConfigFlags
context string
@ -36,7 +36,7 @@ func TestConfigCurrentContext(t *testing.T) {
}
func TestConfigCurrentCluster(t *testing.T) {
name, kubeConfig := "blee", "./assets/config"
name, kubeConfig := "blee", "./testdata/config"
uu := []struct {
flags *genericclioptions.ConfigFlags
cluster string
@ -54,7 +54,7 @@ func TestConfigCurrentCluster(t *testing.T) {
}
func TestConfigCurrentUser(t *testing.T) {
name, kubeConfig := "blee", "./assets/config"
name, kubeConfig := "blee", "./testdata/config"
uu := []struct {
flags *genericclioptions.ConfigFlags
user string
@ -72,7 +72,7 @@ func TestConfigCurrentUser(t *testing.T) {
}
func TestConfigCurrentNamespace(t *testing.T) {
name, kubeConfig := "blee", "./assets/config"
name, kubeConfig := "blee", "./testdata/config"
uu := []struct {
flags *genericclioptions.ConfigFlags
namespace string
@ -91,7 +91,7 @@ func TestConfigCurrentNamespace(t *testing.T) {
}
func TestConfigGetContext(t *testing.T) {
kubeConfig := "./assets/config"
kubeConfig := "./testdata/config"
uu := []struct {
flags *genericclioptions.ConfigFlags
cluster string
@ -114,7 +114,7 @@ func TestConfigGetContext(t *testing.T) {
}
func TestConfigSwitchContext(t *testing.T) {
cluster, kubeConfig := "duh", "./assets/config"
cluster, kubeConfig := "duh", "./testdata/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
ClusterName: &cluster,
@ -129,7 +129,7 @@ func TestConfigSwitchContext(t *testing.T) {
}
func TestConfigClusterNameFromContext(t *testing.T) {
cluster, kubeConfig := "duh", "./assets/config"
cluster, kubeConfig := "duh", "./testdata/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
ClusterName: &cluster,
@ -142,7 +142,7 @@ func TestConfigClusterNameFromContext(t *testing.T) {
}
func TestConfigAccess(t *testing.T) {
cluster, kubeConfig := "duh", "./assets/config"
cluster, kubeConfig := "duh", "./testdata/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
ClusterName: &cluster,
@ -155,7 +155,7 @@ func TestConfigAccess(t *testing.T) {
}
func TestConfigContexts(t *testing.T) {
cluster, kubeConfig := "duh", "./assets/config"
cluster, kubeConfig := "duh", "./testdata/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
ClusterName: &cluster,
@ -168,7 +168,7 @@ func TestConfigContexts(t *testing.T) {
}
func TestConfigContextNames(t *testing.T) {
cluster, kubeConfig := "duh", "./assets/config"
cluster, kubeConfig := "duh", "./testdata/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
ClusterName: &cluster,
@ -181,7 +181,7 @@ func TestConfigContextNames(t *testing.T) {
}
func TestConfigClusterNames(t *testing.T) {
cluster, kubeConfig := "duh", "./assets/config"
cluster, kubeConfig := "duh", "./testdata/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
ClusterName: &cluster,
@ -194,7 +194,7 @@ func TestConfigClusterNames(t *testing.T) {
}
func TestConfigDelContext(t *testing.T) {
cluster, kubeConfig := "duh", "./assets/config.1"
cluster, kubeConfig := "duh", "./testdata/config.1"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
ClusterName: &cluster,
@ -209,7 +209,7 @@ func TestConfigDelContext(t *testing.T) {
}
func TestConfigRestConfig(t *testing.T) {
kubeConfig := "./assets/config"
kubeConfig := "./testdata/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
}
@ -221,7 +221,7 @@ func TestConfigRestConfig(t *testing.T) {
}
func TestConfigBadConfig(t *testing.T) {
kubeConfig := "./assets/bork_config"
kubeConfig := "./testdata/bork_config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
}
@ -232,7 +232,7 @@ func TestConfigBadConfig(t *testing.T) {
}
func TestNamespaceNames(t *testing.T) {
kubeConfig := "./assets/config"
kubeConfig := "./testdata/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,

11
internal/client/tunnel.go Normal file
View File

@ -0,0 +1,11 @@
package client
// PortTunnel represents a host tunnel port mapper.
type PortTunnel struct {
Address, LocalPort, ContainerPort string
}
// PortMap returns a port mapping.
func (t PortTunnel) PortMap() string {
return t.LocalPort + ":" + t.ContainerPort
}

View File

@ -72,7 +72,7 @@ func TestAliasDefine(t *testing.T) {
func TestAliasesLoad(t *testing.T) {
a := config.NewAliases()
assert.Nil(t, a.LoadAliases("test_assets/alias.yml"))
assert.Nil(t, a.LoadAliases("testdata/alias.yml"))
assert.Equal(t, 2, len(a.Alias))
}

View File

@ -105,3 +105,15 @@ func (s *Bench) load(path string) error {
return yaml.Unmarshal(f, &s)
}
// DefaultBenchSpec returns a default bench spec.
func DefaultBenchSpec() BenchConfig {
return BenchConfig{
C: DefaultC,
N: DefaultN,
HTTP: HTTP{
Method: DefaultMethod,
Path: "/",
},
}
}

View File

@ -32,14 +32,14 @@ func TestBenchLoad(t *testing.T) {
coCount int
}{
"goodConfig": {
"test_assets/b_good.yml",
"testdata/b_good.yml",
2,
1000,
2,
0,
},
"malformed": {
"test_assets/b_toast.yml",
"testdata/b_toast.yml",
1,
200,
0,
@ -100,7 +100,7 @@ func TestBenchServiceLoad(t *testing.T) {
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
b, err := NewBench("test_assets/b_good.yml")
b, err := NewBench("testdata/b_good.yml")
assert.Nil(t, err)
assert.Equal(t, 2, len(b.Benchmarks.Services))
@ -119,16 +119,16 @@ func TestBenchServiceLoad(t *testing.T) {
}
func TestBenchReLoad(t *testing.T) {
b, err := NewBench("test_assets/b_containers.yml")
b, err := NewBench("testdata/b_containers.yml")
assert.Nil(t, err)
assert.Equal(t, 2, b.Benchmarks.Defaults.C)
assert.Nil(t, b.Reload("test_assets/b_containers_1.yml"))
assert.Nil(t, b.Reload("testdata/b_containers_1.yml"))
assert.Equal(t, 20, b.Benchmarks.Defaults.C)
}
func TestBenchLoadToast(t *testing.T) {
_, err := NewBench("test_assets/toast.yml")
_, err := NewBench("testdata/toast.yml")
assert.NotNil(t, err)
}
@ -171,7 +171,7 @@ func TestBenchContainerLoad(t *testing.T) {
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
b, err := NewBench("test_assets/b_containers.yml")
b, err := NewBench("testdata/b_containers.yml")
assert.Nil(t, err)
assert.Equal(t, 2, len(b.Benchmarks.Services))

View File

@ -18,7 +18,7 @@ func init() {
}
func TestConfigRefine(t *testing.T) {
cfgFile, ctx, cluster, ns := "test_assets/kubeconfig-test.yml", "test", "c1", "ns1"
cfgFile, ctx, cluster, ns := "testdata/kubeconfig-test.yml", "test", "c1", "ns1"
uu := map[string]struct {
flags *genericclioptions.ConfigFlags
issue bool
@ -85,7 +85,7 @@ func TestConfigValidate(t *testing.T) {
cfg := config.NewConfig(mk)
cfg.SetConnection(mc)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
cfg.Validate()
// mc.VerifyWasCalledOnce().ValidNamespaces()
}
@ -93,7 +93,7 @@ func TestConfigValidate(t *testing.T) {
func TestConfigLoad(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
assert.Equal(t, 2, cfg.K9s.RefreshRate)
assert.Equal(t, 200, cfg.K9s.LogBufferSize)
@ -119,7 +119,7 @@ func TestConfigCurrentCluster(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
assert.NotNil(t, cfg.CurrentCluster())
assert.Equal(t, "kube-system", cfg.CurrentCluster().Namespace.Active)
assert.Equal(t, "ctx", cfg.CurrentCluster().View.Active)
@ -129,7 +129,7 @@ func TestConfigActiveNamespace(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
assert.Equal(t, "kube-system", cfg.ActiveNamespace())
}
@ -142,7 +142,7 @@ func TestConfigSetActiveNamespace(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
assert.Nil(t, cfg.SetActiveNamespace("default"))
assert.Equal(t, "default", cfg.ActiveNamespace())
}
@ -151,7 +151,7 @@ func TestConfigActiveView(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
assert.Equal(t, "ctx", cfg.ActiveView())
}
@ -164,7 +164,7 @@ func TestConfigSetActiveView(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
cfg.SetActiveView("po")
assert.Equal(t, "po", cfg.ActiveView())
}
@ -173,7 +173,7 @@ func TestConfigFavNamespaces(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
expectedNS := []string{"default", "kube-public", "istio-system", "all", "kube-system"}
assert.Equal(t, expectedNS, cfg.FavNamespaces())
}
@ -181,13 +181,13 @@ func TestConfigFavNamespaces(t *testing.T) {
func TestConfigLoadOldCfg(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s_old.yml"))
assert.Nil(t, cfg.Load("testdata/k9s_old.yml"))
}
func TestConfigLoadCrap(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.NotNil(t, cfg.Load("test_assets/k9s_not_there.yml"))
assert.NotNil(t, cfg.Load("testdata/k9s_not_there.yml"))
}
func TestConfigSaveFile(t *testing.T) {
@ -203,7 +203,7 @@ func TestConfigSaveFile(t *testing.T) {
cfg := config.NewConfig(mk)
cfg.SetConnection(mc)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
cfg.K9s.RefreshRate = 100
cfg.K9s.ReadOnly = true
cfg.K9s.LogBufferSize = 500
@ -233,7 +233,7 @@ func TestConfigReset(t *testing.T) {
cfg := config.NewConfig(mk)
cfg.SetConnection(mc)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
cfg.Reset()
cfg.Validate()

View File

@ -9,7 +9,7 @@ import (
func TestHotKeyLoad(t *testing.T) {
h := config.NewHotKeys()
assert.Nil(t, h.LoadHotKeys("test_assets/hot_key.yml"))
assert.Nil(t, h.LoadHotKeys("testdata/hot_key.yml"))
assert.Equal(t, 1, len(h.HotKey))

View File

@ -72,7 +72,7 @@ func TestK9sActiveClusterBlank(t *testing.T) {
func TestK9sActiveCluster(t *testing.T) {
mk := NewMockKubeSettings()
cfg := config.NewConfig(mk)
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
assert.Nil(t, cfg.Load("testdata/k9s.yml"))
cl := cfg.K9s.ActiveCluster()
assert.NotNil(t, cl)

View File

@ -9,7 +9,7 @@ import (
func TestPluginLoad(t *testing.T) {
p := config.NewPlugins()
assert.Nil(t, p.LoadPlugins("test_assets/plugin.yml"))
assert.Nil(t, p.LoadPlugins("testdata/plugin.yml"))
assert.Equal(t, 1, len(p.Plugin))
k, ok := p.Plugin["blah"]

View File

@ -27,7 +27,7 @@ func TestAsColor(t *testing.T) {
func TestSkinNone(t *testing.T) {
s := config.NewStyles()
assert.Nil(t, s.Load("test_assets/empty_skin.yml"))
assert.Nil(t, s.Load("testdata/empty_skin.yml"))
s.Update()
assert.Equal(t, "cadetblue", s.Body().FgColor)
@ -40,7 +40,7 @@ func TestSkinNone(t *testing.T) {
func TestSkin(t *testing.T) {
s := config.NewStyles()
assert.Nil(t, s.Load("test_assets/black_and_wtf.yml"))
assert.Nil(t, s.Load("testdata/black_and_wtf.yml"))
s.Update()
assert.Equal(t, "white", s.Body().FgColor)
@ -53,10 +53,10 @@ func TestSkin(t *testing.T) {
func TestSkinNotExits(t *testing.T) {
s := config.NewStyles()
assert.NotNil(t, s.Load("test_assets/blee.yml"))
assert.NotNil(t, s.Load("testdata/blee.yml"))
}
func TestSkinBoarked(t *testing.T) {
s := config.NewStyles()
assert.NotNil(t, s.Load("test_assets/skin_boarked.yml"))
assert.NotNil(t, s.Load("testdata/skin_boarked.yml"))
}

View File

@ -15,10 +15,10 @@ func TestBenchmarkList(t *testing.T) {
a := dao.Benchmark{}
a.Init(makeFactory(), client.NewGVR("benchmarks"))
ctx := context.WithValue(context.Background(), internal.KeyDir, "test_assets/bench")
ctx := context.WithValue(context.Background(), internal.KeyDir, "testdata/bench")
oo, err := a.List(ctx, "-")
assert.Nil(t, err)
assert.Equal(t, 1, len(oo))
assert.Equal(t, "test_assets/bench/default_fred_1577308050814961000.txt", oo[0].(render.BenchInfo).Path)
assert.Equal(t, "testdata/bench/default_fred_1577308050814961000.txt", oo[0].(render.BenchInfo).Path)
}

View File

@ -29,7 +29,7 @@ func TestCruiserSlice(t *testing.T) {
// Helpers...
func loadJSON(t assert.TestingT, n string) *unstructured.Unstructured {
raw, err := ioutil.ReadFile(fmt.Sprintf("test_assets/%s.json", n))
raw, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.json", n))
assert.Nil(t, err)
var o unstructured.Unstructured

View File

@ -21,6 +21,7 @@ var (
_ Loggable = (*Deployment)(nil)
_ Restartable = (*Deployment)(nil)
_ Scalable = (*Deployment)(nil)
_ Controller = (*Deployment)(nil)
)
// Deployment represents a deployment K8s resource.
@ -51,12 +52,7 @@ func (d *Deployment) Scale(path string, replicas int32) error {
// Restart a Deployment rollout.
func (d *Deployment) Restart(path string) error {
o, err := d.Factory.Get(d.gvr.String(), path, true, labels.Everything())
if err != nil {
return err
}
var ds appsv1.Deployment
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
dp, err := d.GetInstance(path)
if err != nil {
return err
}
@ -69,30 +65,50 @@ func (d *Deployment) Restart(path string) error {
if !auth {
return fmt.Errorf("user is not authorized to restart a deployment")
}
update, err := polymorphichelpers.ObjectRestarterFn(&ds)
update, err := polymorphichelpers.ObjectRestarterFn(dp)
if err != nil {
return err
}
_, err = d.Client().DialOrDie().AppsV1().Deployments(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, update)
_, err = d.Client().DialOrDie().AppsV1().Deployments(dp.Namespace).Patch(dp.Name, types.StrategicMergePatchType, update)
return err
}
// TailLogs tail logs for all pods represented by this Deployment.
func (d *Deployment) TailLogs(ctx context.Context, c chan<- []byte, opts LogOptions) error {
o, err := d.Factory.Get(d.gvr.String(), opts.Path, true, labels.Everything())
dp, err := d.GetInstance(opts.Path)
if err != nil {
return err
}
var dp appsv1.Deployment
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
if err != nil {
return errors.New("expecting Deployment resource")
}
if dp.Spec.Selector == nil || len(dp.Spec.Selector.MatchLabels) == 0 {
return fmt.Errorf("No valid selector found on Deployment %s", opts.Path)
}
return podLogs(ctx, c, dp.Spec.Selector.MatchLabels, opts)
}
// Pod returns a pod victim by name.
func (d *Deployment) Pod(fqn string) (string, error) {
dp, err := d.GetInstance(fqn)
if err != nil {
return "", err
}
return podFromSelector(d.Factory, dp.Namespace, dp.Spec.Selector.MatchLabels)
}
// GetInstance returns a deployment instance.
func (d *Deployment) GetInstance(fqn string) (*appsv1.Deployment, error) {
o, err := d.Factory.Get(d.gvr.String(), fqn, false, labels.Everything())
if err != nil {
return nil, err
}
var dp appsv1.Deployment
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
if err != nil {
return nil, errors.New("expecting Deployment resource")
}
return &dp, nil
}

View File

@ -5,12 +5,10 @@ import (
"errors"
"fmt"
"strings"
"time"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/watch"
"github.com/rs/zerolog/log"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -26,6 +24,7 @@ var (
_ Nuker = (*DaemonSet)(nil)
_ Loggable = (*DaemonSet)(nil)
_ Restartable = (*DaemonSet)(nil)
_ Controller = (*DaemonSet)(nil)
)
// DaemonSet represents a K8s daemonset.
@ -35,12 +34,7 @@ type DaemonSet struct {
// Restart a DaemonSet rollout.
func (d *DaemonSet) Restart(path string) error {
o, err := d.Factory.Get(d.gvr.String(), path, true, labels.Everything())
if err != nil {
return err
}
var ds appsv1.DaemonSet
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
ds, err := d.GetInstance(path)
if err != nil {
return err
}
@ -52,26 +46,21 @@ func (d *DaemonSet) Restart(path string) error {
if !auth {
return fmt.Errorf("user is not authorized to restart a daemonset")
}
update, err := polymorphichelpers.ObjectRestarterFn(&ds)
update, err := polymorphichelpers.ObjectRestarterFn(ds)
if err != nil {
return err
}
_, err = d.Client().DialOrDie().AppsV1().DaemonSets(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, update)
_, err = d.Client().DialOrDie().AppsV1().DaemonSets(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, update)
return err
}
// TailLogs tail logs for all pods represented by this DaemonSet.
func (d *DaemonSet) TailLogs(ctx context.Context, c chan<- []byte, opts LogOptions) error {
o, err := d.Factory.Get(d.gvr.String(), opts.Path, true, labels.Everything())
ds, err := d.GetInstance(opts.Path)
if err != nil {
return err
}
var ds appsv1.DaemonSet
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
if err != nil {
return errors.New("expecting daemonset resource")
}
if ds.Spec.Selector == nil || len(ds.Spec.Selector.MatchLabels) == 0 {
return fmt.Errorf("no valid selector found on daemonset %q", opts.Path)
@ -81,10 +70,6 @@ func (d *DaemonSet) TailLogs(ctx context.Context, c chan<- []byte, opts LogOptio
}
func podLogs(ctx context.Context, c chan<- []byte, sel map[string]string, opts LogOptions) error {
defer func(t time.Time) {
log.Debug().Msgf("POD LOGS %v", time.Since(t))
}(time.Now())
f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
if !ok {
return errors.New("expecting a context factory")
@ -116,7 +101,6 @@ func podLogs(ctx context.Context, c chan<- []byte, sel map[string]string, opts L
if err != nil {
return err
}
log.Debug().Msgf("TAILING logs on pod %q", pod.Name)
opts.Path = client.FQN(pod.Namespace, pod.Name)
if err := po.TailLogs(ctx, c, opts); err != nil {
return err
@ -125,6 +109,32 @@ func podLogs(ctx context.Context, c chan<- []byte, sel map[string]string, opts L
return nil
}
// Pod returns a pod victim by name.
func (d *DaemonSet) Pod(fqn string) (string, error) {
ds, err := d.GetInstance(fqn)
if err != nil {
return "", err
}
return podFromSelector(d.Factory, ds.Namespace, ds.Spec.Selector.MatchLabels)
}
// GetInstance returns a daemonset instance.
func (d *DaemonSet) GetInstance(fqn string) (*appsv1.DaemonSet, error) {
o, err := d.Factory.Get(d.gvr.String(), fqn, false, labels.Everything())
if err != nil {
return nil, err
}
var ds appsv1.DaemonSet
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
if err != nil {
return nil, errors.New("expecting DaemonSet resource")
}
return &ds, nil
}
// ----------------------------------------------------------------------------
// Helpers...

View File

@ -27,9 +27,10 @@ import (
const defaultTimeout = 1 * time.Second
var (
_ Accessor = (*Pod)(nil)
_ Nuker = (*Pod)(nil)
_ Loggable = (*Pod)(nil)
_ Accessor = (*Pod)(nil)
_ Nuker = (*Pod)(nil)
_ Loggable = (*Pod)(nil)
_ Controller = (*Pod)(nil)
)
// Pod represents a pod resource.
@ -122,13 +123,7 @@ func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, er
// Containers returns all container names on pod
func (p *Pod) Containers(path string, includeInit bool) ([]string, error) {
o, err := p.Factory.Get(p.gvr.String(), path, true, labels.Everything())
if err != nil {
return nil, err
}
var pod v1.Pod
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
pod, err := p.GetInstance(path)
if err != nil {
return nil, err
}
@ -147,6 +142,27 @@ func (p *Pod) Containers(path string, includeInit bool) ([]string, error) {
return cc, nil
}
// Pod returns a pod victim by name.
func (p *Pod) Pod(fqn string) (string, error) {
return fqn, nil
}
// GetInstance returns a pod instance.
func (p *Pod) GetInstance(fqn string) (*v1.Pod, error) {
o, err := p.Factory.Get(p.gvr.String(), fqn, false, labels.Everything())
if err != nil {
return nil, err
}
var pod v1.Pod
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
if err != nil {
return nil, err
}
return &pod, nil
}
// TailLogs tails a given container logs
func (p *Pod) TailLogs(ctx context.Context, c chan<- []byte, opts LogOptions) error {
if !opts.HasContainer() {

View File

@ -3,12 +3,14 @@ package dao
import (
"context"
"fmt"
"regexp"
"strings"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/render"
"github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/runtime"
)
@ -40,21 +42,26 @@ func (p *PortForward) Delete(path string, cascade, force bool) error {
// List returns a collection of screen dumps.
func (p *PortForward) List(ctx context.Context, _ string) ([]runtime.Object, error) {
config, ok := ctx.Value(internal.KeyBenchCfg).(*config.Bench)
benchFile, ok := ctx.Value(internal.KeyBenchCfg).(string)
if !ok {
return nil, fmt.Errorf("no benchconfig found in context")
return nil, fmt.Errorf("no bench file found in context")
}
config, err := config.NewBench(benchFile)
if err != nil {
log.Debug().Msgf("No custom benchmark config file found")
}
cc := config.Benchmarks.Containers
oo := make([]runtime.Object, 0, len(p.Factory.Forwarders()))
for _, f := range p.Factory.Forwarders() {
for k, f := range p.Factory.Forwarders() {
cfg := render.BenchCfg{
C: config.Benchmarks.Defaults.C,
N: config.Benchmarks.Defaults.N,
}
if config, ok := cc[containerID(f.Path(), f.Container())]; ok {
cfg.C, cfg.N = config.C, config.N
cfg.Host, cfg.Path = config.HTTP.Host, config.HTTP.Path
if cust, ok := cc[PodToKey(k)]; ok {
cfg.C, cfg.N = cust.C, cust.N
cfg.Host, cfg.Path = cust.HTTP.Host, cust.HTTP.Path
}
oo = append(oo, render.ForwardRes{
Forwarder: f,
@ -68,10 +75,31 @@ func (p *PortForward) List(ctx context.Context, _ string) ([]runtime.Object, err
// ----------------------------------------------------------------------------
// Helpers...
// ContainerID computes container ID based on ns/po/co.
func containerID(path, co string) string {
ns, n := client.Namespaced(path)
po := strings.Split(n, "-")[0]
var podNameRX = regexp.MustCompile(`\A(.+)\-(\w{10})\-(\w{5})\z`)
return ns + "/" + po + ":" + co
// PodToKey converts a pod path to a generic bench config key
func PodToKey(path string) string {
tokens := strings.Split(path, ":")
ns, po := client.Namespaced(tokens[0])
sections := podNameRX.FindStringSubmatch(po)
if len(sections) >= 1 {
po = sections[1]
}
return client.FQN(ns, po) + ":" + tokens[1]
}
// BenchConfigFor returns a custom bench spec if defined otherwise returns the default one.
func BenchConfigFor(benchFile, path string) config.BenchConfig {
def := config.DefaultBenchSpec()
cust, err := config.NewBench(benchFile)
if err != nil {
log.Debug().Msgf("No custom benchmark config file found")
return def
}
if b, ok := cust.Benchmarks.Containers[PodToKey(path)]; ok {
return b
}
def.C, def.N = cust.Benchmarks.Defaults.C, cust.Benchmarks.Defaults.N
return def
}

View File

@ -0,0 +1,33 @@
package dao_test
import (
"testing"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
"github.com/stretchr/testify/assert"
)
func TestBenchForConfig(t *testing.T) {
uu := map[string]struct {
file, key string
spec config.BenchConfig
}{
"no_file": {file: "", key: "", spec: config.DefaultBenchSpec()},
"spec": {file: "testdata/benchspec.yml", key: "default/nginx-123-456:nginx", spec: config.BenchConfig{
C: 2,
N: 3000,
HTTP: config.HTTP{
Method: "GET",
Path: "/",
},
}},
}
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
assert.NotNil(t, u.spec, dao.BenchConfigFor(u.file, u.key))
})
}
}

View File

@ -23,14 +23,9 @@ import (
const localhost = "localhost"
// Tunnel represents a host tunnel port mapper.
type Tunnel struct {
Address, LocalPort, ContainerPort string
}
// PortForwarder tracks a port forward stream.
type PortForwarder struct {
client.Connection
Factory
genericclioptions.IOStreams
stopChan, readyChan chan struct{}
@ -42,11 +37,11 @@ type PortForwarder struct {
}
// NewPortForwarder returns a new port forward streamer.
func NewPortForwarder(c client.Connection) *PortForwarder {
func NewPortForwarder(f Factory) *PortForwarder {
return &PortForwarder{
Connection: c,
stopChan: make(chan struct{}),
readyChan: make(chan struct{}),
Factory: f,
stopChan: make(chan struct{}),
readyChan: make(chan struct{}),
}
}
@ -72,7 +67,12 @@ func (p *PortForwarder) Ports() []string {
// Path returns the pod resource path.
func (p *PortForwarder) Path() string {
return p.path + ":" + p.container
return PortForwardID(p.path, p.container)
}
// PortForwardID computes port-forward identifier.
func PortForwardID(path, co string) string {
return path + ":" + co
}
// Container returns the targetes container.
@ -92,13 +92,23 @@ func (p *PortForwarder) FQN() string {
return p.path + ":" + p.container
}
// HasPortMapping checks if port mapping is defined for this fwd.
func (p *PortForwarder) HasPortMapping(m string) bool {
for _, mapping := range p.ports {
if mapping == m {
return true
}
}
return false
}
// Start initiates a port forward session for a given pod and ports.
func (p *PortForwarder) Start(path, co string, t Tunnel) (*portforward.PortForwarder, error) {
fwds := []string{t.LocalPort + ":" + t.ContainerPort}
func (p *PortForwarder) Start(path, co string, t client.PortTunnel) (*portforward.PortForwarder, error) {
fwds := []string{t.PortMap()}
p.path, p.container, p.ports, p.age = path, co, fwds, time.Now()
ns, n := client.Namespaced(path)
auth, err := p.CanI(ns, "v1/pods", []string{client.GetVerb})
auth, err := p.Client().CanI(ns, "v1/pods", []string{client.GetVerb})
if err != nil {
return nil, err
}
@ -106,8 +116,9 @@ func (p *PortForwarder) Start(path, co string, t Tunnel) (*portforward.PortForwa
return nil, fmt.Errorf("user is not authorized to get pods")
}
// BOZO!! Use the factory!
pod, err := p.DialOrDie().CoreV1().Pods(ns).Get(n, metav1.GetOptions{})
var res Pod
res.Init(p, client.NewGVR("v1/pods"))
pod, err := res.GetInstance(path)
if err != nil {
return nil, err
}
@ -115,7 +126,7 @@ func (p *PortForwarder) Start(path, co string, t Tunnel) (*portforward.PortForwa
return nil, fmt.Errorf("unable to forward port because pod is not running. Current status=%v", pod.Status.Phase)
}
auth, err = p.CanI(ns, "v1/pods:portforward", []string{client.UpdateVerb})
auth, err = p.Client().CanI(ns, "v1/pods:portforward", []string{client.UpdateVerb})
if err != nil {
return nil, err
}
@ -123,7 +134,7 @@ func (p *PortForwarder) Start(path, co string, t Tunnel) (*portforward.PortForwa
return nil, fmt.Errorf("user is not authorized to update portforward")
}
rcfg := p.RestConfigOrDie()
rcfg := p.Client().RestConfigOrDie()
rcfg.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"}
rcfg.APIPath = "/api"
codec, _ := codec()
@ -143,7 +154,7 @@ func (p *PortForwarder) Start(path, co string, t Tunnel) (*portforward.PortForwa
}
func (p *PortForwarder) forwardPorts(method string, url *url.URL, address string, ports []string) (*portforward.PortForwarder, error) {
cfg, err := p.Config().RESTConfig()
cfg, err := p.Client().Config().RESTConfig()
if err != nil {
return nil, err
}

View File

@ -89,7 +89,7 @@ func TestExtractString(t *testing.T) {
// Helpers...
func load(t *testing.T, n string) *unstructured.Unstructured {
raw, err := ioutil.ReadFile(fmt.Sprintf("assets/%s.json", n))
raw, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.json", n))
assert.Nil(t, err)
var o unstructured.Unstructured

View File

@ -21,6 +21,7 @@ var (
_ Loggable = (*StatefulSet)(nil)
_ Restartable = (*StatefulSet)(nil)
_ Scalable = (*StatefulSet)(nil)
_ Controller = (*StatefulSet)(nil)
)
// StatefulSet represents a K8s sts.
@ -51,12 +52,7 @@ func (s *StatefulSet) Scale(path string, replicas int32) error {
// Restart a StatefulSet rollout.
func (s *StatefulSet) Restart(path string) error {
o, err := s.Factory.Get(s.gvr.String(), path, true, labels.Everything())
if err != nil {
return err
}
var ds appsv1.StatefulSet
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
sts, err := s.getStatefulSet(path)
if err != nil {
return err
}
@ -70,24 +66,18 @@ func (s *StatefulSet) Restart(path string) error {
return fmt.Errorf("user is not authorized to update statefulsets")
}
update, err := polymorphichelpers.ObjectRestarterFn(&ds)
update, err := polymorphichelpers.ObjectRestarterFn(sts)
if err != nil {
return err
}
_, err = s.Client().DialOrDie().AppsV1().StatefulSets(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, update)
_, err = s.Client().DialOrDie().AppsV1().StatefulSets(sts.Namespace).Patch(sts.Name, types.StrategicMergePatchType, update)
return err
}
// TailLogs tail logs for all pods represented by this StatefulSet.
func (s *StatefulSet) TailLogs(ctx context.Context, c chan<- []byte, opts LogOptions) error {
o, err := s.Factory.Get(s.gvr.String(), opts.Path, true, labels.Everything())
if err != nil {
return err
}
var sts appsv1.StatefulSet
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sts)
sts, err := s.getStatefulSet(opts.Path)
if err != nil {
return errors.New("expecting StatefulSet resource")
}
@ -97,3 +87,28 @@ func (s *StatefulSet) TailLogs(ctx context.Context, c chan<- []byte, opts LogOpt
return podLogs(ctx, c, sts.Spec.Selector.MatchLabels, opts)
}
// Pod returns a pod victim by name.
func (s *StatefulSet) Pod(fqn string) (string, error) {
sts, err := s.getStatefulSet(fqn)
if err != nil {
return "", err
}
return podFromSelector(s.Factory, sts.Namespace, sts.Spec.Selector.MatchLabels)
}
func (s *StatefulSet) getStatefulSet(fqn string) (*appsv1.StatefulSet, error) {
o, err := s.Factory.Get(s.gvr.String(), fqn, false, labels.Everything())
if err != nil {
return nil, err
}
var sts appsv1.StatefulSet
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sts)
if err != nil {
return nil, errors.New("expecting Service resource")
}
return &sts, nil
}

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"github.com/derailed/k9s/internal/client"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@ -12,8 +13,9 @@ import (
)
var (
_ Accessor = (*Service)(nil)
_ Loggable = (*Service)(nil)
_ Accessor = (*Service)(nil)
_ Loggable = (*Service)(nil)
_ Controller = (*Service)(nil)
)
// Service represents a k8s service.
@ -23,19 +25,61 @@ type Service struct {
// TailLogs tail logs for all pods represented by this Service.
func (s *Service) TailLogs(ctx context.Context, c chan<- []byte, opts LogOptions) error {
o, err := s.Factory.Get(s.gvr.String(), opts.Path, true, labels.Everything())
svc, err := s.GetInstance(opts.Path)
if err != nil {
return err
}
var svc v1.Service
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &svc)
if err != nil {
return errors.New("expecting Service resource")
}
if svc.Spec.Selector == nil || len(svc.Spec.Selector) == 0 {
return fmt.Errorf("no valid selector found on Service %s", opts.Path)
}
return podLogs(ctx, c, svc.Spec.Selector, opts)
}
// Pod returns a pod victim by name.
func (s *Service) Pod(fqn string) (string, error) {
svc, err := s.GetInstance(fqn)
if err != nil {
return "", err
}
return podFromSelector(s.Factory, svc.Namespace, svc.Spec.Selector)
}
// GetInstance returns a service instance.
func (s *Service) GetInstance(fqn string) (*v1.Service, error) {
o, err := s.Factory.Get(s.gvr.String(), fqn, false, labels.Everything())
if err != nil {
return nil, err
}
var svc v1.Service
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &svc)
if err != nil {
return nil, errors.New("expecting Service resource")
}
return &svc, nil
}
// ----------------------------------------------------------------------------
// Helpers...
func podFromSelector(f Factory, ns string, sel map[string]string) (string, error) {
oo, err := f.List("v1/pods", ns, true, labels.Set(sel).AsSelector())
if err != nil {
return "", err
}
if len(oo) == 0 {
return "", fmt.Errorf("no matching pods for %v", sel)
}
var pod v1.Pod
err = runtime.DefaultUnstructuredConverter.FromUnstructured(oo[0].(*unstructured.Unstructured).Object, &pod)
if err != nil {
return "", err
}
return client.FQN(pod.Namespace, pod.Name), nil
}

19
internal/dao/testdata/benchspec.yml vendored Normal file
View File

@ -0,0 +1,19 @@
benchmarks:
defaults:
concurrency: 2
requests: 500
containers:
default/nginx:nginx:
concurrency: 2
requests: 3000
http:
method: GET
path: /
services:
default/nginx:
concurrency: 1
requests: 666
http:
method: GET
host: 192.168.64.1
path: /

View File

@ -91,6 +91,12 @@ type Scalable interface {
Scale(path string, replicas int32) error
}
// Controller represents a pod controller.
type Controller interface {
// Pod returns a pod instance matching the selector.
Pod(path string) (string, error)
}
// Nuker represents a resource deleter.
type Nuker interface {
// Delete removes a resource from the api server.

View File

@ -69,12 +69,6 @@ func TestTableMeta(t *testing.T) {
accessor dao.Accessor
renderer Renderer
}{
// BOZO!!
// "full": {
// gvr: "v1/pods",
// accessor: &pd,
// renderer: &render.Pod{},
// },
"generic": {
gvr: "containers",
accessor: &dao.Container{},
@ -144,7 +138,7 @@ func TestTableGenericHydrate(t *testing.T) {
// Helpers...
func mustLoad(n string) *unstructured.Unstructured {
raw, err := ioutil.ReadFile(fmt.Sprintf("test_assets/%s.json", n))
raw, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.json", n))
if err != nil {
panic(err)
}
@ -156,7 +150,7 @@ func mustLoad(n string) *unstructured.Unstructured {
}
func load(t *testing.T, n string) *unstructured.Unstructured {
raw, err := ioutil.ReadFile(fmt.Sprintf("test_assets/%s.json", n))
raw, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.json", n))
assert.Nil(t, err)
var o unstructured.Unstructured
err = json.Unmarshal(raw, &o)
@ -165,7 +159,7 @@ func load(t *testing.T, n string) *unstructured.Unstructured {
}
func raw(t *testing.T, n string) []byte {
raw, err := ioutil.ReadFile(fmt.Sprintf("test_assets/%s.json", n))
raw, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.json", n))
assert.Nil(t, err)
return raw
}

View File

@ -116,7 +116,7 @@ func makeTableFactory() tableFactory {
}
func mustLoad(n string) *unstructured.Unstructured {
raw, err := ioutil.ReadFile(fmt.Sprintf("test_assets/%s.json", n))
raw, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.json", n))
if err != nil {
panic(err)
}

View File

@ -18,19 +18,19 @@ func TestAugmentRow(t *testing.T) {
e Fields
}{
"cool": {
"assets/b1.txt",
"testdata/b1.txt",
Fields{"pass", "3.3544", "29.8116", "100", "0"},
},
"2XX": {
"assets/b4.txt",
"testdata/b4.txt",
Fields{"pass", "3.3544", "29.8116", "160", "0"},
},
"4XX/5XX": {
"assets/b2.txt",
"testdata/b2.txt",
Fields{"pass", "3.3544", "29.8116", "100", "12"},
},
"toast": {
"assets/b3.txt",
"testdata/b3.txt",
Fields{"fail", "2.3688", "35.4606", "0", "0"},
},
}

View File

@ -12,7 +12,7 @@ import (
// Helpers...
func load(t assert.TestingT, n string) *unstructured.Unstructured {
raw, err := ioutil.ReadFile(fmt.Sprintf("assets/%s.json", n))
raw, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.json", n))
assert.Nil(t, err)
var o unstructured.Unstructured

View File

@ -20,10 +20,10 @@ type synchronizer interface {
// Configurator represents an application configurationa.
type Configurator struct {
skinFile string
Config *config.Config
Styles *config.Styles
Bench *config.Bench
skinFile string
Config *config.Config
Styles *config.Styles
BenchFile string
}
// HasSkin returns true if a skin file was located.
@ -67,21 +67,15 @@ func (c *Configurator) StylesUpdater(ctx context.Context, s synchronizer) error
return w.Add(c.skinFile)
}
// InitBench load benchmark configuration if any.
func (c *Configurator) InitBench(cluster string) {
var err error
if c.Bench, err = config.NewBench(BenchConfig(cluster)); err != nil {
log.Info().Msg("No benchmark config file found, using defaults.")
}
}
// BenchConfig location of the benchmarks configuration file.
func BenchConfig(cluster string) string {
return filepath.Join(config.K9sHome, config.K9sBench+"-"+cluster+".yml")
func BenchConfig(context string) string {
return filepath.Join(config.K9sHome, config.K9sBench+"-"+context+".yml")
}
// RefreshStyles load for skin configuration changes.
func (c *Configurator) RefreshStyles(context string) {
c.BenchFile = BenchConfig(context)
clusterSkins := filepath.Join(config.K9sHome, fmt.Sprintf("%s_skin.yml", context))
if c.Styles == nil {
c.Styles = config.NewStyles()

View File

@ -17,7 +17,7 @@ func TestBenchConfig(t *testing.T) {
}
func TestConfiguratorRefreshStyle(t *testing.T) {
config.K9sStylesFile = filepath.Join("..", "config", "test_assets", "black_and_wtf.yml")
config.K9sStylesFile = filepath.Join("..", "config", "testdata", "black_and_wtf.yml")
cfg := ui.Configurator{}
cfg.RefreshStyles("")
@ -26,15 +26,3 @@ func TestConfiguratorRefreshStyle(t *testing.T) {
assert.Equal(t, tcell.ColorGhostWhite, render.StdColor)
assert.Equal(t, tcell.ColorWhiteSmoke, render.ErrColor)
}
func TestInitBench(t *testing.T) {
config.K9sHome = filepath.Join("..", "config", "test_assets")
cfg := ui.Configurator{}
cfg.InitBench("fred")
assert.NotNil(t, cfg.Bench)
assert.Equal(t, 2, cfg.Bench.Benchmarks.Defaults.C)
assert.Equal(t, 1000, cfg.Bench.Benchmarks.Defaults.N)
assert.Equal(t, 2, len(cfg.Bench.Benchmarks.Services))
}

View File

@ -1,47 +0,0 @@
package dialog
import (
"testing"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview"
"github.com/stretchr/testify/assert"
)
func TestPortForwards(t *testing.T) {
p := ui.NewPages()
cbFunc := func(path, co string, t dao.Tunnel) {}
ShowPortForwards(p, config.NewStyles(), "fred", []string{"8080"}, cbFunc)
d := p.GetPrimitive(portForwardKey).(*tview.ModalForm)
assert.NotNil(t, d)
DismissPortForwards(p)
assert.Nil(t, p.GetPrimitive(portForwardKey))
}
func TestExtractPort(t *testing.T) {
uu := map[string]struct {
port, e string
}{
"full": {
"fred:8000", "8000",
},
"port": {
"8000", "8000",
},
"protocol": {
"dns:53UDP", "53",
},
}
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
assert.Equal(t, u.e, extractPort(u.port))
})
}
}

View File

@ -16,7 +16,7 @@ import (
const (
menuIndexFmt = " [key:-:b]<%d> [fg:-:d]%s "
maxRows = 7
maxRows = 6
)
var menuRX = regexp.MustCompile(`\d`)

View File

@ -18,7 +18,7 @@ import (
"github.com/rs/zerolog/log"
)
// ExitStatus indicates UI exit conditions.
// // ExitStatus indicates UI exit conditions.
var ExitStatus = ""
const (
@ -50,7 +50,6 @@ func NewApp(cfg *config.Config) *App {
Content: NewPageStack(),
}
a.Config = cfg
a.InitBench(cfg.K9s.CurrentCluster)
a.Views()["statusIndicator"] = ui.NewStatusIndicator(a.App, a.Styles)
a.Views()["clusterInfo"] = NewClusterInfo(&a)

View File

@ -145,7 +145,6 @@ func (b *Browser) TableDataChanged(data render.TableData) {
b.app.QueueUpdateDraw(func() {
b.refreshActions()
b.Update(data)
b.App().ClearStatus(false)
})
}

View File

@ -6,13 +6,10 @@ import (
"strings"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
"k8s.io/client-go/tools/portforward"
)
const (
@ -114,23 +111,11 @@ func (c *Container) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
log.Debug().Msgf("CONTAINER-PORTS %#v", ports)
dialog.ShowPortForwards(c.App().Content.Pages, c.App().Styles, c.GetTable().Path, ports, c.portForward)
ShowPortForwards(c, c.GetTable().Path, ports, startFwdCB)
return nil
}
func (c *Container) portForward(path, co string, t dao.Tunnel) {
pf := dao.NewPortForwarder(c.App().Conn())
fw, err := pf.Start(path, co, t)
if err != nil {
c.App().Flash().Err(err)
return
}
log.Debug().Msgf(">>> Starting port forward %q %#v", path, t)
go runForward(c.App(), pf, fw)
}
func (c *Container) isForwardable(path string) ([]string, bool) {
state := c.GetTable().GetSelectedCell(3)
if state != "Running" {
@ -155,24 +140,3 @@ func (c *Container) isForwardable(path string) ([]string, bool) {
return pp, true
}
// ----------------------------------------------------------------------------
// Helpers...
func runForward(a *App, pf *dao.PortForwarder, f *portforward.PortForwarder) {
a.QueueUpdateDraw(func() {
a.factory.AddForwarder(pf)
a.Flash().Infof("PortForward activated %s:%s", pf.Path(), pf.Ports()[0])
dialog.DismissPortForwards(a.Content.Pages)
})
pf.SetActive(true)
if err := f.ForwardPorts(); err != nil {
a.Flash().Err(err)
return
}
a.QueueUpdateDraw(func() {
a.factory.DeleteForwarder(pf.FQN())
pf.SetActive(false)
})
}

View File

@ -2,13 +2,10 @@ package view
import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
const scaleDialogKey = "scale"
@ -21,8 +18,15 @@ type Deploy struct {
// NewDeploy returns a new deployment view.
func NewDeploy(gvr client.GVR) ResourceViewer {
d := Deploy{
ResourceViewer: NewRestartExtender(
NewScaleExtender(NewLogsExtender(NewBrowser(gvr), nil)),
ResourceViewer: NewPortForwardExtender(
NewRestartExtender(
NewScaleExtender(
NewLogsExtender(
NewBrowser(gvr),
nil,
),
),
),
),
}
d.SetBindKeysFn(d.bindKeys)
@ -41,21 +45,19 @@ func (d *Deploy) bindKeys(aa ui.KeyActions) {
}
func (d *Deploy) showPods(app *App, model ui.Tabular, gvr, path string) {
o, err := app.factory.Get(d.GVR(), path, true, labels.Everything())
var res dao.Deployment
res.Init(app.factory, client.NewGVR(d.GVR()))
dp, err := res.GetInstance(path)
if err != nil {
app.Flash().Err(err)
return
}
var dp appsv1.Deployment
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
if err != nil {
app.Flash().Err(err)
}
showPodsFromSelector(app, path, dp.Spec.Selector)
}
// ----------------------------------------------------------------------------
// Helpers...
func showPodsFromSelector(app *App, path string, sel *metav1.LabelSelector) {

View File

@ -13,6 +13,5 @@ func TestDeploy(t *testing.T) {
assert.Nil(t, v.Init(makeCtx()))
assert.Equal(t, "Deployments", v.Name())
assert.Equal(t, 10, len(v.Hints()))
assert.Equal(t, 11, len(v.Hints()))
}

View File

@ -2,12 +2,9 @@ package view
import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
// DaemonSet represents a daemon set custom viewer.
@ -18,8 +15,10 @@ type DaemonSet struct {
// NewDaemonSet returns a new viewer.
func NewDaemonSet(gvr client.GVR) ResourceViewer {
d := DaemonSet{
ResourceViewer: NewRestartExtender(
NewLogsExtender(NewBrowser(gvr), nil),
ResourceViewer: NewPortForwardExtender(
NewRestartExtender(
NewLogsExtender(NewBrowser(gvr), nil),
),
),
}
d.SetBindKeysFn(d.bindKeys)
@ -40,14 +39,10 @@ func (d *DaemonSet) bindKeys(aa ui.KeyActions) {
}
func (d *DaemonSet) showPods(app *App, model ui.Tabular, _, path string) {
o, err := app.factory.Get(d.GVR(), path, true, labels.Everything())
if err != nil {
d.App().Flash().Err(err)
return
}
var res dao.DaemonSet
res.Init(app.factory, client.NewGVR(d.GVR()))
var ds appsv1.DaemonSet
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
ds, err := res.GetInstance(path)
if err != nil {
d.App().Flash().Err(err)
}

View File

@ -13,5 +13,5 @@ func TestDaemonSet(t *testing.T) {
assert.Nil(t, v.Init(makeCtx()))
assert.Equal(t, "DaemonSets", v.Name())
assert.Equal(t, 11, len(v.Hints()))
assert.Equal(t, 12, len(v.Hints()))
}

View File

@ -235,11 +235,11 @@ func (h *Help) showGeneral() model.MenuHints {
Description: "Clear command",
},
{
Mnemonic: "Ctrl-h",
Mnemonic: "t",
Description: "Toggle Header",
},
{
Mnemonic: ":q",
Mnemonic: "q",
Description: "Quit",
},
{

Some files were not shown because too many files have changed in this diff Show More