github.com/google/cloudprober@v0.11.3/rds/kubernetes/client.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 "crypto/tls" 19 "crypto/x509" 20 "fmt" 21 "io/ioutil" 22 "net" 23 "net/http" 24 "os" 25 "strings" 26 27 "github.com/google/cloudprober/common/tlsconfig" 28 "github.com/google/cloudprober/logger" 29 configpb "github.com/google/cloudprober/rds/kubernetes/proto" 30 ) 31 32 // Variables defined by Kubernetes spec to find out local CA cert and token. 33 var ( 34 LocalCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 35 LocalTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" 36 ) 37 38 // client encapsulates an in-cluster kubeapi client. 39 type client struct { 40 cfg *configpb.ProviderConfig 41 httpC *http.Client 42 apiHost string 43 bearer string 44 l *logger.Logger 45 } 46 47 func (c *client) httpRequest(url string) (*http.Request, error) { 48 url = fmt.Sprintf("https://%s/%s", c.apiHost, strings.TrimPrefix(url, "/")) 49 req, err := http.NewRequest("GET", url, nil) 50 if err != nil { 51 return nil, err 52 } 53 if c.bearer != "" { 54 req.Header.Add("Authorization", c.bearer) 55 } 56 57 return req, nil 58 } 59 60 func (c *client) getURL(url string) ([]byte, error) { 61 req, err := c.httpRequest(url) 62 if err != nil { 63 return nil, err 64 } 65 66 c.l.Infof("kubernetes.client: getting URL: %s", req.URL.String()) 67 resp, err := c.httpC.Do(req) 68 if err != nil { 69 return nil, err 70 } 71 72 if resp.StatusCode != http.StatusOK { 73 return nil, fmt.Errorf("HTTP response status code: %d, status: %s", resp.StatusCode, resp.Status) 74 } 75 76 return ioutil.ReadAll(resp.Body) 77 } 78 79 func (c *client) initAPIHost() error { 80 c.apiHost = c.cfg.GetApiServerAddress() 81 if c.apiHost != "" { 82 return nil 83 } 84 85 // If API server address is not given, assume in-cluster operation and try to 86 // get it from environment variables set by pod. 87 host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT") 88 if len(host) == 0 || len(port) == 0 { 89 return fmt.Errorf("initAPIHost: not running in cluster: KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT environment variables not set") 90 } 91 92 c.apiHost = net.JoinHostPort(host, port) 93 return nil 94 } 95 96 func (c *client) initHTTPClient() error { 97 transport := http.DefaultTransport.(*http.Transport).Clone() 98 99 if transport.TLSClientConfig == nil { 100 transport.TLSClientConfig = &tls.Config{} 101 } 102 103 if c.cfg.GetTlsConfig() != nil { 104 if err := tlsconfig.UpdateTLSConfig(transport.TLSClientConfig, c.cfg.GetTlsConfig(), false); err != nil { 105 return err 106 } 107 c.httpC = &http.Client{Transport: transport} 108 return nil 109 } 110 111 // If TLS config is not provided, assume in-cluster. 112 certs, err := ioutil.ReadFile(LocalCACert) 113 if err != nil { 114 return fmt.Errorf("error while reading local ca.crt file (%s): %v", LocalCACert, err) 115 } 116 117 caCertPool := x509.NewCertPool() 118 caCertPool.AppendCertsFromPEM(certs) 119 120 transport.TLSClientConfig.RootCAs = caCertPool 121 122 c.httpC = &http.Client{Transport: transport} 123 124 // Read OAuth token from local file. 125 token, err := ioutil.ReadFile(LocalTokenFile) 126 if err != nil { 127 return fmt.Errorf("error while reading in-cluster local token file (%s): %v", LocalTokenFile, err) 128 } 129 c.bearer = "Bearer " + string(token) 130 131 return nil 132 } 133 134 func newClient(cfg *configpb.ProviderConfig, l *logger.Logger) (*client, error) { 135 c := &client{ 136 cfg: cfg, 137 l: l, 138 } 139 140 if err := c.initHTTPClient(); err != nil { 141 return nil, err 142 } 143 144 if err := c.initAPIHost(); err != nil { 145 return nil, err 146 } 147 148 return c, nil 149 }