Add a rollingupdate lib and command to kubectl

Also decouple conditions from client for testability.
This commit is contained in:
Jeff Lowdermlk
2014-12-10 13:48:48 -08:00
parent ded3ef2827
commit 0ab39df66b
8 changed files with 576 additions and 5 deletions

View File

@@ -118,6 +118,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
cmds.AddCommand(NewCmdNamespace(out))
cmds.AddCommand(f.NewCmdLog(out))
cmds.AddCommand(f.NewCmdRollingUpdate(out))
if err := cmds.Execute(); err != nil {
os.Exit(1)

View File

@@ -24,6 +24,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"
"github.com/golang/glog"
"github.com/spf13/cobra"
@@ -82,6 +83,16 @@ func GetFlagInt(cmd *cobra.Command, flag string) int {
return v
}
func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
v, err := time.ParseDuration(f.Value.String())
checkErr(err)
return v
}
// Returns the first non-empty string out of the ones provided. If all
// strings are empty, returns an empty string.
func FirstNonEmptyString(args ...string) string {

View File

@@ -0,0 +1,109 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"fmt"
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/spf13/cobra"
)
const (
updatePeriod = "1m0s"
timeout = "5m0s"
pollInterval = "3s"
)
func (f *Factory) NewCmdRollingUpdate(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "rollingupdate <old-controller-name> -f <new-controller.json>",
Short: "Perform a rolling update of the given replicationController",
Long: `Perform a rolling update of the given replicationController.",
Replaces named controller with new controller, updating one pod at a time to use the
new PodTemplate. The new-controller.json must specify the same namespace as the
existing controller and overwrite at least one (common) label in its replicaSelector.
Examples:
$ kubectl rollingupdate frontend-v1 -f frontend-v2.json
<update pods of frontend-v1 using new controller data in frontend-v2.json>
$ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f -
<update pods of frontend-v1 using json data passed into stdin>`,
Run: func(cmd *cobra.Command, args []string) {
filename := GetFlagString(cmd, "filename")
if len(filename) == 0 {
usageError(cmd, "Must specify filename for new controller")
}
period := GetFlagDuration(cmd, "update-period")
interval := GetFlagDuration(cmd, "poll-interval")
timeout := GetFlagDuration(cmd, "timeout")
if len(args) != 1 {
usageError(cmd, "Must specify the controller to update")
}
oldName := args[0]
schema, err := f.Validator(cmd)
checkErr(err)
mapping, namespace, newName, data := ResourceFromFile(cmd, filename, f.Typer, f.Mapper, schema)
if mapping.Kind != "ReplicationController" {
usageError(cmd, "%s does not specify a valid ReplicationController", filename)
}
err = CompareNamespaceFromFile(cmd, namespace)
checkErr(err)
client, err := f.ClientBuilder.Client()
checkErr(err)
obj, err := mapping.Codec.Decode(data)
checkErr(err)
newRc := obj.(*api.ReplicationController)
updater := kubectl.NewRollingUpdater(namespace, client)
// fetch rc
oldRc, err := client.ReplicationControllers(namespace).Get(oldName)
checkErr(err)
var hasLabel bool
for key, oldValue := range oldRc.Spec.Selector {
if newValue, ok := newRc.Spec.Selector[key]; ok && newValue != oldValue {
hasLabel = true
break
}
}
if !hasLabel {
usageError(cmd, "%s must specify a matching key with non-equal value in Selector for %s",
filename, oldName)
}
// TODO: handle resizes during rolling update
if newRc.Spec.Replicas == 0 {
newRc.Spec.Replicas = oldRc.Spec.Replicas
}
err = updater.Update(out, oldRc, newRc, period, interval, timeout)
checkErr(err)
fmt.Fprintf(out, "%s\n", newName)
},
}
cmd.Flags().String("update-period", updatePeriod, `Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
cmd.Flags().String("poll-interval", pollInterval, `Time delay between polling controller status after update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
cmd.Flags().String("timeout", timeout, `Max time to wait for a controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
cmd.Flags().StringP("filename", "f", "", "Filename or URL to file to use to create the new controller")
return cmd
}