pkg/git/factory/gitfactory.go (145 lines of code) (raw):
package gitfactory
import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
gogitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
"golang.org/x/crypto/ssh"
"github.com/aws/eks-anywhere/pkg/api/v1alpha1"
"github.com/aws/eks-anywhere/pkg/config"
"github.com/aws/eks-anywhere/pkg/filewriter"
"github.com/aws/eks-anywhere/pkg/git"
"github.com/aws/eks-anywhere/pkg/git/gitclient"
"github.com/aws/eks-anywhere/pkg/git/gogithub"
"github.com/aws/eks-anywhere/pkg/git/providers/codecommit"
"github.com/aws/eks-anywhere/pkg/git/providers/github"
)
type GitTools struct {
Provider git.ProviderClient
Client git.Client
Writer filewriter.FileWriter
RepositoryDirectory string
}
type GitToolsOpt func(opts *GitTools)
func Build(ctx context.Context, cluster *v1alpha1.Cluster, fluxConfig *v1alpha1.FluxConfig, writer filewriter.FileWriter, opts ...GitToolsOpt) (*GitTools, error) {
var repo string
var repoUrl string
var gitAuth transport.AuthMethod
var err error
var tools GitTools
switch {
case fluxConfig.Spec.Github != nil:
githubToken, err := github.GetGithubAccessTokenFromEnv()
if err != nil {
return nil, err
}
tools.Provider, err = buildGithubProvider(ctx, githubToken, fluxConfig.Spec.Github)
if err != nil {
return nil, fmt.Errorf("building github provider: %v", err)
}
gitAuth = &http.BasicAuth{Password: githubToken, Username: fluxConfig.Spec.Github.Owner}
repo = fluxConfig.Spec.Github.Repository
repoUrl = github.RepoUrl(fluxConfig.Spec.Github.Owner, repo)
case fluxConfig.Spec.Git != nil:
privateKeyFile := os.Getenv(config.EksaGitPrivateKeyTokenEnv)
privateKeyPassphrase := os.Getenv(config.EksaGitPassphraseTokenEnv)
gitKnownHosts := os.Getenv(config.EksaGitKnownHostsFileEnv)
if err = os.Setenv(config.SshKnownHostsEnv, gitKnownHosts); err != nil {
return nil, fmt.Errorf("unable to set %s: %v", config.SshKnownHostsEnv, err)
}
user := codecommit.DefaultSSHAuthUser
repoUrl = fluxConfig.Spec.Git.RepositoryUrl
codeCommitUserName, err := codecommit.IsCodeCommitURL(repoUrl)
if err != nil {
return nil, err
}
if codeCommitUserName != "" {
user = codeCommitUserName
}
gitAuth, err = getSSHAuthFromPrivateKey(privateKeyFile, privateKeyPassphrase, user)
if err != nil {
return nil, err
}
repo = path.Base(strings.TrimSuffix(repoUrl, filepath.Ext(repoUrl)))
default:
return nil, fmt.Errorf("no valid git provider in FluxConfigSpec. Spec: %v", fluxConfig)
}
tools.RepositoryDirectory = filepath.Join(cluster.Name, "git", repo)
for _, opt := range opts {
if opt != nil {
opt(&tools)
}
}
tools.Client = buildGitClient(ctx, gitAuth, repoUrl, tools.RepositoryDirectory)
tools.Writer, err = newRepositoryWriter(writer, repo)
if err != nil {
return nil, err
}
return &tools, nil
}
func buildGitClient(ctx context.Context, auth transport.AuthMethod, repoUrl string, repo string) *gitclient.GitClient {
opts := []gitclient.Opt{
gitclient.WithRepositoryUrl(repoUrl),
gitclient.WithRepositoryDirectory(repo),
gitclient.WithAuth(auth),
}
return gitclient.New(opts...)
}
func buildGithubProvider(ctx context.Context, githubToken string, config *v1alpha1.GithubProviderConfig) (git.ProviderClient, error) {
auth := git.TokenAuth{Token: githubToken, Username: config.Owner}
gogithubOpts := gogithub.Options{Auth: auth}
githubProviderClient := gogithub.New(ctx, gogithubOpts)
provider, err := github.New(githubProviderClient, config, auth)
if err != nil {
return nil, err
}
return provider, nil
}
func newRepositoryWriter(writer filewriter.FileWriter, repository string) (filewriter.FileWriter, error) {
localGitWriterPath := filepath.Join("git", repository)
gitwriter, err := writer.WithDir(localGitWriterPath)
if err != nil {
return nil, fmt.Errorf("creating file writer: %v", err)
}
gitwriter.CleanUpTemp()
return gitwriter, nil
}
func WithRepositoryDirectory(repoDir string) GitToolsOpt {
return func(opts *GitTools) {
opts.RepositoryDirectory = repoDir
}
}
func getSSHAuthFromPrivateKey(privateKeyFile string, passphrase string, user string) (gogitssh.AuthMethod, error) {
signer, err := getSignerFromPrivateKeyFile(privateKeyFile, passphrase)
if err != nil {
return nil, err
}
return &gogitssh.PublicKeys{
Signer: signer,
User: user,
}, nil
}
func getSignerFromPrivateKeyFile(privateKeyFile string, passphrase string) (ssh.Signer, error) {
var signer ssh.Signer
var err error
sshKey, err := os.ReadFile(privateKeyFile)
if err != nil {
return nil, err
}
if passphrase == "" {
signer, err = ssh.ParsePrivateKey(sshKey)
if err != nil {
if _, ok := err.(*ssh.PassphraseMissingError); ok {
return nil, fmt.Errorf("%s, please set the EKSA_GIT_SSH_KEY_PASSPHRASE environment variable", err)
}
return nil, err
}
return signer, nil
}
return ssh.ParsePrivateKeyWithPassphrase(sshKey, []byte(passphrase))
}