main.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. "github.com/coreos/flannel/Godeps/_workspace/src/github.com/coreos/pkg/flagutil"
  27. log "github.com/coreos/flannel/Godeps/_workspace/src/github.com/golang/glog"
  28. "github.com/coreos/flannel/Godeps/_workspace/src/golang.org/x/net/context"
  29. "github.com/coreos/flannel/backend"
  30. "github.com/coreos/flannel/network"
  31. "github.com/coreos/flannel/pkg/ip"
  32. "github.com/coreos/flannel/remote"
  33. "github.com/coreos/flannel/subnet"
  34. )
  35. type CmdLineOpts struct {
  36. publicIP string
  37. etcdEndpoints string
  38. etcdPrefix string
  39. etcdKeyfile string
  40. etcdCertfile string
  41. etcdCAFile string
  42. help bool
  43. version bool
  44. ipMasq bool
  45. subnetFile string
  46. subnetDir string
  47. iface string
  48. listen string
  49. remote string
  50. remoteKeyfile string
  51. remoteCertfile string
  52. remoteCAFile string
  53. networks string
  54. }
  55. var opts CmdLineOpts
  56. func init() {
  57. flag.StringVar(&opts.publicIP, "public-ip", "", "IP accessible by other nodes for inter-host communication")
  58. 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")
  59. flag.StringVar(&opts.etcdPrefix, "etcd-prefix", "/coreos.com/network", "etcd prefix")
  60. flag.StringVar(&opts.etcdKeyfile, "etcd-keyfile", "", "SSL key file used to secure etcd communication")
  61. flag.StringVar(&opts.etcdCertfile, "etcd-certfile", "", "SSL certification file used to secure etcd communication")
  62. flag.StringVar(&opts.etcdCAFile, "etcd-cafile", "", "SSL Certificate Authority file used to secure etcd communication")
  63. flag.StringVar(&opts.subnetFile, "subnet-file", "/run/flannel/subnet.env", "filename where env variables (subnet, MTU, ... ) will be written to")
  64. flag.StringVar(&opts.subnetDir, "subnet-dir", "/run/flannel/networks", "directory where files with env variables (subnet, MTU, ...) will be written to")
  65. flag.StringVar(&opts.iface, "iface", "", "interface to use (IP or name) for inter-host communication")
  66. flag.StringVar(&opts.listen, "listen", "", "run as server and listen on specified address (e.g. ':8080')")
  67. flag.StringVar(&opts.remote, "remote", "", "run as client and connect to server on specified address (e.g. '10.1.2.3:8080')")
  68. flag.StringVar(&opts.remoteKeyfile, "remote-keyfile", "", "SSL key file used to secure client/server communication")
  69. flag.StringVar(&opts.remoteCertfile, "remote-certfile", "", "SSL certification file used to secure client/server communication")
  70. flag.StringVar(&opts.remoteCAFile, "remote-cafile", "", "SSL Certificate Authority file used to secure client/server communication")
  71. flag.StringVar(&opts.networks, "networks", "", "run in multi-network mode and service the specified networks")
  72. flag.BoolVar(&opts.ipMasq, "ip-masq", false, "setup IP masquerade rule for traffic destined outside of overlay network")
  73. flag.BoolVar(&opts.help, "help", false, "print this message")
  74. flag.BoolVar(&opts.version, "version", false, "print version and exit")
  75. }
  76. func writeSubnetFile(path string, nw ip.IP4Net, sn *backend.SubnetDef) error {
  77. dir, name := filepath.Split(path)
  78. os.MkdirAll(dir, 0755)
  79. tempFile := filepath.Join(dir, "."+name)
  80. f, err := os.Create(tempFile)
  81. if err != nil {
  82. return err
  83. }
  84. // Write out the first usable IP by incrementing
  85. // sn.IP by one
  86. sn.Net.IP += 1
  87. fmt.Fprintf(f, "FLANNEL_NETWORK=%s\n", nw)
  88. fmt.Fprintf(f, "FLANNEL_SUBNET=%s\n", sn.Net)
  89. fmt.Fprintf(f, "FLANNEL_MTU=%d\n", sn.MTU)
  90. _, err = fmt.Fprintf(f, "FLANNEL_IPMASQ=%v\n", opts.ipMasq)
  91. f.Close()
  92. if err != nil {
  93. return err
  94. }
  95. // rename(2) the temporary file to the desired location so that it becomes
  96. // atomically visible with the contents
  97. return os.Rename(tempFile, path)
  98. }
  99. func lookupIface() (*net.Interface, net.IP, error) {
  100. var iface *net.Interface
  101. var iaddr net.IP
  102. var err error
  103. if len(opts.iface) > 0 {
  104. if iaddr = net.ParseIP(opts.iface); iaddr != nil {
  105. iface, err = ip.GetInterfaceByIP(iaddr)
  106. if err != nil {
  107. return nil, nil, fmt.Errorf("Error looking up interface %s: %s", opts.iface, err)
  108. }
  109. } else {
  110. iface, err = net.InterfaceByName(opts.iface)
  111. if err != nil {
  112. return nil, nil, fmt.Errorf("Error looking up interface %s: %s", opts.iface, err)
  113. }
  114. }
  115. } else {
  116. log.Info("Determining IP address of default interface")
  117. if iface, err = ip.GetDefaultGatewayIface(); err != nil {
  118. return nil, nil, fmt.Errorf("Failed to get default interface: %s", err)
  119. }
  120. }
  121. if iaddr == nil {
  122. iaddr, err = ip.GetIfaceIP4Addr(iface)
  123. if err != nil {
  124. return nil, nil, fmt.Errorf("Failed to find IPv4 address for interface %s", iface.Name)
  125. }
  126. }
  127. return iface, iaddr, nil
  128. }
  129. func isMultiNetwork() bool {
  130. return len(opts.networks) > 0
  131. }
  132. func newSubnetManager() (subnet.Manager, error) {
  133. if opts.remote != "" {
  134. return remote.NewRemoteManager(opts.remote, opts.remoteCAFile, opts.remoteCertfile, opts.remoteKeyfile)
  135. }
  136. cfg := &subnet.EtcdConfig{
  137. Endpoints: strings.Split(opts.etcdEndpoints, ","),
  138. Keyfile: opts.etcdKeyfile,
  139. Certfile: opts.etcdCertfile,
  140. CAFile: opts.etcdCAFile,
  141. Prefix: opts.etcdPrefix,
  142. }
  143. return subnet.NewEtcdManager(cfg)
  144. }
  145. func initAndRun(ctx context.Context, sm subnet.Manager, netnames []string) {
  146. iface, iaddr, err := lookupIface()
  147. if err != nil {
  148. log.Error(err)
  149. return
  150. }
  151. if iface.MTU == 0 {
  152. log.Errorf("Failed to determine MTU for %s interface", iaddr)
  153. return
  154. }
  155. var eaddr net.IP
  156. if len(opts.publicIP) > 0 {
  157. eaddr = net.ParseIP(opts.publicIP)
  158. }
  159. if eaddr == nil {
  160. eaddr = iaddr
  161. }
  162. log.Infof("Using %s as external interface", iaddr)
  163. log.Infof("Using %s as external endpoint", eaddr)
  164. nets := []*network.Network{}
  165. for _, n := range netnames {
  166. nets = append(nets, network.New(sm, n, opts.ipMasq))
  167. }
  168. wg := sync.WaitGroup{}
  169. for _, n := range nets {
  170. go func(n *network.Network) {
  171. wg.Add(1)
  172. defer wg.Done()
  173. sn := n.Init(ctx, iface, iaddr, eaddr)
  174. if sn != nil {
  175. if isMultiNetwork() {
  176. path := filepath.Join(opts.subnetDir, n.Name) + ".env"
  177. if err := writeSubnetFile(path, n.Config.Network, sn); err != nil {
  178. return
  179. }
  180. } else {
  181. if err := writeSubnetFile(opts.subnetFile, n.Config.Network, sn); err != nil {
  182. return
  183. }
  184. daemon.SdNotify("READY=1")
  185. }
  186. n.Run(ctx)
  187. log.Infof("%v exited", n.Name)
  188. }
  189. }(n)
  190. }
  191. wg.Wait()
  192. }
  193. func main() {
  194. // glog will log to tmp files by default. override so all entries
  195. // can flow into journald (if running under systemd)
  196. flag.Set("logtostderr", "true")
  197. // now parse command line args
  198. flag.Parse()
  199. if flag.NArg() > 0 || opts.help {
  200. fmt.Fprintf(os.Stderr, "Usage: %s [OPTION]...\n", os.Args[0])
  201. flag.PrintDefaults()
  202. os.Exit(0)
  203. }
  204. if opts.version {
  205. fmt.Fprintln(os.Stderr, Version)
  206. os.Exit(0)
  207. }
  208. flagutil.SetFlagsFromEnv(flag.CommandLine, "FLANNELD")
  209. sm, err := newSubnetManager()
  210. if err != nil {
  211. log.Error("Failed to create SubnetManager: ", err)
  212. os.Exit(1)
  213. }
  214. var runFunc func(ctx context.Context)
  215. if opts.listen != "" {
  216. if opts.remote != "" {
  217. log.Error("--listen and --remote are mutually exclusive")
  218. os.Exit(1)
  219. }
  220. log.Info("running as server")
  221. runFunc = func(ctx context.Context) {
  222. remote.RunServer(ctx, sm, opts.listen, opts.remoteCAFile, opts.remoteCertfile, opts.remoteKeyfile)
  223. }
  224. } else {
  225. networks := strings.Split(opts.networks, ",")
  226. if len(networks) == 0 {
  227. networks = append(networks, "")
  228. }
  229. runFunc = func(ctx context.Context) {
  230. initAndRun(ctx, sm, networks)
  231. }
  232. }
  233. // Register for SIGINT and SIGTERM
  234. log.Info("Installing signal handlers")
  235. sigs := make(chan os.Signal, 1)
  236. signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
  237. ctx, cancel := context.WithCancel(context.Background())
  238. wg := sync.WaitGroup{}
  239. wg.Add(1)
  240. go func() {
  241. runFunc(ctx)
  242. wg.Done()
  243. }()
  244. <-sigs
  245. // unregister to get default OS nuke behaviour in case we don't exit cleanly
  246. signal.Stop(sigs)
  247. log.Info("Exiting...")
  248. cancel()
  249. wg.Wait()
  250. }