github.com/cycloidio/terraform@v1.1.10-0.20220513142504-76d5c768dc63/getmodules/git_getter_test.go (about)

     1  package getmodules
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"io/ioutil"
     7  	"net/url"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"reflect"
    12  	"runtime"
    13  	"strings"
    14  	"testing"
    15  
    16  	getter "github.com/hashicorp/go-getter"
    17  	urlhelper "github.com/hashicorp/go-getter/helper/url"
    18  )
    19  
    20  var testHasGit bool
    21  
    22  func init() {
    23  	if _, err := exec.LookPath("git"); err == nil {
    24  		testHasGit = true
    25  	}
    26  }
    27  
    28  func TestGitGetter_impl(t *testing.T) {
    29  	var _ getter.Getter = new(gitGetter)
    30  }
    31  
    32  func TestGitGetter(t *testing.T) {
    33  	if !testHasGit {
    34  		t.Skip("git not found, skipping")
    35  	}
    36  
    37  	g := new(gitGetter)
    38  	dst := tempDir(t)
    39  
    40  	repo := testGitRepo(t, "basic")
    41  	repo.commitFile("foo.txt", "hello")
    42  
    43  	// With a dir that doesn't exist
    44  	if err := g.Get(dst, repo.url); err != nil {
    45  		t.Fatalf("err: %s", err)
    46  	}
    47  
    48  	// Verify the main file exists
    49  	mainPath := filepath.Join(dst, "foo.txt")
    50  	if _, err := os.Stat(mainPath); err != nil {
    51  		t.Fatalf("err: %s", err)
    52  	}
    53  }
    54  
    55  func TestGitGetter_branch(t *testing.T) {
    56  	if !testHasGit {
    57  		t.Skip("git not found, skipping")
    58  	}
    59  
    60  	g := new(gitGetter)
    61  	dst := tempDir(t)
    62  
    63  	repo := testGitRepo(t, "branch")
    64  	repo.git("checkout", "-b", "test-branch")
    65  	repo.commitFile("branch.txt", "branch")
    66  
    67  	q := repo.url.Query()
    68  	q.Add("ref", "test-branch")
    69  	repo.url.RawQuery = q.Encode()
    70  
    71  	if err := g.Get(dst, repo.url); err != nil {
    72  		t.Fatalf("err: %s", err)
    73  	}
    74  
    75  	// Verify the main file exists
    76  	mainPath := filepath.Join(dst, "branch.txt")
    77  	if _, err := os.Stat(mainPath); err != nil {
    78  		t.Fatalf("err: %s", err)
    79  	}
    80  
    81  	// Get again should work
    82  	if err := g.Get(dst, repo.url); err != nil {
    83  		t.Fatalf("err: %s", err)
    84  	}
    85  
    86  	// Verify the main file exists
    87  	mainPath = filepath.Join(dst, "branch.txt")
    88  	if _, err := os.Stat(mainPath); err != nil {
    89  		t.Fatalf("err: %s", err)
    90  	}
    91  }
    92  
    93  func TestGitGetter_commitID(t *testing.T) {
    94  	if !testHasGit {
    95  		t.Skip("git not found, skipping")
    96  	}
    97  
    98  	g := new(gitGetter)
    99  	dst := tempDir(t)
   100  
   101  	// We're going to create different content on the main branch vs.
   102  	// another branch here, so that below we can recognize if we
   103  	// correctly cloned the commit actually requested (from the
   104  	// "other branch"), not the one at HEAD.
   105  	repo := testGitRepo(t, "commit_id")
   106  	repo.git("checkout", "-b", "main-branch")
   107  	repo.commitFile("wrong.txt", "Nope")
   108  	repo.git("checkout", "-b", "other-branch")
   109  	repo.commitFile("hello.txt", "Yep")
   110  	commitID, err := repo.latestCommit()
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	// Return to the main branch so that HEAD of this repository
   115  	// will be that, rather than "test-branch".
   116  	repo.git("checkout", "main-branch")
   117  
   118  	q := repo.url.Query()
   119  	q.Add("ref", commitID)
   120  	repo.url.RawQuery = q.Encode()
   121  
   122  	t.Logf("Getting %s", repo.url)
   123  	if err := g.Get(dst, repo.url); err != nil {
   124  		t.Fatalf("err: %s", err)
   125  	}
   126  
   127  	// Verify the main file exists
   128  	mainPath := filepath.Join(dst, "hello.txt")
   129  	if _, err := os.Stat(mainPath); err != nil {
   130  		t.Fatalf("err: %s", err)
   131  	}
   132  
   133  	// Get again should work
   134  	if err := g.Get(dst, repo.url); err != nil {
   135  		t.Fatalf("err: %s", err)
   136  	}
   137  
   138  	// Verify the main file exists
   139  	mainPath = filepath.Join(dst, "hello.txt")
   140  	if _, err := os.Stat(mainPath); err != nil {
   141  		t.Fatalf("err: %s", err)
   142  	}
   143  }
   144  
   145  func TestGitGetter_remoteWithoutMaster(t *testing.T) {
   146  	if !testHasGit {
   147  		t.Log("git not found, skipping")
   148  		t.Skip()
   149  	}
   150  
   151  	g := new(gitGetter)
   152  	dst := tempDir(t)
   153  
   154  	repo := testGitRepo(t, "branch")
   155  	repo.git("checkout", "-b", "test-branch")
   156  	repo.commitFile("branch.txt", "branch")
   157  
   158  	q := repo.url.Query()
   159  	repo.url.RawQuery = q.Encode()
   160  
   161  	if err := g.Get(dst, repo.url); err != nil {
   162  		t.Fatalf("err: %s", err)
   163  	}
   164  
   165  	// Verify the main file exists
   166  	mainPath := filepath.Join(dst, "branch.txt")
   167  	if _, err := os.Stat(mainPath); err != nil {
   168  		t.Fatalf("err: %s", err)
   169  	}
   170  
   171  	// Get again should work
   172  	if err := g.Get(dst, repo.url); err != nil {
   173  		t.Fatalf("err: %s", err)
   174  	}
   175  
   176  	// Verify the main file exists
   177  	mainPath = filepath.Join(dst, "branch.txt")
   178  	if _, err := os.Stat(mainPath); err != nil {
   179  		t.Fatalf("err: %s", err)
   180  	}
   181  }
   182  
   183  func TestGitGetter_shallowClone(t *testing.T) {
   184  	if !testHasGit {
   185  		t.Log("git not found, skipping")
   186  		t.Skip()
   187  	}
   188  
   189  	g := new(gitGetter)
   190  	dst := tempDir(t)
   191  
   192  	repo := testGitRepo(t, "upstream")
   193  	repo.commitFile("upstream.txt", "0")
   194  	repo.commitFile("upstream.txt", "1")
   195  
   196  	// Specifiy a clone depth of 1
   197  	q := repo.url.Query()
   198  	q.Add("depth", "1")
   199  	repo.url.RawQuery = q.Encode()
   200  
   201  	if err := g.Get(dst, repo.url); err != nil {
   202  		t.Fatalf("err: %s", err)
   203  	}
   204  
   205  	// Assert rev-list count is '1'
   206  	cmd := exec.Command("git", "rev-list", "HEAD", "--count")
   207  	cmd.Dir = dst
   208  	b, err := cmd.Output()
   209  	if err != nil {
   210  		t.Fatalf("err: %s", err)
   211  	}
   212  
   213  	out := strings.TrimSpace(string(b))
   214  	if out != "1" {
   215  		t.Fatalf("expected rev-list count to be '1' but got %v", out)
   216  	}
   217  }
   218  
   219  func TestGitGetter_shallowCloneWithTag(t *testing.T) {
   220  	if !testHasGit {
   221  		t.Log("git not found, skipping")
   222  		t.Skip()
   223  	}
   224  
   225  	g := new(gitGetter)
   226  	dst := tempDir(t)
   227  
   228  	repo := testGitRepo(t, "upstream")
   229  	repo.commitFile("v1.0.txt", "0")
   230  	repo.git("tag", "v1.0")
   231  	repo.commitFile("v1.1.txt", "1")
   232  
   233  	// Specifiy a clone depth of 1 with a tag
   234  	q := repo.url.Query()
   235  	q.Add("ref", "v1.0")
   236  	q.Add("depth", "1")
   237  	repo.url.RawQuery = q.Encode()
   238  
   239  	if err := g.Get(dst, repo.url); err != nil {
   240  		t.Fatalf("err: %s", err)
   241  	}
   242  
   243  	// Assert rev-list count is '1'
   244  	cmd := exec.Command("git", "rev-list", "HEAD", "--count")
   245  	cmd.Dir = dst
   246  	b, err := cmd.Output()
   247  	if err != nil {
   248  		t.Fatalf("err: %s", err)
   249  	}
   250  
   251  	out := strings.TrimSpace(string(b))
   252  	if out != "1" {
   253  		t.Fatalf("expected rev-list count to be '1' but got %v", out)
   254  	}
   255  
   256  	// Verify the v1.0 file exists
   257  	mainPath := filepath.Join(dst, "v1.0.txt")
   258  	if _, err := os.Stat(mainPath); err != nil {
   259  		t.Fatalf("err: %s", err)
   260  	}
   261  
   262  	// Verify the v1.1 file does not exists
   263  	mainPath = filepath.Join(dst, "v1.1.txt")
   264  	if _, err := os.Stat(mainPath); err == nil {
   265  		t.Fatalf("expected v1.1 file to not exist")
   266  	}
   267  }
   268  
   269  func TestGitGetter_shallowCloneWithCommitID(t *testing.T) {
   270  	if !testHasGit {
   271  		t.Log("git not found, skipping")
   272  		t.Skip()
   273  	}
   274  
   275  	g := new(gitGetter)
   276  	dst := tempDir(t)
   277  
   278  	repo := testGitRepo(t, "upstream")
   279  	repo.commitFile("v1.0.txt", "0")
   280  	repo.git("tag", "v1.0")
   281  	repo.commitFile("v1.1.txt", "1")
   282  
   283  	commitID, err := repo.latestCommit()
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  
   288  	// Specify a clone depth of 1 with a naked commit ID
   289  	// This is intentionally invalid: shallow clone always requires a named ref.
   290  	q := repo.url.Query()
   291  	q.Add("ref", commitID[:8])
   292  	q.Add("depth", "1")
   293  	repo.url.RawQuery = q.Encode()
   294  
   295  	t.Logf("Getting %s", repo.url)
   296  	err = g.Get(dst, repo.url)
   297  	if err == nil {
   298  		t.Fatalf("success; want error")
   299  	}
   300  	// We use a heuristic to generate an extra hint in the error message if
   301  	// it looks like the user was trying to combine ref=COMMIT with depth.
   302  	if got, want := err.Error(), "(note that setting 'depth' requires 'ref' to be a branch or tag name)"; !strings.Contains(got, want) {
   303  		t.Errorf("missing error message hint\ngot: %s\nwant substring: %s", got, want)
   304  	}
   305  }
   306  
   307  func TestGitGetter_branchUpdate(t *testing.T) {
   308  	if !testHasGit {
   309  		t.Skip("git not found, skipping")
   310  	}
   311  
   312  	g := new(gitGetter)
   313  	dst := tempDir(t)
   314  
   315  	// First setup the state with a fresh branch
   316  	repo := testGitRepo(t, "branch-update")
   317  	repo.git("checkout", "-b", "test-branch")
   318  	repo.commitFile("branch.txt", "branch")
   319  
   320  	// Get the "test-branch" branch
   321  	q := repo.url.Query()
   322  	q.Add("ref", "test-branch")
   323  	repo.url.RawQuery = q.Encode()
   324  	if err := g.Get(dst, repo.url); err != nil {
   325  		t.Fatalf("err: %s", err)
   326  	}
   327  
   328  	// Verify the main file exists
   329  	mainPath := filepath.Join(dst, "branch.txt")
   330  	if _, err := os.Stat(mainPath); err != nil {
   331  		t.Fatalf("err: %s", err)
   332  	}
   333  
   334  	// Commit an update to the branch
   335  	repo.commitFile("branch-update.txt", "branch-update")
   336  
   337  	// Get again should work
   338  	if err := g.Get(dst, repo.url); err != nil {
   339  		t.Fatalf("err: %s", err)
   340  	}
   341  
   342  	// Verify the main file exists
   343  	mainPath = filepath.Join(dst, "branch-update.txt")
   344  	if _, err := os.Stat(mainPath); err != nil {
   345  		t.Fatalf("err: %s", err)
   346  	}
   347  }
   348  
   349  func TestGitGetter_tag(t *testing.T) {
   350  	if !testHasGit {
   351  		t.Skip("git not found, skipping")
   352  	}
   353  
   354  	g := new(gitGetter)
   355  	dst := tempDir(t)
   356  
   357  	repo := testGitRepo(t, "tag")
   358  	repo.commitFile("tag.txt", "tag")
   359  	repo.git("tag", "v1.0")
   360  
   361  	q := repo.url.Query()
   362  	q.Add("ref", "v1.0")
   363  	repo.url.RawQuery = q.Encode()
   364  
   365  	if err := g.Get(dst, repo.url); err != nil {
   366  		t.Fatalf("err: %s", err)
   367  	}
   368  
   369  	// Verify the main file exists
   370  	mainPath := filepath.Join(dst, "tag.txt")
   371  	if _, err := os.Stat(mainPath); err != nil {
   372  		t.Fatalf("err: %s", err)
   373  	}
   374  
   375  	// Get again should work
   376  	if err := g.Get(dst, repo.url); err != nil {
   377  		t.Fatalf("err: %s", err)
   378  	}
   379  
   380  	// Verify the main file exists
   381  	mainPath = filepath.Join(dst, "tag.txt")
   382  	if _, err := os.Stat(mainPath); err != nil {
   383  		t.Fatalf("err: %s", err)
   384  	}
   385  }
   386  
   387  func TestGitGetter_GetFile(t *testing.T) {
   388  	if !testHasGit {
   389  		t.Skip("git not found, skipping")
   390  	}
   391  
   392  	g := new(gitGetter)
   393  	dst := tempTestFile(t)
   394  	defer os.RemoveAll(filepath.Dir(dst))
   395  
   396  	repo := testGitRepo(t, "file")
   397  	repo.commitFile("file.txt", "hello")
   398  
   399  	// Download the file
   400  	repo.url.Path = filepath.Join(repo.url.Path, "file.txt")
   401  	if err := g.GetFile(dst, repo.url); err != nil {
   402  		t.Fatalf("err: %s", err)
   403  	}
   404  
   405  	// Verify the main file exists
   406  	if _, err := os.Stat(dst); err != nil {
   407  		t.Fatalf("err: %s", err)
   408  	}
   409  	assertContents(t, dst, "hello")
   410  }
   411  
   412  func TestGitGetter_gitVersion(t *testing.T) {
   413  	if !testHasGit {
   414  		t.Skip("git not found, skipping")
   415  	}
   416  	if runtime.GOOS == "windows" {
   417  		t.Skip("skipping on windows since the test requires sh")
   418  	}
   419  	dir, err := ioutil.TempDir("", "go-getter")
   420  	if err != nil {
   421  		t.Fatal(err)
   422  	}
   423  	defer os.RemoveAll(dir)
   424  
   425  	script := filepath.Join(dir, "git")
   426  	err = ioutil.WriteFile(
   427  		script,
   428  		[]byte("#!/bin/sh\necho \"git version 2.0 (Some Metadata Here)\n\""),
   429  		0700)
   430  	if err != nil {
   431  		t.Fatal(err)
   432  	}
   433  
   434  	defer func(v string) {
   435  		os.Setenv("PATH", v)
   436  	}(os.Getenv("PATH"))
   437  
   438  	os.Setenv("PATH", dir)
   439  
   440  	// Asking for a higher version throws an error
   441  	if err := checkGitVersion("2.3"); err == nil {
   442  		t.Fatal("expect git version error")
   443  	}
   444  
   445  	// Passes when version is satisfied
   446  	if err := checkGitVersion("1.9"); err != nil {
   447  		t.Fatal(err)
   448  	}
   449  }
   450  
   451  func TestGitGetter_sshKey(t *testing.T) {
   452  	if !testHasGit {
   453  		t.Skip("git not found, skipping")
   454  	}
   455  
   456  	g := new(gitGetter)
   457  	dst := tempDir(t)
   458  
   459  	encodedKey := base64.StdEncoding.EncodeToString([]byte(testGitToken))
   460  
   461  	// avoid getting locked by a github authenticity validation prompt
   462  	os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes")
   463  	defer os.Setenv("GIT_SSH_COMMAND", "")
   464  
   465  	u, err := urlhelper.Parse("ssh://git@github.com/hashicorp/test-private-repo" +
   466  		"?sshkey=" + encodedKey)
   467  	if err != nil {
   468  		t.Fatal(err)
   469  	}
   470  
   471  	if err := g.Get(dst, u); err != nil {
   472  		t.Fatalf("err: %s", err)
   473  	}
   474  
   475  	readmePath := filepath.Join(dst, "README.md")
   476  	if _, err := os.Stat(readmePath); err != nil {
   477  		t.Fatalf("err: %s", err)
   478  	}
   479  }
   480  
   481  func TestGitGetter_sshSCPStyle(t *testing.T) {
   482  	if !testHasGit {
   483  		t.Skip("git not found, skipping")
   484  	}
   485  
   486  	g := new(gitGetter)
   487  	dst := tempDir(t)
   488  
   489  	encodedKey := base64.StdEncoding.EncodeToString([]byte(testGitToken))
   490  
   491  	// avoid getting locked by a github authenticity validation prompt
   492  	os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes")
   493  	defer os.Setenv("GIT_SSH_COMMAND", "")
   494  
   495  	// This test exercises the combination of the git detector and the
   496  	// git getter, to make sure that together they make scp-style URLs work.
   497  	client := &getter.Client{
   498  		Src: "git@github.com:hashicorp/test-private-repo?sshkey=" + encodedKey,
   499  		Dst: dst,
   500  		Pwd: ".",
   501  
   502  		Mode: getter.ClientModeDir,
   503  
   504  		Detectors: []getter.Detector{
   505  			new(getter.GitDetector),
   506  		},
   507  		Getters: map[string]getter.Getter{
   508  			"git": g,
   509  		},
   510  	}
   511  
   512  	if err := client.Get(); err != nil {
   513  		t.Fatalf("client.Get failed: %s", err)
   514  	}
   515  
   516  	readmePath := filepath.Join(dst, "README.md")
   517  	if _, err := os.Stat(readmePath); err != nil {
   518  		t.Fatalf("err: %s", err)
   519  	}
   520  }
   521  
   522  func TestGitGetter_sshExplicitPort(t *testing.T) {
   523  	if !testHasGit {
   524  		t.Skip("git not found, skipping")
   525  	}
   526  
   527  	g := new(gitGetter)
   528  	dst := tempDir(t)
   529  
   530  	encodedKey := base64.StdEncoding.EncodeToString([]byte(testGitToken))
   531  
   532  	// avoid getting locked by a github authenticity validation prompt
   533  	os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes")
   534  	defer os.Setenv("GIT_SSH_COMMAND", "")
   535  
   536  	// This test exercises the combination of the git detector and the
   537  	// git getter, to make sure that together they make scp-style URLs work.
   538  	client := &getter.Client{
   539  		Src: "git::ssh://git@github.com:22/hashicorp/test-private-repo?sshkey=" + encodedKey,
   540  		Dst: dst,
   541  		Pwd: ".",
   542  
   543  		Mode: getter.ClientModeDir,
   544  
   545  		Detectors: []getter.Detector{
   546  			new(getter.GitDetector),
   547  		},
   548  		Getters: map[string]getter.Getter{
   549  			"git": g,
   550  		},
   551  	}
   552  
   553  	if err := client.Get(); err != nil {
   554  		t.Fatalf("client.Get failed: %s", err)
   555  	}
   556  
   557  	readmePath := filepath.Join(dst, "README.md")
   558  	if _, err := os.Stat(readmePath); err != nil {
   559  		t.Fatalf("err: %s", err)
   560  	}
   561  }
   562  
   563  func TestGitGetter_sshSCPStyleInvalidScheme(t *testing.T) {
   564  	if !testHasGit {
   565  		t.Skip("git not found, skipping")
   566  	}
   567  
   568  	g := new(gitGetter)
   569  	dst := tempDir(t)
   570  
   571  	encodedKey := base64.StdEncoding.EncodeToString([]byte(testGitToken))
   572  
   573  	// avoid getting locked by a github authenticity validation prompt
   574  	os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes")
   575  	defer os.Setenv("GIT_SSH_COMMAND", "")
   576  
   577  	// This test exercises the combination of the git detector and the
   578  	// git getter, to make sure that together they make scp-style URLs work.
   579  	client := &getter.Client{
   580  		Src: "git::ssh://git@github.com:hashicorp/test-private-repo?sshkey=" + encodedKey,
   581  		Dst: dst,
   582  		Pwd: ".",
   583  
   584  		Mode: getter.ClientModeDir,
   585  
   586  		Detectors: []getter.Detector{
   587  			new(getter.GitDetector),
   588  		},
   589  		Getters: map[string]getter.Getter{
   590  			"git": g,
   591  		},
   592  	}
   593  
   594  	err := client.Get()
   595  	if err == nil {
   596  		t.Fatalf("get succeeded; want error")
   597  	}
   598  
   599  	got := err.Error()
   600  	want1, want2 := `invalid source string`, `invalid port number "hashicorp"`
   601  	if !(strings.Contains(got, want1) || strings.Contains(got, want2)) {
   602  		t.Fatalf("wrong error\ngot:  %s\nwant: %q or %q", got, want1, want2)
   603  	}
   604  }
   605  
   606  func TestGitGetter_submodule(t *testing.T) {
   607  	if !testHasGit {
   608  		t.Skip("git not found, skipping")
   609  	}
   610  
   611  	g := new(gitGetter)
   612  	dst := tempDir(t)
   613  
   614  	relpath := func(basepath, targpath string) string {
   615  		relpath, err := filepath.Rel(basepath, targpath)
   616  		if err != nil {
   617  			t.Fatal(err)
   618  		}
   619  		return strings.Replace(relpath, `\`, `/`, -1)
   620  		// on windows git still prefers relatives paths
   621  		// containing `/` for submodules
   622  	}
   623  
   624  	// Set up the grandchild
   625  	gc := testGitRepo(t, "grandchild")
   626  	gc.commitFile("grandchild.txt", "grandchild")
   627  
   628  	// Set up the child
   629  	c := testGitRepo(t, "child")
   630  	c.commitFile("child.txt", "child")
   631  	c.git("submodule", "add", "-f", relpath(c.dir, gc.dir))
   632  	c.git("commit", "-m", "Add grandchild submodule")
   633  
   634  	// Set up the parent
   635  	p := testGitRepo(t, "parent")
   636  	p.commitFile("parent.txt", "parent")
   637  	p.git("submodule", "add", "-f", relpath(p.dir, c.dir))
   638  	p.git("commit", "-m", "Add child submodule")
   639  
   640  	// Clone the root repository
   641  	if err := g.Get(dst, p.url); err != nil {
   642  		t.Fatalf("err: %s", err)
   643  	}
   644  
   645  	// Check that the files exist
   646  	for _, path := range []string{
   647  		filepath.Join(dst, "parent.txt"),
   648  		filepath.Join(dst, "child", "child.txt"),
   649  		filepath.Join(dst, "child", "grandchild", "grandchild.txt"),
   650  	} {
   651  		if _, err := os.Stat(path); err != nil {
   652  			t.Fatalf("err: %s", err)
   653  		}
   654  	}
   655  }
   656  
   657  func TestGitGetter_setupGitEnv_sshKey(t *testing.T) {
   658  	if runtime.GOOS == "windows" {
   659  		t.Skip("skipping on windows since the test requires sh")
   660  	}
   661  
   662  	cmd := exec.Command("/bin/sh", "-c", "echo $GIT_SSH_COMMAND")
   663  	setupGitEnv(cmd, "/tmp/foo.pem")
   664  	out, err := cmd.Output()
   665  	if err != nil {
   666  		t.Fatal(err)
   667  	}
   668  
   669  	actual := strings.TrimSpace(string(out))
   670  	if actual != "ssh -i /tmp/foo.pem" {
   671  		t.Fatalf("unexpected GIT_SSH_COMMAND: %q", actual)
   672  	}
   673  }
   674  
   675  func TestGitGetter_setupGitEnvWithExisting_sshKey(t *testing.T) {
   676  	if runtime.GOOS == "windows" {
   677  		t.Skipf("skipping on windows since the test requires sh")
   678  		return
   679  	}
   680  
   681  	// start with an existing ssh command configuration
   682  	os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes")
   683  	defer os.Setenv("GIT_SSH_COMMAND", "")
   684  
   685  	cmd := exec.Command("/bin/sh", "-c", "echo $GIT_SSH_COMMAND")
   686  	setupGitEnv(cmd, "/tmp/foo.pem")
   687  	out, err := cmd.Output()
   688  	if err != nil {
   689  		t.Fatal(err)
   690  	}
   691  
   692  	actual := strings.TrimSpace(string(out))
   693  	if actual != "ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i /tmp/foo.pem" {
   694  		t.Fatalf("unexpected GIT_SSH_COMMAND: %q", actual)
   695  	}
   696  }
   697  
   698  // gitRepo is a helper struct which controls a single temp git repo.
   699  type gitRepo struct {
   700  	t   *testing.T
   701  	url *url.URL
   702  	dir string
   703  }
   704  
   705  // testGitRepo creates a new test git repository.
   706  func testGitRepo(t *testing.T, name string) *gitRepo {
   707  	t.Helper()
   708  	dir, err := ioutil.TempDir("", "go-getter")
   709  	if err != nil {
   710  		t.Fatal(err)
   711  	}
   712  	dir = filepath.Join(dir, name)
   713  	if err := os.Mkdir(dir, 0700); err != nil {
   714  		t.Fatal(err)
   715  	}
   716  
   717  	r := &gitRepo{
   718  		t:   t,
   719  		dir: dir,
   720  	}
   721  
   722  	url, err := urlhelper.Parse("file://" + r.dir)
   723  	if err != nil {
   724  		t.Fatal(err)
   725  	}
   726  	r.url = url
   727  
   728  	t.Logf("initializing git repo in %s", dir)
   729  	r.git("init")
   730  	r.git("config", "user.name", "go-getter")
   731  	r.git("config", "user.email", "go-getter@hashicorp.com")
   732  
   733  	return r
   734  }
   735  
   736  // git runs a git command against the repo.
   737  func (r *gitRepo) git(args ...string) {
   738  	cmd := exec.Command("git", args...)
   739  	cmd.Dir = r.dir
   740  	bfr := bytes.NewBuffer(nil)
   741  	cmd.Stderr = bfr
   742  	if err := cmd.Run(); err != nil {
   743  		r.t.Fatal(err, bfr.String())
   744  	}
   745  }
   746  
   747  // commitFile writes and commits a text file to the repo.
   748  func (r *gitRepo) commitFile(file, content string) {
   749  	path := filepath.Join(r.dir, file)
   750  	if err := ioutil.WriteFile(path, []byte(content), 0600); err != nil {
   751  		r.t.Fatal(err)
   752  	}
   753  	r.git("add", file)
   754  	r.git("commit", "-m", "Adding "+file)
   755  }
   756  
   757  // latestCommit returns the full commit id of the latest commit on the current
   758  // branch.
   759  func (r *gitRepo) latestCommit() (string, error) {
   760  	cmd := exec.Command("git", "rev-parse", "HEAD")
   761  	cmd.Dir = r.dir
   762  	rawOut, err := cmd.Output()
   763  	if err != nil {
   764  		return "", err
   765  	}
   766  	rawOut = bytes.TrimSpace(rawOut)
   767  	return string(rawOut), nil
   768  }
   769  
   770  // This is a read-only deploy key for an empty test repository.
   771  // Note: This is split over multiple lines to avoid being disabled by key
   772  // scanners automatically.
   773  var testGitToken = `-----BEGIN RSA PRIVATE KEY-----
   774  MIIEpAIBAAKCAQEA9cHsxCl3Jjgu9DHpwvmfFOl1XEdY+ShHDR/cMnzJ5ddk5/oV
   775  Wy6EWatvyHZfRSZMwzv4PtKeUPm6iXjqWp4xdWU9khlPzozyj+U9Fq70TRVUW9E5
   776  T1XdQVwJE421yffr4VMMwu60wBqjI1epapH2i2inYvw9Zl9X2MXq0+jTvFvDerbT
   777  mDtfStDPljenELAIZtWVETSvbI46gALwbxbM2292ZUIL4D6jRz0aZMmyy/twYv8r
   778  9WGJLwmYzU518Ie7zqKW/mCTdTrV0WRiDj0MeRaPgrGY9amuHE4r9iG/cJkwpKAO
   779  Ccz0Hs6i89u9vZnTqZU9V7weJqRAQcMjXXR6yQIDAQABAoIBAQDBzICKnGxiTlHw
   780  rd+6qqChnAy5jWYDbZjCJ8q8YZ3RS08+g/8NXZxvHftTqM0uOaq1FviHig3gq15H
   781  hHvCpBc6jXDFYoKFzq6FfO/0kFkE5HoWweIgxwRow0xBCDJAJ+ryUEyy+Ay/pQHb
   782  IAjwilRS0V+WdnVw4mTjBAhPvb4jPOo97Yfy3PYUyx2F3newkqXOZy+zx3G/ANoa
   783  ncypfMGyy76sfCWKqw4J1gVkVQLwbB6gQkXUFGYwY9sRrxbG93kQw76Flc/E/s52
   784  62j4v1IM0fq0t/St+Y/+s6Lkw` + `aqt3ft1nsqWcRaVDdqvMfkzgJGXlw0bGzJG5MEQ
   785  AIBq3dHRAoGBAP8OeG/DKG2Z1VmSfzuz1pas1fbZ+F7venOBrjez3sKlb3Pyl2aH
   786  mt2wjaTUi5v10VrHgYtOEdqyhQeUSYydWXIBKNMag0NLLrfFUKZK+57wrHWFdFjn
   787  VgpsdkLSNTOZpC8gA5OaJ+36IcOPfGqyyP9wuuRoaYnVT1KEzqLa9FEFAoGBAPaq
   788  pglwhil2rxjJE4zq0afQLNpAfi7Xqcrepij+xvJIcIj7nawxXuPxqRFxONE/h3yX
   789  zkybO8wLdbHX9Iw/wc1j50Uf1Z5gHdLf7/hQJoWKpz1RnkWRy6CYON8v1tpVp0tb
   790  OAajR/kZnzebq2mfa7pyy5zDCX++2kp/dcFwHf31AoGAE8oupBVTZLWj7TBFuP8q
   791  LkS40U92Sv9v09iDCQVmylmFvUxcXPM2m+7f/qMTNgWrucxzC7kB/6MMWVszHbrz
   792  vrnCTibnemgx9sZTjKOSxHFOIEw7i85fSa3Cu0qOIDPSnmlwfZpfcMKQrhjLAYhf
   793  uhooFiLX1X78iZ2OXup4PHUCgYEAsmBrm83sp1V1gAYBBlnVbXakyNv0pCk/Vz61
   794  iFXeRt1NzDGxLxGw3kQnED8BaIh5kQcyn8Fud7sdzJMv/LAqlT4Ww60mzNYTGyjo
   795  H3jOsqm3ESfRvduWFreeAQBWbiOczGjV1i8D4EbAFfWT+tjXjchwKBf+6Yt5zn/o
   796  Bw/uEHUCgYAFs+JPOR25oRyBs7ujrMo/OY1z/eXTVVgZxY+tYGe1FJqDeFyR7ytK
   797  +JBB1MuDwQKGm2wSIXdCzTNoIx2B9zTseiPTwT8G7vqNFhXoIaTBp4P2xIQb45mJ
   798  7GkTsMBHwpSMOXgX9Weq3v5xOJ2WxVtjENmd6qzxcYCO5lP15O17hA==
   799  -----END RSA PRIVATE KEY-----`
   800  
   801  func assertContents(t *testing.T, path string, contents string) {
   802  	data, err := ioutil.ReadFile(path)
   803  	if err != nil {
   804  		t.Fatalf("err: %s", err)
   805  	}
   806  
   807  	if !reflect.DeepEqual(data, []byte(contents)) {
   808  		t.Fatalf("bad. expected:\n\n%s\n\nGot:\n\n%s", contents, string(data))
   809  	}
   810  }
   811  
   812  func tempDir(t *testing.T) string {
   813  	dir, err := ioutil.TempDir("", "tf")
   814  	if err != nil {
   815  		t.Fatalf("err: %s", err)
   816  	}
   817  	if err := os.RemoveAll(dir); err != nil {
   818  		t.Fatalf("err: %s", err)
   819  	}
   820  
   821  	return dir
   822  }
   823  
   824  func tempTestFile(t *testing.T) string {
   825  	dir := tempDir(t)
   826  	return filepath.Join(dir, "foo")
   827  }