diff --git a/api/kubernetes/cli/client.go b/api/kubernetes/cli/client.go index aa57d5fbe..428291a96 100644 --- a/api/kubernetes/cli/client.go +++ b/api/kubernetes/cli/client.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "sync" "time" @@ -154,17 +155,29 @@ func (factory *ClientFactory) createCachedAdminKubeClient(endpoint *portainer.En }, nil } -// CreateClient returns a pointer to a new Clientset instance +// CreateClient returns a pointer to a new Clientset instance. func (factory *ClientFactory) CreateClient(endpoint *portainer.Endpoint) (*kubernetes.Clientset, error) { + switch endpoint.Type { + case portainer.KubernetesLocalEnvironment, portainer.AgentOnKubernetesEnvironment, portainer.EdgeAgentOnKubernetesEnvironment: + c, err := factory.CreateConfig(endpoint) + if err != nil { + return nil, err + } + return kubernetes.NewForConfig(c) + } + return nil, errors.New("unsupported environment type") +} + +// CreateConfig returns a pointer to a new kubeconfig ready to create a client. +func (factory *ClientFactory) CreateConfig(endpoint *portainer.Endpoint) (*rest.Config, error) { switch endpoint.Type { case portainer.KubernetesLocalEnvironment: - return buildLocalClient() + return buildLocalConfig() case portainer.AgentOnKubernetesEnvironment: - return factory.buildAgentClient(endpoint) + return factory.buildAgentConfig(endpoint) case portainer.EdgeAgentOnKubernetesEnvironment: - return factory.buildEdgeClient(endpoint) + return factory.buildEdgeConfig(endpoint) } - return nil, errors.New("unsupported environment type") } @@ -184,33 +197,51 @@ func (rt *agentHeaderRoundTripper) RoundTrip(req *http.Request) (*http.Response, return rt.roundTripper.RoundTrip(req) } -func (factory *ClientFactory) buildAgentClient(endpoint *portainer.Endpoint) (*kubernetes.Clientset, error) { - endpointURL := fmt.Sprintf("https://%s/kubernetes", endpoint.URL) +func (factory *ClientFactory) buildAgentConfig(endpoint *portainer.Endpoint) (*rest.Config, error) { + var clientURL strings.Builder + if !strings.HasPrefix(endpoint.URL, "http") { + clientURL.WriteString("https://") + } + clientURL.WriteString(endpoint.URL) + clientURL.WriteString("/kubernetes") - return factory.createRemoteClient(endpointURL) -} + signature, err := factory.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage) + if err != nil { + return nil, err + } -func (factory *ClientFactory) buildEdgeClient(endpoint *portainer.Endpoint) (*kubernetes.Clientset, error) { - tunnel, err := factory.reverseTunnelService.GetActiveTunnel(endpoint) + config, err := clientcmd.BuildConfigFromFlags(clientURL.String(), "") if err != nil { - return nil, errors.Wrap(err, "failed activating tunnel") + return nil, err } - endpointURL := fmt.Sprintf("http://127.0.0.1:%d/kubernetes", tunnel.Port) - return factory.createRemoteClient(endpointURL) + config.Insecure = true + config.QPS = DefaultKubeClientQPS + config.Burst = DefaultKubeClientBurst + + config.Wrap(func(rt http.RoundTripper) http.RoundTripper { + return &agentHeaderRoundTripper{ + signatureHeader: signature, + publicKeyHeader: factory.signatureService.EncodedPublicKey(), + roundTripper: rt, + } + }) + return config, nil } -func (factory *ClientFactory) createRemoteClient(endpointURL string) (*kubernetes.Clientset, error) { - signature, err := factory.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage) +func (factory *ClientFactory) buildEdgeConfig(endpoint *portainer.Endpoint) (*rest.Config, error) { + tunnel, err := factory.reverseTunnelService.GetActiveTunnel(endpoint) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed activating tunnel") } + endpointURL := fmt.Sprintf("http://127.0.0.1:%d/kubernetes", tunnel.Port) config, err := clientcmd.BuildConfigFromFlags(endpointURL, "") if err != nil { return nil, err } + signature, err := factory.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage) config.Insecure = true config.QPS = DefaultKubeClientQPS config.Burst = DefaultKubeClientBurst @@ -223,12 +254,10 @@ func (factory *ClientFactory) createRemoteClient(endpointURL string) (*kubernete } }) - return kubernetes.NewForConfig(config) + return config, nil } -func (factory *ClientFactory) CreateRemoteMetricsClient(endpoint *portainer.Endpoint) (*metricsv.Clientset, error) { - endpointURL := fmt.Sprintf("https://%s/kubernetes", endpoint.URL) - +func (factory *ClientFactory) createRemoteClient(endpointURL string) (*kubernetes.Clientset, error) { signature, err := factory.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage) if err != nil { return nil, err @@ -251,10 +280,18 @@ func (factory *ClientFactory) CreateRemoteMetricsClient(endpoint *portainer.Endp } }) + return kubernetes.NewForConfig(config) +} + +func (factory *ClientFactory) CreateRemoteMetricsClient(endpoint *portainer.Endpoint) (*metricsv.Clientset, error) { + config, err := factory.CreateConfig(endpoint) + if err != nil { + return nil, fmt.Errorf("failed to create metrics KubeConfig") + } return metricsv.NewForConfig(config) } -func buildLocalClient() (*kubernetes.Clientset, error) { +func buildLocalConfig() (*rest.Config, error) { config, err := rest.InClusterConfig() if err != nil { return nil, err @@ -263,7 +300,7 @@ func buildLocalClient() (*kubernetes.Clientset, error) { config.QPS = DefaultKubeClientQPS config.Burst = DefaultKubeClientBurst - return kubernetes.NewForConfig(config) + return config, nil } func (factory *ClientFactory) MigrateEndpointIngresses(e *portainer.Endpoint) error {