github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/proxy/proxier.go (about)

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package proxy
     5  
     6  import (
     7  	"net"
     8  	"net/url"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/mitchellh/mapstructure"
    12  	"k8s.io/client-go/rest"
    13  
    14  	"github.com/juju/juju/caas/kubernetes"
    15  	proxyerrors "github.com/juju/juju/proxy/errors"
    16  )
    17  
    18  type Proxier struct {
    19  	config     ProxierConfig
    20  	tunnel     *kubernetes.Tunnel
    21  	restConfig rest.Config
    22  }
    23  
    24  type ProxierConfig struct {
    25  	APIHost             string `yaml:"api-host" mapstructure:"api-host"`
    26  	CAData              string `yaml:"ca-cert" mapstructure:"ca-cert"`
    27  	Namespace           string `yaml:"namespace" mapstructure:"namespace"`
    28  	RemotePort          string `yaml:"remote-port" mapstructure:"remote-port"`
    29  	Service             string `yaml:"service" mapstructure:"service"`
    30  	ServiceAccountToken string `yaml:"service-account-token" mapstructure:"service-account-token"`
    31  }
    32  
    33  const (
    34  	ProxierTypeKey = "kubernetes-port-forward"
    35  )
    36  
    37  func (p *Proxier) Host() string {
    38  	return "localhost"
    39  }
    40  
    41  func NewProxier(config ProxierConfig) *Proxier {
    42  	p := &Proxier{config: config}
    43  	p.updateRESTConfig()
    44  	return p
    45  }
    46  
    47  // Insecure sets the proxy to be insecure.
    48  func (p *Proxier) Insecure() {
    49  	p.config.CAData = ""
    50  	p.updateRESTConfig()
    51  }
    52  
    53  func (p *Proxier) updateRESTConfig() {
    54  	restConfig := rest.Config{
    55  		BearerToken:     p.config.ServiceAccountToken,
    56  		Host:            p.config.APIHost,
    57  		TLSClientConfig: rest.TLSClientConfig{},
    58  	}
    59  	if p.config.CAData != "" {
    60  		restConfig.TLSClientConfig.CAData = []byte(p.config.CAData)
    61  	} else {
    62  		restConfig.TLSClientConfig.Insecure = true
    63  	}
    64  	p.restConfig = restConfig
    65  }
    66  
    67  func NewProxierConfig() *ProxierConfig {
    68  	return &ProxierConfig{}
    69  }
    70  
    71  func NewProxierFromRawConfig(rawConf interface{}) (*Proxier, error) {
    72  	conf, valid := rawConf.(*ProxierConfig)
    73  	if !valid {
    74  		return nil, errors.NewNotValid(nil, "config is not of type *ProxierConfig")
    75  	}
    76  
    77  	return NewProxier(*conf), nil
    78  }
    79  
    80  // SetAPIHost updates the proxy info to use a different host address.
    81  func (p *Proxier) SetAPIHost(host string) {
    82  	p.restConfig.Host = host
    83  	p.config.APIHost = host
    84  }
    85  
    86  // RawConfig implements Proxier RawConfig interface.
    87  func (p *Proxier) RawConfig() (map[string]interface{}, error) {
    88  	rval := map[string]interface{}{}
    89  	err := mapstructure.Decode(&p.config, &rval)
    90  	return rval, errors.Trace(err)
    91  }
    92  
    93  // MarshalYAML implements the yaml Marshaler interface
    94  func (p *Proxier) MarshalYAML() (interface{}, error) {
    95  	return &p.config, nil
    96  }
    97  
    98  func (p *Proxier) Port() string {
    99  	return p.tunnel.LocalPort
   100  }
   101  
   102  func (p *Proxier) Start() (err error) {
   103  	tunnel, err := kubernetes.NewTunnelForConfig(
   104  		&p.restConfig,
   105  		kubernetes.TunnelKindServices,
   106  		p.config.Namespace,
   107  		p.config.Service,
   108  		p.config.RemotePort,
   109  	)
   110  
   111  	if err != nil {
   112  		return errors.Trace(err)
   113  	}
   114  	p.tunnel = tunnel
   115  
   116  	defer func() {
   117  		err = errors.Annotate(err, "connecting k8s proxy")
   118  	}()
   119  	err = p.tunnel.ForwardPort()
   120  	urlErr, ok := errors.Cause(err).(*url.Error)
   121  	if !ok {
   122  		return errors.Trace(err)
   123  	}
   124  	if _, ok = urlErr.Err.(*net.OpError); !ok {
   125  		return errors.Trace(err)
   126  	}
   127  	return proxyerrors.NewProxyConnectError(err, p.Type())
   128  }
   129  
   130  func (p *Proxier) Stop() {
   131  	if p.tunnel != nil {
   132  		p.tunnel.Close()
   133  	}
   134  }
   135  
   136  func (p *Proxier) Type() string {
   137  	return ProxierTypeKey
   138  }