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 }