mirror of https://github.com/portainer/portainer
Introduced ClonePrivateRepositoryWithDeploymentKey and its handler
parent
7dd9e9e365
commit
f496776490
|
@ -1,11 +1,12 @@
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"golang.org/x/crypto/ssh"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/src-d/go-git.v4"
|
"gopkg.in/src-d/go-git.v4"
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||||
|
gitSsh "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service represents a service for managing Git.
|
// Service represents a service for managing Git.
|
||||||
|
@ -44,3 +45,34 @@ func cloneRepository(repositoryURL, referenceName string, destination string) er
|
||||||
_, err := git.PlainClone(destination, false, options)
|
_, err := git.PlainClone(destination, false, options)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service *Service) ClonePrivateRepositoryWithDeploymentKey(repositoryURL, referenceName string, destination string, privateKeyPem []byte) error {
|
||||||
|
// url := "git@github.com:ssbkang/personal-development.git"
|
||||||
|
// directory := "personal-development"
|
||||||
|
// https://github.com/portainer/portainer-compose
|
||||||
|
|
||||||
|
signer, _ := ssh.ParsePrivateKey(privateKeyPem)
|
||||||
|
auth := &gitSsh.PublicKeys{
|
||||||
|
User: "git",
|
||||||
|
Signer: signer,
|
||||||
|
HostKeyCallbackHelper: gitSsh.HostKeyCallbackHelper{
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
repositoryURL = strings.Replace(repositoryURL, "https://", "git@", 1)
|
||||||
|
repositoryURL += ".git"
|
||||||
|
|
||||||
|
options := &git.CloneOptions{
|
||||||
|
URL: repositoryURL,
|
||||||
|
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
|
||||||
|
Auth: auth,
|
||||||
|
}
|
||||||
|
|
||||||
|
if referenceName != "" {
|
||||||
|
options.ReferenceName = plumbing.ReferenceName(referenceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := git.PlainClone(destination, false, options)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -87,11 +87,13 @@ func (handler *Handler) createComposeStackFromFileContent(w http.ResponseWriter,
|
||||||
return response.JSON(w, stack)
|
return response.JSON(w, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update struc to cater for deployment key
|
||||||
type composeStackFromGitRepositoryPayload struct {
|
type composeStackFromGitRepositoryPayload struct {
|
||||||
Name string
|
Name string
|
||||||
RepositoryURL string
|
RepositoryURL string
|
||||||
RepositoryReferenceName string
|
RepositoryReferenceName string
|
||||||
RepositoryAuthentication bool
|
RepositoryAuthentication bool
|
||||||
|
RepositoryDeploymentKey string
|
||||||
RepositoryUsername string
|
RepositoryUsername string
|
||||||
RepositoryPassword string
|
RepositoryPassword string
|
||||||
ComposeFilePathInRepository string
|
ComposeFilePathInRepository string
|
||||||
|
@ -145,11 +147,13 @@ func (handler *Handler) createComposeStackFromGitRepository(w http.ResponseWrite
|
||||||
projectPath := handler.FileService.GetStackProjectPath(strconv.Itoa(int(stack.ID)))
|
projectPath := handler.FileService.GetStackProjectPath(strconv.Itoa(int(stack.ID)))
|
||||||
stack.ProjectPath = projectPath
|
stack.ProjectPath = projectPath
|
||||||
|
|
||||||
|
// Add Deployment Key
|
||||||
gitCloneParams := &cloneRepositoryParameters{
|
gitCloneParams := &cloneRepositoryParameters{
|
||||||
url: payload.RepositoryURL,
|
url: payload.RepositoryURL,
|
||||||
referenceName: payload.RepositoryReferenceName,
|
referenceName: payload.RepositoryReferenceName,
|
||||||
path: projectPath,
|
path: projectPath,
|
||||||
authentication: payload.RepositoryAuthentication,
|
authentication: payload.RepositoryAuthentication,
|
||||||
|
deploymentKey: payload.RepositoryDeploymentKey,
|
||||||
username: payload.RepositoryUsername,
|
username: payload.RepositoryUsername,
|
||||||
password: payload.RepositoryPassword,
|
password: payload.RepositoryPassword,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
package stacks
|
package stacks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
httperror "github.com/portainer/libhttp/error"
|
httperror "github.com/portainer/libhttp/error"
|
||||||
"github.com/portainer/libhttp/request"
|
"github.com/portainer/libhttp/request"
|
||||||
|
@ -12,6 +8,9 @@ import (
|
||||||
"github.com/portainer/portainer/api"
|
"github.com/portainer/portainer/api"
|
||||||
"github.com/portainer/portainer/api/filesystem"
|
"github.com/portainer/portainer/api/filesystem"
|
||||||
"github.com/portainer/portainer/api/http/security"
|
"github.com/portainer/portainer/api/http/security"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type swarmStackFromFileContentPayload struct {
|
type swarmStackFromFileContentPayload struct {
|
||||||
|
@ -92,6 +91,7 @@ func (handler *Handler) createSwarmStackFromFileContent(w http.ResponseWriter, r
|
||||||
return response.JSON(w, stack)
|
return response.JSON(w, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update struc to cater for deployment key
|
||||||
type swarmStackFromGitRepositoryPayload struct {
|
type swarmStackFromGitRepositoryPayload struct {
|
||||||
Name string
|
Name string
|
||||||
SwarmID string
|
SwarmID string
|
||||||
|
@ -99,6 +99,7 @@ type swarmStackFromGitRepositoryPayload struct {
|
||||||
RepositoryURL string
|
RepositoryURL string
|
||||||
RepositoryReferenceName string
|
RepositoryReferenceName string
|
||||||
RepositoryAuthentication bool
|
RepositoryAuthentication bool
|
||||||
|
RepositoryDeploymentKey string
|
||||||
RepositoryUsername string
|
RepositoryUsername string
|
||||||
RepositoryPassword string
|
RepositoryPassword string
|
||||||
ComposeFilePathInRepository string
|
ComposeFilePathInRepository string
|
||||||
|
@ -111,12 +112,18 @@ func (payload *swarmStackFromGitRepositoryPayload) Validate(r *http.Request) err
|
||||||
if govalidator.IsNull(payload.SwarmID) {
|
if govalidator.IsNull(payload.SwarmID) {
|
||||||
return portainer.Error("Invalid Swarm ID")
|
return portainer.Error("Invalid Swarm ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Need to improve validators for SSH based URL and deployment key type
|
||||||
|
|
||||||
|
/*
|
||||||
if govalidator.IsNull(payload.RepositoryURL) || !govalidator.IsURL(payload.RepositoryURL) {
|
if govalidator.IsNull(payload.RepositoryURL) || !govalidator.IsURL(payload.RepositoryURL) {
|
||||||
return portainer.Error("Invalid repository URL. Must correspond to a valid URL format")
|
return portainer.Error("Invalid repository URL. Must correspond to a valid URL format")
|
||||||
}
|
}
|
||||||
if payload.RepositoryAuthentication && (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) {
|
|
||||||
return portainer.Error("Invalid repository credentials. Username and password must be specified when authentication is enabled")
|
if payload.RepositoryAuthentication && (govalidator.IsNull(payload.RepositoryDeploymentKey)) || (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) {
|
||||||
|
return portainer.Error("Invalid repository credentials or deploymenet key. Either Username & password or a deployment key must be specified when authentication is enabled")
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
if govalidator.IsNull(payload.ComposeFilePathInRepository) {
|
if govalidator.IsNull(payload.ComposeFilePathInRepository) {
|
||||||
payload.ComposeFilePathInRepository = filesystem.ComposeFileDefaultName
|
payload.ComposeFilePathInRepository = filesystem.ComposeFileDefaultName
|
||||||
}
|
}
|
||||||
|
@ -160,6 +167,7 @@ func (handler *Handler) createSwarmStackFromGitRepository(w http.ResponseWriter,
|
||||||
referenceName: payload.RepositoryReferenceName,
|
referenceName: payload.RepositoryReferenceName,
|
||||||
path: projectPath,
|
path: projectPath,
|
||||||
authentication: payload.RepositoryAuthentication,
|
authentication: payload.RepositoryAuthentication,
|
||||||
|
deploymentKey: payload.RepositoryDeploymentKey,
|
||||||
username: payload.RepositoryUsername,
|
username: payload.RepositoryUsername,
|
||||||
password: payload.RepositoryPassword,
|
password: payload.RepositoryPassword,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,18 @@ type cloneRepositoryParameters struct {
|
||||||
referenceName string
|
referenceName string
|
||||||
path string
|
path string
|
||||||
authentication bool
|
authentication bool
|
||||||
|
deploymentKey string
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) cloneGitRepository(parameters *cloneRepositoryParameters) error {
|
func (handler *Handler) cloneGitRepository(parameters *cloneRepositoryParameters) error {
|
||||||
if parameters.authentication {
|
if parameters.authentication && parameters.username != "" && parameters.password != "" {
|
||||||
return handler.GitService.ClonePrivateRepositoryWithBasicAuth(parameters.url, parameters.referenceName, parameters.path, parameters.username, parameters.password)
|
return handler.GitService.ClonePrivateRepositoryWithBasicAuth(parameters.url, parameters.referenceName, parameters.path, parameters.username, parameters.password)
|
||||||
}
|
}
|
||||||
|
if parameters.authentication && parameters.deploymentKey != "" {
|
||||||
|
deploymentKey, _ := handler.DeploymentKeyService.DeploymentKeyByName(parameters.deploymentKey)
|
||||||
|
return handler.GitService.ClonePrivateRepositoryWithDeploymentKey(parameters.url, parameters.referenceName, parameters.path, deploymentKey.PrivateKey)
|
||||||
|
}
|
||||||
return handler.GitService.ClonePublicRepository(parameters.url, parameters.referenceName, parameters.path)
|
return handler.GitService.ClonePublicRepository(parameters.url, parameters.referenceName, parameters.path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ type Handler struct {
|
||||||
stackDeletionMutex *sync.Mutex
|
stackDeletionMutex *sync.Mutex
|
||||||
requestBouncer *security.RequestBouncer
|
requestBouncer *security.RequestBouncer
|
||||||
*mux.Router
|
*mux.Router
|
||||||
|
DeploymentKeyService portainer.DeploymentKeyService
|
||||||
FileService portainer.FileService
|
FileService portainer.FileService
|
||||||
GitService portainer.GitService
|
GitService portainer.GitService
|
||||||
StackService portainer.StackService
|
StackService portainer.StackService
|
||||||
|
|
|
@ -182,6 +182,7 @@ func (server *Server) Start() error {
|
||||||
stackHandler.FileService = server.FileService
|
stackHandler.FileService = server.FileService
|
||||||
stackHandler.StackService = server.StackService
|
stackHandler.StackService = server.StackService
|
||||||
stackHandler.EndpointService = server.EndpointService
|
stackHandler.EndpointService = server.EndpointService
|
||||||
|
stackHandler.DeploymentKeyService = server.DeploymentKeyService
|
||||||
stackHandler.ResourceControlService = server.ResourceControlService
|
stackHandler.ResourceControlService = server.ResourceControlService
|
||||||
stackHandler.SwarmStackManager = server.SwarmStackManager
|
stackHandler.SwarmStackManager = server.SwarmStackManager
|
||||||
stackHandler.ComposeStackManager = server.ComposeStackManager
|
stackHandler.ComposeStackManager = server.ComposeStackManager
|
||||||
|
|
|
@ -817,6 +817,7 @@ type (
|
||||||
GitService interface {
|
GitService interface {
|
||||||
ClonePublicRepository(repositoryURL, referenceName string, destination string) error
|
ClonePublicRepository(repositoryURL, referenceName string, destination string) error
|
||||||
ClonePrivateRepositoryWithBasicAuth(repositoryURL, referenceName string, destination, username, password string) error
|
ClonePrivateRepositoryWithBasicAuth(repositoryURL, referenceName string, destination, username, password string) error
|
||||||
|
ClonePrivateRepositoryWithDeploymentKey(repositoryURL, referenceName string, destination string, privateKeyPem []byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobScheduler represents a service to run jobs on a periodic basis
|
// JobScheduler represents a service to run jobs on a periodic basis
|
||||||
|
|
Loading…
Reference in New Issue