mirror of
https://github.com/portainer/portainer.git
synced 2025-07-23 07:19:41 +02:00
fix(kube): Use KubeClusterAccessService for Helm operations [EE-2500] (#6559)
This commit is contained in:
parent
cf7746082b
commit
c486130a9f
10 changed files with 105 additions and 103 deletions
|
@ -7,26 +7,27 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
// KubeConfigService represents a service that is responsible for handling kubeconfig operations
|
||||
type KubeConfigService interface {
|
||||
// KubeClusterAccessService represents a service that is responsible for centralizing kube cluster access data
|
||||
type KubeClusterAccessService interface {
|
||||
IsSecure() bool
|
||||
GetKubeConfigInternal(endpointId portainer.EndpointID, authToken string) kubernetesClusterAccess
|
||||
GetData(hostURL string, endpointId portainer.EndpointID) kubernetesClusterAccessData
|
||||
}
|
||||
|
||||
// KubernetesClusterAccess represents core details which can be used to generate KubeConfig file/data
|
||||
type kubernetesClusterAccess struct {
|
||||
type kubernetesClusterAccessData struct {
|
||||
ClusterServerURL string `example:"https://mycompany.k8s.com"`
|
||||
CertificateAuthorityFile string `example:"/data/tls/localhost.crt"`
|
||||
CertificateAuthorityData string `example:"MIIC5TCCAc2gAwIBAgIJAJ+...+xuhOaFXwQ=="`
|
||||
AuthToken string `example:"ey..."`
|
||||
}
|
||||
|
||||
type kubeConfigCAService struct {
|
||||
type kubeClusterAccessService struct {
|
||||
baseURL string
|
||||
httpsBindAddr string
|
||||
certificateAuthorityFile string
|
||||
certificateAuthorityData string
|
||||
|
@ -39,14 +40,15 @@ var (
|
|||
errTLSCertValidation = errors.New("failed to parse tls certificate")
|
||||
)
|
||||
|
||||
// NewKubeConfigCAService encapsulates generation of core KubeConfig data
|
||||
func NewKubeConfigCAService(httpsBindAddr string, tlsCertPath string) KubeConfigService {
|
||||
// NewKubeClusterAccessService creates a new instance of a KubeClusterAccessService
|
||||
func NewKubeClusterAccessService(baseURL, httpsBindAddr, tlsCertPath string) KubeClusterAccessService {
|
||||
certificateAuthorityData, err := getCertificateAuthorityData(tlsCertPath)
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] [internal,kubeconfig] [message: %s, generated KubeConfig will be insecure]", err.Error())
|
||||
}
|
||||
|
||||
return &kubeConfigCAService{
|
||||
return &kubeClusterAccessService{
|
||||
baseURL: baseURL,
|
||||
httpsBindAddr: httpsBindAddr,
|
||||
certificateAuthorityFile: tlsCertPath,
|
||||
certificateAuthorityData: certificateAuthorityData,
|
||||
|
@ -82,23 +84,27 @@ func getCertificateAuthorityData(tlsCertPath string) (string, error) {
|
|||
// this is based on the fact that we can successfully extract `certificateAuthorityData` from
|
||||
// certificate file at `tlsCertPath`. If we can successfully extract `certificateAuthorityData`,
|
||||
// then this will be used as `certificate-authority-data` attribute in a generated KubeConfig.
|
||||
func (kccas *kubeConfigCAService) IsSecure() bool {
|
||||
return kccas.certificateAuthorityData != ""
|
||||
func (service *kubeClusterAccessService) IsSecure() bool {
|
||||
return service.certificateAuthorityData != ""
|
||||
}
|
||||
|
||||
// GetKubeConfigInternal returns K8s cluster access details for the specified environment(endpoint).
|
||||
// On startup, portainer generates a certificate against localhost at specified `httpsBindAddr` port, hence
|
||||
// the kubeconfig generated should only be utilised by internal portainer binaries as the `ClusterServerURL`
|
||||
// points to the internally accessible `https` based `localhost` address.
|
||||
// GetData returns K8s cluster access details for the specified environment(endpoint).
|
||||
// The struct can be used to:
|
||||
// - generate a kubeconfig file
|
||||
// - pass down params to binaries
|
||||
func (kccas *kubeConfigCAService) GetKubeConfigInternal(endpointId portainer.EndpointID, authToken string) kubernetesClusterAccess {
|
||||
clusterServerUrl := fmt.Sprintf("https://localhost%s/api/endpoints/%s/kubernetes", kccas.httpsBindAddr, fmt.Sprint(endpointId))
|
||||
return kubernetesClusterAccess{
|
||||
ClusterServerURL: clusterServerUrl,
|
||||
CertificateAuthorityFile: kccas.certificateAuthorityFile,
|
||||
CertificateAuthorityData: kccas.certificateAuthorityData,
|
||||
AuthToken: authToken,
|
||||
func (service *kubeClusterAccessService) GetData(hostURL string, endpointID portainer.EndpointID) kubernetesClusterAccessData {
|
||||
baseURL := service.baseURL
|
||||
if baseURL != "/" {
|
||||
baseURL = fmt.Sprintf("/%s/", strings.Trim(baseURL, "/"))
|
||||
}
|
||||
|
||||
clusterURL := hostURL + service.httpsBindAddr + baseURL
|
||||
|
||||
clusterServerURL := fmt.Sprintf("https://%sapi/endpoints/%d/kubernetes", clusterURL, endpointID)
|
||||
|
||||
return kubernetesClusterAccessData{
|
||||
ClusterServerURL: clusterServerURL,
|
||||
CertificateAuthorityFile: service.certificateAuthorityFile,
|
||||
CertificateAuthorityData: service.certificateAuthorityData,
|
||||
}
|
||||
}
|
|
@ -78,11 +78,11 @@ func Test_getCertificateAuthorityData(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestKubeConfigService_IsSecure(t *testing.T) {
|
||||
func TestKubeClusterAccessService_IsSecure(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
t.Run("IsSecure should be false", func(t *testing.T) {
|
||||
kcs := NewKubeConfigCAService("", "")
|
||||
kcs := NewKubeClusterAccessService("", "", "")
|
||||
is.False(kcs.IsSecure(), "should be false if TLS cert not provided")
|
||||
})
|
||||
|
||||
|
@ -90,39 +90,32 @@ func TestKubeConfigService_IsSecure(t *testing.T) {
|
|||
filePath, teardown := createTempFile("valid-cert.crt", certData)
|
||||
defer teardown()
|
||||
|
||||
kcs := NewKubeConfigCAService("", filePath)
|
||||
kcs := NewKubeClusterAccessService("", "", filePath)
|
||||
is.True(kcs.IsSecure(), "should be true if valid TLS cert (path and content) provided")
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeConfigService_GetKubeConfigInternal(t *testing.T) {
|
||||
func TestKubeClusterAccessService_GetKubeConfigInternal(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
t.Run("GetKubeConfigInternal returns localhost address", func(t *testing.T) {
|
||||
kcs := NewKubeConfigCAService("", "")
|
||||
clusterAccessDetails := kcs.GetKubeConfigInternal(1, "some-token")
|
||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "https://localhost"), "should contain localhost address")
|
||||
t.Run("GetData contains host address", func(t *testing.T) {
|
||||
kcs := NewKubeClusterAccessService("/", "", "")
|
||||
clusterAccessDetails := kcs.GetData("mysite.com", 1)
|
||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "https://mysite.com"), "should contain host address")
|
||||
})
|
||||
|
||||
t.Run("GetKubeConfigInternal contains https bind address port", func(t *testing.T) {
|
||||
kcs := NewKubeConfigCAService(":1010", "")
|
||||
clusterAccessDetails := kcs.GetKubeConfigInternal(1, "some-token")
|
||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, ":1010"), "should contain bind address port")
|
||||
})
|
||||
|
||||
t.Run("GetKubeConfigInternal contains environment proxy url", func(t *testing.T) {
|
||||
kcs := NewKubeConfigCAService("", "")
|
||||
clusterAccessDetails := kcs.GetKubeConfigInternal(100, "some-token")
|
||||
t.Run("GetData contains environment proxy url", func(t *testing.T) {
|
||||
kcs := NewKubeClusterAccessService("/", "", "")
|
||||
clusterAccessDetails := kcs.GetData("mysite.com", 100)
|
||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "api/endpoints/100/kubernetes"), "should contain environment proxy url")
|
||||
})
|
||||
|
||||
t.Run("GetKubeConfigInternal returns insecure cluster access config", func(t *testing.T) {
|
||||
kcs := NewKubeConfigCAService("", "")
|
||||
clusterAccessDetails := kcs.GetKubeConfigInternal(1, "some-token")
|
||||
t.Run("GetData returns insecure cluster access config", func(t *testing.T) {
|
||||
kcs := NewKubeClusterAccessService("/", ":9443", "")
|
||||
clusterAccessDetails := kcs.GetData("mysite.com", 1)
|
||||
|
||||
wantClusterAccessDetails := kubernetesClusterAccess{
|
||||
ClusterServerURL: "https://localhost/api/endpoints/1/kubernetes",
|
||||
AuthToken: "some-token",
|
||||
wantClusterAccessDetails := kubernetesClusterAccessData{
|
||||
ClusterServerURL: "https://mysite.com:9443/api/endpoints/1/kubernetes",
|
||||
CertificateAuthorityFile: "",
|
||||
CertificateAuthorityData: "",
|
||||
}
|
||||
|
@ -130,16 +123,15 @@ func TestKubeConfigService_GetKubeConfigInternal(t *testing.T) {
|
|||
is.Equal(clusterAccessDetails, wantClusterAccessDetails)
|
||||
})
|
||||
|
||||
t.Run("GetKubeConfigInternal returns secure cluster access config", func(t *testing.T) {
|
||||
t.Run("GetData returns secure cluster access config", func(t *testing.T) {
|
||||
filePath, teardown := createTempFile("valid-cert.crt", certData)
|
||||
defer teardown()
|
||||
|
||||
kcs := NewKubeConfigCAService("", filePath)
|
||||
clusterAccessDetails := kcs.GetKubeConfigInternal(1, "some-token")
|
||||
kcs := NewKubeClusterAccessService("/", "", filePath)
|
||||
clusterAccessDetails := kcs.GetData("localhost", 1)
|
||||
|
||||
wantClusterAccessDetails := kubernetesClusterAccess{
|
||||
wantClusterAccessDetails := kubernetesClusterAccessData{
|
||||
ClusterServerURL: "https://localhost/api/endpoints/1/kubernetes",
|
||||
AuthToken: "some-token",
|
||||
CertificateAuthorityFile: filePath,
|
||||
CertificateAuthorityData: certDataString,
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue