mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Retry resizing replication controllers in kubectl
This commit is contained in:
parent
71e545bf81
commit
1970c2d201
@ -144,7 +144,7 @@ func (s *CMServer) Run(_ []string) error {
|
|||||||
go util.Forever(func() { endpoints.SyncServiceEndpoints() }, time.Second*10)
|
go util.Forever(func() { endpoints.SyncServiceEndpoints() }, time.Second*10)
|
||||||
|
|
||||||
controllerManager := replicationControllerPkg.NewReplicationManager(kubeClient)
|
controllerManager := replicationControllerPkg.NewReplicationManager(kubeClient)
|
||||||
controllerManager.Run(10 * time.Second)
|
controllerManager.Run(replicationControllerPkg.DefaultSyncPeriod)
|
||||||
|
|
||||||
kubeletClient, err := client.NewKubeletClient(&s.KubeletConfig)
|
kubeletClient, err := client.NewKubeletClient(&s.KubeletConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -133,7 +133,7 @@ func runControllerManager(machineList []string, cl *client.Client, nodeMilliCPU,
|
|||||||
go util.Forever(func() { endpoints.SyncServiceEndpoints() }, time.Second*10)
|
go util.Forever(func() { endpoints.SyncServiceEndpoints() }, time.Second*10)
|
||||||
|
|
||||||
controllerManager := controller.NewReplicationManager(cl)
|
controllerManager := controller.NewReplicationManager(cl)
|
||||||
controllerManager.Run(10 * time.Second)
|
controllerManager.Run(controller.DefaultSyncPeriod)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startComponents(etcdClient tools.EtcdClient, cl *client.Client, addr net.IP, port int) {
|
func startComponents(etcdClient tools.EtcdClient, cl *client.Client, addr net.IP, port int) {
|
||||||
|
@ -56,6 +56,9 @@ type RealPodControl struct {
|
|||||||
kubeClient client.Interface
|
kubeClient client.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Time period of main replication controller sync loop
|
||||||
|
const DefaultSyncPeriod = 10 * time.Second
|
||||||
|
|
||||||
func (r RealPodControl) createReplica(namespace string, controller api.ReplicationController) {
|
func (r RealPodControl) createReplica(namespace string, controller api.ReplicationController) {
|
||||||
desiredLabels := make(labels.Set)
|
desiredLabels := make(labels.Set)
|
||||||
for k, v := range controller.Spec.Template.Labels {
|
for k, v := range controller.Spec.Template.Labels {
|
||||||
|
@ -19,9 +19,12 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,6 +40,9 @@ $ kubectl resize --replicas=3 replicationcontrollers foo
|
|||||||
|
|
||||||
// If the replication controller named foo's current size is 2, resize foo to 3.
|
// If the replication controller named foo's current size is 2, resize foo to 3.
|
||||||
$ kubectl resize --current-replicas=2 --replicas=3 replicationcontrollers foo`
|
$ kubectl resize --current-replicas=2 --replicas=3 replicationcontrollers foo`
|
||||||
|
|
||||||
|
retryFrequency = controller.DefaultSyncPeriod / 100
|
||||||
|
retryTimeout = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f *Factory) NewCmdResize(out io.Writer) *cobra.Command {
|
func (f *Factory) NewCmdResize(out io.Writer) *cobra.Command {
|
||||||
@ -63,9 +69,15 @@ func (f *Factory) NewCmdResize(out io.Writer) *cobra.Command {
|
|||||||
|
|
||||||
resourceVersion := util.GetFlagString(cmd, "resource-version")
|
resourceVersion := util.GetFlagString(cmd, "resource-version")
|
||||||
currentSize := util.GetFlagInt(cmd, "current-replicas")
|
currentSize := util.GetFlagInt(cmd, "current-replicas")
|
||||||
s, err := resizer.Resize(namespace, name, &kubectl.ResizePrecondition{currentSize, resourceVersion}, uint(count))
|
precondition := &kubectl.ResizePrecondition{currentSize, resourceVersion}
|
||||||
checkErr(err)
|
cond := kubectl.ResizeCondition(resizer, precondition, namespace, name, uint(count))
|
||||||
fmt.Fprintf(out, "%s\n", s)
|
|
||||||
|
msg := "resized"
|
||||||
|
if err = wait.Poll(retryFrequency, retryTimeout, cond); err != nil {
|
||||||
|
msg = fmt.Sprintf("Failed to resize controller in spite of retrying for %s", retryTimeout)
|
||||||
|
checkErr(err)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "%s\n", msg)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.Flags().String("resource-version", "", "Precondition for resource version. Requires that the current resource version match this value in order to resize.")
|
cmd.Flags().String("resource-version", "", "Precondition for resource version. Requires that the current resource version match this value in order to resize.")
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResizePrecondition describes a condition that must be true for the resize to take place
|
// ResizePrecondition describes a condition that must be true for the resize to take place
|
||||||
@ -33,23 +34,46 @@ type ResizePrecondition struct {
|
|||||||
ResourceVersion string
|
ResourceVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A PreconditionError is returned when a replication controller fails to match
|
||||||
|
// the resize preconditions passed to kubectl.
|
||||||
type PreconditionError struct {
|
type PreconditionError struct {
|
||||||
Precondition string
|
Precondition string
|
||||||
ExpectedValue string
|
ExpectedValue string
|
||||||
ActualValue string
|
ActualValue string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *PreconditionError) Error() string {
|
func (pe PreconditionError) Error() string {
|
||||||
return fmt.Sprintf("Expected %s to be %s, was %s", pe.Precondition, pe.ExpectedValue, pe.ActualValue)
|
return fmt.Sprintf("Expected %s to be %s, was %s", pe.Precondition, pe.ExpectedValue, pe.ActualValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ControllerResizeErrorType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ControllerResizeGetFailure ControllerResizeErrorType = iota
|
||||||
|
ControllerResizeUpdateFailure
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ControllerResizeError is returned when a the resize request passes
|
||||||
|
// preconditions but fails to actually resize the controller.
|
||||||
|
type ControllerResizeError struct {
|
||||||
|
FailureType ControllerResizeErrorType
|
||||||
|
ResourceVersion string
|
||||||
|
ActualError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ControllerResizeError) Error() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Resizing the controller failed with: %s; Current resource version %s",
|
||||||
|
c.ActualError, c.ResourceVersion)
|
||||||
|
}
|
||||||
|
|
||||||
// Validate ensures that the preconditions match. Returns nil if they are valid, an error otherwise
|
// Validate ensures that the preconditions match. Returns nil if they are valid, an error otherwise
|
||||||
func (precondition *ResizePrecondition) Validate(controller *api.ReplicationController) error {
|
func (precondition *ResizePrecondition) Validate(controller *api.ReplicationController) error {
|
||||||
if precondition.Size != -1 && controller.Spec.Replicas != precondition.Size {
|
if precondition.Size != -1 && controller.Spec.Replicas != precondition.Size {
|
||||||
return &PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(controller.Spec.Replicas)}
|
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(controller.Spec.Replicas)}
|
||||||
}
|
}
|
||||||
if precondition.ResourceVersion != "" && controller.ResourceVersion != precondition.ResourceVersion {
|
if precondition.ResourceVersion != "" && controller.ResourceVersion != precondition.ResourceVersion {
|
||||||
return &PreconditionError{"resource version", precondition.ResourceVersion, controller.ResourceVersion}
|
return PreconditionError{"resource version", precondition.ResourceVersion, controller.ResourceVersion}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -70,11 +94,27 @@ type ReplicationControllerResizer struct {
|
|||||||
client.Interface
|
client.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResizeCondition is a closure around Resize that facilitates retries via util.wait
|
||||||
|
func ResizeCondition(r Resizer, precondition *ResizePrecondition, namespace, name string, count uint) wait.ConditionFunc {
|
||||||
|
return func() (bool, error) {
|
||||||
|
_, err := r.Resize(namespace, name, precondition, count)
|
||||||
|
switch e, _ := err.(ControllerResizeError); err.(type) {
|
||||||
|
case nil:
|
||||||
|
return true, nil
|
||||||
|
case ControllerResizeError:
|
||||||
|
if e.FailureType == ControllerResizeUpdateFailure {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (resize *ReplicationControllerResizer) Resize(namespace, name string, preconditions *ResizePrecondition, newSize uint) (string, error) {
|
func (resize *ReplicationControllerResizer) Resize(namespace, name string, preconditions *ResizePrecondition, newSize uint) (string, error) {
|
||||||
rc := resize.ReplicationControllers(namespace)
|
rc := resize.ReplicationControllers(namespace)
|
||||||
controller, err := rc.Get(name)
|
controller, err := rc.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", ControllerResizeError{ControllerResizeGetFailure, "Unknown", err}
|
||||||
}
|
}
|
||||||
|
|
||||||
if preconditions != nil {
|
if preconditions != nil {
|
||||||
@ -86,7 +126,7 @@ func (resize *ReplicationControllerResizer) Resize(namespace, name string, preco
|
|||||||
controller.Spec.Replicas = int(newSize)
|
controller.Spec.Replicas = int(newSize)
|
||||||
// TODO: do retry on 409 errors here?
|
// TODO: do retry on 409 errors here?
|
||||||
if _, err := rc.Update(controller); err != nil {
|
if _, err := rc.Update(controller); err != nil {
|
||||||
return "", err
|
return "", ControllerResizeError{ControllerResizeUpdateFailure, controller.ResourceVersion, err}
|
||||||
}
|
}
|
||||||
// TODO: do a better job of printing objects here.
|
// TODO: do a better job of printing objects here.
|
||||||
return "resized", nil
|
return "resized", nil
|
||||||
|
@ -17,14 +17,53 @@ limitations under the License.
|
|||||||
package kubectl
|
package kubectl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "strings"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
// "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ErrorReplicationControllers struct {
|
||||||
|
client.FakeReplicationControllers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorReplicationControllers) Update(controller *api.ReplicationController) (*api.ReplicationController, error) {
|
||||||
|
return nil, errors.New("Replication controller update failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorReplicationControllerClient struct {
|
||||||
|
client.Fake
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorReplicationControllerClient) ReplicationControllers(namespace string) client.ReplicationControllerInterface {
|
||||||
|
return &ErrorReplicationControllers{client.FakeReplicationControllers{&c.Fake, namespace}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplicationControllerResizeRetry(t *testing.T) {
|
||||||
|
fake := &ErrorReplicationControllerClient{Fake: client.Fake{}}
|
||||||
|
resizer := ReplicationControllerResizer{fake}
|
||||||
|
preconditions := ResizePrecondition{-1, ""}
|
||||||
|
count := uint(3)
|
||||||
|
name := "foo"
|
||||||
|
namespace := "default"
|
||||||
|
|
||||||
|
resizeFunc := ResizeCondition(&resizer, &preconditions, namespace, name, count)
|
||||||
|
pass, err := resizeFunc()
|
||||||
|
if pass != false {
|
||||||
|
t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Did not expect an error on update failure, got %v", err)
|
||||||
|
}
|
||||||
|
preconditions = ResizePrecondition{3, ""}
|
||||||
|
resizeFunc = ResizeCondition(&resizer, &preconditions, namespace, name, count)
|
||||||
|
pass, err = resizeFunc()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error on precondition failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestReplicationControllerResize(t *testing.T) {
|
func TestReplicationControllerResize(t *testing.T) {
|
||||||
fake := &client.Fake{}
|
fake := &client.Fake{}
|
||||||
resizer := ReplicationControllerResizer{fake}
|
resizer := ReplicationControllerResizer{fake}
|
||||||
|
@ -68,6 +68,13 @@ func TestPoll(t *testing.T) {
|
|||||||
if invocations == 0 {
|
if invocations == 0 {
|
||||||
t.Errorf("Expected at least one invocation, got zero")
|
t.Errorf("Expected at least one invocation, got zero")
|
||||||
}
|
}
|
||||||
|
expectedError := errors.New("Expected error")
|
||||||
|
f = ConditionFunc(func() (bool, error) {
|
||||||
|
return false, expectedError
|
||||||
|
})
|
||||||
|
if err := Poll(time.Microsecond, time.Microsecond, f); err == nil || err != expectedError {
|
||||||
|
t.Fatalf("Expected error %v, got none %v", expectedError, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPollForever(t *testing.T) {
|
func TestPollForever(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user