mirror of https://github.com/portainer/portainer
separate internal configs with isInternal (#8690)
client-key: /Users/aliharris/.minikube/profiles/minikube/client.key Co-authored-by: testa113 <testa113>pull/8720/head
parent
945798a662
commit
22f4c5d650
|
@ -110,7 +110,7 @@ func (handler *Handler) getHelmClusterAccess(r *http.Request) (*options.Kubernet
|
||||||
hostURL = r.Host
|
hostURL = r.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeConfigInternal := handler.kubeClusterAccessService.GetData(hostURL, endpoint.ID)
|
kubeConfigInternal := handler.kubeClusterAccessService.GetClusterDetails(hostURL, endpoint.ID, true)
|
||||||
return &options.KubernetesClusterAccess{
|
return &options.KubernetesClusterAccess{
|
||||||
ClusterServerURL: kubeConfigInternal.ClusterServerURL,
|
ClusterServerURL: kubeConfigInternal.ClusterServerURL,
|
||||||
CertificateAuthorityFile: kubeConfigInternal.CertificateAuthorityFile,
|
CertificateAuthorityFile: kubeConfigInternal.CertificateAuthorityFile,
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (handler *Handler) getKubernetesConfig(w http.ResponseWriter, r *http.Reque
|
||||||
return httperror.BadRequest("empty endpoints list", errors.New("empty endpoints list"))
|
return httperror.BadRequest("empty endpoints list", errors.New("empty endpoints list"))
|
||||||
}
|
}
|
||||||
|
|
||||||
config, handlerErr := handler.buildConfig(r, tokenData, bearerToken, endpoints)
|
config, handlerErr := handler.buildConfig(r, tokenData, bearerToken, endpoints, false)
|
||||||
if handlerErr != nil {
|
if handlerErr != nil {
|
||||||
return handlerErr
|
return handlerErr
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ func (handler *Handler) filterUserKubeEndpoints(r *http.Request) ([]portainer.En
|
||||||
return filteredEndpoints, nil
|
return filteredEndpoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenData, bearerToken string, endpoints []portainer.Endpoint) (*clientV1.Config, *httperror.HandlerError) {
|
func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenData, bearerToken string, endpoints []portainer.Endpoint, isInternal bool) (*clientV1.Config, *httperror.HandlerError) {
|
||||||
configClusters := make([]clientV1.NamedCluster, len(endpoints))
|
configClusters := make([]clientV1.NamedCluster, len(endpoints))
|
||||||
configContexts := make([]clientV1.NamedContext, len(endpoints))
|
configContexts := make([]clientV1.NamedContext, len(endpoints))
|
||||||
var configAuthInfos []clientV1.NamedAuthInfo
|
var configAuthInfos []clientV1.NamedAuthInfo
|
||||||
|
@ -125,7 +125,7 @@ func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenD
|
||||||
instanceID := handler.KubernetesClientFactory.GetInstanceID()
|
instanceID := handler.KubernetesClientFactory.GetInstanceID()
|
||||||
serviceAccountName := kcli.UserServiceAccountName(int(tokenData.ID), instanceID)
|
serviceAccountName := kcli.UserServiceAccountName(int(tokenData.ID), instanceID)
|
||||||
|
|
||||||
configClusters[idx] = handler.buildCluster(r, endpoint)
|
configClusters[idx] = handler.buildCluster(r, endpoint, isInternal)
|
||||||
configContexts[idx] = buildContext(serviceAccountName, endpoint)
|
configContexts[idx] = buildContext(serviceAccountName, endpoint)
|
||||||
if !authInfosSet[serviceAccountName] {
|
if !authInfosSet[serviceAccountName] {
|
||||||
configAuthInfos = append(configAuthInfos, buildAuthInfo(serviceAccountName, bearerToken))
|
configAuthInfos = append(configAuthInfos, buildAuthInfo(serviceAccountName, bearerToken))
|
||||||
|
@ -143,8 +143,9 @@ func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenD
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) buildCluster(r *http.Request, endpoint portainer.Endpoint) clientV1.NamedCluster {
|
// buildCluster builds a Kubernetes cluster configuration based on the endpoint and if it's used internally or externally.
|
||||||
kubeConfigInternal := handler.kubeClusterAccessService.GetData(r.Host, endpoint.ID)
|
func (handler *Handler) buildCluster(r *http.Request, endpoint portainer.Endpoint, isInternal bool) clientV1.NamedCluster {
|
||||||
|
kubeConfigInternal := handler.kubeClusterAccessService.GetClusterDetails(r.Host, endpoint.ID, isInternal)
|
||||||
|
|
||||||
return clientV1.NamedCluster{
|
return clientV1.NamedCluster{
|
||||||
Name: buildClusterName(endpoint.Name),
|
Name: buildClusterName(endpoint.Name),
|
||||||
|
|
|
@ -171,6 +171,7 @@ func (handler *Handler) kubeClient(next http.Handler) http.Handler {
|
||||||
tokenData,
|
tokenData,
|
||||||
bearerToken,
|
bearerToken,
|
||||||
singleEndpointList,
|
singleEndpointList,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httperror.WriteError(
|
httperror.WriteError(
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strconv"
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import (
|
||||||
// KubeClusterAccessService represents a service that is responsible for centralizing kube cluster access data
|
// KubeClusterAccessService represents a service that is responsible for centralizing kube cluster access data
|
||||||
type KubeClusterAccessService interface {
|
type KubeClusterAccessService interface {
|
||||||
IsSecure() bool
|
IsSecure() bool
|
||||||
GetData(hostURL string, endpointId portainer.EndpointID) kubernetesClusterAccessData
|
GetClusterDetails(hostURL string, endpointId portainer.EndpointID, isInternal bool) kubernetesClusterAccessData
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubernetesClusterAccess represents core details which can be used to generate KubeConfig file/data
|
// KubernetesClusterAccess represents core details which can be used to generate KubeConfig file/data
|
||||||
|
@ -89,21 +89,20 @@ func (service *kubeClusterAccessService) IsSecure() bool {
|
||||||
return service.certificateAuthorityData != ""
|
return service.certificateAuthorityData != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetData returns K8s cluster access details for the specified environment(endpoint).
|
// GetClusterDetails returns K8s cluster access details for the specified environment(endpoint).
|
||||||
// The struct can be used to:
|
// The struct can be used to:
|
||||||
// - generate a kubeconfig file
|
// - generate a kubeconfig file
|
||||||
// - pass down params to binaries
|
// - pass down params to binaries
|
||||||
func (service *kubeClusterAccessService) GetData(hostURL string, endpointID portainer.EndpointID) kubernetesClusterAccessData {
|
// - isInternal is used to determine whether the kubeclient is accessed internally (for example using the kube client for backend calls) or externally (for example downloading the kubeconfig file)
|
||||||
baseURL := service.baseURL
|
func (service *kubeClusterAccessService) GetClusterDetails(hostURL string, endpointID portainer.EndpointID, isInternal bool) kubernetesClusterAccessData {
|
||||||
|
|
||||||
// When the api call is internal, the baseURL should not be used.
|
|
||||||
if hostURL == "localhost" {
|
if hostURL == "localhost" {
|
||||||
hostURL += service.httpsBindAddr
|
hostURL += service.httpsBindAddr
|
||||||
baseURL = "/"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if baseURL != "/" {
|
baseURL := service.baseURL
|
||||||
baseURL = fmt.Sprintf("/%s/", strings.Trim(baseURL, "/"))
|
// When the kubeclient call is internal, the baseURL should not be used.
|
||||||
|
if isInternal {
|
||||||
|
baseURL = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().
|
log.Debug().
|
||||||
|
@ -112,9 +111,10 @@ func (service *kubeClusterAccessService) GetData(hostURL string, endpointID port
|
||||||
Str("base_URL", baseURL).
|
Str("base_URL", baseURL).
|
||||||
Msg("kubeconfig")
|
Msg("kubeconfig")
|
||||||
|
|
||||||
clusterURL := hostURL + baseURL
|
clusterServerURL, err := url.JoinPath("https://", hostURL, baseURL, "/api/endpoints/", strconv.Itoa(int(endpointID)), "/kubernetes")
|
||||||
|
if err != nil {
|
||||||
clusterServerURL := fmt.Sprintf("https://%sapi/endpoints/%d/kubernetes", clusterURL, endpointID)
|
log.Error().Err(err).Msg("Failed to create Kubeconfig cluster URL")
|
||||||
|
}
|
||||||
|
|
||||||
return kubernetesClusterAccessData{
|
return kubernetesClusterAccessData{
|
||||||
ClusterServerURL: clusterServerURL,
|
ClusterServerURL: clusterServerURL,
|
||||||
|
|
|
@ -91,21 +91,21 @@ func TestKubeClusterAccessService_IsSecure(t *testing.T) {
|
||||||
func TestKubeClusterAccessService_GetKubeConfigInternal(t *testing.T) {
|
func TestKubeClusterAccessService_GetKubeConfigInternal(t *testing.T) {
|
||||||
is := assert.New(t)
|
is := assert.New(t)
|
||||||
|
|
||||||
t.Run("GetData contains host address", func(t *testing.T) {
|
t.Run("GetClusterDetails contains host address", func(t *testing.T) {
|
||||||
kcs := NewKubeClusterAccessService("/", "", "")
|
kcs := NewKubeClusterAccessService("/", "", "")
|
||||||
clusterAccessDetails := kcs.GetData("mysite.com", 1)
|
clusterAccessDetails := kcs.GetClusterDetails("mysite.com", 1, true)
|
||||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "https://mysite.com"), "should contain host address")
|
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "https://mysite.com"), "should contain host address")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetData contains environment proxy url", func(t *testing.T) {
|
t.Run("GetClusterDetails contains environment proxy url", func(t *testing.T) {
|
||||||
kcs := NewKubeClusterAccessService("/", "", "")
|
kcs := NewKubeClusterAccessService("/", "", "")
|
||||||
clusterAccessDetails := kcs.GetData("mysite.com", 100)
|
clusterAccessDetails := kcs.GetClusterDetails("mysite.com", 100, true)
|
||||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "api/endpoints/100/kubernetes"), "should contain environment proxy url")
|
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "api/endpoints/100/kubernetes"), "should contain environment proxy url")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetData returns insecure cluster access config", func(t *testing.T) {
|
t.Run("GetClusterDetails returns insecure cluster access config", func(t *testing.T) {
|
||||||
kcs := NewKubeClusterAccessService("/", ":9443", "")
|
kcs := NewKubeClusterAccessService("/", ":9443", "")
|
||||||
clusterAccessDetails := kcs.GetData("mysite.com", 1)
|
clusterAccessDetails := kcs.GetClusterDetails("mysite.com", 1, true)
|
||||||
|
|
||||||
wantClusterAccessDetails := kubernetesClusterAccessData{
|
wantClusterAccessDetails := kubernetesClusterAccessData{
|
||||||
ClusterServerURL: "https://mysite.com/api/endpoints/1/kubernetes",
|
ClusterServerURL: "https://mysite.com/api/endpoints/1/kubernetes",
|
||||||
|
@ -113,14 +113,14 @@ func TestKubeClusterAccessService_GetKubeConfigInternal(t *testing.T) {
|
||||||
CertificateAuthorityData: "",
|
CertificateAuthorityData: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
is.Equal(clusterAccessDetails, wantClusterAccessDetails)
|
is.Equal(wantClusterAccessDetails, clusterAccessDetails)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetData returns secure cluster access config", func(t *testing.T) {
|
t.Run("GetClusterDetails returns secure cluster access config", func(t *testing.T) {
|
||||||
filePath := createTempFile("valid-cert.crt", certData, t)
|
filePath := createTempFile("valid-cert.crt", certData, t)
|
||||||
|
|
||||||
kcs := NewKubeClusterAccessService("/", "", filePath)
|
kcs := NewKubeClusterAccessService("/", "", filePath)
|
||||||
clusterAccessDetails := kcs.GetData("localhost", 1)
|
clusterAccessDetails := kcs.GetClusterDetails("localhost", 1, true)
|
||||||
|
|
||||||
wantClusterAccessDetails := kubernetesClusterAccessData{
|
wantClusterAccessDetails := kubernetesClusterAccessData{
|
||||||
ClusterServerURL: "https://localhost/api/endpoints/1/kubernetes",
|
ClusterServerURL: "https://localhost/api/endpoints/1/kubernetes",
|
||||||
|
@ -128,6 +128,6 @@ func TestKubeClusterAccessService_GetKubeConfigInternal(t *testing.T) {
|
||||||
CertificateAuthorityData: certDataString,
|
CertificateAuthorityData: certDataString,
|
||||||
}
|
}
|
||||||
|
|
||||||
is.Equal(clusterAccessDetails, wantClusterAccessDetails)
|
is.Equal(wantClusterAccessDetails, clusterAccessDetails)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue