kubectl: Support scaling deployments
This commit adds support for using kubectl scale to scale deployments. Makes use of the deployments/scale endpoint instead of updating deployment.spec.replicas directly.
This commit is contained in:
parent
a7425bf070
commit
99fc35880b
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
.PP
|
.PP
|
||||||
kubectl scale \- Set a new size for a Replication Controller.
|
kubectl scale \- Set a new size for a Replication Controller, Job, or Deployment.
|
||||||
|
|
||||||
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -13,7 +13,7 @@ kubectl scale \- Set a new size for a Replication Controller.
|
|||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.PP
|
.PP
|
||||||
Set a new size for a Replication Controller.
|
Set a new size for a Replication Controller, Job, or Deployment.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
Scale also allows users to specify one or more preconditions for the scale action.
|
Scale also allows users to specify one or more preconditions for the scale action.
|
||||||
@ -25,11 +25,11 @@ scale is sent to the server.
|
|||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.PP
|
.PP
|
||||||
\fB\-\-current\-replicas\fP=\-1
|
\fB\-\-current\-replicas\fP=\-1
|
||||||
Precondition for current size. Requires that the current size of the replication controller match this value in order to scale.
|
Precondition for current size. Requires that the current size of the resource match this value in order to scale.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
\fB\-f\fP, \fB\-\-filename\fP=[]
|
\fB\-f\fP, \fB\-\-filename\fP=[]
|
||||||
Filename, directory, or URL to a file identifying the replication controller to set a new size
|
Filename, directory, or URL to a file identifying the resource to set a new size
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
\fB\-o\fP, \fB\-\-output\fP=""
|
\fB\-o\fP, \fB\-\-output\fP=""
|
||||||
@ -148,16 +148,19 @@ scale is sent to the server.
|
|||||||
|
|
||||||
.nf
|
.nf
|
||||||
# Scale replication controller named 'foo' to 3.
|
# Scale replication controller named 'foo' to 3.
|
||||||
$ kubectl scale \-\-replicas=3 replicationcontrollers foo
|
$ kubectl scale \-\-replicas=3 rc/foo
|
||||||
|
|
||||||
# Scale a replication controller identified by type and name specified in "foo\-controller.yaml" to 3.
|
# Scale a resource identified by type and name specified in "foo.yaml" to 3.
|
||||||
$ kubectl scale \-\-replicas=3 \-f foo\-controller.yaml
|
$ kubectl scale \-\-replicas=3 \-f foo.yaml
|
||||||
|
|
||||||
# If the replication controller named foo's current size is 2, scale foo to 3.
|
# If the deployment named mysql's current size is 2, scale mysql to 3.
|
||||||
$ kubectl scale \-\-current\-replicas=2 \-\-replicas=3 replicationcontrollers foo
|
$ kubectl scale \-\-current\-replicas=2 \-\-replicas=3 deployment/mysql
|
||||||
|
|
||||||
# Scale multiple replication controllers.
|
# Scale multiple replication controllers.
|
||||||
$ kubectl scale \-\-replicas=5 rc/foo rc/bar
|
$ kubectl scale \-\-replicas=5 rc/foo rc/bar rc/baz
|
||||||
|
|
||||||
|
# Scale job named 'cron' to 3.
|
||||||
|
$ kubectl scale \-\-replicas=3 job/cron
|
||||||
|
|
||||||
.fi
|
.fi
|
||||||
.RE
|
.RE
|
||||||
|
@ -101,10 +101,10 @@ kubectl
|
|||||||
* [kubectl replace](kubectl_replace.md) - Replace a resource by filename or stdin.
|
* [kubectl replace](kubectl_replace.md) - Replace a resource by filename or stdin.
|
||||||
* [kubectl rolling-update](kubectl_rolling-update.md) - Perform a rolling update of the given ReplicationController.
|
* [kubectl rolling-update](kubectl_rolling-update.md) - Perform a rolling update of the given ReplicationController.
|
||||||
* [kubectl run](kubectl_run.md) - Run a particular image on the cluster.
|
* [kubectl run](kubectl_run.md) - Run a particular image on the cluster.
|
||||||
* [kubectl scale](kubectl_scale.md) - Set a new size for a Replication Controller.
|
* [kubectl scale](kubectl_scale.md) - Set a new size for a Replication Controller, Job, or Deployment.
|
||||||
* [kubectl version](kubectl_version.md) - Print the client and server version information.
|
* [kubectl version](kubectl_version.md) - Print the client and server version information.
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra on 24-Nov-2015
|
###### Auto generated by spf13/cobra on 25-Nov-2015
|
||||||
|
|
||||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||||
[]()
|
[]()
|
||||||
|
@ -33,12 +33,12 @@ Documentation for other releases can be found at
|
|||||||
|
|
||||||
## kubectl scale
|
## kubectl scale
|
||||||
|
|
||||||
Set a new size for a Replication Controller.
|
Set a new size for a Replication Controller, Job, or Deployment.
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
Set a new size for a Replication Controller.
|
Set a new size for a Replication Controller, Job, or Deployment.
|
||||||
|
|
||||||
Scale also allows users to specify one or more preconditions for the scale action.
|
Scale also allows users to specify one or more preconditions for the scale action.
|
||||||
If --current-replicas or --resource-version is specified, it is validated before the
|
If --current-replicas or --resource-version is specified, it is validated before the
|
||||||
@ -53,23 +53,26 @@ kubectl scale [--resource-version=version] [--current-replicas=count] --replicas
|
|||||||
|
|
||||||
```
|
```
|
||||||
# Scale replication controller named 'foo' to 3.
|
# Scale replication controller named 'foo' to 3.
|
||||||
$ kubectl scale --replicas=3 replicationcontrollers foo
|
$ kubectl scale --replicas=3 rc/foo
|
||||||
|
|
||||||
# Scale a replication controller identified by type and name specified in "foo-controller.yaml" to 3.
|
# Scale a resource identified by type and name specified in "foo.yaml" to 3.
|
||||||
$ kubectl scale --replicas=3 -f foo-controller.yaml
|
$ kubectl scale --replicas=3 -f foo.yaml
|
||||||
|
|
||||||
# If the replication controller named foo's current size is 2, scale foo to 3.
|
# If the deployment named mysql's current size is 2, scale mysql to 3.
|
||||||
$ kubectl scale --current-replicas=2 --replicas=3 replicationcontrollers foo
|
$ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql
|
||||||
|
|
||||||
# Scale multiple replication controllers.
|
# Scale multiple replication controllers.
|
||||||
$ kubectl scale --replicas=5 rc/foo rc/bar
|
$ kubectl scale --replicas=5 rc/foo rc/bar rc/baz
|
||||||
|
|
||||||
|
# Scale job named 'cron' to 3.
|
||||||
|
$ kubectl scale --replicas=3 job/cron
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
--current-replicas=-1: Precondition for current size. Requires that the current size of the replication controller match this value in order to scale.
|
--current-replicas=-1: Precondition for current size. Requires that the current size of the resource match this value in order to scale.
|
||||||
-f, --filename=[]: Filename, directory, or URL to a file identifying the replication controller to set a new size
|
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to set a new size
|
||||||
-o, --output="": Output mode. Use "-o name" for shorter output (resource/name).
|
-o, --output="": Output mode. Use "-o name" for shorter output (resource/name).
|
||||||
--replicas=-1: The new desired number of replicas. Required.
|
--replicas=-1: The new desired number of replicas. Required.
|
||||||
--resource-version="": Precondition for resource version. Requires that the current resource version match this value in order to scale.
|
--resource-version="": Precondition for resource version. Requires that the current resource version match this value in order to scale.
|
||||||
|
@ -219,6 +219,8 @@ runTests() {
|
|||||||
hpa_min_field=".spec.minReplicas"
|
hpa_min_field=".spec.minReplicas"
|
||||||
hpa_max_field=".spec.maxReplicas"
|
hpa_max_field=".spec.maxReplicas"
|
||||||
hpa_cpu_field=".spec.cpuUtilization.targetPercentage"
|
hpa_cpu_field=".spec.cpuUtilization.targetPercentage"
|
||||||
|
job_parallelism_field=".spec.parallelism"
|
||||||
|
deployment_replicas=".spec.replicas"
|
||||||
|
|
||||||
# Passing no arguments to create is an error
|
# Passing no arguments to create is an error
|
||||||
! kubectl create
|
! kubectl create
|
||||||
@ -873,6 +875,23 @@ __EOF__
|
|||||||
# Clean-up
|
# Clean-up
|
||||||
kubectl delete rc redis-{master,slave} "${kube_flags[@]}"
|
kubectl delete rc redis-{master,slave} "${kube_flags[@]}"
|
||||||
|
|
||||||
|
### Scale a job
|
||||||
|
kubectl create -f docs/user-guide/job.yaml "${kube_flags[@]}"
|
||||||
|
# Command
|
||||||
|
kubectl scale --replicas=2 job/pi
|
||||||
|
# Post-condition: 2 replicas for pi
|
||||||
|
kube::test::get_object_assert 'job pi' "{{$job_parallelism_field}}" '2'
|
||||||
|
# Clean-up
|
||||||
|
kubectl delete job/pi "${kube_flags[@]}"
|
||||||
|
### Scale a deployment
|
||||||
|
kubectl create -f examples/extensions/deployment.yaml "${kube_flags[@]}"
|
||||||
|
# Command
|
||||||
|
kubectl scale --current-replicas=3 --replicas=1 deployment/nginx-deployment
|
||||||
|
# Post-condition: 1 replica for nginx-deployment
|
||||||
|
kube::test::get_object_assert 'deployment nginx-deployment' "{{$deployment_replicas}}" '1'
|
||||||
|
# Clean-up
|
||||||
|
kubectl delete deployment/nginx-deployment "${kube_flags[@]}"
|
||||||
|
|
||||||
### Expose replication controller as service
|
### Expose replication controller as service
|
||||||
# Pre-condition: 2 replicas
|
# Pre-condition: 2 replicas
|
||||||
kube::test::get_object_assert 'rc frontend' "{{$rc_replicas_field}}" '2'
|
kube::test::get_object_assert 'rc frontend' "{{$rc_replicas_field}}" '2'
|
||||||
|
@ -18,9 +18,9 @@ package extensions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
)
|
)
|
||||||
@ -65,3 +65,21 @@ func PodSelectorAsSelector(ps *PodSelector) (labels.Selector, error) {
|
|||||||
sort.Sort(labels.ByKey(selector))
|
sort.Sort(labels.ByKey(selector))
|
||||||
return selector, nil
|
return selector, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScaleFromDeployment returns a scale subresource for a deployment.
|
||||||
|
func ScaleFromDeployment(deployment *Deployment) *Scale {
|
||||||
|
return &Scale{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: deployment.Name,
|
||||||
|
Namespace: deployment.Namespace,
|
||||||
|
CreationTimestamp: deployment.CreationTimestamp,
|
||||||
|
},
|
||||||
|
Spec: ScaleSpec{
|
||||||
|
Replicas: deployment.Spec.Replicas,
|
||||||
|
},
|
||||||
|
Status: ScaleStatus{
|
||||||
|
Replicas: deployment.Status.Replicas,
|
||||||
|
Selector: deployment.Spec.Selector,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -45,10 +45,10 @@ func ControllerHasDesiredReplicas(c Interface, controller *api.ReplicationContro
|
|||||||
|
|
||||||
// JobHasDesiredParallelism returns a condition that will be true if the desired parallelism count
|
// JobHasDesiredParallelism returns a condition that will be true if the desired parallelism count
|
||||||
// for a job equals the current active counts or is less by an appropriate successful/unsuccessful count.
|
// for a job equals the current active counts or is less by an appropriate successful/unsuccessful count.
|
||||||
func JobHasDesiredParallelism(c Interface, job *extensions.Job) wait.ConditionFunc {
|
func JobHasDesiredParallelism(c ExtensionsInterface, job *extensions.Job) wait.ConditionFunc {
|
||||||
|
|
||||||
return func() (bool, error) {
|
return func() (bool, error) {
|
||||||
job, err := c.Extensions().Jobs(job.Namespace).Get(job.Name)
|
job, err := c.Jobs(job.Namespace).Get(job.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -62,3 +62,17 @@ func JobHasDesiredParallelism(c Interface, job *extensions.Job) wait.ConditionFu
|
|||||||
return progress == 0, nil
|
return progress == 0, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeploymentHasDesiredReplicas returns a condition that will be true if and only if
|
||||||
|
// the desired replica count for a deployment equals its updated replicas count.
|
||||||
|
// (non-terminated pods that have the desired template spec).
|
||||||
|
func DeploymentHasDesiredReplicas(c ExtensionsInterface, deployment *extensions.Deployment) wait.ConditionFunc {
|
||||||
|
|
||||||
|
return func() (bool, error) {
|
||||||
|
deployment, err := c.Deployments(deployment.Namespace).Get(deployment.Name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return deployment.Status.UpdatedReplicas == deployment.Spec.Replicas, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -315,6 +315,11 @@ func (c *Fake) SwaggerSchema(version string) (*swagger.ApiDeclaration, error) {
|
|||||||
return &swagger.ApiDeclaration{}, nil
|
return &swagger.ApiDeclaration{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSimpleFakeExp returns a client that will respond with the provided objects
|
||||||
|
func NewSimpleFakeExp(objects ...runtime.Object) *FakeExperimental {
|
||||||
|
return &FakeExperimental{Fake: NewSimpleFake(objects...)}
|
||||||
|
}
|
||||||
|
|
||||||
type FakeExperimental struct {
|
type FakeExperimental struct {
|
||||||
*Fake
|
*Fake
|
||||||
}
|
}
|
||||||
|
@ -36,23 +36,26 @@ type ScaleOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
scale_long = `Set a new size for a Replication Controller.
|
scale_long = `Set a new size for a Replication Controller, Job, or Deployment.
|
||||||
|
|
||||||
Scale also allows users to specify one or more preconditions for the scale action.
|
Scale also allows users to specify one or more preconditions for the scale action.
|
||||||
If --current-replicas or --resource-version is specified, it is validated before the
|
If --current-replicas or --resource-version is specified, it is validated before the
|
||||||
scale is attempted, and it is guaranteed that the precondition holds true when the
|
scale is attempted, and it is guaranteed that the precondition holds true when the
|
||||||
scale is sent to the server.`
|
scale is sent to the server.`
|
||||||
scale_example = `# Scale replication controller named 'foo' to 3.
|
scale_example = `# Scale replication controller named 'foo' to 3.
|
||||||
$ kubectl scale --replicas=3 replicationcontrollers foo
|
$ kubectl scale --replicas=3 rc/foo
|
||||||
|
|
||||||
# Scale a replication controller identified by type and name specified in "foo-controller.yaml" to 3.
|
# Scale a resource identified by type and name specified in "foo.yaml" to 3.
|
||||||
$ kubectl scale --replicas=3 -f foo-controller.yaml
|
$ kubectl scale --replicas=3 -f foo.yaml
|
||||||
|
|
||||||
# If the replication controller named foo's current size is 2, scale foo to 3.
|
# If the deployment named mysql's current size is 2, scale mysql to 3.
|
||||||
$ kubectl scale --current-replicas=2 --replicas=3 replicationcontrollers foo
|
$ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql
|
||||||
|
|
||||||
# Scale multiple replication controllers.
|
# Scale multiple replication controllers.
|
||||||
$ kubectl scale --replicas=5 rc/foo rc/bar`
|
$ kubectl scale --replicas=5 rc/foo rc/bar rc/baz
|
||||||
|
|
||||||
|
# Scale job named 'cron' to 3.
|
||||||
|
$ kubectl scale --replicas=3 job/cron`
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale
|
// NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale
|
||||||
@ -63,7 +66,7 @@ func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
|||||||
Use: "scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)",
|
Use: "scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)",
|
||||||
// resize is deprecated
|
// resize is deprecated
|
||||||
Aliases: []string{"resize"},
|
Aliases: []string{"resize"},
|
||||||
Short: "Set a new size for a Replication Controller.",
|
Short: "Set a new size for a Replication Controller, Job, or Deployment.",
|
||||||
Long: scale_long,
|
Long: scale_long,
|
||||||
Example: scale_example,
|
Example: scale_example,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
@ -74,13 +77,13 @@ func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.Flags().String("resource-version", "", "Precondition for resource version. Requires that the current resource version match this value in order to scale.")
|
cmd.Flags().String("resource-version", "", "Precondition for resource version. Requires that the current resource version match this value in order to scale.")
|
||||||
cmd.Flags().Int("current-replicas", -1, "Precondition for current size. Requires that the current size of the replication controller match this value in order to scale.")
|
cmd.Flags().Int("current-replicas", -1, "Precondition for current size. Requires that the current size of the resource match this value in order to scale.")
|
||||||
cmd.Flags().Int("replicas", -1, "The new desired number of replicas. Required.")
|
cmd.Flags().Int("replicas", -1, "The new desired number of replicas. Required.")
|
||||||
cmd.MarkFlagRequired("replicas")
|
cmd.MarkFlagRequired("replicas")
|
||||||
cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a scale operation, zero means don't wait.")
|
cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a scale operation, zero means don't wait.")
|
||||||
cmdutil.AddOutputFlagsForMutation(cmd)
|
cmdutil.AddOutputFlagsForMutation(cmd)
|
||||||
|
|
||||||
usage := "Filename, directory, or URL to a file identifying the replication controller to set a new size"
|
usage := "Filename, directory, or URL to a file identifying the resource to set a new size"
|
||||||
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
|
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -127,11 +130,11 @@ func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
|
|||||||
|
|
||||||
resourceVersion := cmdutil.GetFlagString(cmd, "resource-version")
|
resourceVersion := cmdutil.GetFlagString(cmd, "resource-version")
|
||||||
if len(resourceVersion) != 0 && len(infos) > 1 {
|
if len(resourceVersion) != 0 && len(infos) > 1 {
|
||||||
return fmt.Errorf("cannot use --resource-version with multiple controllers")
|
return fmt.Errorf("cannot use --resource-version with multiple resources")
|
||||||
}
|
}
|
||||||
currentSize := cmdutil.GetFlagInt(cmd, "current-replicas")
|
currentSize := cmdutil.GetFlagInt(cmd, "current-replicas")
|
||||||
if currentSize != -1 && len(infos) > 1 {
|
if currentSize != -1 && len(infos) > 1 {
|
||||||
return fmt.Errorf("cannot use --current-replicas with multiple controllers")
|
return fmt.Errorf("cannot use --current-replicas with multiple resources")
|
||||||
}
|
}
|
||||||
precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: resourceVersion}
|
precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: resourceVersion}
|
||||||
retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout)
|
retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout)
|
||||||
|
@ -28,74 +28,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/util/wait"
|
"k8s.io/kubernetes/pkg/util/wait"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ScalePrecondition describes a condition that must be true for the scale to take place
|
// Scaler provides an interface for resources that can be scaled.
|
||||||
// If CurrentSize == -1, it is ignored.
|
|
||||||
// If CurrentResourceVersion is the empty string, it is ignored.
|
|
||||||
// Otherwise they must equal the values in the replication controller for it to be valid.
|
|
||||||
type ScalePrecondition struct {
|
|
||||||
Size int
|
|
||||||
ResourceVersion string
|
|
||||||
}
|
|
||||||
|
|
||||||
// A PreconditionError is returned when a replication controller fails to match
|
|
||||||
// the scale preconditions passed to kubectl.
|
|
||||||
type PreconditionError struct {
|
|
||||||
Precondition string
|
|
||||||
ExpectedValue string
|
|
||||||
ActualValue string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pe PreconditionError) Error() string {
|
|
||||||
return fmt.Sprintf("Expected %s to be %s, was %s", pe.Precondition, pe.ExpectedValue, pe.ActualValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ControllerScaleErrorType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
ControllerScaleGetFailure ControllerScaleErrorType = iota
|
|
||||||
ControllerScaleUpdateFailure
|
|
||||||
ControllerScaleUpdateInvalidFailure
|
|
||||||
)
|
|
||||||
|
|
||||||
// A ControllerScaleError is returned when a scale request passes
|
|
||||||
// preconditions but fails to actually scale the controller.
|
|
||||||
type ControllerScaleError struct {
|
|
||||||
FailureType ControllerScaleErrorType
|
|
||||||
ResourceVersion string
|
|
||||||
ActualError error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c ControllerScaleError) Error() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"Scaling the controller failed with: %s; Current resource version %s",
|
|
||||||
c.ActualError, c.ResourceVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateReplicationController ensures that the preconditions match. Returns nil if they are valid, an error otherwise
|
|
||||||
func (precondition *ScalePrecondition) ValidateReplicationController(controller *api.ReplicationController) error {
|
|
||||||
if precondition.Size != -1 && controller.Spec.Replicas != precondition.Size {
|
|
||||||
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(controller.Spec.Replicas)}
|
|
||||||
}
|
|
||||||
if precondition.ResourceVersion != "" && controller.ResourceVersion != precondition.ResourceVersion {
|
|
||||||
return PreconditionError{"resource version", precondition.ResourceVersion, controller.ResourceVersion}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateJob ensures that the preconditions match. Returns nil if they are valid, an error otherwise
|
|
||||||
func (precondition *ScalePrecondition) ValidateJob(job *extensions.Job) error {
|
|
||||||
if precondition.Size != -1 && job.Spec.Parallelism == nil {
|
|
||||||
return PreconditionError{"parallelism", strconv.Itoa(precondition.Size), "nil"}
|
|
||||||
}
|
|
||||||
if precondition.Size != -1 && *job.Spec.Parallelism != precondition.Size {
|
|
||||||
return PreconditionError{"parallelism", strconv.Itoa(precondition.Size), strconv.Itoa(*job.Spec.Parallelism)}
|
|
||||||
}
|
|
||||||
if precondition.ResourceVersion != "" && job.ResourceVersion != precondition.ResourceVersion {
|
|
||||||
return PreconditionError{"resource version", precondition.ResourceVersion, job.ResourceVersion}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Scaler interface {
|
type Scaler interface {
|
||||||
// Scale scales the named resource after checking preconditions. It optionally
|
// Scale scales the named resource after checking preconditions. It optionally
|
||||||
// retries in the event of resource version mismatch (if retry is not nil),
|
// retries in the event of resource version mismatch (if retry is not nil),
|
||||||
@ -111,16 +44,54 @@ func ScalerFor(kind string, c client.Interface) (Scaler, error) {
|
|||||||
case "ReplicationController":
|
case "ReplicationController":
|
||||||
return &ReplicationControllerScaler{c}, nil
|
return &ReplicationControllerScaler{c}, nil
|
||||||
case "Job":
|
case "Job":
|
||||||
return &JobScaler{c}, nil
|
return &JobScaler{c.Extensions()}, nil
|
||||||
|
case "Deployment":
|
||||||
|
return &DeploymentScaler{c.Extensions()}, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no scaler has been implemented for %q", kind)
|
return nil, fmt.Errorf("no scaler has been implemented for %q", kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReplicationControllerScaler struct {
|
// ScalePrecondition describes a condition that must be true for the scale to take place
|
||||||
c client.Interface
|
// If CurrentSize == -1, it is ignored.
|
||||||
|
// If CurrentResourceVersion is the empty string, it is ignored.
|
||||||
|
// Otherwise they must equal the values in the resource for it to be valid.
|
||||||
|
type ScalePrecondition struct {
|
||||||
|
Size int
|
||||||
|
ResourceVersion string
|
||||||
}
|
}
|
||||||
type JobScaler struct {
|
|
||||||
c client.Interface
|
// A PreconditionError is returned when a resource fails to match
|
||||||
|
// the scale preconditions passed to kubectl.
|
||||||
|
type PreconditionError struct {
|
||||||
|
Precondition string
|
||||||
|
ExpectedValue string
|
||||||
|
ActualValue string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe PreconditionError) Error() string {
|
||||||
|
return fmt.Sprintf("Expected %s to be %s, was %s", pe.Precondition, pe.ExpectedValue, pe.ActualValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScaleErrorType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ScaleGetFailure ScaleErrorType = iota
|
||||||
|
ScaleUpdateFailure
|
||||||
|
ScaleUpdateInvalidFailure
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ScaleError is returned when a scale request passes
|
||||||
|
// preconditions but fails to actually scale the controller.
|
||||||
|
type ScaleError struct {
|
||||||
|
FailureType ScaleErrorType
|
||||||
|
ResourceVersion string
|
||||||
|
ActualError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ScaleError) Error() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Scaling the resource failed with: %v; Current resource version %s",
|
||||||
|
c.ActualError, c.ResourceVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetryParams encapsulates the retry parameters used by kubectl's scaler.
|
// RetryParams encapsulates the retry parameters used by kubectl's scaler.
|
||||||
@ -136,15 +107,15 @@ func NewRetryParams(interval, timeout time.Duration) *RetryParams {
|
|||||||
func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name string, count uint) wait.ConditionFunc {
|
func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name string, count uint) wait.ConditionFunc {
|
||||||
return func() (bool, error) {
|
return func() (bool, error) {
|
||||||
err := r.ScaleSimple(namespace, name, precondition, count)
|
err := r.ScaleSimple(namespace, name, precondition, count)
|
||||||
switch e, _ := err.(ControllerScaleError); err.(type) {
|
switch e, _ := err.(ScaleError); err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return true, nil
|
return true, nil
|
||||||
case ControllerScaleError:
|
case ScaleError:
|
||||||
// if it's invalid we shouldn't keep waiting
|
// if it's invalid we shouldn't keep waiting
|
||||||
if e.FailureType == ControllerScaleUpdateInvalidFailure {
|
if e.FailureType == ScaleUpdateInvalidFailure {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if e.FailureType == ControllerScaleUpdateFailure {
|
if e.FailureType == ScaleUpdateFailure {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,10 +123,25 @@ func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateReplicationController ensures that the preconditions match. Returns nil if they are valid, an error otherwise
|
||||||
|
func (precondition *ScalePrecondition) ValidateReplicationController(controller *api.ReplicationController) error {
|
||||||
|
if precondition.Size != -1 && controller.Spec.Replicas != precondition.Size {
|
||||||
|
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(controller.Spec.Replicas)}
|
||||||
|
}
|
||||||
|
if len(precondition.ResourceVersion) != 0 && controller.ResourceVersion != precondition.ResourceVersion {
|
||||||
|
return PreconditionError{"resource version", precondition.ResourceVersion, controller.ResourceVersion}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReplicationControllerScaler struct {
|
||||||
|
c client.Interface
|
||||||
|
}
|
||||||
|
|
||||||
func (scaler *ReplicationControllerScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
|
func (scaler *ReplicationControllerScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
|
||||||
controller, err := scaler.c.ReplicationControllers(namespace).Get(name)
|
controller, err := scaler.c.ReplicationControllers(namespace).Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ControllerScaleError{ControllerScaleGetFailure, "Unknown", err}
|
return ScaleError{ScaleGetFailure, "Unknown", err}
|
||||||
}
|
}
|
||||||
if preconditions != nil {
|
if preconditions != nil {
|
||||||
if err := preconditions.ValidateReplicationController(controller); err != nil {
|
if err := preconditions.ValidateReplicationController(controller); err != nil {
|
||||||
@ -166,11 +152,10 @@ func (scaler *ReplicationControllerScaler) ScaleSimple(namespace, name string, p
|
|||||||
// TODO: do retry on 409 errors here?
|
// TODO: do retry on 409 errors here?
|
||||||
if _, err := scaler.c.ReplicationControllers(namespace).Update(controller); err != nil {
|
if _, err := scaler.c.ReplicationControllers(namespace).Update(controller); err != nil {
|
||||||
if errors.IsInvalid(err) {
|
if errors.IsInvalid(err) {
|
||||||
return ControllerScaleError{ControllerScaleUpdateInvalidFailure, controller.ResourceVersion, err}
|
return ScaleError{ScaleUpdateInvalidFailure, controller.ResourceVersion, err}
|
||||||
}
|
}
|
||||||
return ControllerScaleError{ControllerScaleUpdateFailure, controller.ResourceVersion, err}
|
return ScaleError{ScaleUpdateFailure, controller.ResourceVersion, err}
|
||||||
}
|
}
|
||||||
// TODO: do a better job of printing objects here.
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,11 +185,29 @@ func (scaler *ReplicationControllerScaler) Scale(namespace, name string, newSize
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateJob ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
|
||||||
|
func (precondition *ScalePrecondition) ValidateJob(job *extensions.Job) error {
|
||||||
|
if precondition.Size != -1 && job.Spec.Parallelism == nil {
|
||||||
|
return PreconditionError{"parallelism", strconv.Itoa(precondition.Size), "nil"}
|
||||||
|
}
|
||||||
|
if precondition.Size != -1 && *job.Spec.Parallelism != precondition.Size {
|
||||||
|
return PreconditionError{"parallelism", strconv.Itoa(precondition.Size), strconv.Itoa(*job.Spec.Parallelism)}
|
||||||
|
}
|
||||||
|
if len(precondition.ResourceVersion) != 0 && job.ResourceVersion != precondition.ResourceVersion {
|
||||||
|
return PreconditionError{"resource version", precondition.ResourceVersion, job.ResourceVersion}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobScaler struct {
|
||||||
|
c client.ExtensionsInterface
|
||||||
|
}
|
||||||
|
|
||||||
// ScaleSimple is responsible for updating job's parallelism.
|
// ScaleSimple is responsible for updating job's parallelism.
|
||||||
func (scaler *JobScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
|
func (scaler *JobScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
|
||||||
job, err := scaler.c.Extensions().Jobs(namespace).Get(name)
|
job, err := scaler.c.Jobs(namespace).Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ControllerScaleError{ControllerScaleGetFailure, "Unknown", err}
|
return ScaleError{ScaleGetFailure, "Unknown", err}
|
||||||
}
|
}
|
||||||
if preconditions != nil {
|
if preconditions != nil {
|
||||||
if err := preconditions.ValidateJob(job); err != nil {
|
if err := preconditions.ValidateJob(job); err != nil {
|
||||||
@ -213,12 +216,11 @@ func (scaler *JobScaler) ScaleSimple(namespace, name string, preconditions *Scal
|
|||||||
}
|
}
|
||||||
parallelism := int(newSize)
|
parallelism := int(newSize)
|
||||||
job.Spec.Parallelism = ¶llelism
|
job.Spec.Parallelism = ¶llelism
|
||||||
if _, err := scaler.c.Extensions().Jobs(namespace).Update(job); err != nil {
|
if _, err := scaler.c.Jobs(namespace).Update(job); err != nil {
|
||||||
if errors.IsInvalid(err) {
|
if errors.IsInvalid(err) {
|
||||||
return ControllerScaleError{ControllerScaleUpdateInvalidFailure, job.ResourceVersion, err}
|
return ScaleError{ScaleUpdateInvalidFailure, job.ResourceVersion, err}
|
||||||
}
|
}
|
||||||
return ControllerScaleError{ControllerScaleUpdateFailure, job.ResourceVersion, err}
|
return ScaleError{ScaleUpdateFailure, job.ResourceVersion, err}
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -239,7 +241,7 @@ func (scaler *JobScaler) Scale(namespace, name string, newSize uint, preconditio
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if waitForReplicas != nil {
|
if waitForReplicas != nil {
|
||||||
job, err := scaler.c.Extensions().Jobs(namespace).Get(name)
|
job, err := scaler.c.Jobs(namespace).Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -248,3 +250,65 @@ func (scaler *JobScaler) Scale(namespace, name string, newSize uint, preconditio
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateDeployment ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
|
||||||
|
func (precondition *ScalePrecondition) ValidateDeployment(deployment *extensions.Deployment) error {
|
||||||
|
if precondition.Size != -1 && deployment.Spec.Replicas != precondition.Size {
|
||||||
|
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(deployment.Spec.Replicas)}
|
||||||
|
}
|
||||||
|
if len(precondition.ResourceVersion) != 0 && deployment.ResourceVersion != precondition.ResourceVersion {
|
||||||
|
return PreconditionError{"resource version", precondition.ResourceVersion, deployment.ResourceVersion}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeploymentScaler struct {
|
||||||
|
c client.ExtensionsInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScaleSimple is responsible for updating a deployment's desired replicas count.
|
||||||
|
func (scaler *DeploymentScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
|
||||||
|
deployment, err := scaler.c.Deployments(namespace).Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return ScaleError{ScaleGetFailure, "Unknown", err}
|
||||||
|
}
|
||||||
|
if preconditions != nil {
|
||||||
|
if err := preconditions.ValidateDeployment(deployment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scale := extensions.ScaleFromDeployment(deployment)
|
||||||
|
scale.Spec.Replicas = int(newSize)
|
||||||
|
if _, err := scaler.c.Scales(namespace).Update("Deployment", scale); err != nil {
|
||||||
|
if errors.IsInvalid(err) {
|
||||||
|
return ScaleError{ScaleUpdateInvalidFailure, deployment.ResourceVersion, err}
|
||||||
|
}
|
||||||
|
return ScaleError{ScaleUpdateFailure, deployment.ResourceVersion, err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale updates a deployment to a new size, with optional precondition check (if preconditions is not nil),
|
||||||
|
// optional retries (if retry is not nil), and then optionally waits for the status to reach desired count.
|
||||||
|
func (scaler *DeploymentScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
|
||||||
|
if preconditions == nil {
|
||||||
|
preconditions = &ScalePrecondition{-1, ""}
|
||||||
|
}
|
||||||
|
if retry == nil {
|
||||||
|
// Make it try only once, immediately
|
||||||
|
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||||
|
}
|
||||||
|
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize)
|
||||||
|
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if waitForReplicas != nil {
|
||||||
|
deployment, err := scaler.c.Deployments(namespace).Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout,
|
||||||
|
client.DeploymentHasDesiredReplicas(scaler.c, deployment))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -48,45 +48,6 @@ func (c *ErrorReplicationControllerClient) ReplicationControllers(namespace stri
|
|||||||
return &ErrorReplicationControllers{testclient.FakeReplicationControllers{Fake: &c.Fake, Namespace: namespace}, c.invalid}
|
return &ErrorReplicationControllers{testclient.FakeReplicationControllers{Fake: &c.Fake, Namespace: namespace}, c.invalid}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorJobs struct {
|
|
||||||
testclient.FakeJobs
|
|
||||||
invalid bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ErrorJobs) Update(job *extensions.Job) (*extensions.Job, error) {
|
|
||||||
if c.invalid {
|
|
||||||
return nil, kerrors.NewInvalid(job.Kind, job.Name, nil)
|
|
||||||
}
|
|
||||||
return nil, errors.New("Job update failure")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ErrorJobs) Get(name string) (*extensions.Job, error) {
|
|
||||||
zero := 0
|
|
||||||
return &extensions.Job{
|
|
||||||
Spec: extensions.JobSpec{
|
|
||||||
Parallelism: &zero,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorJobClient struct {
|
|
||||||
testclient.FakeExperimental
|
|
||||||
invalid bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ErrorJobClient) Jobs(namespace string) client.JobInterface {
|
|
||||||
return &ErrorJobs{testclient.FakeJobs{Fake: &c.FakeExperimental, Namespace: namespace}, c.invalid}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorExtensionsClient struct {
|
|
||||||
testclient.Fake
|
|
||||||
invalid bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ErrorExtensionsClient) Extensions() client.ExtensionsInterface {
|
|
||||||
return &ErrorJobClient{testclient.FakeExperimental{&c.Fake}, c.invalid}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplicationControllerScaleRetry(t *testing.T) {
|
func TestReplicationControllerScaleRetry(t *testing.T) {
|
||||||
fake := &ErrorReplicationControllerClient{Fake: testclient.Fake{}, invalid: false}
|
fake := &ErrorReplicationControllerClient{Fake: testclient.Fake{}, invalid: false}
|
||||||
scaler := ReplicationControllerScaler{fake}
|
scaler := ReplicationControllerScaler{fake}
|
||||||
@ -124,8 +85,8 @@ func TestReplicationControllerScaleInvalid(t *testing.T) {
|
|||||||
if pass {
|
if pass {
|
||||||
t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass)
|
t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass)
|
||||||
}
|
}
|
||||||
e, ok := err.(ControllerScaleError)
|
e, ok := err.(ScaleError)
|
||||||
if err == nil || !ok || e.FailureType != ControllerScaleUpdateInvalidFailure {
|
if err == nil || !ok || e.FailureType != ScaleUpdateInvalidFailure {
|
||||||
t.Errorf("Expected error on invalid update failure, got %v", err)
|
t.Errorf("Expected error on invalid update failure, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,8 +247,38 @@ func TestValidateReplicationController(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrorJobs struct {
|
||||||
|
testclient.FakeJobs
|
||||||
|
invalid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorJobs) Update(job *extensions.Job) (*extensions.Job, error) {
|
||||||
|
if c.invalid {
|
||||||
|
return nil, kerrors.NewInvalid(job.Kind, job.Name, nil)
|
||||||
|
}
|
||||||
|
return nil, errors.New("Job update failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorJobs) Get(name string) (*extensions.Job, error) {
|
||||||
|
zero := 0
|
||||||
|
return &extensions.Job{
|
||||||
|
Spec: extensions.JobSpec{
|
||||||
|
Parallelism: &zero,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorJobClient struct {
|
||||||
|
testclient.FakeExperimental
|
||||||
|
invalid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorJobClient) Jobs(namespace string) client.JobInterface {
|
||||||
|
return &ErrorJobs{testclient.FakeJobs{Fake: &c.FakeExperimental, Namespace: namespace}, c.invalid}
|
||||||
|
}
|
||||||
|
|
||||||
func TestJobScaleRetry(t *testing.T) {
|
func TestJobScaleRetry(t *testing.T) {
|
||||||
fake := &ErrorExtensionsClient{Fake: testclient.Fake{}, invalid: false}
|
fake := &ErrorJobClient{FakeExperimental: testclient.FakeExperimental{}, invalid: false}
|
||||||
scaler := JobScaler{fake}
|
scaler := JobScaler{fake}
|
||||||
preconditions := ScalePrecondition{-1, ""}
|
preconditions := ScalePrecondition{-1, ""}
|
||||||
count := uint(3)
|
count := uint(3)
|
||||||
@ -311,7 +302,7 @@ func TestJobScaleRetry(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJobScale(t *testing.T) {
|
func TestJobScale(t *testing.T) {
|
||||||
fake := &testclient.Fake{}
|
fake := &testclient.FakeExperimental{Fake: &testclient.Fake{}}
|
||||||
scaler := JobScaler{fake}
|
scaler := JobScaler{fake}
|
||||||
preconditions := ScalePrecondition{-1, ""}
|
preconditions := ScalePrecondition{-1, ""}
|
||||||
count := uint(3)
|
count := uint(3)
|
||||||
@ -331,7 +322,7 @@ func TestJobScale(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJobScaleInvalid(t *testing.T) {
|
func TestJobScaleInvalid(t *testing.T) {
|
||||||
fake := &ErrorExtensionsClient{Fake: testclient.Fake{}, invalid: true}
|
fake := &ErrorJobClient{FakeExperimental: testclient.FakeExperimental{}, invalid: true}
|
||||||
scaler := JobScaler{fake}
|
scaler := JobScaler{fake}
|
||||||
preconditions := ScalePrecondition{-1, ""}
|
preconditions := ScalePrecondition{-1, ""}
|
||||||
count := uint(3)
|
count := uint(3)
|
||||||
@ -343,8 +334,8 @@ func TestJobScaleInvalid(t *testing.T) {
|
|||||||
if pass {
|
if pass {
|
||||||
t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass)
|
t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass)
|
||||||
}
|
}
|
||||||
e, ok := err.(ControllerScaleError)
|
e, ok := err.(ScaleError)
|
||||||
if err == nil || !ok || e.FailureType != ControllerScaleUpdateInvalidFailure {
|
if err == nil || !ok || e.FailureType != ScaleUpdateInvalidFailure {
|
||||||
t.Errorf("Expected error on invalid update failure, got %v", err)
|
t.Errorf("Expected error on invalid update failure, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,7 +347,7 @@ func TestJobScaleFailsPreconditions(t *testing.T) {
|
|||||||
Parallelism: &ten,
|
Parallelism: &ten,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
scaler := JobScaler{fake}
|
scaler := JobScaler{&testclient.FakeExperimental{fake}}
|
||||||
preconditions := ScalePrecondition{2, ""}
|
preconditions := ScalePrecondition{2, ""}
|
||||||
count := uint(3)
|
count := uint(3)
|
||||||
name := "foo"
|
name := "foo"
|
||||||
@ -496,3 +487,267 @@ func TestValidateJob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrorScales struct {
|
||||||
|
testclient.FakeScales
|
||||||
|
invalid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorScales) Update(kind string, scale *extensions.Scale) (*extensions.Scale, error) {
|
||||||
|
if c.invalid {
|
||||||
|
return nil, kerrors.NewInvalid(scale.Kind, scale.Name, nil)
|
||||||
|
}
|
||||||
|
return nil, errors.New("scale update failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorScales) Get(kind, name string) (*extensions.Scale, error) {
|
||||||
|
return &extensions.Scale{
|
||||||
|
Spec: extensions.ScaleSpec{
|
||||||
|
Replicas: 0,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorDeployments struct {
|
||||||
|
testclient.FakeDeployments
|
||||||
|
invalid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorDeployments) Update(deployment *extensions.Deployment) (*extensions.Deployment, error) {
|
||||||
|
if c.invalid {
|
||||||
|
return nil, kerrors.NewInvalid(deployment.Kind, deployment.Name, nil)
|
||||||
|
}
|
||||||
|
return nil, errors.New("deployment update failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorDeployments) Get(name string) (*extensions.Deployment, error) {
|
||||||
|
return &extensions.Deployment{
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: 0,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorDeploymentClient struct {
|
||||||
|
testclient.FakeExperimental
|
||||||
|
invalid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorDeploymentClient) Deployments(namespace string) client.DeploymentInterface {
|
||||||
|
return &ErrorDeployments{testclient.FakeDeployments{Fake: &c.FakeExperimental, Namespace: namespace}, c.invalid}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ErrorDeploymentClient) Scales(namespace string) client.ScaleInterface {
|
||||||
|
return &ErrorScales{testclient.FakeScales{Fake: &c.FakeExperimental, Namespace: namespace}, c.invalid}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeploymentScaleRetry(t *testing.T) {
|
||||||
|
fake := &ErrorDeploymentClient{FakeExperimental: testclient.FakeExperimental{Fake: &testclient.Fake{}}, invalid: false}
|
||||||
|
scaler := &DeploymentScaler{fake}
|
||||||
|
preconditions := &ScalePrecondition{-1, ""}
|
||||||
|
count := uint(3)
|
||||||
|
name := "foo"
|
||||||
|
namespace := "default"
|
||||||
|
|
||||||
|
scaleFunc := ScaleCondition(scaler, preconditions, namespace, name, count)
|
||||||
|
pass, err := scaleFunc()
|
||||||
|
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 = &ScalePrecondition{3, ""}
|
||||||
|
scaleFunc = ScaleCondition(scaler, preconditions, namespace, name, count)
|
||||||
|
pass, err = scaleFunc()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error on precondition failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeploymentScale(t *testing.T) {
|
||||||
|
fake := &testclient.FakeExperimental{Fake: &testclient.Fake{}}
|
||||||
|
scaler := DeploymentScaler{fake}
|
||||||
|
preconditions := ScalePrecondition{-1, ""}
|
||||||
|
count := uint(3)
|
||||||
|
name := "foo"
|
||||||
|
scaler.Scale("default", name, count, &preconditions, nil, nil)
|
||||||
|
|
||||||
|
actions := fake.Actions()
|
||||||
|
if len(actions) != 2 {
|
||||||
|
t.Errorf("unexpected actions: %v, expected 2 actions (get, update)", actions)
|
||||||
|
}
|
||||||
|
if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "deployments" || action.GetName() != name {
|
||||||
|
t.Errorf("unexpected action: %v, expected get-replicationController %s", actions[0], name)
|
||||||
|
}
|
||||||
|
// TODO: The testclient needs to support subresources
|
||||||
|
if action, ok := actions[1].(testclient.UpdateAction); !ok || action.GetResource() != "Deployment" || action.GetObject().(*extensions.Scale).Spec.Replicas != int(count) {
|
||||||
|
t.Errorf("unexpected action %v, expected update-deployment-scale with replicas = %d", actions[1], count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeploymentScaleInvalid(t *testing.T) {
|
||||||
|
fake := &ErrorDeploymentClient{FakeExperimental: testclient.FakeExperimental{Fake: &testclient.Fake{}}, invalid: true}
|
||||||
|
scaler := DeploymentScaler{fake}
|
||||||
|
preconditions := ScalePrecondition{-1, ""}
|
||||||
|
count := uint(3)
|
||||||
|
name := "foo"
|
||||||
|
namespace := "default"
|
||||||
|
|
||||||
|
scaleFunc := ScaleCondition(&scaler, &preconditions, namespace, name, count)
|
||||||
|
pass, err := scaleFunc()
|
||||||
|
if pass {
|
||||||
|
t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass)
|
||||||
|
}
|
||||||
|
e, ok := err.(ScaleError)
|
||||||
|
if err == nil || !ok || e.FailureType != ScaleUpdateInvalidFailure {
|
||||||
|
t.Errorf("Expected error on invalid update failure, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeploymentScaleFailsPreconditions(t *testing.T) {
|
||||||
|
fake := testclient.NewSimpleFake(&extensions.Deployment{
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: 10,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
scaler := DeploymentScaler{&testclient.FakeExperimental{fake}}
|
||||||
|
preconditions := ScalePrecondition{2, ""}
|
||||||
|
count := uint(3)
|
||||||
|
name := "foo"
|
||||||
|
scaler.Scale("default", name, count, &preconditions, nil, nil)
|
||||||
|
|
||||||
|
actions := fake.Actions()
|
||||||
|
if len(actions) != 1 {
|
||||||
|
t.Errorf("unexpected actions: %v, expected 1 actions (get)", actions)
|
||||||
|
}
|
||||||
|
if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "deployments" || action.GetName() != name {
|
||||||
|
t.Errorf("unexpected action: %v, expected get-deployment %s", actions[0], name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateDeployment(t *testing.T) {
|
||||||
|
zero, ten, twenty := 0, 10, 20
|
||||||
|
tests := []struct {
|
||||||
|
preconditions ScalePrecondition
|
||||||
|
deployment extensions.Deployment
|
||||||
|
expectError bool
|
||||||
|
test string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{-1, ""},
|
||||||
|
expectError: false,
|
||||||
|
test: "defaults",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{-1, ""},
|
||||||
|
deployment: extensions.Deployment{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
ResourceVersion: "foo",
|
||||||
|
},
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: ten,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
test: "defaults 2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{0, ""},
|
||||||
|
deployment: extensions.Deployment{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
ResourceVersion: "foo",
|
||||||
|
},
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: zero,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
test: "size matches",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{-1, "foo"},
|
||||||
|
deployment: extensions.Deployment{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
ResourceVersion: "foo",
|
||||||
|
},
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: ten,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
test: "resource version matches",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{10, "foo"},
|
||||||
|
deployment: extensions.Deployment{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
ResourceVersion: "foo",
|
||||||
|
},
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: ten,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
test: "both match",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{10, "foo"},
|
||||||
|
deployment: extensions.Deployment{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
ResourceVersion: "foo",
|
||||||
|
},
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: twenty,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
test: "size different",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{10, "foo"},
|
||||||
|
deployment: extensions.Deployment{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
ResourceVersion: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
test: "no replicas",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{10, "foo"},
|
||||||
|
deployment: extensions.Deployment{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
ResourceVersion: "bar",
|
||||||
|
},
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: ten,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
test: "version different",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconditions: ScalePrecondition{10, "foo"},
|
||||||
|
deployment: extensions.Deployment{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
ResourceVersion: "bar",
|
||||||
|
},
|
||||||
|
Spec: extensions.DeploymentSpec{
|
||||||
|
Replicas: twenty,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
test: "both different",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
err := test.preconditions.ValidateDeployment(&test.deployment)
|
||||||
|
if err != nil && !test.expectError {
|
||||||
|
t.Errorf("unexpected error: %v (%s)", err, test.test)
|
||||||
|
}
|
||||||
|
if err == nil && test.expectError {
|
||||||
|
t.Errorf("unexpected non-error: %v (%s)", err, test.test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -132,20 +132,7 @@ func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.NewNotFound("scale", name)
|
return nil, errors.NewNotFound("scale", name)
|
||||||
}
|
}
|
||||||
return &extensions.Scale{
|
return extensions.ScaleFromDeployment(deployment), nil
|
||||||
ObjectMeta: api.ObjectMeta{
|
|
||||||
Name: name,
|
|
||||||
Namespace: deployment.Namespace,
|
|
||||||
CreationTimestamp: deployment.CreationTimestamp,
|
|
||||||
},
|
|
||||||
Spec: extensions.ScaleSpec{
|
|
||||||
Replicas: deployment.Spec.Replicas,
|
|
||||||
},
|
|
||||||
Status: extensions.ScaleStatus{
|
|
||||||
Replicas: deployment.Status.Replicas,
|
|
||||||
Selector: deployment.Spec.Selector,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
|
func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
|
||||||
@ -170,18 +157,5 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, errors.NewConflict("scale", scale.Name, err)
|
return nil, false, errors.NewConflict("scale", scale.Name, err)
|
||||||
}
|
}
|
||||||
return &extensions.Scale{
|
return extensions.ScaleFromDeployment(deployment), false, nil
|
||||||
ObjectMeta: api.ObjectMeta{
|
|
||||||
Name: deployment.Name,
|
|
||||||
Namespace: deployment.Namespace,
|
|
||||||
CreationTimestamp: deployment.CreationTimestamp,
|
|
||||||
},
|
|
||||||
Spec: extensions.ScaleSpec{
|
|
||||||
Replicas: deployment.Spec.Replicas,
|
|
||||||
},
|
|
||||||
Status: extensions.ScaleStatus{
|
|
||||||
Replicas: deployment.Status.Replicas,
|
|
||||||
Selector: deployment.Spec.Selector,
|
|
||||||
},
|
|
||||||
}, false, nil
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user