diff --git a/go.mod b/go.mod index 72026535..69e180a7 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,9 @@ module github.com/derailed/k9s go 1.12 replace ( - k8s.io/api => k8s.io/api v0.0.0-20190222213804-5cb15d344471 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20190325193600-475668423e9f - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190221213512-86fb29eff628 k8s.io/apiserver => k8s.io/apiserver v0.0.0-20190319190228-a4358799e4fe k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20190325194458-f2b4781c3ae1 - k8s.io/client-go => k8s.io/client-go v10.0.0+incompatible k8s.io/metrics => k8s.io/metrics v0.0.0-20190325194013-29123f6a4aa6 ) @@ -31,7 +28,6 @@ require ( github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/imdario/mergo v0.3.7 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/json-iterator/go v1.1.6 // indirect github.com/mattn/go-runewidth v0.0.4 github.com/onsi/ginkgo v1.8.0 // indirect @@ -40,8 +36,7 @@ require ( github.com/petergtz/pegomock v0.0.0-20181206220228-b113d17a7e81 github.com/rakyll/hey v0.1.2 github.com/rs/zerolog v1.14.3 - github.com/spf13/cobra v0.0.3 - github.com/spf13/pflag v1.0.3 // indirect + github.com/spf13/cobra v0.0.5 github.com/stretchr/testify v1.3.0 github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 // indirect golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 // indirect @@ -54,12 +49,12 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.2.2 gotest.tools v2.2.0+incompatible - k8s.io/api v0.0.0-20190425012535-181e1f9c52c1 + k8s.io/api v0.0.0-20190222213804-5cb15d344471 k8s.io/apiextensions-apiserver v0.0.0-20190426053235-842c4571cde0 // indirect - k8s.io/apimachinery v0.0.0-20190425132440-17f84483f500 + k8s.io/apimachinery v0.0.0-20190221213512-86fb29eff628 k8s.io/apiserver v0.0.0-20190426133039-accf7b6d6716 // indirect k8s.io/cli-runtime v0.0.0-20190325194458-f2b4781c3ae1 - k8s.io/client-go v10.0.0+incompatible + k8s.io/client-go v0.0.0-20190307161346-7621a5ebb88b k8s.io/klog v0.3.0 k8s.io/kube-openapi v0.0.0-20190426233423-c5d3b0f4bee0 // indirect k8s.io/kubernetes v1.13.5 diff --git a/go.sum b/go.sum index 23f1046d..0c280119 100644 --- a/go.sum +++ b/go.sum @@ -27,11 +27,16 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -110,6 +115,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= @@ -131,11 +137,14 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08 h1:5MnxBC15uMxFv5FY/J/8vzyaBiArCOkMdFT9Jsw78iY= github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a h1:TpvdAwDAt1K4ANVOfcihouRdvP+MgAfDWwBuct4l6ZY= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da h1:ZQGIPjr1iTtUPXZFk8WShqb5G+Qg65VHFLtSvmHh+Mw= @@ -152,6 +161,7 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/petergtz/pegomock v0.0.0-20181206220228-b113d17a7e81 h1:MhSbvsIs4KvpPYr4taOvb6j+r9VNbj/08AfjsKi+Ui0= @@ -182,25 +192,35 @@ github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYe github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.14.3 h1:4EGfSkR2hJDB0s3oFfrlPqjU1e4WLncergLil3nEKW0= github.com/rs/zerolog v1.14.3/go.mod h1:3WXPzbXEEliJ+a6UFE4vhIxV8qR1EML6ngzP9ug4eYg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 h1:j2hhcujLRHAg872RWAV5yaUrEjHEObwDv3aImCaNLek= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo= @@ -240,6 +260,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -303,6 +324,8 @@ k8s.io/apiserver v0.0.0-20190319190228-a4358799e4fe h1:zD63Eo0qbcR9JzZ90yQsFMzXY k8s.io/apiserver v0.0.0-20190319190228-a4358799e4fe/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w= k8s.io/cli-runtime v0.0.0-20190325194458-f2b4781c3ae1 h1:yIrGaL3GC1eoxtkSXoNMHKzF1QIbC0TLq07OApgVvc0= k8s.io/cli-runtime v0.0.0-20190325194458-f2b4781c3ae1/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM= +k8s.io/client-go v0.0.0-20190307161346-7621a5ebb88b h1:bLd5Yb1SyymFAEOqKAKd376ak8SN++QHZprkosLhV8Q= +k8s.io/client-go v0.0.0-20190307161346-7621a5ebb88b/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/client-go v10.0.0+incompatible h1:F1IqCqw7oMBzDkqlcBymRq1450wD0eNqLE9jzUrIi34= k8s.io/client-go v10.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/internal/k8s/dp.go b/internal/k8s/dp.go index 08dd9a70..defa1c9b 100644 --- a/internal/k8s/dp.go +++ b/internal/k8s/dp.go @@ -26,6 +26,7 @@ func (d *Deployment) List(ns string) (Collection, error) { LabelSelector: d.labelSelector, FieldSelector: d.fieldSelector, } + rr, err := d.DialOrDie().Apps().Deployments(ns).List(opts) if err != nil { return nil, err @@ -42,3 +43,15 @@ func (d *Deployment) List(ns string) (Collection, error) { func (d *Deployment) Delete(ns, n string, cascade, force bool) error { return d.DialOrDie().Apps().Deployments(ns).Delete(n, nil) } + +// Scale a Deployment. +func (d *Deployment) Scale(ns, n string, replicas int32) error { + scale, err := d.DialOrDie().Apps().Deployments(ns).GetScale(n, metav1.GetOptions{}) + if err != nil { + return err + } + + scale.Spec.Replicas = replicas + _, err = d.DialOrDie().Apps().Deployments(ns).UpdateScale(n, scale) + return err +} diff --git a/internal/k8s/rc.go b/internal/k8s/rc.go index 9340eea3..24fcea9c 100644 --- a/internal/k8s/rc.go +++ b/internal/k8s/rc.go @@ -48,3 +48,15 @@ func (r *ReplicationController) Delete(ns, n string, cascade, force bool) error PropagationPolicy: &p, }) } + +// Scale a ReplicationController. +func (r *ReplicationController) Scale(ns, n string, replicas int32) error { + scale, err := r.DialOrDie().Core().ReplicationControllers(ns).GetScale(n, metav1.GetOptions{}) + if err != nil { + return err + } + + scale.Spec.Replicas = replicas + _, err = r.DialOrDie().Core().ReplicationControllers(ns).UpdateScale(n, scale) + return err +} diff --git a/internal/k8s/sts.go b/internal/k8s/sts.go index 2b64412c..dc198aca 100644 --- a/internal/k8s/sts.go +++ b/internal/k8s/sts.go @@ -48,3 +48,15 @@ func (s *StatefulSet) Delete(ns, n string, cascade, force bool) error { PropagationPolicy: &p, }) } + +// Scale a StatefulSet. +func (s *StatefulSet) Scale(ns, n string, replicas int32) error { + scale, err := s.DialOrDie().Apps().StatefulSets(ns).GetScale(n, metav1.GetOptions{}) + if err != nil { + return err + } + + scale.Spec.Replicas = replicas + _, err = s.DialOrDie().Apps().StatefulSets(ns).UpdateScale(n, scale) + return err +} diff --git a/internal/resource/base.go b/internal/resource/base.go index 5296e2b2..0e1e14d5 100644 --- a/internal/resource/base.go +++ b/internal/resource/base.go @@ -20,7 +20,7 @@ import ( ) type ( - // Cruder represent a crudable Kubernetes resource. + // Cruder represents a crudable Kubernetes resource. Cruder interface { Get(ns string, name string) (interface{}, error) List(ns string) (k8s.Collection, error) @@ -32,6 +32,11 @@ type ( HasSelectors() bool } + // Scalable represents a scalable Kubernetes resource. + Scalable interface { + Scale(ns string, name string, replicas int32) error + } + // Connection represents a Kubenetes apiserver connection. Connection k8s.Connection diff --git a/internal/resource/dp.go b/internal/resource/dp.go index 97a09b77..49ea3a7d 100644 --- a/internal/resource/dp.go +++ b/internal/resource/dp.go @@ -124,3 +124,8 @@ func (r *Deployment) Fields(ns string) Row { toAge(i.ObjectMeta.CreationTimestamp), ) } + +// Scale the specified resource. +func (r *Deployment) Scale(ns, n string, replicas int32) error { + return r.Resource.(Scalable).Scale(ns, n, replicas) +} diff --git a/internal/resource/rc.go b/internal/resource/rc.go index fe28f3a3..5de14bee 100644 --- a/internal/resource/rc.go +++ b/internal/resource/rc.go @@ -89,3 +89,8 @@ func (r *ReplicationController) Fields(ns string) Row { toAge(i.ObjectMeta.CreationTimestamp), ) } + +// Scale the specified resource. +func (r *ReplicationController) Scale(ns, n string, replicas int32) error { + return r.Resource.(Scalable).Scale(ns, n, replicas) +} \ No newline at end of file diff --git a/internal/resource/sts.go b/internal/resource/sts.go index b79db816..ec955a32 100644 --- a/internal/resource/sts.go +++ b/internal/resource/sts.go @@ -113,3 +113,8 @@ func (r *StatefulSet) Fields(ns string) Row { toAge(i.ObjectMeta.CreationTimestamp), ) } + +// Scale the specified resource. +func (r *StatefulSet) Scale(ns, n string, replicas int32) error { + return r.Resource.(Scalable).Scale(ns, n, replicas) +} diff --git a/internal/views/dp.go b/internal/views/dp.go index 996001c8..a4175253 100644 --- a/internal/views/dp.go +++ b/internal/views/dp.go @@ -1,18 +1,27 @@ package views import ( + "fmt" + "strconv" + "github.com/derailed/k9s/internal/k8s" "github.com/derailed/k9s/internal/resource" + "github.com/derailed/tview" + "github.com/gdamore/tcell" v1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type deployView struct { *logResourceView + scalableResourceView *scalableResourceView } +const scaleDialogKey = "scale" + func newDeployView(title string, app *appView, list resource.List) resourceViewer { - v := deployView{newLogResourceView(title, app, list)} + logResourceView := newLogResourceView(title, app, list) + v := deployView{logResourceView, newScalableResourceViewForParent(logResourceView.resourceView)} v.extraActionsFn = v.extraActions v.enterFn = v.showPods @@ -21,6 +30,7 @@ func newDeployView(title string, app *appView, list resource.List) resourceViewe func (v *deployView) extraActions(aa keyActions) { v.logResourceView.extraActions(aa) + v.scalableResourceView.extraActions(aa) aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(2, false), true) aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(3, false), true) } @@ -43,3 +53,66 @@ func (v *deployView) showPods(app *appView, _, res, sel string) { showPods(app, ns, l.String(), "", v.backCmd) } + +func (v *deployView) scaleCmd(evt *tcell.EventKey) *tcell.EventKey { + if !v.rowSelected() { + return evt + } + + v.showScaleDialog(v.getList().GetName(), v.getSelectedItem()) + return nil +} + +func (v *deployView) scale(selection string, replicas int) { + ns, n := namespaced(selection) + d := k8s.NewDeployment(v.app.conn()) + + err := d.Scale(ns, n, int32(replicas)) + if err != nil { + v.app.flash().err(err) + } +} + +func (v *deployView) showScaleDialog(resourceType string, resourceName string) { + f := tview.NewForm() + f.SetItemPadding(0) + f.SetButtonsAlign(tview.AlignCenter). + SetButtonBackgroundColor(tview.Styles.PrimitiveBackgroundColor). + SetButtonTextColor(tview.Styles.PrimaryTextColor). + SetLabelColor(tcell.ColorAqua). + SetFieldTextColor(tcell.ColorOrange) + + replicas := "1" + f.AddInputField("Replicas:", replicas, 4, func(textToCheck string, lastChar rune) bool { + _, err := strconv.Atoi(textToCheck) + return err == nil + }, func(changed string) { + replicas = changed + }) + + dismiss := func() { + v.Pages.RemovePage(scaleDialogKey) + } + + f.AddButton("OK", func() { + if val, err := strconv.Atoi(replicas); err == nil { + v.scale(v.getSelectedItem(), val) + } else { + v.app.flash().err(err) + } + + dismiss() + }) + + f.AddButton("Cancel", func() { + dismiss() + }) + + confirm := tview.NewModalForm("", f) + confirm.SetText(fmt.Sprintf("Scale %s %s", resourceType, resourceName)) + confirm.SetDoneFunc(func(int, string) { + dismiss() + }) + v.AddPage(scaleDialogKey, confirm, false, false) + v.ShowPage(scaleDialogKey) +} diff --git a/internal/views/registrar.go b/internal/views/registrar.go index c98e15bb..170fa7cb 100644 --- a/internal/views/registrar.go +++ b/internal/views/registrar.go @@ -256,7 +256,7 @@ func coreRes(m map[string]resCmd) { crdCmd: crdCmd{ api: "", }, - viewFn: newResourceView, + viewFn: newScalableResourceView, listFn: resource.NewReplicationControllerList, colorerFn: rsColorer, } diff --git a/internal/views/scalable_resource.go b/internal/views/scalable_resource.go new file mode 100644 index 00000000..6d75fc25 --- /dev/null +++ b/internal/views/scalable_resource.go @@ -0,0 +1,96 @@ +package views + +import ( + "fmt" + "strconv" + + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/tview" + "github.com/gdamore/tcell" +) + +type ( + scalableResourceView struct { + *resourceView + } +) + +func newScalableResourceView(title string, app *appView, list resource.List) resourceViewer { + return *newScalableResourceViewForParent(newResourceView(title, app, list).(*resourceView)) +} + +func newScalableResourceViewForParent(parent *resourceView) *scalableResourceView { + v := scalableResourceView{ + parent, + } + parent.extraActionsFn = v.extraActions + return &v +} + +func (v *scalableResourceView) extraActions(aa keyActions) { + aa[KeyS] = newKeyAction("Scale", v.scaleCmd, true) +} + +func (v *scalableResourceView) scaleCmd(evt *tcell.EventKey) *tcell.EventKey { + if !v.rowSelected() { + return evt + } + + v.showScaleDialog(v.list.GetName(), v.getSelectedItem()) + return nil +} + +func (v *scalableResourceView) scale(selection string, replicas int) { + ns, n := namespaced(selection) + + r := v.list.Resource().(resource.Scalable) + + err := r.Scale(ns, n, int32(replicas)) + if err != nil { + v.app.flash().err(err) + } +} + +func (v *scalableResourceView) showScaleDialog(resourceType string, resourceName string) { + f := tview.NewForm() + f.SetItemPadding(0) + f.SetButtonsAlign(tview.AlignCenter). + SetButtonBackgroundColor(tview.Styles.PrimitiveBackgroundColor). + SetButtonTextColor(tview.Styles.PrimaryTextColor). + SetLabelColor(tcell.ColorAqua). + SetFieldTextColor(tcell.ColorOrange) + + replicas := "1" + f.AddInputField("Replicas:", replicas, 4, func(textToCheck string, lastChar rune) bool { + _, err := strconv.Atoi(textToCheck) + return err == nil + }, func(changed string) { + replicas = changed + }) + + dismiss := func() { + v.Pages.RemovePage(scaleDialogKey) + } + + f.AddButton("OK", func() { + if val, err := strconv.Atoi(replicas); err == nil { + v.scale(v.selectedItem, val) + } else { + v.app.flash().err(err) + } + + dismiss() + }) + + f.AddButton("Cancel", func() { + dismiss() + }) + + confirm := tview.NewModalForm("", f) + confirm.SetText(fmt.Sprintf("Scale %s %s", resourceType, resourceName)) + confirm.SetDoneFunc(func(int, string) { + dismiss() + }) + v.AddPage(scaleDialogKey, confirm, false, false) + v.ShowPage(scaleDialogKey) +} diff --git a/internal/views/sts.go b/internal/views/sts.go index baf0a96c..38a976af 100644 --- a/internal/views/sts.go +++ b/internal/views/sts.go @@ -10,10 +10,12 @@ import ( type statefulSetView struct { *logResourceView + scalableResourceView *scalableResourceView } func newStatefulSetView(t string, app *appView, list resource.List) resourceViewer { - v := statefulSetView{newLogResourceView(t, app, list)} + logResourceView := newLogResourceView(t, app, list) + v := statefulSetView{logResourceView, newScalableResourceViewForParent(logResourceView.resourceView)} v.extraActionsFn = v.extraActions v.enterFn = v.showPods @@ -22,6 +24,7 @@ func newStatefulSetView(t string, app *appView, list resource.List) resourceView func (v *statefulSetView) extraActions(aa keyActions) { v.logResourceView.extraActions(aa) + v.scalableResourceView.extraActions(aa) aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(1, false), true) aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(2, false), true) }