github.com/google/cloudprober@v0.11.3/targets/gce/gce.go (about) 1 // Copyright 2017-2019 The Cloudprober 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 15 // Package gce implements Google Compute Engine (GCE) targets for Cloudprober. 16 // 17 // It currently supports following GCE targets: 18 // Instances 19 // Forwarding Rules (only regional currently) 20 // 21 // Targets are configured through a config file, based on the protobuf defined 22 // in the config.proto file in the same directory. Example config: 23 // 24 // All instances matching a certain regex: 25 // targets { 26 // gce_targets { 27 // instances {} 28 // } 29 // regex: "ig-proxy-.*" 30 // } 31 // 32 // Public IP of all instances matching a certain regex: 33 // targets { 34 // gce_targets { 35 // instances { 36 // public_ip: true 37 // } 38 // } 39 // regex: "ig-proxy-.*" 40 // } 41 // 42 // All forwarding rules in the local region: 43 // targets { 44 // gce_targets { 45 // forwarding_rules {} 46 // } 47 // } 48 package gce 49 50 import ( 51 "context" 52 "errors" 53 "fmt" 54 "net" 55 "sync" 56 57 "cloud.google.com/go/compute/metadata" 58 "github.com/golang/protobuf/proto" 59 "github.com/google/cloudprober/logger" 60 "github.com/google/cloudprober/targets/endpoint" 61 configpb "github.com/google/cloudprober/targets/gce/proto" 62 dnsRes "github.com/google/cloudprober/targets/resolver" 63 64 "github.com/google/cloudprober/rds/client" 65 clientconfigpb "github.com/google/cloudprober/rds/client/proto" 66 "github.com/google/cloudprober/rds/gcp" 67 rdspb "github.com/google/cloudprober/rds/proto" 68 "github.com/google/cloudprober/rds/server" 69 serverconfigpb "github.com/google/cloudprober/rds/server/proto" 70 ) 71 72 var global struct { 73 mu sync.RWMutex 74 servers map[string]*server.Server 75 } 76 77 // Targets interface is redefined here (originally defined in targets.go) to 78 // allow returning private gceResources. 79 type Targets interface { 80 // ListEndpoints produces list of targets. 81 ListEndpoints() []endpoint.Endpoint 82 // Resolve, given a target name and IP Version will return the IP address for 83 // that target. 84 Resolve(name string, ipVer int) (net.IP, error) 85 } 86 87 // gceResources encapsulates a set of GCE resources of a particular type. It's 88 // mostly a wrapper around RDS clients created during the initialization step. 89 type gceResources struct { 90 c *configpb.TargetsConf 91 globalOpts *configpb.GlobalOptions 92 l *logger.Logger 93 94 // RDS parameters 95 resourceType string 96 ipConfig *rdspb.IPConfig 97 filters []*rdspb.Filter 98 clients map[string]*client.Client 99 100 // DNS config 101 r *dnsRes.Resolver 102 useDNS bool 103 } 104 105 func initRDSServer(resourceType, apiVersion string, projects []string, reEvalInterval int, l *logger.Logger) (*server.Server, error) { 106 global.mu.Lock() 107 defer global.mu.Unlock() 108 109 if global.servers == nil { 110 global.servers = make(map[string]*server.Server) 111 } 112 113 if global.servers[resourceType] != nil { 114 return global.servers[resourceType], nil 115 } 116 117 pc := gcp.DefaultProviderConfig(projects, map[string]string{gcp.ResourceTypes.GCEInstances: ""}, reEvalInterval, apiVersion) 118 srv, err := server.New(context.Background(), &serverconfigpb.ServerConf{Provider: []*serverconfigpb.Provider{pc}}, nil, l) 119 if err != nil { 120 return nil, err 121 } 122 123 global.servers[resourceType] = srv 124 return srv, nil 125 } 126 127 func newRDSClient(req *rdspb.ListResourcesRequest, listResourcesFunc client.ListResourcesFunc, l *logger.Logger) (*client.Client, error) { 128 c := &clientconfigpb.ClientConf{ 129 Request: req, 130 ReEvalSec: proto.Int32(10), // Refresh client every 10s. 131 } 132 133 client, err := client.New(c, listResourcesFunc, l) 134 if err != nil { 135 return nil, err 136 } 137 138 return client, nil 139 } 140 141 func (gr *gceResources) rdsRequest(project string) *rdspb.ListResourcesRequest { 142 return &rdspb.ListResourcesRequest{ 143 Provider: proto.String("gcp"), 144 ResourcePath: proto.String(fmt.Sprintf("%s/%s", gr.resourceType, project)), 145 Filter: gr.filters, 146 IpConfig: gr.ipConfig, 147 } 148 } 149 150 func (gr *gceResources) initClients(projects []string) error { 151 srv, err := initRDSServer(gr.resourceType, gr.globalOpts.GetApiVersion(), projects, int(gr.globalOpts.GetReEvalSec()), gr.l) 152 if err != nil { 153 return err 154 } 155 156 for _, project := range projects { 157 client, err := newRDSClient(gr.rdsRequest(project), srv.ListResources, gr.l) 158 if err != nil { 159 return err 160 } 161 gr.clients[project] = client 162 } 163 164 return nil 165 } 166 167 // ListEndpoints returns the list of GCE endpoints. 168 func (gr *gceResources) ListEndpoints() []endpoint.Endpoint { 169 var ep []endpoint.Endpoint 170 for _, client := range gr.clients { 171 ep = append(ep, client.ListEndpoints()...) 172 } 173 return ep 174 } 175 176 // Resolve resolves the name into an IP address. Unless explicitly configured 177 // to use DNS, we use the RDS client to determine the resource IPs. 178 func (gr *gceResources) Resolve(name string, ipVer int) (net.IP, error) { 179 if gr.useDNS { 180 return gr.r.Resolve(name, ipVer) 181 } 182 183 var ip net.IP 184 var err error 185 186 for _, client := range gr.clients { 187 ip, err = client.Resolve(name, ipVer) 188 if err == nil { 189 return ip, err 190 } 191 } 192 193 return ip, err 194 } 195 196 // New is a helper function to unpack a Targets proto into a Targets interface. 197 func New(conf *configpb.TargetsConf, globalOpts *configpb.GlobalOptions, res *dnsRes.Resolver, l *logger.Logger) (Targets, error) { 198 projects := conf.GetProject() 199 if projects == nil { 200 if !metadata.OnGCE() { 201 return nil, errors.New("targets.gce.New(): project is required for GCE targets when not running on GCE") 202 } 203 currentProj, err := metadata.ProjectID() 204 projects = append([]string{}, currentProj) 205 if err != nil { 206 return nil, fmt.Errorf("targets.gce.New(): Error getting project ID: %v", err) 207 } 208 } 209 210 gr := &gceResources{ 211 c: conf, 212 ipConfig: instancesIPConfig(conf.GetInstances()), 213 clients: make(map[string]*client.Client), 214 globalOpts: globalOpts, 215 l: l, 216 } 217 218 switch conf.Type.(type) { 219 case *configpb.TargetsConf_Instances: 220 gr.resourceType = "gce_instances" 221 // Verify that config is correct. 222 if err := verifyInstancesConfig(conf.GetInstances(), res); err != nil { 223 return nil, err 224 } 225 226 if len(conf.GetInstances().GetLabel()) > 0 { 227 filters, err := parseLabels(conf.GetInstances().GetLabel()) 228 if err != nil { 229 return nil, err 230 } 231 gr.filters = filters 232 } 233 return gr, gr.initClients(projects) 234 235 case *configpb.TargetsConf_ForwardingRules: 236 // TODO(manugarg): implement forwarding rules using gceResources like instances.go. 237 frp, err := newForwardingRules(projects[0], globalOpts, conf.GetForwardingRules(), l) 238 if err != nil { 239 return nil, err 240 } 241 return frp, nil 242 } 243 244 return nil, errors.New("unknown GCE targets type") 245 }