separate internal configs with isInternal (#8690)

client-key: /Users/aliharris/.minikube/profiles/minikube/client.key

Co-authored-by: testa113 <testa113>
pull/8720/head
Ali 2023-05-11 08:13:54 +12:00 committed by GitHub
parent 945798a662
commit 22f4c5d650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 32 additions and 30 deletions

View File

@ -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,

View File

@ -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),

View File

@ -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(

View File

@ -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,

View File

@ -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)
}) })
} }