diff --git a/api/exec/kubernetes_deploy.go b/api/exec/kubernetes_deploy.go index c91bcd3ca..84e9bd3ef 100644 --- a/api/exec/kubernetes_deploy.go +++ b/api/exec/kubernetes_deploy.go @@ -3,7 +3,6 @@ package exec import ( "bytes" "fmt" - "net/http" "os/exec" "path" "runtime" @@ -14,7 +13,6 @@ import ( "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/http/security" "github.com/portainer/portainer/api/kubernetes/cli" portainer "github.com/portainer/portainer/api" @@ -44,7 +42,7 @@ func NewKubernetesDeployer(kubernetesTokenCacheManager *kubernetes.TokenCacheMan } } -func (deployer *KubernetesDeployer) getToken(request *http.Request, endpoint *portainer.Endpoint, setLocalAdminToken bool, getAdminToken bool) (string, error) { +func (deployer *KubernetesDeployer) getToken(userID portainer.UserID, endpoint *portainer.Endpoint, setLocalAdminToken bool) (string, error) { kubeCLI, err := deployer.kubernetesClientFactory.GetKubeClient(endpoint) if err != nil { return "", err @@ -57,20 +55,16 @@ func (deployer *KubernetesDeployer) getToken(request *http.Request, endpoint *po return "", err } - if getAdminToken { - return tokenManager.GetAdminServiceAccountToken(), nil - } - - tokenData, err := security.RetrieveTokenData(request) + user, err := deployer.dataStore.User().User(userID) if err != nil { - return "", err + return "", errors.Wrap(err, "failed to fetch the user") } - if tokenData.Role == portainer.AdministratorRole { + if user.Role == portainer.AdministratorRole { return tokenManager.GetAdminServiceAccountToken(), nil } - token, err := tokenManager.GetUserServiceAccountToken(int(tokenData.ID), endpoint.ID) + token, err := tokenManager.GetUserServiceAccountToken(int(user.ID), endpoint.ID) if err != nil { return "", err } @@ -83,8 +77,8 @@ func (deployer *KubernetesDeployer) getToken(request *http.Request, endpoint *po // Deploy will deploy a Kubernetes manifest inside a specific namespace in a Kubernetes endpoint. // Otherwise it will use kubectl to deploy the manifest. -func (deployer *KubernetesDeployer) Deploy(request *http.Request, endpoint *portainer.Endpoint, manifestFiles []string, namespace string, deployAsAdmin bool) (string, error) { - token, err := deployer.getToken(request, endpoint, endpoint.Type == portainer.KubernetesLocalEnvironment, deployAsAdmin) +func (deployer *KubernetesDeployer) Deploy(userID portainer.UserID, endpoint *portainer.Endpoint, manifestFiles []string, namespace string) (string, error) { + token, err := deployer.getToken(userID, endpoint, endpoint.Type == portainer.KubernetesLocalEnvironment) if err != nil { return "", err } diff --git a/api/http/handler/stacks/create_kubernetes_stack.go b/api/http/handler/stacks/create_kubernetes_stack.go index 4d8661493..a4022b4a5 100644 --- a/api/http/handler/stacks/create_kubernetes_stack.go +++ b/api/http/handler/stacks/create_kubernetes_stack.go @@ -112,6 +112,7 @@ func (handler *Handler) createKubernetesStackFromFileContent(w http.ResponseWrit CreationDate: time.Now().Unix(), CreatedBy: user.Username, IsComposeFormat: payload.ComposeFormat, + OwnerUserID: user.ID, } stackFolder := strconv.Itoa(int(stack.ID)) @@ -129,7 +130,7 @@ func (handler *Handler) createKubernetesStackFromFileContent(w http.ResponseWrit doCleanUp := true defer handler.cleanUp(stack, &doCleanUp) - output, err := handler.deployKubernetesStack(r, endpoint, stack, k.KubeAppLabels{ + output, err := handler.deployKubernetesStack(user.ID, endpoint, stack, k.KubeAppLabels{ StackID: stackID, Name: stack.Name, Owner: stack.CreatedBy, @@ -195,6 +196,8 @@ func (handler *Handler) createKubernetesStackFromGitRepository(w http.ResponseWr CreatedBy: user.Username, IsComposeFormat: payload.ComposeFormat, AutoUpdate: payload.AutoUpdate, + AdditionalFiles: payload.AdditionalFiles, + OwnerUserID: user.ID, } if payload.RepositoryAuthentication { @@ -228,7 +231,7 @@ func (handler *Handler) createKubernetesStackFromGitRepository(w http.ResponseWr return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Failed to clone git repository", Err: err} } - output, err := handler.deployKubernetesStack(r, endpoint, stack, k.KubeAppLabels{ + output, err := handler.deployKubernetesStack(user.ID, endpoint, stack, k.KubeAppLabels{ StackID: stackID, Name: stack.Name, Owner: stack.CreatedBy, @@ -285,6 +288,7 @@ func (handler *Handler) createKubernetesStackFromManifestURL(w http.ResponseWrit CreationDate: time.Now().Unix(), CreatedBy: user.Username, IsComposeFormat: payload.ComposeFormat, + OwnerUserID: user.ID, } var manifestContent []byte @@ -303,7 +307,7 @@ func (handler *Handler) createKubernetesStackFromManifestURL(w http.ResponseWrit doCleanUp := true defer handler.cleanUp(stack, &doCleanUp) - output, err := handler.deployKubernetesStack(r, endpoint, stack, k.KubeAppLabels{ + output, err := handler.deployKubernetesStack(user.ID, endpoint, stack, k.KubeAppLabels{ StackID: stackID, Name: stack.Name, Owner: stack.CreatedBy, @@ -325,7 +329,7 @@ func (handler *Handler) createKubernetesStackFromManifestURL(w http.ResponseWrit return response.JSON(w, resp) } -func (handler *Handler) deployKubernetesStack(r *http.Request, endpoint *portainer.Endpoint, stack *portainer.Stack, appLabels k.KubeAppLabels) (string, error) { +func (handler *Handler) deployKubernetesStack(userID portainer.UserID, endpoint *portainer.Endpoint, stack *portainer.Stack, appLabels k.KubeAppLabels) (string, error) { handler.stackCreationMutex.Lock() defer handler.stackCreationMutex.Unlock() @@ -334,5 +338,5 @@ func (handler *Handler) deployKubernetesStack(r *http.Request, endpoint *portain return "", errors.Wrap(err, "failed to create temp kub deployment files") } defer os.RemoveAll(tempDir) - return handler.KubernetesDeployer.Deploy(r, endpoint, manifestFilePaths, stack.Namespace, false) + return handler.KubernetesDeployer.Deploy(userID, endpoint, manifestFilePaths, stack.Namespace) } diff --git a/api/http/handler/stacks/stack_update_git_redeploy.go b/api/http/handler/stacks/stack_update_git_redeploy.go index b0868d001..3efcdfa3f 100644 --- a/api/http/handler/stacks/stack_update_git_redeploy.go +++ b/api/http/handler/stacks/stack_update_git_redeploy.go @@ -214,7 +214,11 @@ func (handler *Handler) deployStack(r *http.Request, stack *portainer.Stack, end if stack.Namespace == "" { return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Invalid namespace", Err: errors.New("Namespace must not be empty when redeploying kubernetes stacks")} } - _, err := handler.deployKubernetesStack(r, endpoint, stack, k.KubeAppLabels{ + tokenData, err := security.RetrieveTokenData(r) + if err != nil { + return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Failed to retrieve user token data", Err: err} + } + _, err = handler.deployKubernetesStack(tokenData.ID, endpoint, stack, k.KubeAppLabels{ StackID: int(stack.ID), Name: stack.Name, Owner: stack.CreatedBy, diff --git a/api/http/handler/stacks/update_kubernetes_stack.go b/api/http/handler/stacks/update_kubernetes_stack.go index 18753d8ba..4969f960b 100644 --- a/api/http/handler/stacks/update_kubernetes_stack.go +++ b/api/http/handler/stacks/update_kubernetes_stack.go @@ -15,6 +15,7 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/filesystem" gittypes "github.com/portainer/portainer/api/git/types" + "github.com/portainer/portainer/api/http/security" k "github.com/portainer/portainer/api/kubernetes" ) @@ -95,6 +96,11 @@ func (handler *Handler) updateKubernetesStack(r *http.Request, stack *portainer. return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid request payload", Err: err} } + tokenData, err := security.RetrieveTokenData(r) + if err != nil { + return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Failed to retrieve user token data", Err: err} + } + tempFileDir, _ := ioutil.TempDir("", "kub_file_content") defer os.RemoveAll(tempFileDir) @@ -106,7 +112,7 @@ func (handler *Handler) updateKubernetesStack(r *http.Request, stack *portainer. //so if the deployment failed, the original file won't be over-written stack.ProjectPath = tempFileDir - _, err = handler.deployKubernetesStack(r, endpoint, stack, k.KubeAppLabels{ + _, err = handler.deployKubernetesStack(tokenData.ID, endpoint, stack, k.KubeAppLabels{ StackID: int(stack.ID), Name: stack.Name, Owner: stack.CreatedBy, diff --git a/api/portainer.go b/api/portainer.go index a47e5ede2..3798d0c75 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -3,7 +3,6 @@ package portainer import ( "context" "io" - "net/http" "time" gittypes "github.com/portainer/portainer/api/git/types" @@ -763,6 +762,8 @@ type ( Namespace string `example:"default"` // IsComposeFormat indicates if the Kubernetes stack is created from a Docker Compose file IsComposeFormat bool `example:"false"` + // OwnerUserID represents the Stack owner/creator's user ID + OwnerUserID UserID `example:"1"` } //StackAutoUpdate represents the git auto sync config for stack deployment @@ -1249,7 +1250,7 @@ type ( // KubernetesDeployer represents a service to deploy a manifest inside a Kubernetes endpoint KubernetesDeployer interface { - Deploy(request *http.Request, endpoint *Endpoint, manifestFiles []string, namespace string, deployAsAdmin bool) (string, error) + Deploy(userID UserID, endpoint *Endpoint, manifestFiles []string, namespace string) (string, error) ConvertCompose(data []byte) ([]byte, error) } diff --git a/api/stacks/deployer.go b/api/stacks/deployer.go index 61903f29b..58bcdf0d3 100644 --- a/api/stacks/deployer.go +++ b/api/stacks/deployer.go @@ -77,7 +77,7 @@ func (d *stackDeployer) DeployKubernetesStack(stack *portainer.Stack, endpoint * } defer os.RemoveAll(tempDir) - _, err = d.kubernetesDeployer.Deploy(nil, endpoint, manifestFilePaths, stack.Namespace, true) + _, err = d.kubernetesDeployer.Deploy(stack.OwnerUserID, endpoint, manifestFilePaths, stack.Namespace) if err != nil { return errors.Wrap(err, "failed to deploy kubernetes application") }