Skip to content

Commit

Permalink
Upgrade docker-compose framework (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrproliu committed Jun 24, 2021
1 parent 64b81f3 commit c5d37eb
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 114 deletions.
5 changes: 3 additions & 2 deletions dist/LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,13 @@ The text of each license is the standard Apache 2.0 license.
cli-runtime v0.19.0: https://github.com/kubernetes/cli-runtime Apache 2.0
client-go v0.19.0 https://github.com/kubernetes/client-go Apache 2.0
kubectl v0.19.0: https://github.com/kubernetes/kubectl Apache 2.0
docker v20.10.3: https://github.com/docker/docker Apache 2.0
docker v20.10.7: https://github.com/docker/docker Apache 2.0
utils v0.0.0-20201110183641-67b214c5f920: https://github.com/kubernetes/utils Apache 2.0
containerd v1.4.3: https://github.com/containerd/containerd Apache 2.0
go-connections v0.4.0: https://github.com/docker/go-connections Apache 2.0
go-units v0.4.0: https://github.com/docker/go-units Apache 2.0
image-spec v1.0.1: https://github.com/opencontainers/image-spec Apache 2.0
go-connections v0.4.0: https://github.com/opencontainers/image-spec Apache 2.0

========================================================================
MIT licenses
Expand All @@ -242,7 +243,7 @@ The text of each license is also included at licenses/LICENSE-[project].txt.
logrus 1.7.0: https://github.com/sirupsen/logrus MIT
go-winio v0.4.16: https://github.com/Microsoft/go-winio MIT
aec v1.0.0: https://github.com/morikuni/aec MIT
testcontainers-go v0.11.0: https://github.com/testcontainers/testcontainers-go MIT
testcontainers-go v0.11.1: https://github.com/testcontainers/testcontainers-go MIT

========================================================================
BSD licenses
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ module github.com/apache/skywalking-infra-e2e
go 1.13

require (
github.com/docker/docker v20.10.6+incompatible
github.com/docker/docker v20.10.7+incompatible
github.com/docker/go-connections v0.4.0
github.com/google/go-cmp v0.5.4
github.com/gorilla/mux v1.8.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v1.1.1
github.com/testcontainers/testcontainers-go v0.11.0
github.com/testcontainers/testcontainers-go v0.11.1
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.20.7
k8s.io/apimachinery v0.20.7
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TT
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
Expand Down Expand Up @@ -642,8 +642,8 @@ github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/testcontainers/testcontainers-go v0.11.0 h1:HO5YOx2DYBHqcg4MzVWPj3FuHAv7USWVu94vCSsgiaM=
github.com/testcontainers/testcontainers-go v0.11.0/go.mod h1:HztBCODzuA+YpMXGK8amjO8j50jz2gcT0BOzSKUiYIs=
github.com/testcontainers/testcontainers-go v0.11.1 h1:FiYsB83LSGbiawoV8TpAZGfcCUbtaeeg1SXqEKUxh08=
github.com/testcontainers/testcontainers-go v0.11.1/go.mod h1:/V0UVq+1e7NWYoqTPog179clf0Qp9TOyp4EcXaEFQz8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
Expand Down
152 changes: 46 additions & 106 deletions internal/components/setup/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ package setup
import (
"context"
"fmt"
"net"
"os"
"regexp"
"syscall"
"strconv"
"strings"
"time"

"github.com/docker/go-connections/nat"
"github.com/testcontainers/testcontainers-go/wait"

"github.com/apache/skywalking-infra-e2e/internal/config"
"github.com/apache/skywalking-infra-e2e/internal/constant"
"github.com/apache/skywalking-infra-e2e/internal/logger"
Expand Down Expand Up @@ -57,30 +60,46 @@ func ComposeSetup(e2eConfig *config.E2EConfig) error {
}
identifier := GetIdentity()
compose := testcontainers.NewLocalDockerCompose(composeFilePaths, identifier)
execError := compose.WithCommand([]string{"up", "-d"}).Invoke()
if execError.Error != nil {
return execError.Error
}

// record time now
timeNow := time.Now()
// bind wait port
timeout := e2eConfig.Setup.Timeout
var waitTimeout time.Duration
if timeout <= 0 {
waitTimeout = constant.DefaultWaitTimeout
} else {
waitTimeout = time.Duration(timeout) * time.Second
}
logger.Log.Debugf("wait timeout is %d seconds", int(waitTimeout.Seconds()))

// find exported port and build env
serviceWithPorts := make(map[string][]int)
for service, content := range compose.Services {
serviceConfig := content.(map[interface{}]interface{})
ports := serviceConfig["ports"]
if ports == nil {
continue
}
serviceWithPorts[service] = []int{}

portList := ports.([]interface{})
for inx := range portList {
exportPort, err := getExpectPort(portList[inx])
if err != nil {
return err
}
serviceWithPorts[service] = append(serviceWithPorts[service], exportPort)

compose.WithExposedService(
service,
exportPort,
wait.NewHostPortStrategy(nat.Port(fmt.Sprintf("%d/tcp", exportPort))).WithStartupTimeout(waitTimeout))
}
}

execError := compose.WithCommand([]string{"up", "-d"}).Invoke()
if execError.Error != nil {
return execError.Error
}

// find exported port and build env
for service, portList := range serviceWithPorts {
container, err := findContainer(cli, fmt.Sprintf("%s_%s", identifier, getInstanceName(service)))
if err != nil {
return err
Expand All @@ -89,20 +108,10 @@ func ComposeSetup(e2eConfig *config.E2EConfig) error {

for inx := range portList {
for _, containerPort := range containerPorts {
if int(containerPort.PrivatePort) != portList[inx].(int) {
if int(containerPort.PrivatePort) != portList[inx] {
continue
}

// calculate max wait time
waitTimeout = NewTimeout(timeNow, waitTimeout)
timeNow = time.Now()

// wait port and export
err := waitTCPPortStarted(context.Background(), cli, container, int(containerPort.PublicPort), int(containerPort.PrivatePort), waitTimeout)
if err != nil {
return fmt.Errorf("could wait port exported: %s:%d, %v", service, portList[inx], err)
}

// expose env config to env
// format: <service_name>_<port>
envKey := fmt.Sprintf("%s_%d", service, containerPort.PrivatePort)
Expand All @@ -112,13 +121,28 @@ func ComposeSetup(e2eConfig *config.E2EConfig) error {
return fmt.Errorf("could not set env for %s:%d, %v", service, portList[inx], err)
}
logger.Log.Infof("expose env : %s : %s", envKey, envValue)
break
}
}
}

return nil
}

func getExpectPort(portConfig interface{}) (int, error) {
switch conf := portConfig.(type) {
case int:
return conf, nil
case string:
portInfo := strings.Split(conf, ":")
if len(portInfo) > 1 {
return strconv.Atoi(portInfo[1])
}
return strconv.Atoi(portInfo[0])
}
return 0, fmt.Errorf("unknown port information: %v", portConfig)
}

func findContainer(c *client.Client, instanceName string) (*types.Container, error) {
f := filters.NewArgs(filters.Arg("name", instanceName))
containerListOptions := types.ContainerListOptions{Filters: f}
Expand All @@ -133,90 +157,6 @@ func findContainer(c *client.Client, instanceName string) (*types.Container, err
return &containers[0], nil
}

func waitTCPPortStarted(ctx context.Context, c *client.Client, container *types.Container, publicPort, interPort int, timeout time.Duration) error {
// limit context to startupTimeout
ctx, cancelContext := context.WithTimeout(ctx, timeout)
defer cancelContext()

var waitInterval = 100 * time.Millisecond

// external check
dialer := net.Dialer{}
address := net.JoinHostPort("127.0.0.1", fmt.Sprintf("%d", publicPort))
for {
conn, err := dialer.DialContext(ctx, "tcp", address)
if err != nil {
if v, ok := err.(*net.OpError); ok {
if v2, ok := (v.Err).(*os.SyscallError); ok {
if isConnRefusedErr(v2.Err) {
time.Sleep(waitInterval)
continue
}
}
}
return err
}
conn.Close()
break
}

// internal check
command := buildInternalCheckCommand(interPort)
for {
if ctx.Err() != nil {
return ctx.Err()
}
response, err := c.ContainerExecCreate(ctx, container.ID, types.ExecConfig{
Cmd: []string{"/bin/sh", "-c", command},
AttachStderr: true,
AttachStdout: true,
})
if err != nil {
return err
}

err = c.ContainerExecStart(ctx, response.ID, types.ExecStartCheck{
Detach: false,
})
if err != nil {
return err
}

var exitCode int
for {
execResp, err := c.ContainerExecInspect(ctx, response.ID)
if err != nil {
return err
}

if !execResp.Running {
exitCode = execResp.ExitCode
break
}

time.Sleep(waitInterval)
}

if exitCode == 0 {
return nil
}
}
}

func buildInternalCheckCommand(internalPort int) string {
command := `(
nc -vz -w 1 localhost %d ||
cat /proc/net/tcp | awk '{print $2}' | grep -i :%d ||
</dev/tcp/localhost/%d
)
`
return "true && " + fmt.Sprintf(command, internalPort, internalPort, internalPort)
}

func isConnRefusedErr(err error) bool {
return err == syscall.ECONNREFUSED
}

func getInstanceName(serviceName string) string {
match, err := regexp.MatchString(".*_[0-9]+", serviceName)
if err != nil {
Expand Down

0 comments on commit c5d37eb

Please sign in to comment.