litmus/litmus-portal/graphql-server/pkg/myhub/gitops/gitops.go

296 lines
8.1 KiB
Go

package gitops
import (
"fmt"
ssh2 "golang.org/x/crypto/ssh"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
"gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
"os"
"strings"
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
log "github.com/sirupsen/logrus"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
)
//MyHubConfig ...
type MyHubConfig struct {
ProjectID string
RepositoryURL string
RemoteName string
LocalCommit string
RemoteCommit string
HubName string
Branch string
IsPrivate bool
UserName *string
Password *string
AuthType model.AuthType
Token *string
SSHPrivateKey *string
}
const (
defaultPath = "/tmp/version/"
)
//GetClonePath is used to construct path for Repository.
func GetClonePath(c MyHubConfig) string {
RepoPath := defaultPath + c.ProjectID + "/" + c.HubName
return RepoPath
}
//GitConfigConstruct is used for constructing the gitconfig
func GitConfigConstruct(repoData model.CloningInput) MyHubConfig {
gitConfig := MyHubConfig{
ProjectID: repoData.ProjectID,
HubName: repoData.HubName,
RepositoryURL: repoData.RepoURL,
RemoteName: "origin",
Branch: repoData.RepoBranch,
IsPrivate: repoData.IsPrivate,
UserName: repoData.UserName,
Password: repoData.Password,
AuthType: repoData.AuthType,
Token: repoData.Token,
SSHPrivateKey: repoData.SSHPrivateKey,
}
return gitConfig
}
//GitClone Trigger is reponsible for setting off the go routine for git-op
func GitClone(repoData model.CloningInput) error {
gitConfig := GitConfigConstruct(repoData)
if repoData.IsPrivate {
_, err := gitConfig.getPrivateChaosChartRepo()
if err != nil {
fmt.Print("Error in cloning")
return err
}
} else {
_, err := gitConfig.getChaosChartRepo()
if err != nil {
fmt.Print("Error in cloning")
return err
}
}
//Successfully Cloned
return nil
}
//getChaosChartVersion is responsible for plain cloning the repository
func (c MyHubConfig) getChaosChartRepo() (string, error) {
ClonePath := GetClonePath(c)
os.RemoveAll(ClonePath)
_, err := git.PlainClone(ClonePath, false, &git.CloneOptions{
URL: c.RepositoryURL, Progress: os.Stdout,
ReferenceName: plumbing.NewBranchReferenceName(c.Branch),
})
return c.Branch, err
}
//getPrivateChaosChartVersion is responsible for plain cloning the private repository
func (c MyHubConfig) getPrivateChaosChartRepo() (string, error) {
ClonePath := GetClonePath(c)
os.RemoveAll(ClonePath)
auth, err := c.generateAuthMethod()
if err != nil {
return "", err
}
_, err = git.PlainClone(ClonePath, false, &git.CloneOptions{
Auth: auth,
URL: c.RepositoryURL,
Progress: os.Stdout,
ReferenceName: plumbing.NewBranchReferenceName(c.Branch),
})
return c.Branch, err
}
//GitSyncHandlerForProjects ...
func GitSyncHandlerForProjects(repoData model.CloningInput) error {
gitConfig := GitConfigConstruct(repoData)
if err := gitConfig.chaosChartSyncHandler(); err != nil {
log.Error(err)
return err
}
//Repository syncing completed
return nil
}
// chaosChartSyncHandler is responsible for all the handler functions
func (c MyHubConfig) chaosChartSyncHandler() error {
repositoryExists, err := c.isRepositoryExists()
if err != nil {
return fmt.Errorf("Error while checking repo exists, err: %s", err)
}
log.WithFields(log.Fields{"repositoryExists": repositoryExists}).Info("Executed isRepositoryExists()... ")
if !repositoryExists {
return GitClone(model.CloningInput{
HubName: c.HubName,
ProjectID: c.ProjectID,
RepoURL: c.RepositoryURL,
RepoBranch: c.Branch,
IsPrivate: c.IsPrivate,
AuthType: c.AuthType,
Token: c.Token,
UserName: c.UserName,
Password: c.Password,
SSHPrivateKey: c.SSHPrivateKey,
})
}
return c.GitPull()
}
// isRepositoryExists checks for the existence of this past existence of this repository
func (c MyHubConfig) isRepositoryExists() (bool, error) {
RepoPath := GetClonePath(c)
_, err := os.Stat(RepoPath)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
return true, nil
}
func (c MyHubConfig) setterRepositoryWorktreeReference() (*git.Repository, *git.Worktree, *plumbing.Reference, error) {
RepoPath := GetClonePath(c)
repository, err := git.PlainOpen(RepoPath)
if err != nil {
return nil, nil, nil, fmt.Errorf("error in executing PlainOpen: %s", err)
}
workTree, err := repository.Worktree()
if err != nil {
return nil, nil, nil, fmt.Errorf("error in executing Worktree: %s", err)
}
plumbingRef, err := repository.Head()
if err != nil {
return nil, nil, nil, fmt.Errorf("error in executing Head: %s", err)
}
return repository, workTree, plumbingRef, nil
}
// GitHardReset executes "git reset --hard HEAD" in provided Repository Path
func (c MyHubConfig) GitHardReset() error {
RepoPath := GetClonePath(c)
repository, err := git.PlainOpen(RepoPath)
if err != nil {
return fmt.Errorf("error in executing PlainOpen: %s", err)
}
workTree, err := repository.Worktree()
if err != nil {
return fmt.Errorf("error in executing Worktree: %s", err)
}
if workTree.Reset(&git.ResetOptions{Mode: git.HardReset}) != nil {
return fmt.Errorf("error in executing Reset: %s", err)
}
return nil
}
// CompareLocalandRemoteCommit compares local and remote latest commit
func (c MyHubConfig) CompareLocalandRemoteCommit() (bool, error) {
RepoPath := GetClonePath(c)
repository, err := git.PlainOpen(RepoPath)
if err != nil {
return false, fmt.Errorf("error in executing PlainOpen: %s", err)
}
hash, err := repository.ResolveRevision(plumbing.Revision(c.Branch))
if err != nil {
return false, fmt.Errorf("error in executing ResolveRevision: %s", err)
}
c.RemoteCommit = hash.String()
return c.RemoteCommit == c.LocalCommit, nil
}
// GitPull updates the repository in provided Path
func (c MyHubConfig) GitPull() error {
_, workTree, plumbingRef, err := c.setterRepositoryWorktreeReference()
if err != nil {
return err
}
var referenceName plumbing.ReferenceName
referenceName = plumbing.NewBranchReferenceName(c.Branch)
if !c.IsPrivate {
err = workTree.Pull(&git.PullOptions{RemoteName: c.RemoteName, ReferenceName: referenceName})
if err == git.NoErrAlreadyUpToDate {
log.Print("Already up-to-date")
return nil
} else if err != nil {
return err
}
c.LocalCommit = strings.Split(plumbingRef.String(), " ")[0]
return nil
}
err = c.gitPullPrivateRepo()
if err == git.NoErrAlreadyUpToDate {
log.Print("Already up-to-date")
return nil
} else if err != nil {
return err
}
return nil
}
// gitPullPrivateRepo updates the repository of private hubs
func (c MyHubConfig) gitPullPrivateRepo() error {
_, workTree, _, err := c.setterRepositoryWorktreeReference()
if err != nil {
return err
}
var referenceName plumbing.ReferenceName
referenceName = plumbing.NewBranchReferenceName(c.Branch)
auth, err := c.generateAuthMethod()
if err != nil {
return nil
}
err = workTree.Pull(&git.PullOptions{RemoteName: c.RemoteName, ReferenceName: referenceName, Auth: auth})
if err != nil {
return err
}
return nil
}
// HandlerForMismatchCommits calls relative functions if the Local and Remote Commits do not match
func (c MyHubConfig) HandlerForMismatchCommits() error {
err := c.GitPull()
if err != nil {
return err
}
log.WithFields(log.Fields{"execution": "complete"}).Info("Executed GitPull()... ")
return nil
}
// generateAuthMethod creates AuthMethod for private repos
func (c MyHubConfig) generateAuthMethod() (transport.AuthMethod, error) {
var auth transport.AuthMethod
if c.AuthType == model.AuthTypeToken {
auth = &http.BasicAuth{
Username: "kubera", // this can be anything except an empty string
Password: *c.Token,
}
} else if c.AuthType == model.AuthTypeBasic {
auth = &http.BasicAuth{
Username: *c.UserName,
Password: *c.Password,
}
} else if c.AuthType == model.AuthTypeSSH {
publicKey, err := ssh.NewPublicKeys("git", []byte(*c.SSHPrivateKey), "")
if err != nil {
return nil, err
}
auth = publicKey
auth.(*ssh.PublicKeys).HostKeyCallback = ssh2.InsecureIgnoreHostKey()
}
return auth, nil
}