// CreateSecretTLS is the implementation of the create secret tls command func CreateSecretTLS(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { name, err := NameFromCommandArgs(cmd, args) if err != nil { return err } requiredFlags := []string{"cert", "key"} for _, requiredFlag := range requiredFlags { if value := cmdutil.GetFlagString(cmd, requiredFlag); len(value) == 0 { return cmdutil.UsageError(cmd, "flag %s is required", requiredFlag) } } var generator kubectl.StructuredGenerator switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName { case cmdutil.SecretForTLSV1GeneratorName: generator = &kubectl.SecretForTLSGeneratorV1{ Name: name, Key: cmdutil.GetFlagString(cmd, "key"), Cert: cmdutil.GetFlagString(cmd, "cert"), } default: return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName)) } return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{ Name: name, StructuredGenerator: generator, DryRun: cmdutil.GetFlagBool(cmd, "dry-run"), OutputFormat: cmdutil.GetFlagString(cmd, "output"), }) }
func validateArguments(cmd *cobra.Command, filenames, args []string) error { deploymentKey := cmdutil.GetFlagString(cmd, "deployment-label-key") image := cmdutil.GetFlagString(cmd, "image") rollback := cmdutil.GetFlagBool(cmd, "rollback") if len(deploymentKey) == 0 { return cmdutil.UsageError(cmd, "--deployment-label-key can not be empty") } if len(filenames) > 1 { return cmdutil.UsageError(cmd, "May only specify a single filename for new controller") } if !rollback { if len(filenames) == 0 && len(image) == 0 { return cmdutil.UsageError(cmd, "Must specify --filename or --image for new controller") } if len(filenames) != 0 && len(image) != 0 { return cmdutil.UsageError(cmd, "--filename and --image can not both be specified") } } else { if len(filenames) != 0 || len(image) != 0 { return cmdutil.UsageError(cmd, "Don't specify --filename or --image on rollback") } } if len(args) < 1 { return cmdutil.UsageError(cmd, "Must specify the controller to update") } return nil }
func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *DeleteOptions) error { cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } deleteAll := cmdutil.GetFlagBool(cmd, "all") mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). SelectorParam(cmdutil.GetFlagString(cmd, "selector")). SelectAllParam(deleteAll). ResourceTypeOrNameArgs(false, args...).RequireObject(false). Flatten(). Do() err = r.Err() if err != nil { return err } ignoreNotFound := cmdutil.GetFlagBool(cmd, "ignore-not-found") if deleteAll { f := cmd.Flags().Lookup("ignore-not-found") // The flag should never be missing if f == nil { return fmt.Errorf("missing --ignore-not-found flag") } // If the user didn't explicitly set the option, default to ignoring NotFound errors when used with --all if !f.Changed { ignoreNotFound = true } } gracePeriod := cmdutil.GetFlagInt(cmd, "grace-period") if cmdutil.GetFlagBool(cmd, "now") { if gracePeriod != -1 { return fmt.Errorf("--now and --grace-period cannot be specified together") } gracePeriod = 0 } shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" // By default use a reaper to delete all related resources. if cmdutil.GetFlagBool(cmd, "cascade") { return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, cmdutil.GetFlagDuration(cmd, "timeout"), gracePeriod, shortOutput, mapper) } return DeleteResult(r, out, ignoreNotFound, shortOutput, mapper) }
// NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command { options := &ScaleOptions{} cmd := &cobra.Command{ Use: "scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)", // resize is deprecated Aliases: []string{"resize"}, Short: "Set a new size for a Deployment, ReplicaSet, Replication Controller, or Job.", Long: scale_long, Example: scale_example, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" err := RunScale(f, out, cmd, args, shortOutput, options) cmdutil.CheckErr(err) }, } 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 resource match this value in order to scale.") cmd.Flags().Int("replicas", -1, "The new desired number of replicas. Required.") 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.") cmdutil.AddOutputFlagsForMutation(cmd) cmdutil.AddRecordFlag(cmd) cmdutil.AddInclude3rdPartyFlags(cmd) usage := "Filename, directory, or URL to a file identifying the resource to set a new size" kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) cmdutil.AddRecursiveFlag(cmd, &options.Recursive) return cmd }
func RunClusterInfo(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { if len(os.Args) > 1 && os.Args[1] == "clusterinfo" { printDeprecationWarning("cluster-info", "clusterinfo") } client, err := f.ClientConfig() if err != nil { return err } printService(out, "Kubernetes master", client.Host) mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) cmdNamespace := cmdutil.GetFlagString(cmd, "namespace") if cmdNamespace == "" { cmdNamespace = api.NamespaceSystem } // TODO use generalized labels once they are implemented (#341) b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). SelectorParam("kubernetes.io/cluster-service=true"). ResourceTypeOrNameArgs(false, []string{"services"}...). Latest() b.Do().Visit(func(r *resource.Info, err error) error { if err != nil { return err } services := r.Object.(*api.ServiceList).Items for _, service := range services { var link string if len(service.Status.LoadBalancer.Ingress) > 0 { ingress := service.Status.LoadBalancer.Ingress[0] ip := ingress.IP if ip == "" { ip = ingress.Hostname } for _, port := range service.Spec.Ports { link += "http://" + ip + ":" + strconv.Itoa(int(port.Port)) + " " } } else { if len(client.GroupVersion.Group) == 0 { link = client.Host + "/api/" + client.GroupVersion.Version + "/proxy/namespaces/" + service.ObjectMeta.Namespace + "/services/" + service.ObjectMeta.Name } else { link = client.Host + "/api/" + client.GroupVersion.Group + "/" + client.GroupVersion.Version + "/proxy/namespaces/" + service.ObjectMeta.Namespace + "/services/" + service.ObjectMeta.Name } } name := service.ObjectMeta.Labels["kubernetes.io/name"] if len(name) == 0 { name = service.ObjectMeta.Name } printService(out, name, link) } return nil }) out.Write([]byte("\nTo further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.\n")) return nil // TODO consider printing more information about cluster }
// CreateSecretDockerRegistry is the implementation of the create secret docker-registry command func CreateSecretDockerRegistry(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { name, err := NameFromCommandArgs(cmd, args) if err != nil { return err } requiredFlags := []string{"docker-username", "docker-password", "docker-email", "docker-server"} for _, requiredFlag := range requiredFlags { if value := cmdutil.GetFlagString(cmd, requiredFlag); len(value) == 0 { return cmdutil.UsageError(cmd, "flag %s is required", requiredFlag) } } var generator kubectl.StructuredGenerator switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName { case cmdutil.SecretForDockerRegistryV1GeneratorName: generator = &kubectl.SecretForDockerRegistryGeneratorV1{ Name: name, Username: cmdutil.GetFlagString(cmd, "docker-username"), Email: cmdutil.GetFlagString(cmd, "docker-email"), Password: cmdutil.GetFlagString(cmd, "docker-password"), Server: cmdutil.GetFlagString(cmd, "docker-server"), } default: return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName)) } return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{ Name: name, StructuredGenerator: generator, DryRun: cmdutil.GetDryRunFlag(cmd), OutputFormat: cmdutil.GetFlagString(cmd, "output"), }) }
// CreateServiceAccount implements the behavior to run the create service account command func CreateServiceAccount(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { name, err := NameFromCommandArgs(cmd, args) if err != nil { return err } var generator kubectl.StructuredGenerator switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName { case cmdutil.ServiceAccountV1GeneratorName: generator = &kubectl.ServiceAccountGeneratorV1{Name: name} default: return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName)) } return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{ Name: name, StructuredGenerator: generator, DryRun: cmdutil.GetDryRunFlag(cmd), OutputFormat: cmdutil.GetFlagString(cmd, "output"), }) }
func setupOutputWriter(cmd *cobra.Command, defaultWriter io.Writer, filename string) io.Writer { dir := cmdutil.GetFlagString(cmd, "output-directory") if len(dir) == 0 || dir == "-" { return defaultWriter } fullFile := path.Join(dir, filename) parent := path.Dir(fullFile) cmdutil.CheckErr(os.MkdirAll(parent, 0755)) file, err := os.Create(path.Join(dir, filename)) cmdutil.CheckErr(err) return file }
func generateService(f *cmdutil.Factory, cmd *cobra.Command, args []string, serviceGenerator string, paramsIn map[string]interface{}, namespace string, out io.Writer) error { generators := f.Generators("expose") generator, found := generators[serviceGenerator] if !found { return fmt.Errorf("missing service generator: %s", serviceGenerator) } names := generator.ParamNames() port := cmdutil.GetFlagInt(cmd, "port") if port < 1 { return fmt.Errorf("--port must be a positive integer when exposing a service") } params := map[string]interface{}{} for key, value := range paramsIn { _, isString := value.(string) if isString { params[key] = value } } name, found := params["name"] if !found || len(name.(string)) == 0 { return fmt.Errorf("name is a required parameter") } selector, found := params["labels"] if !found || len(selector.(string)) == 0 { selector = fmt.Sprintf("run=%s", name.(string)) } params["selector"] = selector if defaultName, found := params["default-name"]; !found || len(defaultName.(string)) == 0 { params["default-name"] = name } obj, _, mapper, mapping, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "service-overrides"), namespace) if err != nil { return err } if cmdutil.GetFlagString(cmd, "output") != "" { return f.PrintObject(cmd, mapper, obj, out) } cmdutil.PrintSuccess(mapper, false, out, mapping.Resource, args[0], "created") return nil }
func getRestartPolicy(cmd *cobra.Command, interactive bool) (api.RestartPolicy, error) { restart := cmdutil.GetFlagString(cmd, "restart") if len(restart) == 0 { if interactive { return api.RestartPolicyOnFailure, nil } else { return api.RestartPolicyAlways, nil } } switch api.RestartPolicy(restart) { case api.RestartPolicyAlways: return api.RestartPolicyAlways, nil case api.RestartPolicyOnFailure: return api.RestartPolicyOnFailure, nil case api.RestartPolicyNever: return api.RestartPolicyNever, nil default: return "", cmdutil.UsageError(cmd, fmt.Sprintf("invalid restart policy: %s", restart)) } }
func (o *ImageOptions) Complete(f *cmdutil.Factory, cmd *cobra.Command, args []string) error { o.Mapper, o.Typer = f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) o.UpdatePodSpecForObject = f.UpdatePodSpecForObject o.Encoder = f.JSONEncoder() o.ShortOutput = cmdutil.GetFlagString(cmd, "output") == "name" o.Record = cmdutil.GetRecordFlag(cmd) o.ChangeCause = f.Command() o.PrintObject = f.PrintObject o.Cmd = cmd cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } o.Resources, o.ContainerImages, err = getResourcesAndImages(args) if err != nil { return err } builder := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, o.Recursive, o.Filenames...). Flatten() if !o.Local { builder = builder. SelectorParam(o.Selector). ResourceTypeOrNameArgs(o.All, o.Resources...). Latest() } o.Infos, err = builder.Do().Infos() if err != nil { return err } return nil }
func dumpClusterInfo(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer) error { var c *unversioned.Client var err error if c, err = f.Client(); err != nil { return err } printer, _, err := kubectl.GetPrinter("json", "") if err != nil { return err } nodes, err := c.Nodes().List(api.ListOptions{}) if err != nil { return err } if err := printer.PrintObj(nodes, setupOutputWriter(cmd, out, "nodes.json")); err != nil { return err } var namespaces []string if cmdutil.GetFlagBool(cmd, "all-namespaces") { namespaceList, err := c.Namespaces().List(api.ListOptions{}) if err != nil { return err } for ix := range namespaceList.Items { namespaces = append(namespaces, namespaceList.Items[ix].Name) } } else { namespaces = cmdutil.GetFlagStringSlice(cmd, "namespaces") if len(namespaces) == 0 { cmdNamespace, _, err := f.DefaultNamespace() if err != nil { return err } namespaces = []string{ api.NamespaceSystem, cmdNamespace, } } } for _, namespace := range namespaces { // TODO: this is repetitive in the extreme. Use reflection or // something to make this a for loop. events, err := c.Events(namespace).List(api.ListOptions{}) if err != nil { return err } if err := printer.PrintObj(events, setupOutputWriter(cmd, out, path.Join(namespace, "events.json"))); err != nil { return err } rcs, err := c.ReplicationControllers(namespace).List(api.ListOptions{}) if err != nil { return err } if err := printer.PrintObj(rcs, setupOutputWriter(cmd, out, path.Join(namespace, "replication-controllers.json"))); err != nil { return err } svcs, err := c.Services(namespace).List(api.ListOptions{}) if err != nil { return err } if err := printer.PrintObj(svcs, setupOutputWriter(cmd, out, path.Join(namespace, "services.json"))); err != nil { return err } sets, err := c.DaemonSets(namespace).List(api.ListOptions{}) if err != nil { return err } if err := printer.PrintObj(sets, setupOutputWriter(cmd, out, path.Join(namespace, "daemonsets.json"))); err != nil { return err } deps, err := c.Deployments(namespace).List(api.ListOptions{}) if err != nil { return err } if err := printer.PrintObj(deps, setupOutputWriter(cmd, out, path.Join(namespace, "deployments.json"))); err != nil { return err } rps, err := c.ReplicaSets(namespace).List(api.ListOptions{}) if err != nil { return err } if err := printer.PrintObj(rps, setupOutputWriter(cmd, out, path.Join(namespace, "replicasets.json"))); err != nil { return err } pods, err := c.Pods(namespace).List(api.ListOptions{}) if err != nil { return err } if err := printer.PrintObj(pods, setupOutputWriter(cmd, out, path.Join(namespace, "pods.json"))); err != nil { return err } for ix := range pods.Items { pod := &pods.Items[ix] writer := setupOutputWriter(cmd, out, path.Join(namespace, pod.Name, "logs.txt")) writer.Write([]byte(fmt.Sprintf("==== START logs for %s/%s ====\n", pod.Namespace, pod.Name))) request, err := f.LogsForObject(pod, &api.PodLogOptions{}) if err != nil { return err } data, err := request.DoRaw() if err != nil { return err } writer.Write(data) writer.Write([]byte(fmt.Sprintf("==== END logs for %s/%s ====\n", pod.Namespace, pod.Name))) } } dir := cmdutil.GetFlagString(cmd, "output-directory") if len(dir) == 0 { dir = "." } if dir != "-" { fmt.Fprintf(out, "Cluster info dumped to %s", dir) } return nil }
// RunScale executes the scaling func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *ScaleOptions) error { if len(os.Args) > 1 && os.Args[1] == "resize" { printDeprecationWarning("scale", "resize") } count := cmdutil.GetFlagInt(cmd, "replicas") if count < 0 { return cmdutil.UsageError(cmd, "--replicas=COUNT is required, and COUNT must be greater than or equal to 0") } cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() err = r.Err() if err != nil { return err } infos := []*resource.Info{} err = r.Visit(func(info *resource.Info, err error) error { if err == nil { infos = append(infos, info) } return nil }) resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") if len(resourceVersion) != 0 && len(infos) > 1 { return fmt.Errorf("cannot use --resource-version with multiple resources") } counter := 0 err = r.Visit(func(info *resource.Info, err error) error { if err != nil { return err } mapping := info.ResourceMapping() scaler, err := f.Scaler(mapping) if err != nil { return err } currentSize := cmdutil.GetFlagInt(cmd, "current-replicas") precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: resourceVersion} retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout) var waitForReplicas *kubectl.RetryParams if timeout := cmdutil.GetFlagDuration(cmd, "timeout"); timeout != 0 { waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout) } if err := scaler.Scale(info.Namespace, info.Name, uint(count), precondition, retry, waitForReplicas); err != nil { return err } if cmdutil.ShouldRecord(cmd, info) { patchBytes, err := cmdutil.ChangeResourcePatch(info, f.Command()) if err != nil { return err } mapping := info.ResourceMapping() client, err := f.ClientForMapping(mapping) if err != nil { return err } helper := resource.NewHelper(client, mapping) _, err = helper.Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patchBytes) if err != nil { return err } } counter++ cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "scaled") return nil }) if err != nil { return err } if counter == 0 { return fmt.Errorf("no objects passed to scale") } return nil }
func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *ApplyOptions) error { shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) if err != nil { return err } cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). Flatten(). Do() err = r.Err() if err != nil { return err } encoder := f.JSONEncoder() decoder := f.Decoder(false) count := 0 err = r.Visit(func(info *resource.Info, err error) error { // In this method, info.Object contains the object retrieved from the server // and info.VersionedObject contains the object decoded from the input source. if err != nil { return err } // Get the modified configuration of the object. Embed the result // as an annotation in the modified configuration, so that it will appear // in the patch sent to the server. modified, err := kubectl.GetModifiedConfiguration(info, true, encoder) if err != nil { return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err) } if err := info.Get(); err != nil { if !errors.IsNotFound(err) { return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err) } // Create the resource if it doesn't exist // First, update the annotation used by kubectl apply if err := kubectl.CreateApplyAnnotation(info, encoder); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } if cmdutil.ShouldRecord(cmd, info) { if err := cmdutil.RecordChangeCause(info.Object, f.Command()); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } } // Then create the resource and skip the three-way merge if err := createAndRefresh(info); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } count++ cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "created") return nil } helper := resource.NewHelper(info.Client, info.Mapping) patcher := NewPatcher(encoder, decoder, info.Mapping, helper) patchBytes, err := patcher.patch(info.Object, modified, info.Source, info.Namespace, info.Name) if err != nil { return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patchBytes, info), info.Source, err) } if cmdutil.ShouldRecord(cmd, info) { patch, err := cmdutil.ChangeResourcePatch(info, f.Command()) if err != nil { return err } _, err = helper.Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch) if err != nil { return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patch, info), info.Source, err) } } count++ cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "configured") return nil }) if err != nil { return err } if count == 0 { return fmt.Errorf("no objects passed to apply") } return nil }
func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *RollingUpdateOptions) error { if len(os.Args) > 1 && os.Args[1] == "rollingupdate" { printDeprecationWarning("rolling-update", "rollingupdate") } err := validateArguments(cmd, options.Filenames, args) if err != nil { return err } deploymentKey := cmdutil.GetFlagString(cmd, "deployment-label-key") filename := "" image := cmdutil.GetFlagString(cmd, "image") pullPolicy := cmdutil.GetFlagString(cmd, "image-pull-policy") oldName := args[0] rollback := cmdutil.GetFlagBool(cmd, "rollback") period := cmdutil.GetFlagDuration(cmd, "update-period") interval := cmdutil.GetFlagDuration(cmd, "poll-interval") timeout := cmdutil.GetFlagDuration(cmd, "timeout") dryrun := cmdutil.GetDryRunFlag(cmd) outputFormat := cmdutil.GetFlagString(cmd, "output") container := cmdutil.GetFlagString(cmd, "container") if len(options.Filenames) > 0 { filename = options.Filenames[0] } cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } client, err := f.Client() if err != nil { return err } var newRc *api.ReplicationController // fetch rc oldRc, err := client.ReplicationControllers(cmdNamespace).Get(oldName) if err != nil { if !errors.IsNotFound(err) || len(image) == 0 || len(args) > 1 { return err } // We're in the middle of a rename, look for an RC with a source annotation of oldName newRc, err := kubectl.FindSourceController(client, cmdNamespace, oldName) if err != nil { return err } return kubectl.Rename(client, newRc, oldName) } var keepOldName bool var replicasDefaulted bool mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) if len(filename) != 0 { schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) if err != nil { return err } request := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, false, filename). Do() obj, err := request.Object() if err != nil { return err } var ok bool // Handle filename input from stdin. The resource builder always returns an api.List // when creating resource(s) from a stream. if list, ok := obj.(*api.List); ok { if len(list.Items) > 1 { return cmdutil.UsageError(cmd, "%s specifies multiple items", filename) } obj = list.Items[0] } newRc, ok = obj.(*api.ReplicationController) if !ok { if gvks, _, err := typer.ObjectKinds(obj); err == nil { return cmdutil.UsageError(cmd, "%s contains a %v not a ReplicationController", filename, gvks[0]) } glog.V(4).Infof("Object %#v is not a ReplicationController", obj) return cmdutil.UsageError(cmd, "%s does not specify a valid ReplicationController", filename) } infos, err := request.Infos() if err != nil || len(infos) != 1 { glog.V(2).Infof("was not able to recover adequate information to discover if .spec.replicas was defaulted") } else { replicasDefaulted = isReplicasDefaulted(infos[0]) } } // If the --image option is specified, we need to create a new rc with at least one different selector // than the old rc. This selector is the hash of the rc, with a suffix to provide uniqueness for // same-image updates. if len(image) != 0 { codec := api.Codecs.LegacyCodec(client.APIVersion()) keepOldName = len(args) == 1 newName := findNewName(args, oldRc) if newRc, err = kubectl.LoadExistingNextReplicationController(client, cmdNamespace, newName); err != nil { return err } if newRc != nil { if inProgressImage := newRc.Spec.Template.Spec.Containers[0].Image; inProgressImage != image { return cmdutil.UsageError(cmd, "Found existing in-progress update to image (%s).\nEither continue in-progress update with --image=%s or rollback with --rollback", inProgressImage, inProgressImage) } fmt.Fprintf(out, "Found existing update in progress (%s), resuming.\n", newRc.Name) } else { config := &kubectl.NewControllerConfig{ Namespace: cmdNamespace, OldName: oldName, NewName: newName, Image: image, Container: container, DeploymentKey: deploymentKey, } if oldRc.Spec.Template.Spec.Containers[0].Image == image { if len(pullPolicy) == 0 { return cmdutil.UsageError(cmd, "--image-pull-policy (Always|Never|IfNotPresent) must be provided when --image is the same as existing container image") } config.PullPolicy = api.PullPolicy(pullPolicy) } newRc, err = kubectl.CreateNewControllerFromCurrentController(client, codec, config) if err != nil { return err } } // Update the existing replication controller with pointers to the 'next' controller // and adding the <deploymentKey> label if necessary to distinguish it from the 'next' controller. oldHash, err := api.HashObject(oldRc, codec) if err != nil { return err } // If new image is same as old, the hash may not be distinct, so add a suffix. oldHash += "-orig" oldRc, err = kubectl.UpdateExistingReplicationController(client, oldRc, cmdNamespace, newRc.Name, deploymentKey, oldHash, out) if err != nil { return err } } if rollback { keepOldName = len(args) == 1 newName := findNewName(args, oldRc) if newRc, err = kubectl.LoadExistingNextReplicationController(client, cmdNamespace, newName); err != nil { return err } if newRc == nil { return cmdutil.UsageError(cmd, "Could not find %s to rollback.\n", newName) } } if oldName == newRc.Name { return cmdutil.UsageError(cmd, "%s cannot have the same name as the existing ReplicationController %s", filename, oldName) } updater := kubectl.NewRollingUpdater(newRc.Namespace, client) // To successfully pull off a rolling update the new and old rc have to differ // by at least one selector. Every new pod should have the selector and every // old pod should not have the selector. 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 { return cmdutil.UsageError(cmd, "%s must specify a matching key with non-equal value in Selector for %s", filename, oldName) } // TODO: handle scales during rolling update if replicasDefaulted { newRc.Spec.Replicas = oldRc.Spec.Replicas } if dryrun { oldRcData := &bytes.Buffer{} newRcData := &bytes.Buffer{} if outputFormat == "" { oldRcData.WriteString(oldRc.Name) newRcData.WriteString(newRc.Name) } else { if err := f.PrintObject(cmd, mapper, oldRc, oldRcData); err != nil { return err } if err := f.PrintObject(cmd, mapper, newRc, newRcData); err != nil { return err } } fmt.Fprintf(out, "Rolling from:\n%s\nTo:\n%s\n", string(oldRcData.Bytes()), string(newRcData.Bytes())) return nil } updateCleanupPolicy := kubectl.DeleteRollingUpdateCleanupPolicy if keepOldName { updateCleanupPolicy = kubectl.RenameRollingUpdateCleanupPolicy } config := &kubectl.RollingUpdaterConfig{ Out: out, OldRc: oldRc, NewRc: newRc, UpdatePeriod: period, Interval: interval, Timeout: timeout, CleanupPolicy: updateCleanupPolicy, MaxUnavailable: intstr.FromInt(0), MaxSurge: intstr.FromInt(1), } if rollback { err = kubectl.AbortRollingUpdate(config) if err != nil { return err } client.ReplicationControllers(config.NewRc.Namespace).Update(config.NewRc) } err = updater.Update(config) if err != nil { return err } message := "rolling updated" if keepOldName { newRc.Name = oldName } else { message = fmt.Sprintf("rolling updated to %q", newRc.Name) } newRc, err = client.ReplicationControllers(cmdNamespace).Get(newRc.Name) if err != nil { return err } if outputFormat != "" { return f.PrintObject(cmd, mapper, newRc, out) } kinds, _, err := api.Scheme.ObjectKinds(newRc) if err != nil { return err } _, res := meta.KindToResource(kinds[0]) cmdutil.PrintSuccess(mapper, false, out, res.Resource, oldName, message) return nil }
func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string, argsLenAtDash int) error { if len(os.Args) > 1 && os.Args[1] == "run-container" { printDeprecationWarning("run", "run-container") } // Let kubectl run follow rules for `--`, see #13004 issue if len(args) == 0 || argsLenAtDash == 0 { return cmdutil.UsageError(cmd, "NAME is required for run") } interactive := cmdutil.GetFlagBool(cmd, "stdin") tty := cmdutil.GetFlagBool(cmd, "tty") if tty && !interactive { return cmdutil.UsageError(cmd, "-i/--stdin is required for containers with -t/--tty=true") } replicas := cmdutil.GetFlagInt(cmd, "replicas") if interactive && replicas != 1 { return cmdutil.UsageError(cmd, fmt.Sprintf("-i/--stdin requires that replicas is 1, found %d", replicas)) } namespace, _, err := f.DefaultNamespace() if err != nil { return err } restartPolicy, err := getRestartPolicy(cmd, interactive) if err != nil { return err } if restartPolicy != api.RestartPolicyAlways && replicas != 1 { return cmdutil.UsageError(cmd, fmt.Sprintf("--restart=%s requires that --replicas=1, found %d", restartPolicy, replicas)) } generatorName := cmdutil.GetFlagString(cmd, "generator") if len(generatorName) == 0 { client, err := f.Client() if err != nil { return err } resourcesList, err := client.Discovery().ServerResources() // ServerResources ignores errors for old servers do not expose discovery if err != nil { return fmt.Errorf("failed to discover supported resources: %v", err) } switch restartPolicy { case api.RestartPolicyAlways: if contains(resourcesList, v1beta1.SchemeGroupVersion.WithResource("deployments")) { generatorName = "deployment/v1beta1" } else { generatorName = "run/v1" } case api.RestartPolicyOnFailure: if contains(resourcesList, batchv1.SchemeGroupVersion.WithResource("jobs")) { generatorName = "job/v1" } else if contains(resourcesList, v1beta1.SchemeGroupVersion.WithResource("jobs")) { generatorName = "job/v1beta1" } else { generatorName = "run-pod/v1" } case api.RestartPolicyNever: generatorName = "run-pod/v1" } } generators := f.Generators("run") generator, found := generators[generatorName] if !found { return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) } names := generator.ParamNames() params := kubectl.MakeParams(cmd, names) params["name"] = args[0] if len(args) > 1 { params["args"] = args[1:] } params["env"] = cmdutil.GetFlagStringSlice(cmd, "env") obj, _, mapper, mapping, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "overrides"), namespace) if err != nil { return err } if cmdutil.GetFlagBool(cmd, "expose") { serviceGenerator := cmdutil.GetFlagString(cmd, "service-generator") if len(serviceGenerator) == 0 { return cmdutil.UsageError(cmd, fmt.Sprintf("No service generator specified")) } if err := generateService(f, cmd, args, serviceGenerator, params, namespace, cmdOut); err != nil { return err } } attachFlag := cmd.Flags().Lookup("attach") attach := cmdutil.GetFlagBool(cmd, "attach") if !attachFlag.Changed && interactive { attach = true } remove := cmdutil.GetFlagBool(cmd, "rm") if !attach && remove { return cmdutil.UsageError(cmd, "--rm should only be used for attached containers") } if attach { opts := &AttachOptions{ In: cmdIn, Out: cmdOut, Err: cmdErr, Stdin: interactive, TTY: tty, CommandName: cmd.Parent().CommandPath() + " attach", Attach: &DefaultRemoteAttach{}, } config, err := f.ClientConfig() if err != nil { return err } opts.Config = config client, err := f.Client() if err != nil { return err } opts.Client = client attachablePod, err := f.AttachablePodForObject(obj) if err != nil { return err } err = handleAttachPod(f, client, attachablePod, opts) if err != nil { return err } if remove { namespace, err = mapping.MetadataAccessor.Namespace(obj) if err != nil { return err } var name string name, err = mapping.MetadataAccessor.Name(obj) if err != nil { return err } _, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). ResourceNames(mapping.Resource, name). Flatten(). Do() return ReapResult(r, f, cmdOut, true, true, 0, -1, false, mapper) } return nil } outputFormat := cmdutil.GetFlagString(cmd, "output") if outputFormat != "" { return f.PrintObject(cmd, mapper, obj, cmdOut) } cmdutil.PrintSuccess(mapper, false, cmdOut, mapping.Resource, args[0], "created") return nil }