|
|
|
package lich
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/md5"
|
|
|
|
"encoding/json"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/bilibili/kratos/pkg/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
retry int
|
|
|
|
noDown bool
|
|
|
|
yamlPath string
|
|
|
|
pathHash string
|
|
|
|
services map[string]*Container
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
flag.StringVar(&yamlPath, "f", "docker-compose.yaml", "composer yaml path.")
|
|
|
|
flag.IntVar(&retry, "retry", 5, "number of retries on network failure.")
|
|
|
|
flag.BoolVar(&noDown, "nodown", false, "containers are not recycled.")
|
|
|
|
}
|
|
|
|
|
|
|
|
func runCompose(args ...string) (output []byte, err error) {
|
|
|
|
if _, err = os.Stat(yamlPath); os.IsNotExist(err) {
|
|
|
|
log.Error("os.Stat(%s) composer yaml is not exist!", yamlPath)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if yamlPath, err = filepath.Abs(yamlPath); err != nil {
|
|
|
|
log.Error("filepath.Abs(%s) error(%v)", yamlPath, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
pathHash = fmt.Sprintf("%x", md5.Sum([]byte(yamlPath)))[:9]
|
|
|
|
args = append([]string{"-f", yamlPath, "-p", pathHash}, args...)
|
|
|
|
if output, err = exec.Command("docker-compose", args...).CombinedOutput(); err != nil {
|
|
|
|
log.Error("exec.Command(docker-compose) args(%v) stdout(%s) error(%v)", args, string(output), err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup setup UT related environment dependence for everything.
|
|
|
|
func Setup() (err error) {
|
|
|
|
if _, err = runCompose("up", "-d"); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
Teardown()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if _, err = getServices(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, err = checkServices()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Teardown unsetup all environment dependence.
|
|
|
|
func Teardown() (err error) {
|
|
|
|
if !noDown {
|
|
|
|
_, err = runCompose("down")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getServices() (output []byte, err error) {
|
|
|
|
if output, err = runCompose("config", "--services"); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var eol = []byte("\n")
|
|
|
|
if output = bytes.TrimSpace(output); runtime.GOOS == "windows" {
|
|
|
|
eol = []byte("\r\n")
|
|
|
|
}
|
|
|
|
services = make(map[string]*Container)
|
|
|
|
for _, svr := range bytes.Split(output, eol) {
|
|
|
|
if output, err = runCompose("ps", "-q", string(svr)); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var (
|
|
|
|
id = string(bytes.TrimSpace(output))
|
|
|
|
args = []string{"inspect", id, "--format", "'{{json .}}'"}
|
|
|
|
)
|
|
|
|
if output, err = exec.Command("docker", args...).CombinedOutput(); err != nil {
|
|
|
|
log.Error("exec.Command(docker) args(%v) stdout(%s) error(%v)", args, string(output), err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if output = bytes.TrimSpace(output); bytes.Equal(output, []byte("")) {
|
|
|
|
err = fmt.Errorf("service: %s | container: %s fails to launch", svr, id)
|
|
|
|
log.Error("exec.Command(docker) args(%v) error(%v)", args, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var c = &Container{}
|
|
|
|
if err = json.Unmarshal(bytes.Trim(output, "'"), c); err != nil {
|
|
|
|
log.Error("json.Unmarshal(%s) error(%v)", string(output), err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
services[string(svr)] = c
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkServices() (output []byte, err error) {
|
|
|
|
defer func() {
|
|
|
|
if err != nil && retry > 0 {
|
|
|
|
retry--
|
|
|
|
getServices()
|
|
|
|
time.Sleep(time.Second * 5)
|
|
|
|
output, err = checkServices()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
for svr, c := range services {
|
|
|
|
if err = c.Healthcheck(); err != nil {
|
|
|
|
log.Error("healthcheck(%s) error(%v) retrying %d times...", svr, err, retry)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// TODO About container check and more...
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|