k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/genyaml/gen_kubectl_yaml.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"strings"
    25  
    26  	"github.com/spf13/cobra"
    27  	"github.com/spf13/pflag"
    28  	"gopkg.in/yaml.v2"
    29  	"k8s.io/cli-runtime/pkg/genericiooptions"
    30  	"k8s.io/kubectl/pkg/cmd"
    31  	"k8s.io/kubernetes/cmd/genutils"
    32  )
    33  
    34  type cmdOption struct {
    35  	Name         string
    36  	Shorthand    string `yaml:",omitempty"`
    37  	DefaultValue string `yaml:"default_value,omitempty"`
    38  	Usage        string `yaml:",omitempty"`
    39  }
    40  
    41  type cmdDoc struct {
    42  	Name             string
    43  	Synopsis         string      `yaml:",omitempty"`
    44  	Description      string      `yaml:",omitempty"`
    45  	Options          []cmdOption `yaml:",omitempty"`
    46  	InheritedOptions []cmdOption `yaml:"inherited_options,omitempty"`
    47  	Example          string      `yaml:",omitempty"`
    48  	SeeAlso          []string    `yaml:"see_also,omitempty"`
    49  }
    50  
    51  func main() {
    52  	path := "docs/yaml/kubectl"
    53  	if len(os.Args) == 2 {
    54  		path = os.Args[1]
    55  	} else if len(os.Args) > 2 {
    56  		fmt.Fprintf(os.Stderr, "usage: %s [output directory]\n", os.Args[0])
    57  		os.Exit(1)
    58  	}
    59  
    60  	outDir, err := genutils.OutDir(path)
    61  	if err != nil {
    62  		fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err)
    63  		os.Exit(1)
    64  	}
    65  
    66  	// Set environment variables used by kubectl so the output is consistent,
    67  	// regardless of where we run.
    68  	os.Setenv("HOME", "/home/username")
    69  	kubectl := cmd.NewKubectlCommand(cmd.KubectlOptions{IOStreams: genericiooptions.IOStreams{In: bytes.NewReader(nil), Out: io.Discard, ErrOut: io.Discard}})
    70  	genYaml(kubectl, "", outDir)
    71  	for _, c := range kubectl.Commands() {
    72  		genYaml(c, "kubectl", outDir)
    73  	}
    74  }
    75  
    76  // Temporary workaround for yaml lib generating incorrect yaml with long strings
    77  // that do not contain \n.
    78  func forceMultiLine(s string) string {
    79  	if len(s) > 60 && !strings.Contains(s, "\n") {
    80  		s = s + "\n"
    81  	}
    82  	return s
    83  }
    84  
    85  func genFlagResult(flags *pflag.FlagSet) []cmdOption {
    86  	result := []cmdOption{}
    87  
    88  	flags.VisitAll(func(flag *pflag.Flag) {
    89  		// Todo, when we mark a shorthand is deprecated, but specify an empty message.
    90  		// The flag.ShorthandDeprecated is empty as the shorthand is deprecated.
    91  		// Using len(flag.ShorthandDeprecated) > 0 can't handle this, others are ok.
    92  		if !(len(flag.ShorthandDeprecated) > 0) && len(flag.Shorthand) > 0 {
    93  			opt := cmdOption{
    94  				flag.Name,
    95  				flag.Shorthand,
    96  				flag.DefValue,
    97  				forceMultiLine(flag.Usage),
    98  			}
    99  			result = append(result, opt)
   100  		} else {
   101  			opt := cmdOption{
   102  				Name:         flag.Name,
   103  				DefaultValue: forceMultiLine(flag.DefValue),
   104  				Usage:        forceMultiLine(flag.Usage),
   105  			}
   106  			result = append(result, opt)
   107  		}
   108  	})
   109  
   110  	return result
   111  }
   112  
   113  func genYaml(command *cobra.Command, parent, docsDir string) {
   114  	doc := cmdDoc{}
   115  
   116  	doc.Name = command.Name()
   117  	doc.Synopsis = forceMultiLine(command.Short)
   118  	doc.Description = forceMultiLine(command.Long)
   119  
   120  	flags := command.NonInheritedFlags()
   121  	if flags.HasFlags() {
   122  		doc.Options = genFlagResult(flags)
   123  	}
   124  	flags = command.InheritedFlags()
   125  	if flags.HasFlags() {
   126  		doc.InheritedOptions = genFlagResult(flags)
   127  	}
   128  
   129  	if len(command.Example) > 0 {
   130  		doc.Example = command.Example
   131  	}
   132  
   133  	if len(command.Commands()) > 0 || len(parent) > 0 {
   134  		result := []string{}
   135  		if len(parent) > 0 {
   136  			result = append(result, parent)
   137  		}
   138  		for _, c := range command.Commands() {
   139  			result = append(result, c.Name())
   140  		}
   141  		doc.SeeAlso = result
   142  	}
   143  
   144  	final, err := yaml.Marshal(&doc)
   145  	if err != nil {
   146  		fmt.Println(err)
   147  		os.Exit(1)
   148  	}
   149  
   150  	var filename string
   151  
   152  	if parent == "" {
   153  		filename = docsDir + doc.Name + ".yaml"
   154  	} else {
   155  		filename = docsDir + parent + "_" + doc.Name + ".yaml"
   156  	}
   157  
   158  	outFile, err := os.Create(filename)
   159  	if err != nil {
   160  		fmt.Println(err)
   161  		os.Exit(1)
   162  	}
   163  	defer outFile.Close()
   164  	_, err = outFile.Write(final)
   165  	if err != nil {
   166  		fmt.Println(err)
   167  		os.Exit(1)
   168  	}
   169  }