mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-07 01:26:43 +00:00
226 lines
5.3 KiB
Go
226 lines
5.3 KiB
Go
package getter
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
|
"github.com/hashicorp/go-version"
|
|
)
|
|
|
|
// GitGetter is a Getter implementation that will download a module from
|
|
// a git repository.
|
|
type GitGetter struct{}
|
|
|
|
func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) {
|
|
return ClientModeDir, nil
|
|
}
|
|
|
|
func (g *GitGetter) Get(dst string, u *url.URL) error {
|
|
if _, err := exec.LookPath("git"); err != nil {
|
|
return fmt.Errorf("git must be available and on the PATH")
|
|
}
|
|
|
|
// Extract some query parameters we use
|
|
var ref, sshKey string
|
|
q := u.Query()
|
|
if len(q) > 0 {
|
|
ref = q.Get("ref")
|
|
q.Del("ref")
|
|
|
|
sshKey = q.Get("sshkey")
|
|
q.Del("sshkey")
|
|
|
|
// Copy the URL
|
|
var newU url.URL = *u
|
|
u = &newU
|
|
u.RawQuery = q.Encode()
|
|
}
|
|
|
|
var sshKeyFile string
|
|
if sshKey != "" {
|
|
// Check that the git version is sufficiently new.
|
|
if err := checkGitVersion("2.3"); err != nil {
|
|
return fmt.Errorf("Error using ssh key: %v", err)
|
|
}
|
|
|
|
// We have an SSH key - decode it.
|
|
raw, err := base64.StdEncoding.DecodeString(sshKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create a temp file for the key and ensure it is removed.
|
|
fh, err := ioutil.TempFile("", "go-getter")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sshKeyFile = fh.Name()
|
|
defer os.Remove(sshKeyFile)
|
|
|
|
// Set the permissions prior to writing the key material.
|
|
if err := os.Chmod(sshKeyFile, 0600); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write the raw key into the temp file.
|
|
_, err = fh.Write(raw)
|
|
fh.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Clone or update the repository
|
|
_, err := os.Stat(dst)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
if err == nil {
|
|
err = g.update(dst, sshKeyFile, ref)
|
|
} else {
|
|
err = g.clone(dst, sshKeyFile, u)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Next: check out the proper tag/branch if it is specified, and checkout
|
|
if ref != "" {
|
|
if err := g.checkout(dst, ref); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Lastly, download any/all submodules.
|
|
return g.fetchSubmodules(dst, sshKeyFile)
|
|
}
|
|
|
|
// GetFile for Git doesn't support updating at this time. It will download
|
|
// the file every time.
|
|
func (g *GitGetter) GetFile(dst string, u *url.URL) error {
|
|
td, err := ioutil.TempDir("", "getter-git")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.RemoveAll(td); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get the filename, and strip the filename from the URL so we can
|
|
// just get the repository directly.
|
|
filename := filepath.Base(u.Path)
|
|
u.Path = filepath.Dir(u.Path)
|
|
|
|
// Get the full repository
|
|
if err := g.Get(td, u); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Copy the single file
|
|
u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fg := &FileGetter{Copy: true}
|
|
return fg.GetFile(dst, u)
|
|
}
|
|
|
|
func (g *GitGetter) checkout(dst string, ref string) error {
|
|
cmd := exec.Command("git", "checkout", ref)
|
|
cmd.Dir = dst
|
|
return getRunCommand(cmd)
|
|
}
|
|
|
|
func (g *GitGetter) clone(dst, sshKeyFile string, u *url.URL) error {
|
|
cmd := exec.Command("git", "clone", u.String(), dst)
|
|
setupGitEnv(cmd, sshKeyFile)
|
|
return getRunCommand(cmd)
|
|
}
|
|
|
|
func (g *GitGetter) update(dst, sshKeyFile, ref string) error {
|
|
// Determine if we're a branch. If we're NOT a branch, then we just
|
|
// switch to master prior to checking out
|
|
cmd := exec.Command("git", "show-ref", "-q", "--verify", "refs/heads/"+ref)
|
|
cmd.Dir = dst
|
|
|
|
if getRunCommand(cmd) != nil {
|
|
// Not a branch, switch to master. This will also catch non-existent
|
|
// branches, in which case we want to switch to master and then
|
|
// checkout the proper branch later.
|
|
ref = "master"
|
|
}
|
|
|
|
// We have to be on a branch to pull
|
|
if err := g.checkout(dst, ref); err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd = exec.Command("git", "pull", "--ff-only")
|
|
cmd.Dir = dst
|
|
setupGitEnv(cmd, sshKeyFile)
|
|
return getRunCommand(cmd)
|
|
}
|
|
|
|
// fetchSubmodules downloads any configured submodules recursively.
|
|
func (g *GitGetter) fetchSubmodules(dst, sshKeyFile string) error {
|
|
cmd := exec.Command("git", "submodule", "update", "--init", "--recursive")
|
|
cmd.Dir = dst
|
|
setupGitEnv(cmd, sshKeyFile)
|
|
return getRunCommand(cmd)
|
|
}
|
|
|
|
// setupGitEnv sets up the environment for the given command. This is used to
|
|
// pass configuration data to git and ssh and enables advanced cloning methods.
|
|
func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) {
|
|
var sshOpts []string
|
|
|
|
if sshKeyFile != "" {
|
|
// We have an SSH key temp file configured, tell ssh about this.
|
|
sshOpts = append(sshOpts, "-i", sshKeyFile)
|
|
}
|
|
|
|
cmd.Env = append(os.Environ(),
|
|
// Set the ssh command to use for clones.
|
|
"GIT_SSH_COMMAND=ssh "+strings.Join(sshOpts, " "),
|
|
)
|
|
}
|
|
|
|
// checkGitVersion is used to check the version of git installed on the system
|
|
// against a known minimum version. Returns an error if the installed version
|
|
// is older than the given minimum.
|
|
func checkGitVersion(min string) error {
|
|
want, err := version.NewVersion(min)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
out, err := exec.Command("git", "version").Output()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fields := strings.Fields(string(out))
|
|
if len(fields) != 3 {
|
|
return fmt.Errorf("Unexpected 'git version' output: %q", string(out))
|
|
}
|
|
|
|
have, err := version.NewVersion(fields[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if have.LessThan(want) {
|
|
return fmt.Errorf("Required git version = %s, have %s", want, have)
|
|
}
|
|
|
|
return nil
|
|
}
|