func (v *VolumeOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, out io.Writer) error { clientConfig, err := f.ClientConfig() if err != nil { return err } v.OutputVersion = kcmdutil.OutputVersion(cmd, clientConfig.Version) cmdNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() v.DefaultNamespace = cmdNamespace v.Writer = out v.Mapper = mapper v.Typer = typer v.RESTClientFactory = f.Factory.RESTClient v.UpdatePodSpecForObject = f.UpdatePodSpecForObject if v.Add && len(v.Name) == 0 { v.Name = string(kutil.NewUUID()) if len(v.Output) == 0 { fmt.Fprintf(v.Writer, "Generated volume name: %s\n", v.Name) } } // In case of volume source ignore the default volume type if len(v.AddOpts.Source) > 0 { v.AddOpts.Type = "" } return nil }
// RunEnv contains all the necessary functionality for the OpenShift cli env command func RunEnv(f *clientcmd.Factory, in io.Reader, out io.Writer, cmd *cobra.Command, args []string, envParams, filenames kutil.StringList) error { resources, envArgs := []string{}, []string{} first := true for _, s := range args { isEnv := strings.Contains(s, "=") || strings.HasSuffix(s, "-") switch { case first && isEnv: first = false fallthrough case !first && isEnv: envArgs = append(envArgs, s) case first && !isEnv: resources = append(resources, s) case !first && !isEnv: return cmdutil.UsageError(cmd, "all resources must be specified before environment changes: %s", s) } } if len(filenames) == 0 && len(resources) < 1 { return cmdutil.UsageError(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>") } containerMatch := cmdutil.GetFlagString(cmd, "containers") list := cmdutil.GetFlagBool(cmd, "list") selector := cmdutil.GetFlagString(cmd, "selector") all := cmdutil.GetFlagBool(cmd, "all") //overwrite := cmdutil.GetFlagBool(cmd, "overwrite") resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") outputFormat := cmdutil.GetFlagString(cmd, "output") if list && len(outputFormat) > 0 { return cmdutil.UsageError(cmd, "--list and --output may not be specified together") } clientConfig, err := f.ClientConfig() if err != nil { return err } outputVersion := cmdutil.OutputVersion(cmd, clientConfig.Version) cmdNamespace, err := f.DefaultNamespace() if err != nil { return err } env, remove, err := parseEnv(append(envParams, envArgs...), in) if err != nil { return err } mapper, typer := f.Object() b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(all, resources...). Flatten() one := false infos, err := b.Do().IntoSingular(&one).Infos() if 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") } skipped := 0 for _, info := range infos { ok, err := f.UpdatePodSpecForObject(info.Object, func(spec *kapi.PodSpec) error { containers, _ := selectContainers(spec.Containers, containerMatch) if len(containers) == 0 { fmt.Fprintf(cmd.Out(), "warning: %s/%s does not have any containers matching %q\n", info.Mapping.Resource, info.Name, containerMatch) return nil } for _, c := range containers { c.Env = updateEnv(c.Env, env, remove) if list { fmt.Fprintf(out, "# %s %s, container %s\n", info.Mapping.Resource, info.Name, c.Name) for _, env := range c.Env { // if env.ValueFrom != nil && env.ValueFrom.FieldRef != nil { // fmt.Fprintf(cmd.Out(), "%s= # calculated from pod %s %s\n", env.Name, env.ValueFrom.FieldRef.FieldPath, env.ValueFrom.FieldRef.APIVersion) // continue // } fmt.Fprintf(out, "%s=%s\n", env.Name, env.Value) } } } return nil }) if !ok { skipped++ continue } if err != nil { fmt.Fprintf(cmd.Out(), "error: %s/%s %v\n", info.Mapping.Resource, info.Name, err) continue } } if one && skipped == len(infos) { return fmt.Errorf("the %s %s is not a pod or does not have a pod template", infos[0].Mapping.Resource, infos[0].Name) } if list { return nil } objects, err := resource.AsVersionedObject(infos, false, outputVersion) if err != nil { return err } if len(outputFormat) != 0 { p, _, err := kubectl.GetPrinter(outputFormat, "") if err != nil { return err } return p.PrintObj(objects, out) } failed := false for _, info := range infos { data, err := info.Mapping.Codec.Encode(info.Object) if err != nil { fmt.Fprintf(cmd.Out(), "Error: %v\n", err) failed = true continue } obj, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, true, data) if err != nil { handlePodUpdateError(cmd.Out(), err, "environment variables") failed = true continue } info.Refresh(obj, true) fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name) } if failed { return errExit } return nil }
func RunEdit(fullName string, f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error { var printer kubectl.ResourcePrinter var ext string switch format := cmdutil.GetFlagString(cmd, "output"); format { case "json": printer = &kubectl.JSONPrinter{} ext = ".json" case "yaml": printer = &kubectl.YAMLPrinter{} ext = ".yaml" default: return cmdutil.UsageError(cmd, "The flag 'output' must be one of yaml|json") } cmdNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() rmap := &resource.Mapper{ ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand(), } b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(filenames...). //SelectorParam(selector). ResourceTypeOrNameArgs(true, args...). Latest() if err != nil { return err } clientConfig, err := f.ClientConfig() if err != nil { return err } r := b.Flatten().Do() infos, err := r.Infos() if err != nil { return err } defaultVersion := cmdutil.OutputVersion(cmd, clientConfig.Version) results := editResults{} for { obj, err := resource.AsVersionedObject(infos, false, defaultVersion) if err != nil { return preservedFile(err, results.file, cmd.Out()) } // TODO: add an annotating YAML printer that can print inline comments on each field, // including descriptions or validation errors // generate the file to edit buf := &bytes.Buffer{} if err := results.header.WriteTo(buf); err != nil { return preservedFile(err, results.file, cmd.Out()) } if err := printer.PrintObj(obj, buf); err != nil { return preservedFile(err, results.file, cmd.Out()) } original := buf.Bytes() // launch the editor edit := editor.NewDefaultEditor() edited, file, err := edit.LaunchTempFile("oc-edit-", ext, buf) if err != nil { return preservedFile(err, results.file, cmd.Out()) } // cleanup any file from the previous pass if len(results.file) > 0 { os.Remove(results.file) } glog.V(4).Infof("User edited:\n%s", string(edited)) lines, err := hasLines(bytes.NewBuffer(edited)) if err != nil { return preservedFile(err, file, cmd.Out()) } if bytes.Equal(original, edited) { if len(results.edit) > 0 { preservedFile(nil, file, cmd.Out()) } else { os.Remove(file) } fmt.Fprintln(cmd.Out(), "Edit cancelled, no changes made.") return nil } if !lines { if len(results.edit) > 0 { preservedFile(nil, file, cmd.Out()) } else { os.Remove(file) } fmt.Fprintln(cmd.Out(), "Edit cancelled, saved file was empty.") return nil } results = editResults{ file: file, } // parse the edited file updates, err := rmap.InfoForData(edited, "edited-file") if err != nil { results.header.reasons = append(results.header.reasons, editReason{ head: fmt.Sprintf("The edited file had a syntax error: %v", err), }) continue } visitor := resource.NewFlattenListVisitor(updates, rmap) // need to make sure the original namespace wasn't changed while editing if err = visitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil { return preservedFile(err, file, cmd.Out()) } // attempt to calculate a delta for merging conflicts delta, err := jsonmerge.NewDelta(original, edited) if err != nil { glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err) delta = nil } else { delta.AddPreconditions(jsonmerge.RequireKeyUnchanged("apiVersion")) results.delta = delta results.version = defaultVersion } err = visitor.Visit(func(info *resource.Info) error { data, err := info.Mapping.Codec.Encode(info.Object) if err != nil { return err } updated, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, false, data) if err != nil { fmt.Fprintln(cmd.Out(), results.AddError(err, info)) return nil } info.Refresh(updated, true) fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name) return nil }) if err != nil { return preservedFile(err, file, cmd.Out()) } if results.retryable > 0 { fmt.Fprintf(cmd.Out(), "You can run `%s update -f %s` to try this update again.\n", fullName, file) return errExit } if results.conflict > 0 { fmt.Fprintf(cmd.Out(), "You must update your local resource version and run `%s update -f %s` to overwrite the remote changes.\n", fullName, file) return errExit } if len(results.edit) == 0 { if results.notfound == 0 { os.Remove(file) } else { fmt.Fprintf(cmd.Out(), "The edits you made on deleted resources have been saved to %q\n", file) } return nil } // loop again and edit the remaining items infos = results.edit } return nil }
func RunExport(f *clientcmd.Factory, exporter Exporter, in io.Reader, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error { selector := cmdutil.GetFlagString(cmd, "selector") all := cmdutil.GetFlagBool(cmd, "all") exact := cmdutil.GetFlagBool(cmd, "exact") asTemplate := cmdutil.GetFlagString(cmd, "as-template") raw := cmdutil.GetFlagBool(cmd, "raw") if exact && raw { return cmdutil.UsageError(cmd, "--exact and --raw may not both be specified") } clientConfig, err := f.ClientConfig() if err != nil { return err } outputVersion := cmdutil.OutputVersion(cmd, clientConfig.Version) cmdNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(all, args...). Flatten() one := false infos, err := b.Do().IntoSingular(&one).Infos() if err != nil { return err } if len(infos) == 0 { return fmt.Errorf("no resources found - nothing to export") } if !raw { newInfos := []*resource.Info{} errs := []error{} for _, info := range infos { if err := exporter.Export(info.Object, exact); err != nil { if err == ErrExportOmit { continue } errs = append(errs, err) } newInfos = append(newInfos, info) } if len(errs) > 0 { return utilerrors.NewAggregate(errs) } infos = newInfos } var result runtime.Object if len(asTemplate) > 0 { objects, err := resource.AsVersionedObjects(infos, outputVersion) if err != nil { return err } template := &templateapi.Template{ Objects: objects, } template.Name = asTemplate result, err = kapi.Scheme.ConvertToVersion(template, outputVersion) if err != nil { return err } } else { object, err := resource.AsVersionedObject(infos, !one, outputVersion) if err != nil { return err } result = object } // use YAML as the default format outputFormat := cmdutil.GetFlagString(cmd, "output") templateFile := cmdutil.GetFlagString(cmd, "template") if len(outputFormat) == 0 && len(templateFile) != 0 { outputFormat = "template" } if len(outputFormat) == 0 { outputFormat = "yaml" } p, _, err := kubectl.GetPrinter(outputFormat, templateFile) if err != nil { return err } return p.PrintObj(result, out) }