forked from adampointer/cali
-
Notifications
You must be signed in to change notification settings - Fork 7
/
git.go
165 lines (138 loc) · 4.09 KB
/
git.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package cali
import (
"crypto/md5"
"fmt"
"os"
"regexp"
"strings"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
log "github.com/sirupsen/logrus"
)
// GitCheckoutConfig is input for Git.Checkout
type GitCheckoutConfig struct {
Repo, Branch, RelPath, Image string
}
const gitImage = "indiehosters/git:latest"
// Git returns a new instance
func (c *DockerClient) Git() *Git {
return &Git{c: c, Image: gitImage}
}
// Git is used to interact with containerised git
type Git struct {
c *DockerClient
Image string
}
// Checkout will create and start a container, checkout repo and leave container stopped
// so volume can be imported
func (g *Git) Checkout(cfg *GitCheckoutConfig) (string, error) {
containerName, err := cfg.GetContainerName()
if err != nil {
return "", fmt.Errorf("Failed to create data container for %s: %s", cfg.Repo, err)
}
exists, err := g.c.ContainerExists(containerName)
if err != nil {
return "", err
}
if exists {
log.Infof("Existing data container found: %s", containerName)
if _, err := g.Pull(containerName); err != nil {
log.Warnf("Git pull error: %s", err)
return containerName, err
}
return containerName, nil
}
log.WithFields(log.Fields{
"git_url": cfg.Repo,
"image": g.Image,
}).Info("Creating data containers")
co := container.Config{
Cmd: []string{"clone", cfg.Repo, "-b", cfg.Branch, "--depth", "1", "."},
Image: gitImage,
Tty: true,
AttachStdout: true,
AttachStderr: true,
WorkingDir: "/tmp/workspace",
Entrypoint: []string{"git"},
}
hc := container.HostConfig{
Binds: []string{
"/tmp/workspace",
fmt.Sprintf("%s/.ssh:/root/.ssh", os.Getenv("HOME")),
},
}
nc := network.NetworkingConfig{}
g.c.SetConf(&co)
g.c.SetHostConf(&hc)
g.c.SetNetConf(&nc)
id, err := g.c.StartContainer(false, containerName)
if err != nil {
return "", fmt.Errorf("Failed to create data container for %s: %s", cfg.Repo, err)
}
return id, nil
}
// Pull will perform a git pull in a git repo container
func (g *Git) Pull(name string) (string, error) {
co := container.Config{
Cmd: []string{"pull"},
Image: g.Image,
Tty: true,
AttachStdout: true,
AttachStderr: true,
WorkingDir: "/tmp/workspace",
Entrypoint: []string{"git"},
}
hc := container.HostConfig{
VolumesFrom: []string{name},
Binds: []string{
fmt.Sprintf("%s/.ssh:/root/.ssh", os.Getenv("HOME")),
},
}
nc := network.NetworkingConfig{}
g.c.SetConf(&co)
g.c.SetHostConf(&hc)
g.c.SetNetConf(&nc)
return g.c.StartContainer(true, "")
}
// GetContainerName returns a container name for provided Git config
func (cfg GitCheckoutConfig) GetContainerName() (string, error) {
repoName, err := repoNameFromURL(cfg.Repo)
if err != nil {
return "", fmt.Errorf("Failed to get container name for %s: %s", cfg.Repo, err)
}
var containerName string
if cfg.RelPath == "." || cfg.RelPath == "" {
containerName = fmt.Sprintf("data_%s_%s_%x",
repoName,
strings.Replace(cfg.Branch, "/", "-", -1),
md5.Sum([]byte(cfg.Repo)),
)
} else {
containerName = fmt.Sprintf("data_%s_%s_%s_%x",
repoName,
strings.Replace(cfg.RelPath, "/", "-", -1),
strings.Replace(cfg.Branch, "/", "-", -1),
md5.Sum([]byte(cfg.Repo)),
)
}
return containerName, nil
}
// repoNameFromUrl takes a git repo URL and returns a string
// representing the repository name
func repoNameFromURL(url string) (string, error) {
// Strip out the https:// or git:// protocol
protocolRe := regexp.MustCompile("^.*//")
url = protocolRe.ReplaceAllString(url, "")
// Remove trailing .git
dotGitRe := regexp.MustCompile(".git$")
url = dotGitRe.ReplaceAllString(url, "")
// Remove user@
userAtRe := regexp.MustCompile(".*@")
url = userAtRe.ReplaceAllString(url, "")
// Actual regex for container names: [a-zA-Z0-9][a-zA-Z0-9_.-]
// https://github.com/moby/moby/blob/master/daemon/names/names.go
// but to simplify, as we're doing an inverse match, just use [a-zA-Z0-9]
nonContainerRe := regexp.MustCompile("[^a-zA-Z0-9]")
repoName := nonContainerRe.ReplaceAllString(url, "-")
return repoName, nil
}