github.com/google/cloudprober@v0.11.3/rds/kubernetes/pods.go (about) 1 // Copyright 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 kubernetes 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "math/rand" 21 "sync" 22 "time" 23 24 "github.com/golang/protobuf/proto" 25 "github.com/google/cloudprober/logger" 26 configpb "github.com/google/cloudprober/rds/kubernetes/proto" 27 pb "github.com/google/cloudprober/rds/proto" 28 "github.com/google/cloudprober/rds/server/filter" 29 ) 30 31 type podsLister struct { 32 c *configpb.Pods 33 namespace string 34 kClient *client 35 36 mu sync.RWMutex // Mutex for names and cache 37 keys []resourceKey 38 cache map[resourceKey]*podInfo 39 l *logger.Logger 40 } 41 42 func podsURL(ns string) string { 43 if ns == "" { 44 return "api/v1/pods" 45 } 46 return fmt.Sprintf("api/v1/namespaces/%s/pods", ns) 47 } 48 49 func (pl *podsLister) listResources(req *pb.ListResourcesRequest) ([]*pb.Resource, error) { 50 var resources []*pb.Resource 51 52 allFilters, err := filter.ParseFilters(req.GetFilter(), SupportedFilters.RegexFilterKeys, "") 53 if err != nil { 54 return nil, err 55 } 56 57 nameFilter, nsFilter, labelsFilter := allFilters.RegexFilters["name"], allFilters.RegexFilters["namespace"], allFilters.LabelsFilter 58 59 pl.mu.RLock() 60 defer pl.mu.RUnlock() 61 62 for _, key := range pl.keys { 63 if nameFilter != nil && !nameFilter.Match(key.name, pl.l) { 64 continue 65 } 66 67 pod := pl.cache[key] 68 if nsFilter != nil && !nsFilter.Match(pod.Metadata.Namespace, pl.l) { 69 continue 70 } 71 if labelsFilter != nil && !labelsFilter.Match(pod.Metadata.Labels, pl.l) { 72 continue 73 } 74 75 resources = append(resources, &pb.Resource{ 76 Name: proto.String(key.name), 77 Ip: proto.String(pod.Status.PodIP), 78 Labels: pod.Metadata.Labels, 79 }) 80 } 81 82 pl.l.Infof("kubernetes.listResources: returning %d pods", len(resources)) 83 return resources, nil 84 } 85 86 type podInfo struct { 87 Metadata kMetadata 88 Status struct { 89 Phase string 90 PodIP string 91 } 92 } 93 94 func parsePodsJSON(resp []byte) (keys []resourceKey, pods map[resourceKey]*podInfo, err error) { 95 var itemList struct { 96 Items []*podInfo 97 } 98 99 if err = json.Unmarshal(resp, &itemList); err != nil { 100 return 101 } 102 103 keys = make([]resourceKey, len(itemList.Items)) 104 pods = make(map[resourceKey]*podInfo) 105 for i, item := range itemList.Items { 106 if item.Status.Phase != "Running" { 107 continue 108 } 109 keys[i] = resourceKey{item.Metadata.Namespace, item.Metadata.Name} 110 pods[keys[i]] = item 111 } 112 113 return 114 } 115 116 func (pl *podsLister) expand() { 117 resp, err := pl.kClient.getURL(podsURL(pl.namespace)) 118 if err != nil { 119 pl.l.Warningf("podsLister.expand(): error while getting pods list from API: %v", err) 120 } 121 122 keys, pods, err := parsePodsJSON(resp) 123 if err != nil { 124 pl.l.Warningf("podsLister.expand(): error while parsing pods API response (%s): %v", string(resp), err) 125 } 126 127 pl.l.Infof("podsLister.expand(): got %d pods", len(keys)) 128 129 pl.mu.Lock() 130 defer pl.mu.Unlock() 131 pl.keys = keys 132 pl.cache = pods 133 } 134 135 func newPodsLister(c *configpb.Pods, namespace string, reEvalInterval time.Duration, kc *client, l *logger.Logger) (*podsLister, error) { 136 pl := &podsLister{ 137 c: c, 138 namespace: namespace, 139 kClient: kc, 140 l: l, 141 } 142 143 go func() { 144 pl.expand() 145 // Introduce a random delay between 0-reEvalInterval before 146 // starting the refresh loop. If there are multiple cloudprober 147 // gceInstances, this will make sure that each instance calls GCE 148 // API at a different point of time. 149 rand.Seed(time.Now().UnixNano()) 150 randomDelaySec := rand.Intn(int(reEvalInterval.Seconds())) 151 time.Sleep(time.Duration(randomDelaySec) * time.Second) 152 for range time.Tick(reEvalInterval) { 153 pl.expand() 154 } 155 }() 156 157 return pl, nil 158 }