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  }