func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error { schema, err := f.Validator() if err != nil { return err } cmdNamespace, err := f.DefaultNamespace() if err != nil { return err } patch := cmdutil.GetFlagString(cmd, "patch") if len(filenames) == 0 && len(patch) == 0 { return cmdutil.UsageError(cmd, "Must specify --filename or --patch to update") } if len(filenames) != 0 && len(patch) != 0 { return cmdutil.UsageError(cmd, "Can not specify both --filename and --patch") } // TODO: Make patching work with -f, updating with patched JSON input files if len(filenames) == 0 { name, err := updateWithPatch(cmd, args, f, patch) if err != nil { return err } fmt.Fprintf(out, "%s\n", name) return nil } if len(filenames) == 0 { return cmdutil.UsageError(cmd, "Must specify --filename to update") } mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).RequireNamespace(). FilenameParam(filenames...). Flatten(). Do() err = r.Err() if err != nil { return err } return r.Visit(func(info *resource.Info) error { data, err := info.Mapping.Codec.Encode(info.Object) if err != nil { return err } obj, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, true, data) if err != nil { return err } info.Refresh(obj, true) printObjectSpecificMessage(obj, out) fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name) return nil }) }
func Run(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { if os.Args[1] == "run-container" { printDeprecationWarning("run", "run-container") } if len(args) != 1 { return cmdutil.UsageError(cmd, "NAME is required for run") } namespace, err := f.DefaultNamespace() if err != nil { return err } client, err := f.Client() if err != nil { return err } generatorName := cmdutil.GetFlagString(cmd, "generator") generator, found := f.Generator(generatorName) if !found { return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not found.", generator)) } names := generator.ParamNames() params := qingctl.MakeParams(cmd, names) params["name"] = args[0] err = qingctl.ValidateParams(names, params) if err != nil { return err } controller, err := generator.Generate(params) if err != nil { return err } inline := cmdutil.GetFlagString(cmd, "overrides") if len(inline) > 0 { controller, err = cmdutil.Merge(controller, inline, "ReplicationController") if err != nil { return err } } // TODO: extract this flag to a central location, when such a location exists. if !cmdutil.GetFlagBool(cmd, "dry-run") { controller, err = client.ReplicationControllers(namespace).Create(controller.(*api.ReplicationController)) if err != nil { return err } } return f.PrintObject(cmd, controller, out) }
func RunPortForward(f *cmdutil.Factory, cmd *cobra.Command, args []string, fw portForwarder) error { podName := cmdutil.GetFlagString(cmd, "pod") if len(podName) == 0 { return cmdutil.UsageError(cmd, "POD is required for exec") } if len(args) < 1 { return cmdutil.UsageError(cmd, "at least 1 PORT is required for port-forward") } namespace, err := f.DefaultNamespace() if err != nil { return err } client, err := f.Client() if err != nil { return err } pod, err := client.Pods(namespace).Get(podName) if err != nil { return err } if pod.Status.Phase != api.PodRunning { glog.Fatalf("Unable to execute command because pod is not running. Current status=%v", pod.Status.Phase) } config, err := f.ClientConfig() if err != nil { return err } signals := make(chan os.Signal, 1) signal.Notify(signals, os.Interrupt) defer signal.Stop(signals) stopCh := make(chan struct{}, 1) go func() { <-signals close(stopCh) }() req := client.RESTClient.Get(). Resource("pods"). Namespace(namespace). Name(pod.Name). SubResource("portforward") return fw.ForwardPorts(req, config, args, stopCh) }
func validateArguments(cmd *cobra.Command, args []string) (deploymentKey, filename, image, oldName string, err error) { deploymentKey = cmdutil.GetFlagString(cmd, "deployment-label-key") filename = cmdutil.GetFlagString(cmd, "filename") image = cmdutil.GetFlagString(cmd, "image") if len(deploymentKey) == 0 { return "", "", "", "", cmdutil.UsageError(cmd, "--deployment-label-key can not be empty") } if len(filename) == 0 && len(image) == 0 { return "", "", "", "", cmdutil.UsageError(cmd, "Must specify --filename or --image for new controller") } if len(filename) != 0 && len(image) != 0 { return "", "", "", "", cmdutil.UsageError(cmd, "--filename and --image can not both be specified") } if len(args) < 1 { return "", "", "", "", cmdutil.UsageError(cmd, "Must specify the controller to update") } return deploymentKey, filename, image, args[0], nil }
func extractPodAndContainer(cmd *cobra.Command, argsIn []string, p *execParams) (podName string, containerName string, args []string, err error) { if len(p.podName) == 0 && len(argsIn) == 0 { return "", "", nil, cmdutil.UsageError(cmd, "POD is required for exec") } if len(p.podName) != 0 { printDeprecationWarning("exec POD", "-p POD") podName = p.podName if len(argsIn) < 1 { return "", "", nil, cmdutil.UsageError(cmd, "COMMAND is required for exec") } args = argsIn } else { podName = argsIn[0] args = argsIn[1:] if len(args) < 1 { return "", "", nil, cmdutil.UsageError(cmd, "COMMAND is required for exec") } } return podName, p.containerName, args, nil }
func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { if os.Args[1] == "rollingupdate" { printDeprecationWarning("rolling-update", "rollingupdate") } deploymentKey, filename, image, oldName, err := validateArguments(cmd, args) if err != nil { return err } period := cmdutil.GetFlagDuration(cmd, "update-period") interval := cmdutil.GetFlagDuration(cmd, "poll-interval") timeout := cmdutil.GetFlagDuration(cmd, "timeout") dryrun := cmdutil.GetFlagBool(cmd, "dry-run") cmdNamespace, err := f.DefaultNamespace() if err != nil { return err } client, err := f.Client() if err != nil { return err } updaterClient := qingctl.NewRollingUpdaterClient(client) 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 := qingctl.FindSourceController(updaterClient, cmdNamespace, oldName) if err != nil { return err } return qingctl.Rename(qingctl.NewRollingUpdaterClient(client), newRc, oldName) } var keepOldName bool mapper, typer := f.Object() if len(filename) != 0 { schema, err := f.Validator() if err != nil { return err } obj, err := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). Schema(schema). NamespaceParam(cmdNamespace).RequireNamespace(). FilenameParam(filename). Do(). 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 _, kind, err := typer.ObjectVersionAndKind(obj); err == nil { return cmdutil.UsageError(cmd, "%s contains a %s not a ReplicationController", filename, kind) } glog.V(4).Infof("Object %#v is not a ReplicationController", obj) return cmdutil.UsageError(cmd, "%s does not specify a valid ReplicationController", filename) } } // 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, which will differ because the new rc has a // different image. if len(image) != 0 { keepOldName = len(args) == 1 newName := findNewName(args, oldRc) if newRc, err = qingctl.LoadExistingNextReplicationController(client, cmdNamespace, newName); err != nil { return err } if newRc != nil { fmt.Fprintf(out, "Found existing update in progress (%s), resuming.\n", newRc.Name) } else { newRc, err = qingctl.CreateNewControllerFromCurrentController(client, cmdNamespace, oldName, newName, image, deploymentKey) 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, client.Codec) if err != nil { return err } oldRc, err = qingctl.UpdateExistingReplicationController(client, oldRc, cmdNamespace, newRc.Name, deploymentKey, oldHash, out) if err != nil { return err } } if oldName == newRc.Name { return cmdutil.UsageError(cmd, "%s cannot have the same name as the existing ReplicationController %s", filename, oldName) } updater := qingctl.NewRollingUpdater(newRc.Namespace, updaterClient) // 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 newRc.Spec.Replicas == 0 { newRc.Spec.Replicas = oldRc.Spec.Replicas } if dryrun { oldRcData := &bytes.Buffer{} if err := f.PrintObject(cmd, oldRc, oldRcData); err != nil { return err } newRcData := &bytes.Buffer{} if err := f.PrintObject(cmd, 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 := qingctl.DeleteRollingUpdateCleanupPolicy if keepOldName { updateCleanupPolicy = qingctl.RenameRollingUpdateCleanupPolicy } config := &qingctl.RollingUpdaterConfig{ Out: out, OldRc: oldRc, NewRc: newRc, UpdatePeriod: period, Interval: interval, Timeout: timeout, CleanupPolicy: updateCleanupPolicy, } if cmdutil.GetFlagBool(cmd, "rollback") { qingctl.AbortRollingUpdate(config) client.ReplicationControllers(config.NewRc.Namespace).Update(config.NewRc) } err = updater.Update(config) if err != nil { return err } if keepOldName { fmt.Fprintf(out, "%s\n", oldName) } else { fmt.Fprintf(out, "%s\n", newRc.Name) } return nil }
func ValidateArgs(cmd *cobra.Command, args []string) error { if len(args) != 0 { return cmdutil.UsageError(cmd, "Unexpected args: %v", args) } return nil }
func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { namespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() err = r.Err() if err != nil { return err } mapping, err := r.ResourceMapping() if err != nil { return err } infos, err := r.Infos() if err != nil { return err } if len(infos) > 1 { return fmt.Errorf("multiple resources provided: %v", args) } info := infos[0] // Get the input object client, err := f.RESTClient(mapping) if err != nil { return err } inputObject, err := resource.NewHelper(client, mapping).Get(info.Namespace, info.Name) if err != nil { return err } // Get the generator, setup and validate all required parameters generatorName := cmdutil.GetFlagString(cmd, "generator") generator, found := f.Generator(generatorName) if !found { return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) } names := generator.ParamNames() params := qingctl.MakeParams(cmd, names) params["default-name"] = info.Name if s, found := params["selector"]; !found || len(s) == 0 || cmdutil.GetFlagInt(cmd, "port") < 1 { if len(s) == 0 { s, err := f.PodSelectorForObject(inputObject) if err != nil { return err } params["selector"] = s } noPorts := true for _, param := range names { if param.Name == "port" { noPorts = false break } } if cmdutil.GetFlagInt(cmd, "port") < 0 && !noPorts { ports, err := f.PortsForObject(inputObject) if err != nil { return err } switch len(ports) { case 0: return cmdutil.UsageError(cmd, "couldn't find a suitable port via --port flag or introspection") case 1: params["port"] = ports[0] default: return cmdutil.UsageError(cmd, "more than one port to choose from, please explicitly specify a port using the --port flag.") } } } if cmdutil.GetFlagBool(cmd, "create-external-load-balancer") { params["create-external-load-balancer"] = "true" } if len(params["labels"]) == 0 { labels, err := f.LabelsForObject(inputObject) if err != nil { return err } params["labels"] = qingctl.MakeLabels(labels) } if v := cmdutil.GetFlagString(cmd, "type"); v != "" { params["type"] = v } err = qingctl.ValidateParams(names, params) if err != nil { return err } // Expose new object object, err := generator.Generate(params) if err != nil { return err } inline := cmdutil.GetFlagString(cmd, "overrides") if len(inline) > 0 { object, err = cmdutil.Merge(object, inline, mapping.Kind) if err != nil { return err } } // TODO: extract this flag to a central location, when such a location exists. if !cmdutil.GetFlagBool(cmd, "dry-run") { resourceMapper := &resource.Mapper{typer, mapper, f.ClientMapperForCommand()} info, err := resourceMapper.InfoForObject(object) if err != nil { return err } data, err := info.Mapping.Codec.Encode(object) if err != nil { return err } _, err = resource.NewHelper(info.Client, info.Mapping).Create(namespace, false, data) if err != nil { return err } } if cmdutil.GetFlagBool(cmd, "create-external-load-balancer") { msg := fmt.Sprintf(` An external load-balanced service was created. On many platforms (e.g. Google Compute Engine), you will also need to explicitly open a firewall rule for the service port (%d) to serve traffic. See https://github.com/qingyuancloud/QingYuan/tree/master/docs/services-firewall.md for more details. `, cmdutil.GetFlagInt(cmd, "port")) out.Write([]byte(msg)) } return f.PrintObject(cmd, object, out) }
func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { resources, labelArgs := []string{}, []string{} first := true for _, s := range args { isLabel := strings.Contains(s, "=") || strings.HasSuffix(s, "-") switch { case first && isLabel: first = false fallthrough case !first && isLabel: labelArgs = append(labelArgs, s) case first && !isLabel: resources = append(resources, s) case !first && !isLabel: return cmdutil.UsageError(cmd, "all resources must be specified before label changes: %s", s) } } if len(resources) < 1 { return cmdutil.UsageError(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>") } if len(labelArgs) < 1 { return cmdutil.UsageError(cmd, "at least one label update is required") } selector := cmdutil.GetFlagString(cmd, "selector") all := cmdutil.GetFlagBool(cmd, "all") overwrite := cmdutil.GetFlagBool(cmd, "overwrite") resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") cmdNamespace, err := f.DefaultNamespace() if err != nil { return err } labels, remove, err := parseLabels(labelArgs) if err != nil { return cmdutil.UsageError(cmd, err.Error()) } mapper, typer := f.Object() b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). SelectorParam(selector). ResourceTypeOrNameArgs(all, resources...). Flatten(). Latest() one := false r := b.Do().IntoSingular(&one) if err := r.Err(); err != nil { return err } // only apply resource version locking on a single resource if !one && len(resourceVersion) > 0 { return cmdutil.UsageError(cmd, "--resource-version may only be used with a single resource") } // TODO: support bulk generic output a la Get return r.Visit(func(info *resource.Info) error { obj, err := updateObject(info, func(obj runtime.Object) (runtime.Object, error) { outObj, err := labelFunc(obj, overwrite, resourceVersion, labels, remove) if err != nil { return nil, err } return outObj, nil }) if err != nil { return err } printer, err := f.PrinterForMapping(cmd, info.Mapping, false) if err != nil { return err } return printer.PrintObj(obj, out) }) }
// RunLog retrieves a pod log func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { if len(os.Args) > 1 && os.Args[1] == "log" { printDeprecationWarning("logs", "log") } if len(args) == 0 { return cmdutil.UsageError(cmd, "POD is required for log") } if len(args) > 2 { return cmdutil.UsageError(cmd, "log POD [CONTAINER]") } namespace, err := f.DefaultNamespace() if err != nil { return err } client, err := f.Client() if err != nil { return err } podID := args[0] pod, err := client.Pods(namespace).Get(podID) if err != nil { return err } var container string if len(args) == 1 { if len(pod.Spec.Containers) != 1 { return fmt.Errorf("POD %s has more than one container; please specify the container to print logs for", pod.ObjectMeta.Name) } container = pod.Spec.Containers[0].Name } else { container = args[1] } follow := false if cmdutil.GetFlagBool(cmd, "follow") { follow = true } previous := false if cmdutil.GetFlagBool(cmd, "previous") { previous = true } readCloser, err := client.RESTClient.Get(). Namespace(namespace). Name(podID). Resource("pods"). SubResource("log"). Param("follow", strconv.FormatBool(follow)). Param("container", container). Param("previous", strconv.FormatBool(previous)). Stream() if err != nil { return err } defer readCloser.Close() _, err = io.Copy(out, readCloser) return err }