hyperkube.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // CAUTION: If you update code in this file, you may need to also update code
  14. // in contrib/mesos/cmd/km/hyperkube.go
  15. package main
  16. import (
  17. "errors"
  18. "flag"
  19. "fmt"
  20. "io"
  21. "io/ioutil"
  22. "os"
  23. "path"
  24. "k8s.io/kubernetes/pkg/util"
  25. utilflag "k8s.io/kubernetes/pkg/util/flag"
  26. "k8s.io/kubernetes/pkg/util/logs"
  27. "k8s.io/kubernetes/pkg/version/verflag"
  28. "github.com/spf13/pflag"
  29. )
  30. // HyperKube represents a single binary that can morph/manage into multiple
  31. // servers.
  32. type HyperKube struct {
  33. Name string // The executable name, used for help and soft-link invocation
  34. Long string // A long description of the binary. It will be world wrapped before output.
  35. servers []Server
  36. baseFlags *pflag.FlagSet
  37. out io.Writer
  38. helpFlagVal bool
  39. makeSymlinksFlagVal bool
  40. }
  41. // AddServer adds a server to the HyperKube object.
  42. func (hk *HyperKube) AddServer(s *Server) {
  43. hk.servers = append(hk.servers, *s)
  44. hk.servers[len(hk.servers)-1].hk = hk
  45. }
  46. // FindServer will find a specific server named name.
  47. func (hk *HyperKube) FindServer(name string) (*Server, error) {
  48. for _, s := range hk.servers {
  49. if s.Name() == name {
  50. return &s, nil
  51. }
  52. }
  53. return nil, fmt.Errorf("Server not found: %s", name)
  54. }
  55. // Servers returns a list of all of the registered servers
  56. func (hk *HyperKube) Servers() []Server {
  57. return hk.servers
  58. }
  59. // Flags returns a flagset for "global" flags.
  60. func (hk *HyperKube) Flags() *pflag.FlagSet {
  61. if hk.baseFlags == nil {
  62. hk.baseFlags = pflag.NewFlagSet(hk.Name, pflag.ContinueOnError)
  63. hk.baseFlags.SetOutput(ioutil.Discard)
  64. hk.baseFlags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
  65. hk.baseFlags.BoolVarP(&hk.helpFlagVal, "help", "h", false, "help for "+hk.Name)
  66. hk.baseFlags.BoolVar(&hk.makeSymlinksFlagVal, "make-symlinks", false, "create a symlink for each server in current directory")
  67. hk.baseFlags.MarkHidden("make-symlinks") // hide this flag from appearing in servers' usage output
  68. // These will add all of the "global" flags (defined with both the
  69. // flag and pflag packages) to the new flag set we have.
  70. hk.baseFlags.AddGoFlagSet(flag.CommandLine)
  71. hk.baseFlags.AddFlagSet(pflag.CommandLine)
  72. }
  73. return hk.baseFlags
  74. }
  75. // Out returns the io.Writer that is used for all usage/error information
  76. func (hk *HyperKube) Out() io.Writer {
  77. if hk.out == nil {
  78. hk.out = os.Stderr
  79. }
  80. return hk.out
  81. }
  82. // SetOut sets the output writer for all usage/error information
  83. func (hk *HyperKube) SetOut(w io.Writer) {
  84. hk.out = w
  85. }
  86. // Print is a convenience method to Print to the defined output
  87. func (hk *HyperKube) Print(i ...interface{}) {
  88. fmt.Fprint(hk.Out(), i...)
  89. }
  90. // Println is a convenience method to Println to the defined output
  91. func (hk *HyperKube) Println(i ...interface{}) {
  92. fmt.Fprintln(hk.Out(), i...)
  93. }
  94. // Printf is a convenience method to Printf to the defined output
  95. func (hk *HyperKube) Printf(format string, i ...interface{}) {
  96. fmt.Fprintf(hk.Out(), format, i...)
  97. }
  98. // Run the server. This will pick the appropriate server and run it.
  99. func (hk *HyperKube) Run(args []string) error {
  100. // If we are called directly, parse all flags up to the first real
  101. // argument. That should be the server to run.
  102. command := args[0]
  103. baseCommand := path.Base(command)
  104. serverName := baseCommand
  105. args = args[1:]
  106. if serverName == hk.Name {
  107. baseFlags := hk.Flags()
  108. baseFlags.SetInterspersed(false) // Only parse flags up to the next real command
  109. err := baseFlags.Parse(args)
  110. if err != nil || hk.helpFlagVal {
  111. if err != nil {
  112. hk.Println("Error:", err)
  113. }
  114. hk.Usage()
  115. return err
  116. }
  117. if hk.makeSymlinksFlagVal {
  118. return hk.MakeSymlinks(command)
  119. }
  120. verflag.PrintAndExitIfRequested()
  121. args = baseFlags.Args()
  122. if len(args) > 0 && len(args[0]) > 0 {
  123. serverName = args[0]
  124. baseCommand = baseCommand + " " + serverName
  125. args = args[1:]
  126. } else {
  127. err = errors.New("No server specified")
  128. hk.Printf("Error: %v\n\n", err)
  129. hk.Usage()
  130. return err
  131. }
  132. }
  133. s, err := hk.FindServer(serverName)
  134. if err != nil {
  135. hk.Printf("Error: %v\n\n", err)
  136. hk.Usage()
  137. return err
  138. }
  139. s.Flags().AddFlagSet(hk.Flags())
  140. err = s.Flags().Parse(args)
  141. if err != nil || hk.helpFlagVal {
  142. if err != nil {
  143. hk.Printf("Error: %v\n\n", err)
  144. }
  145. s.Usage()
  146. return err
  147. }
  148. verflag.PrintAndExitIfRequested()
  149. logs.InitLogs()
  150. defer logs.FlushLogs()
  151. err = s.Run(s, s.Flags().Args())
  152. if err != nil {
  153. hk.Println("Error:", err)
  154. }
  155. return err
  156. }
  157. // RunToExit will run the hyperkube and then call os.Exit with an appropriate exit code.
  158. func (hk *HyperKube) RunToExit(args []string) {
  159. err := hk.Run(args)
  160. if err != nil {
  161. fmt.Fprint(os.Stderr, err.Error())
  162. os.Exit(1)
  163. }
  164. os.Exit(0)
  165. }
  166. // Usage will write out a summary for all servers that this binary supports.
  167. func (hk *HyperKube) Usage() {
  168. tt := `{{if .Long}}{{.Long | trim | wrap ""}}
  169. {{end}}Usage
  170. {{.Name}} <server> [flags]
  171. Servers
  172. {{range .Servers}}
  173. {{.Name}}
  174. {{.Long | trim | wrap " "}}{{end}}
  175. Call '{{.Name}} --make-symlinks' to create symlinks for each server in the local directory.
  176. Call '{{.Name}} <server> --help' for help on a specific server.
  177. `
  178. util.ExecuteTemplate(hk.Out(), tt, hk)
  179. }
  180. // MakeSymlinks will create a symlink for each registered hyperkube server in the local directory.
  181. func (hk *HyperKube) MakeSymlinks(command string) error {
  182. wd, err := os.Getwd()
  183. if err != nil {
  184. return err
  185. }
  186. var errs bool
  187. for _, s := range hk.servers {
  188. link := path.Join(wd, s.Name())
  189. err := os.Symlink(command, link)
  190. if err != nil {
  191. errs = true
  192. hk.Println(err)
  193. }
  194. }
  195. if errs {
  196. return errors.New("Error creating one or more symlinks.")
  197. }
  198. return nil
  199. }