-
Notifications
You must be signed in to change notification settings - Fork 1
/
deploy.go
126 lines (117 loc) · 3.97 KB
/
deploy.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
package deploy
import (
"context"
"fmt"
"io/fs"
"os"
"path/filepath"
"reflect"
"strings"
"go.uber.org/zap"
"k8s.io/utils/exec"
"github.com/ShotaKitazawa/isucontinuous/pkg/config"
myerrros "github.com/ShotaKitazawa/isucontinuous/pkg/errors"
"github.com/ShotaKitazawa/isucontinuous/pkg/shell"
"github.com/ShotaKitazawa/isucontinuous/pkg/template"
)
type Deployer struct {
log *zap.Logger
shell shell.Iface
template *template.Templator
localRepoPath string
}
type NewDeployersFunc func(*zap.Logger, *template.Templator, string, []config.Host) (map[string]*Deployer, error)
func NewDeployers(
logger *zap.Logger, templator *template.Templator, localRepoPath string,
hosts []config.Host,
) (map[string]*Deployer, error) {
deployers := make(map[string]*Deployer)
var err error
for _, host := range hosts {
var s shell.Iface
if host.IsLocal() {
s = shell.NewLocalClient(exec.New())
} else {
s, err = shell.NewSshClient(host.Host, host.Port, host.User, host.Password, host.Key)
if err != nil {
return nil, err
}
}
deployers[host.Host] = new(logger, s, templator, localRepoPath)
}
return deployers, nil
}
func new(logger *zap.Logger, s shell.Iface, templator *template.Templator, localRepoPath string) *Deployer {
return &Deployer{logger, s, templator, localRepoPath}
}
func (d Deployer) Deploy(ctx context.Context, host string, targets []config.DeployTarget) error {
realHost := d.shell.Host()
for _, target := range targets {
src := filepath.Join(d.localRepoPath, host, target.Src)
if err := filepath.WalkDir(src, func(path string, info fs.DirEntry, err error) error {
if info != nil && !reflect.ValueOf(info).IsNil() && !info.IsDir() {
dst := filepath.Join(target.Target, strings.TrimPrefix(path, src))
dirname := filepath.Dir(dst)
if _, _, err := d.shell.Execf(ctx, "", `test -d "%s"`, dirname); err != nil {
d.log.Debug(fmt.Sprintf("%s does not exist, mkdir", dirname), zap.String("host", realHost))
if _, _, err := d.shell.Execf(ctx, "", `mkdir -p "%s"`, dirname); err != nil {
return err
}
}
finfo, err := info.Info()
if err != nil {
return err
}
if finfo.Mode()&os.ModeSymlink == os.ModeSymlink { // copy source is symlink
origin, err := filepath.EvalSymlinks(path)
if err != nil {
return err
}
newHostAndSrc := strings.TrimPrefix(origin, d.localRepoPath+"/")
if newHostAndSrc == origin {
return fmt.Errorf("%s: cannot seek symlink bacause source of symlic isn't in localRepoPath", origin)
}
newHostAndSrcSlice := strings.Split(newHostAndSrc, "/")
newHost := newHostAndSrcSlice[0]
newSrc := strings.Join(newHostAndSrcSlice[1:], "/")
if len(newHostAndSrcSlice) < 2 {
return fmt.Errorf("%s: cannot seek symlink bacause this directory is hostdir", origin)
}
d.log.Debug(fmt.Sprintf("%s is symlink, seek to %s", path, origin), zap.String("host", realHost))
if err := d.Deploy(ctx, newHost, []config.DeployTarget{{Src: newSrc, Target: dst}}); err != nil {
return err
}
} else { // copy source is file
d.log.Debug(fmt.Sprintf("deploy %s to %s", path, dst), zap.String("host", realHost))
return d.shell.Deploy(ctx, path, dst)
}
}
return nil
}); err != nil {
return err
}
if target.Compile != "" {
d.log.Debug(fmt.Sprintf(`exec compile: "%s"`, target.Compile), zap.String("host", host))
if _, stderr, err := d.shell.Exec(ctx, target.Target, target.Compile); err != nil {
return myerrros.NewErrorCommandExecutionFailed(stderr)
}
}
}
return nil
}
func (d Deployer) RunCommand(ctx context.Context, command string) error {
if command == "" {
return nil
}
var err error
command, err = d.template.Exec(command)
if err != nil {
return err
}
d.log.Debug(fmt.Sprintf(`exec command: "%s"`, command), zap.String("host", d.shell.Host()))
_, stderr, err := d.shell.Exec(ctx, "", command)
if err != nil {
return myerrros.NewErrorCommandExecutionFailed(stderr)
}
return nil
}