gce.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // Copyright 2015 flannel authors
  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. // This work borrows from the https://github.com/kelseyhightower/flannel-route-manager
  15. // project which has the following license agreement.
  16. // Copyright (c) 2014 Kelsey Hightower
  17. // Permission is hereby granted, free of charge, to any person obtaining a copy of
  18. // this software and associated documentation files (the "Software"), to deal in
  19. // the Software without restriction, including without limitation the rights to
  20. // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  21. // of the Software, and to permit persons to whom the Software is furnished to do
  22. // so, subject to the following conditions:
  23. // The above copyright notice and this permission notice shall be included in all
  24. // copies or substantial portions of the Software.
  25. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  26. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  27. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  28. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  29. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  30. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31. // SOFTWARE.
  32. package gce
  33. import (
  34. "fmt"
  35. "net"
  36. "strings"
  37. "sync"
  38. "time"
  39. "github.com/coreos/flannel/Godeps/_workspace/src/code.google.com/p/goauth2/compute/serviceaccount"
  40. "github.com/coreos/flannel/Godeps/_workspace/src/code.google.com/p/google-api-go-client/compute/v1"
  41. "github.com/coreos/flannel/Godeps/_workspace/src/code.google.com/p/google-api-go-client/googleapi"
  42. log "github.com/coreos/flannel/Godeps/_workspace/src/github.com/golang/glog"
  43. "github.com/coreos/flannel/Godeps/_workspace/src/golang.org/x/net/context"
  44. "github.com/coreos/flannel/backend"
  45. "github.com/coreos/flannel/pkg/ip"
  46. "github.com/coreos/flannel/subnet"
  47. )
  48. var metadataEndpoint = "http://169.254.169.254/computeMetadata/v1"
  49. var replacer = strings.NewReplacer(".", "-", "/", "-")
  50. type GCEBackend struct {
  51. network string
  52. project string
  53. sm subnet.Manager
  54. config *subnet.Config
  55. lease *subnet.Lease
  56. ctx context.Context
  57. cancel context.CancelFunc
  58. wg sync.WaitGroup
  59. computeService *compute.Service
  60. gceNetwork *compute.Network
  61. gceInstance *compute.Instance
  62. }
  63. func New(sm subnet.Manager, network string, config *subnet.Config) backend.Backend {
  64. ctx, cancel := context.WithCancel(context.Background())
  65. gb := GCEBackend{
  66. sm: sm,
  67. config: config,
  68. ctx: ctx,
  69. cancel: cancel,
  70. network: network,
  71. }
  72. return &gb
  73. }
  74. func (g *GCEBackend) Init(extIface *net.Interface, extIaddr net.IP, extEaddr net.IP) (*backend.SubnetDef, error) {
  75. attrs := subnet.LeaseAttrs{
  76. PublicIP: ip.FromIP(extEaddr),
  77. }
  78. l, err := g.sm.AcquireLease(g.ctx, g.network, &attrs)
  79. switch err {
  80. case nil:
  81. g.lease = l
  82. case context.Canceled, context.DeadlineExceeded:
  83. return nil, err
  84. default:
  85. return nil, fmt.Errorf("failed to acquire lease: %v", err)
  86. }
  87. client, err := serviceaccount.NewClient(&serviceaccount.Options{})
  88. if err != nil {
  89. return nil, fmt.Errorf("error creating client: %v", err)
  90. }
  91. g.computeService, err = compute.New(client)
  92. if err != nil {
  93. return nil, fmt.Errorf("error creating compute service: %v", err)
  94. }
  95. networkName, err := networkFromMetadata()
  96. if err != nil {
  97. return nil, fmt.Errorf("error getting network metadata: %v", err)
  98. }
  99. g.project, err = projectFromMetadata()
  100. if err != nil {
  101. return nil, fmt.Errorf("error getting project: %v", err)
  102. }
  103. instanceName, err := instanceNameFromMetadata()
  104. if err != nil {
  105. return nil, fmt.Errorf("error getting instance name: %v", err)
  106. }
  107. instanceZone, err := instanceZoneFromMetadata()
  108. if err != nil {
  109. return nil, fmt.Errorf("error getting instance zone: %v", err)
  110. }
  111. g.gceNetwork, err = g.computeService.Networks.Get(g.project, networkName).Do()
  112. if err != nil {
  113. return nil, fmt.Errorf("error getting network from compute service: %v", err)
  114. }
  115. g.gceInstance, err = g.computeService.Instances.Get(g.project, instanceZone, instanceName).Do()
  116. if err != nil {
  117. return nil, fmt.Errorf("error getting instance from compute service: %v", err)
  118. }
  119. found, err := g.handleMatchingRoute(l.Subnet.String())
  120. if err != nil {
  121. return nil, fmt.Errorf("error handling matching route: %v", err)
  122. }
  123. if !found {
  124. operation, err := g.insertRoute(l.Subnet.String())
  125. if err != nil {
  126. return nil, fmt.Errorf("error inserting route: %v", err)
  127. }
  128. err = g.pollOperationStatus(operation.Name)
  129. if err != nil {
  130. return nil, fmt.Errorf("insert operaiton failed: ", err)
  131. }
  132. }
  133. return &backend.SubnetDef{
  134. Net: l.Subnet,
  135. MTU: extIface.MTU,
  136. }, nil
  137. }
  138. func (g *GCEBackend) Run() {
  139. log.Info("GCE backend running")
  140. subnet.LeaseRenewer(g.ctx, g.sm, g.network, g.lease)
  141. }
  142. func (g *GCEBackend) Stop() {
  143. log.Info("GCE backend stopping")
  144. g.cancel()
  145. }
  146. func (g *GCEBackend) Name() string {
  147. return "gce"
  148. }
  149. func (g *GCEBackend) pollOperationStatus(operationName string) error {
  150. for i := 0; i < 100; i++ {
  151. operation, err := g.computeService.GlobalOperations.Get(g.project, operationName).Do()
  152. if err != nil {
  153. return fmt.Errorf("error fetching operation status: %v", err)
  154. }
  155. if operation.Error != nil {
  156. return fmt.Errorf("error running operation: %v", operation.Error)
  157. }
  158. if i%5 == 0 {
  159. log.Infof("%v operation status: %v waiting for completion...", operation.OperationType, operation.Status)
  160. }
  161. if operation.Status == "DONE" {
  162. return nil
  163. }
  164. time.Sleep(time.Second)
  165. }
  166. return fmt.Errorf("timeout waiting for operation to finish")
  167. }
  168. //returns true if an exact matching rule is found
  169. func (g *GCEBackend) handleMatchingRoute(subnet string) (bool, error) {
  170. matchingRoute, err := g.getRoute(subnet)
  171. if err != nil {
  172. if apiError, ok := err.(*googleapi.Error); ok {
  173. if apiError.Code != 404 {
  174. return false, fmt.Errorf("error getting the route err: %v", err)
  175. }
  176. return false, nil
  177. }
  178. return false, fmt.Errorf("error getting googleapi: %v", err)
  179. }
  180. if matchingRoute.NextHopInstance == g.gceInstance.SelfLink {
  181. log.Info("Exact pre-existing route found")
  182. return true, nil
  183. }
  184. log.Info("Deleting conflicting route")
  185. operation, err := g.deleteRoute(subnet)
  186. if err != nil {
  187. return false, fmt.Errorf("error deleting conflicting route : %v", err)
  188. }
  189. err = g.pollOperationStatus(operation.Name)
  190. if err != nil {
  191. return false, fmt.Errorf("delete operation failed: %v", err)
  192. }
  193. return false, nil
  194. }
  195. func (g *GCEBackend) getRoute(subnet string) (*compute.Route, error) {
  196. routeName := formatRouteName(subnet)
  197. return g.computeService.Routes.Get(g.project, routeName).Do()
  198. }
  199. func (g *GCEBackend) deleteRoute(subnet string) (*compute.Operation, error) {
  200. routeName := formatRouteName(subnet)
  201. return g.computeService.Routes.Delete(g.project, routeName).Do()
  202. }
  203. func (g *GCEBackend) insertRoute(subnet string) (*compute.Operation, error) {
  204. log.Infof("Inserting route for subnet: %v", subnet)
  205. route := &compute.Route{
  206. Name: formatRouteName(subnet),
  207. DestRange: subnet,
  208. Network: g.gceNetwork.SelfLink,
  209. NextHopInstance: g.gceInstance.SelfLink,
  210. Priority: 1000,
  211. Tags: []string{},
  212. }
  213. return g.computeService.Routes.Insert(g.project, route).Do()
  214. }
  215. func formatRouteName(subnet string) string {
  216. return fmt.Sprintf("flannel-%s", replacer.Replace(subnet))
  217. }