1package fakegit // import "github.com/docker/docker/internal/test/fakegit"
2
3import (
4	"fmt"
5	"io/ioutil"
6	"net/http"
7	"net/http/httptest"
8	"os"
9	"os/exec"
10	"path/filepath"
11
12	"github.com/docker/docker/internal/test"
13	"github.com/docker/docker/internal/test/fakecontext"
14	"github.com/docker/docker/internal/test/fakestorage"
15	"gotest.tools/assert"
16)
17
18type testingT interface {
19	assert.TestingT
20	logT
21	skipT
22	Fatal(args ...interface{})
23	Fatalf(string, ...interface{})
24}
25
26type logT interface {
27	Logf(string, ...interface{})
28}
29
30type skipT interface {
31	Skip(...interface{})
32}
33
34type gitServer interface {
35	URL() string
36	Close() error
37}
38
39type localGitServer struct {
40	*httptest.Server
41}
42
43func (r *localGitServer) Close() error {
44	r.Server.Close()
45	return nil
46}
47
48func (r *localGitServer) URL() string {
49	return r.Server.URL
50}
51
52// FakeGit is a fake git server
53type FakeGit struct {
54	root    string
55	server  gitServer
56	RepoURL string
57}
58
59// Close closes the server, implements Closer interface
60func (g *FakeGit) Close() {
61	g.server.Close()
62	os.RemoveAll(g.root)
63}
64
65// New create a fake git server that can be used for git related tests
66func New(c testingT, name string, files map[string]string, enforceLocalServer bool) *FakeGit {
67	if ht, ok := c.(test.HelperT); ok {
68		ht.Helper()
69	}
70	ctx := fakecontext.New(c, "", fakecontext.WithFiles(files))
71	defer ctx.Close()
72	curdir, err := os.Getwd()
73	if err != nil {
74		c.Fatal(err)
75	}
76	defer os.Chdir(curdir)
77
78	if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil {
79		c.Fatalf("error trying to init repo: %s (%s)", err, output)
80	}
81	err = os.Chdir(ctx.Dir)
82	if err != nil {
83		c.Fatal(err)
84	}
85	if output, err := exec.Command("git", "config", "user.name", "Fake User").CombinedOutput(); err != nil {
86		c.Fatalf("error trying to set 'user.name': %s (%s)", err, output)
87	}
88	if output, err := exec.Command("git", "config", "user.email", "fake.user@example.com").CombinedOutput(); err != nil {
89		c.Fatalf("error trying to set 'user.email': %s (%s)", err, output)
90	}
91	if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil {
92		c.Fatalf("error trying to add files to repo: %s (%s)", err, output)
93	}
94	if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil {
95		c.Fatalf("error trying to commit to repo: %s (%s)", err, output)
96	}
97
98	root, err := ioutil.TempDir("", "docker-test-git-repo")
99	if err != nil {
100		c.Fatal(err)
101	}
102	repoPath := filepath.Join(root, name+".git")
103	if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil {
104		os.RemoveAll(root)
105		c.Fatalf("error trying to clone --bare: %s (%s)", err, output)
106	}
107	err = os.Chdir(repoPath)
108	if err != nil {
109		os.RemoveAll(root)
110		c.Fatal(err)
111	}
112	if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil {
113		os.RemoveAll(root)
114		c.Fatalf("error trying to git update-server-info: %s (%s)", err, output)
115	}
116	err = os.Chdir(curdir)
117	if err != nil {
118		os.RemoveAll(root)
119		c.Fatal(err)
120	}
121
122	var server gitServer
123	if !enforceLocalServer {
124		// use fakeStorage server, which might be local or remote (at test daemon)
125		server = fakestorage.New(c, root)
126	} else {
127		// always start a local http server on CLI test machine
128		httpServer := httptest.NewServer(http.FileServer(http.Dir(root)))
129		server = &localGitServer{httpServer}
130	}
131	return &FakeGit{
132		root:    root,
133		server:  server,
134		RepoURL: fmt.Sprintf("%s/%s.git", server.URL(), name),
135	}
136}
137