forked from google/cloudprober
/
forwarding_rules.go
157 lines (144 loc) · 5.23 KB
/
forwarding_rules.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gce
import (
"fmt"
"net"
"strings"
"sync"
"time"
"cloud.google.com/go/compute/metadata"
"github.com/google/cloudprober/logger"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
compute "google.golang.org/api/compute/v1"
)
// globalForwardingRules is a singleton instance of the forwardingRules struct.
// It is presented as a singleton because, like instances, forwardingRules provides
// a cache layer that is best shared by all probes.
var (
globalForwardingRules *forwardingRules
onceForwardingRules sync.Once
)
// forwardingRules is a lister which lists GCE forwarding rules (see
// https://cloud.google.com/compute/docs/load-balancing/network/forwarding-rules
// for information on forwarding rules). In addition to being able to list the
// rules, this particular lister implements a cache. On a timer (configured by
// GlobalGCETargetsOptions.re_eval_sec cloudprober/targets/targets.proto) in the
// background the cache will be populated (by the equivalent of running "gcloud
// compute forwarding-rules list"). Listing actually only returns the current
// contents of that cache.
//
// Note that because this uses the GCLOUD API, GCE staging is unable to use this
// target type. See b/26320525 for more on this.
//
// TODO: The cache layer provided by this, instances, lameduck, and resolver
// are all pretty similar. RTC will need a similar cache. I should
// abstract out this whole cache layer. It will be more testable that
// way, and probably more readable, as well.
type forwardingRules struct {
project string
names []string
localRegion string
cache map[string]*compute.ForwardingRule
l *logger.Logger
}
// List produces a list of all the forwarding rules. The list is similar to
// "gcloud compute forwarding-rules list", but with a cache layer reducing the
// number of actual API calls made.
func (frp *forwardingRules) List() []string {
return frp.names
}
// Resolve returns the IP address associated with the forwarding
// rule. Eventually we can expand this to return protocol and port as well.
func (frp *forwardingRules) Resolve(name string, ipVer int) (net.IP, error) {
f := frp.cache[name]
if f == nil {
return nil, fmt.Errorf("gce.forwardingRulesProvider.resolve(%s): forwarding rule not in in-memory GCE forwardingRules database", name)
}
return net.ParseIP(f.IPAddress), nil
}
// listForwardingRules runs equivalent API calls as "gcloud compute
// forwarding-rules list", and is what is used to populate the cache.
func listForwardingRules(project, region string) ([]*compute.ForwardingRule, error) {
client, err := google.DefaultClient(oauth2.NoContext, compute.ComputeScope)
if err != nil {
return nil, err
}
cs, err := compute.New(client)
if err != nil {
return nil, err
}
l, err := cs.ForwardingRules.List(project, region).Do()
if err != nil {
return nil, err
}
return l.Items, nil
}
// expand will refill the cache, and update names.
func (frp *forwardingRules) expand() {
frp.l.Infof("gce.forwardingRules.expand: expanding GCE targets")
forwardingRules, err := listForwardingRules(frp.project, frp.localRegion)
if err != nil {
frp.l.Errorf("gce.forwardingRules.expand: error while getting list of all forwardingRules: %v", err)
return
}
var result []string
for _, ins := range forwardingRules {
frp.cache[ins.Name] = ins
result = append(result, ins.Name)
}
frp.l.Debugf("Expanded target list: %q", result)
frp.names = result
}
// Instance's region is not stored in the metadata, we need to get it from the zone.
func getLocalRegion() (string, error) {
zone, err := metadata.Zone()
if err != nil {
return "", err
}
zoneParts := strings.Split(zone, "-")
return strings.Join(zoneParts[0:len(zoneParts)-1], "-"), nil
}
// newForwardingrules will (if needed) initialize and return the
// globalForwardingRules singleton.
func newForwardingRules(project string, reEvalInterval time.Duration, l *logger.Logger) (*forwardingRules, error) {
var localRegion string
var err error
// Initialize forwardingRules provider only once
onceForwardingRules.Do(func() {
localRegion, err = getLocalRegion()
if err != nil {
err = fmt.Errorf("gce.newForwardingRules: error while getting local region: %v", err)
return
}
l.Infof("gce.newForwardingRules: local region: %s", localRegion)
globalForwardingRules = &forwardingRules{
project: project,
localRegion: localRegion,
cache: make(map[string]*compute.ForwardingRule),
l: l,
}
go func() {
globalForwardingRules.expand()
for _ = range time.Tick(reEvalInterval) {
globalForwardingRules.expand()
}
}()
})
if err != nil {
return nil, err
}
return globalForwardingRules, err
}