config.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package control
  2. import (
  3. "fmt"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "sort"
  8. "strings"
  9. "text/template"
  10. log "github.com/Sirupsen/logrus"
  11. yaml "github.com/cloudfoundry-incubator/candiedyaml"
  12. "github.com/codegangsta/cli"
  13. "github.com/rancher/os/config"
  14. "github.com/rancher/os/util"
  15. )
  16. func configSubcommands() []cli.Command {
  17. return []cli.Command{
  18. {
  19. Name: "get",
  20. Usage: "get value",
  21. Action: configGet,
  22. },
  23. {
  24. Name: "set",
  25. Usage: "set a value",
  26. Action: configSet,
  27. },
  28. {
  29. Name: "images",
  30. Usage: "List Docker images for a configuration from a file",
  31. Action: runImages,
  32. Flags: []cli.Flag{
  33. cli.StringFlag{
  34. Name: "input, i",
  35. Usage: "File from which to read config",
  36. },
  37. },
  38. },
  39. {
  40. Name: "generate",
  41. Usage: "Generate a configuration file from a template",
  42. Action: runGenerate,
  43. HideHelp: true,
  44. },
  45. {
  46. Name: "export",
  47. Usage: "export configuration",
  48. Flags: []cli.Flag{
  49. cli.StringFlag{
  50. Name: "output, o",
  51. Usage: "File to which to save",
  52. },
  53. cli.BoolFlag{
  54. Name: "private, p",
  55. Usage: "Include the generated private keys",
  56. },
  57. cli.BoolFlag{
  58. Name: "full, f",
  59. Usage: "Export full configuration, including internal and default settings",
  60. },
  61. },
  62. Action: export,
  63. },
  64. {
  65. Name: "merge",
  66. Usage: "merge configuration from stdin",
  67. Action: merge,
  68. },
  69. }
  70. }
  71. func imagesFromConfig(cfg *config.CloudConfig) []string {
  72. imagesMap := map[string]int{}
  73. for _, service := range cfg.Rancher.BootstrapContainers {
  74. imagesMap[service.Image] = 1
  75. }
  76. for _, service := range cfg.Rancher.Autoformat {
  77. imagesMap[service.Image] = 1
  78. }
  79. for _, service := range cfg.Rancher.Services {
  80. imagesMap[service.Image] = 1
  81. }
  82. images := make([]string, len(imagesMap))
  83. i := 0
  84. for image := range imagesMap {
  85. images[i] = image
  86. i += 1
  87. }
  88. sort.Strings(images)
  89. return images
  90. }
  91. func runImages(c *cli.Context) error {
  92. configFile := c.String("input")
  93. cfg, err := config.ReadConfig(nil, false, configFile)
  94. if err != nil {
  95. log.WithFields(log.Fields{"err": err, "file": configFile}).Fatalf("Could not read config from file")
  96. }
  97. images := imagesFromConfig(cfg)
  98. fmt.Println(strings.Join(images, " "))
  99. return nil
  100. }
  101. func runGenerate(c *cli.Context) error {
  102. if err := genTpl(os.Stdin, os.Stdout); err != nil {
  103. log.Fatalf("Failed to generate config, err: '%s'", err)
  104. }
  105. return nil
  106. }
  107. func genTpl(in io.Reader, out io.Writer) error {
  108. bytes, err := ioutil.ReadAll(in)
  109. if err != nil {
  110. log.Fatal("Could not read from stdin")
  111. }
  112. tpl := template.Must(template.New("osconfig").Parse(string(bytes)))
  113. return tpl.Execute(out, env2map(os.Environ()))
  114. }
  115. func env2map(env []string) map[string]string {
  116. m := make(map[string]string, len(env))
  117. for _, s := range env {
  118. d := strings.Split(s, "=")
  119. m[d[0]] = d[1]
  120. }
  121. return m
  122. }
  123. func configSet(c *cli.Context) error {
  124. key := c.Args().Get(0)
  125. value := c.Args().Get(1)
  126. if key == "" {
  127. return nil
  128. }
  129. err := config.Set(key, value)
  130. if err != nil {
  131. log.Fatal(err)
  132. }
  133. return nil
  134. }
  135. func configGet(c *cli.Context) error {
  136. arg := c.Args().Get(0)
  137. if arg == "" {
  138. return nil
  139. }
  140. val, err := config.Get(arg)
  141. if err != nil {
  142. log.WithFields(log.Fields{"key": arg, "val": val, "err": err}).Fatal("config get: failed to retrieve value")
  143. }
  144. printYaml := false
  145. switch val.(type) {
  146. case []interface{}:
  147. printYaml = true
  148. case map[interface{}]interface{}:
  149. printYaml = true
  150. }
  151. if printYaml {
  152. bytes, err := yaml.Marshal(val)
  153. if err != nil {
  154. log.Fatal(err)
  155. }
  156. fmt.Println(string(bytes))
  157. } else {
  158. fmt.Println(val)
  159. }
  160. return nil
  161. }
  162. func merge(c *cli.Context) error {
  163. bytes, err := ioutil.ReadAll(os.Stdin)
  164. if err != nil {
  165. log.Fatal(err)
  166. }
  167. err = config.Merge(bytes)
  168. if err != nil {
  169. log.Fatal(err)
  170. }
  171. return nil
  172. }
  173. func export(c *cli.Context) error {
  174. content, err := config.Export(c.Bool("private"), c.Bool("full"))
  175. if err != nil {
  176. log.Fatal(err)
  177. }
  178. output := c.String("output")
  179. if output == "" {
  180. fmt.Println(content)
  181. } else {
  182. err := util.WriteFileAtomic(output, []byte(content), 0400)
  183. if err != nil {
  184. log.Fatal(err)
  185. }
  186. }
  187. return nil
  188. }