139 lines
4.8 KiB
Go
139 lines
4.8 KiB
Go
package exec
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
portainer "github.com/portainer/portainer/api"
|
|
"github.com/portainer/portainer/api/dataservices"
|
|
"github.com/portainer/portainer/api/http/proxy"
|
|
"github.com/portainer/portainer/api/http/proxy/factory"
|
|
"github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
|
|
"github.com/portainer/portainer/api/kubernetes/cli"
|
|
"github.com/portainer/portainer/pkg/libkubectl"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
defaultServerURL = "https://kubernetes.default.svc"
|
|
)
|
|
|
|
// KubernetesDeployer represents a service to deploy resources inside a Kubernetes environment(endpoint).
|
|
type KubernetesDeployer struct {
|
|
dataStore dataservices.DataStore
|
|
reverseTunnelService portainer.ReverseTunnelService
|
|
signatureService portainer.DigitalSignatureService
|
|
kubernetesClientFactory *cli.ClientFactory
|
|
kubernetesTokenCacheManager *kubernetes.TokenCacheManager
|
|
proxyManager *proxy.Manager
|
|
}
|
|
|
|
// NewKubernetesDeployer initializes a new KubernetesDeployer service.
|
|
func NewKubernetesDeployer(kubernetesTokenCacheManager *kubernetes.TokenCacheManager, kubernetesClientFactory *cli.ClientFactory, datastore dataservices.DataStore, reverseTunnelService portainer.ReverseTunnelService, signatureService portainer.DigitalSignatureService, proxyManager *proxy.Manager) *KubernetesDeployer {
|
|
return &KubernetesDeployer{
|
|
dataStore: datastore,
|
|
reverseTunnelService: reverseTunnelService,
|
|
signatureService: signatureService,
|
|
kubernetesClientFactory: kubernetesClientFactory,
|
|
kubernetesTokenCacheManager: kubernetesTokenCacheManager,
|
|
proxyManager: proxyManager,
|
|
}
|
|
}
|
|
|
|
func (deployer *KubernetesDeployer) getToken(userID portainer.UserID, endpoint *portainer.Endpoint, setLocalAdminToken bool) (string, error) {
|
|
kubeCLI, err := deployer.kubernetesClientFactory.GetPrivilegedKubeClient(endpoint)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
tokenCache := deployer.kubernetesTokenCacheManager.GetOrCreateTokenCache(endpoint.ID)
|
|
|
|
tokenManager, err := kubernetes.NewTokenManager(kubeCLI, deployer.dataStore, tokenCache, setLocalAdminToken)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
user, err := deployer.dataStore.User().Read(userID)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed to fetch the user")
|
|
}
|
|
|
|
if user.Role == portainer.AdministratorRole {
|
|
return tokenManager.GetAdminServiceAccountToken(), nil
|
|
}
|
|
|
|
token, err := tokenManager.GetUserServiceAccountToken(int(user.ID), endpoint.ID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if token == "" {
|
|
return "", errors.New("can not get a valid user service account token")
|
|
}
|
|
|
|
return token, nil
|
|
}
|
|
|
|
// Deploy upserts Kubernetes resources defined in manifest(s)
|
|
func (deployer *KubernetesDeployer) Deploy(userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) {
|
|
return deployer.command("apply", userID, endpoint, resources, namespace)
|
|
}
|
|
|
|
// Remove deletes Kubernetes resources defined in manifest(s)
|
|
func (deployer *KubernetesDeployer) Remove(userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) {
|
|
return deployer.command("delete", userID, endpoint, resources, namespace)
|
|
}
|
|
|
|
func (deployer *KubernetesDeployer) command(operation string, userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) {
|
|
token, err := deployer.getToken(userID, endpoint, endpoint.Type == portainer.KubernetesLocalEnvironment)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed generating a user token")
|
|
}
|
|
|
|
serverURL := defaultServerURL
|
|
if endpoint.Type == portainer.AgentOnKubernetesEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment {
|
|
url, proxy, err := deployer.getAgentURL(endpoint)
|
|
if err != nil {
|
|
return "", errors.WithMessage(err, "failed generating endpoint URL")
|
|
}
|
|
defer proxy.Close()
|
|
|
|
serverURL = url
|
|
}
|
|
|
|
client, err := libkubectl.NewClient(&libkubectl.ClientAccess{
|
|
Token: token,
|
|
ServerUrl: serverURL,
|
|
}, namespace, "", true)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed to create kubectl client")
|
|
}
|
|
|
|
operations := map[string]func(context.Context, []string) (string, error){
|
|
"apply": client.Apply,
|
|
"delete": client.Delete,
|
|
}
|
|
|
|
operationFunc, ok := operations[operation]
|
|
if !ok {
|
|
return "", errors.Errorf("unsupported operation: %s", operation)
|
|
}
|
|
|
|
output, err := operationFunc(context.Background(), resources)
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "failed to execute kubectl %s command", operation)
|
|
}
|
|
|
|
return output, nil
|
|
}
|
|
|
|
func (deployer *KubernetesDeployer) getAgentURL(endpoint *portainer.Endpoint) (string, *factory.ProxyServer, error) {
|
|
proxy, err := deployer.proxyManager.CreateAgentProxyServer(endpoint)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
return fmt.Sprintf("http://127.0.0.1:%d/kubernetes", proxy.Port), proxy, nil
|
|
}
|