main.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright 2015 CoreOS, Inc.
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package main
  15. import (
  16. "flag"
  17. "fmt"
  18. "net"
  19. "os"
  20. "os/signal"
  21. "path/filepath"
  22. "strings"
  23. "sync"
  24. "syscall"
  25. "github.com/coreos/flannel/Godeps/_workspace/src/github.com/coreos/go-systemd/daemon"
  26. log "github.com/coreos/flannel/Godeps/_workspace/src/github.com/golang/glog"
  27. "github.com/coreos/flannel/Godeps/_workspace/src/golang.org/x/net/context"
  28. "github.com/coreos/flannel/backend"
  29. "github.com/coreos/flannel/network"
  30. "github.com/coreos/flannel/pkg/ip"
  31. "github.com/coreos/flannel/subnet"
  32. )
  33. type CmdLineOpts struct {
  34. etcdEndpoints string
  35. etcdPrefix string
  36. etcdKeyfile string
  37. etcdCertfile string
  38. etcdCAFile string
  39. help bool
  40. version bool
  41. ipMasq bool
  42. subnetFile string
  43. subnetDir string
  44. iface string
  45. networks string
  46. }
  47. var opts CmdLineOpts
  48. func init() {
  49. flag.StringVar(&opts.etcdEndpoints, "etcd-endpoints", "http://127.0.0.1:4001,http://127.0.0.1:2379", "a comma-delimited list of etcd endpoints")
  50. flag.StringVar(&opts.etcdPrefix, "etcd-prefix", "/coreos.com/network", "etcd prefix")
  51. flag.StringVar(&opts.etcdKeyfile, "etcd-keyfile", "", "SSL key file used to secure etcd communication")
  52. flag.StringVar(&opts.etcdCertfile, "etcd-certfile", "", "SSL certification file used to secure etcd communication")
  53. flag.StringVar(&opts.etcdCAFile, "etcd-cafile", "", "SSL Certificate Authority file used to secure etcd communication")
  54. flag.StringVar(&opts.subnetFile, "subnet-file", "/run/flannel/subnet.env", "filename where env variables (subnet, MTU, ... ) will be written to")
  55. flag.StringVar(&opts.subnetDir, "subnet-dir", "/run/flannel/networks", "directory where files with env variables (subnet, MTU, ...) will be written to")
  56. flag.StringVar(&opts.iface, "iface", "", "interface to use (IP or name) for inter-host communication")
  57. flag.StringVar(&opts.networks, "networks", "", "run in multi-network mode and service the specified networks")
  58. flag.BoolVar(&opts.ipMasq, "ip-masq", false, "setup IP masquerade rule for traffic destined outside of overlay network")
  59. flag.BoolVar(&opts.help, "help", false, "print this message")
  60. flag.BoolVar(&opts.version, "version", false, "print version and exit")
  61. }
  62. // TODO: This is yet another copy (others found in etcd, fleet) -- Pull it out!
  63. // flagsFromEnv parses all registered flags in the given flagset,
  64. // and if they are not already set it attempts to set their values from
  65. // environment variables. Environment variables take the name of the flag but
  66. // are UPPERCASE, have the given prefix, and any dashes are replaced by
  67. // underscores - for example: some-flag => PREFIX_SOME_FLAG
  68. func flagsFromEnv(prefix string, fs *flag.FlagSet) {
  69. alreadySet := make(map[string]bool)
  70. fs.Visit(func(f *flag.Flag) {
  71. alreadySet[f.Name] = true
  72. })
  73. fs.VisitAll(func(f *flag.Flag) {
  74. if !alreadySet[f.Name] {
  75. key := strings.ToUpper(prefix + "_" + strings.Replace(f.Name, "-", "_", -1))
  76. val := os.Getenv(key)
  77. if val != "" {
  78. fs.Set(f.Name, val)
  79. }
  80. }
  81. })
  82. }
  83. func writeSubnetFile(path string, sn *backend.SubnetDef) error {
  84. dir, name := filepath.Split(path)
  85. os.MkdirAll(dir, 0755)
  86. tempFile := filepath.Join(dir, "."+name)
  87. f, err := os.Create(tempFile)
  88. if err != nil {
  89. return err
  90. }
  91. // Write out the first usable IP by incrementing
  92. // sn.IP by one
  93. sn.Net.IP += 1
  94. fmt.Fprintf(f, "FLANNEL_SUBNET=%s\n", sn.Net)
  95. fmt.Fprintf(f, "FLANNEL_MTU=%d\n", sn.MTU)
  96. _, err = fmt.Fprintf(f, "FLANNEL_IPMASQ=%v\n", opts.ipMasq)
  97. f.Close()
  98. if err != nil {
  99. return err
  100. }
  101. // rename(2) the temporary file to the desired location so that it becomes
  102. // atomically visible with the contents
  103. return os.Rename(tempFile, path)
  104. }
  105. func lookupIface() (*net.Interface, net.IP, error) {
  106. var iface *net.Interface
  107. var ipaddr net.IP
  108. var err error
  109. if len(opts.iface) > 0 {
  110. if ipaddr = net.ParseIP(opts.iface); ipaddr != nil {
  111. iface, err = ip.GetInterfaceByIP(ipaddr)
  112. if err != nil {
  113. return nil, nil, fmt.Errorf("Error looking up interface %s: %s", opts.iface, err)
  114. }
  115. } else {
  116. iface, err = net.InterfaceByName(opts.iface)
  117. if err != nil {
  118. return nil, nil, fmt.Errorf("Error looking up interface %s: %s", opts.iface, err)
  119. }
  120. }
  121. } else {
  122. log.Info("Determining IP address of default interface")
  123. if iface, err = ip.GetDefaultGatewayIface(); err != nil {
  124. return nil, nil, fmt.Errorf("Failed to get default interface: %s", err)
  125. }
  126. }
  127. if ipaddr == nil {
  128. ipaddr, err = ip.GetIfaceIP4Addr(iface)
  129. if err != nil {
  130. return nil, nil, fmt.Errorf("Failed to find IPv4 address for interface %s", iface.Name)
  131. }
  132. }
  133. return iface, ipaddr, nil
  134. }
  135. func isMultiNetwork() bool {
  136. return len(opts.networks) > 0
  137. }
  138. func newSubnetManager() (subnet.Manager, error) {
  139. cfg := &subnet.EtcdConfig{
  140. Endpoints: strings.Split(opts.etcdEndpoints, ","),
  141. Keyfile: opts.etcdKeyfile,
  142. Certfile: opts.etcdCertfile,
  143. CAFile: opts.etcdCAFile,
  144. Prefix: opts.etcdPrefix,
  145. }
  146. return subnet.NewEtcdManager(cfg)
  147. }
  148. func initAndRun(ctx context.Context, sm subnet.Manager, netnames []string) {
  149. iface, ipaddr, err := lookupIface()
  150. if err != nil {
  151. log.Error(err)
  152. return
  153. }
  154. if iface.MTU == 0 {
  155. log.Errorf("Failed to determine MTU for %s interface", ipaddr)
  156. return
  157. }
  158. log.Infof("Using %s as external interface", ipaddr)
  159. nets := []*network.Network{}
  160. for _, n := range netnames {
  161. nets = append(nets, network.New(sm, n, opts.ipMasq))
  162. }
  163. wg := sync.WaitGroup{}
  164. for _, n := range nets {
  165. go func(n *network.Network) {
  166. wg.Add(1)
  167. defer wg.Done()
  168. sn := n.Init(ctx, iface, ipaddr)
  169. if sn != nil {
  170. if isMultiNetwork() {
  171. path := filepath.Join(opts.subnetDir, n.Name) + ".env"
  172. if err := writeSubnetFile(path, sn); err != nil {
  173. return
  174. }
  175. } else {
  176. if err := writeSubnetFile(opts.subnetFile, sn); err != nil {
  177. return
  178. }
  179. daemon.SdNotify("READY=1")
  180. }
  181. n.Run(ctx)
  182. log.Infof("%v exited", n.Name)
  183. }
  184. }(n)
  185. }
  186. wg.Wait()
  187. }
  188. func main() {
  189. // glog will log to tmp files by default. override so all entries
  190. // can flow into journald (if running under systemd)
  191. flag.Set("logtostderr", "true")
  192. // now parse command line args
  193. flag.Parse()
  194. if opts.help {
  195. fmt.Fprintf(os.Stderr, "Usage: %s [OPTION]...\n", os.Args[0])
  196. flag.PrintDefaults()
  197. os.Exit(0)
  198. }
  199. if opts.version {
  200. fmt.Fprintln(os.Stderr, Version)
  201. os.Exit(0)
  202. }
  203. flagsFromEnv("FLANNELD", flag.CommandLine)
  204. sm, err := newSubnetManager()
  205. if err != nil {
  206. log.Error("Failed to create SubnetManager: ", err)
  207. os.Exit(1)
  208. }
  209. var runFunc func(ctx context.Context)
  210. networks := strings.Split(opts.networks, ",")
  211. if len(networks) == 0 {
  212. networks = append(networks, "")
  213. }
  214. runFunc = func(ctx context.Context) {
  215. initAndRun(ctx, sm, networks)
  216. }
  217. // Register for SIGINT and SIGTERM
  218. log.Info("Installing signal handlers")
  219. sigs := make(chan os.Signal, 1)
  220. signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
  221. ctx, cancel := context.WithCancel(context.Background())
  222. wg := sync.WaitGroup{}
  223. wg.Add(1)
  224. go func() {
  225. runFunc(ctx)
  226. wg.Done()
  227. }()
  228. <-sigs
  229. // unregister to get default OS nuke behaviour in case we don't exit cleanly
  230. signal.Stop(sigs)
  231. log.Info("Exiting...")
  232. cancel()
  233. wg.Wait()
  234. }