blob: 07ec57cb5deb962ee84616591e01cd1e54b7ec41 [file] [log] [blame]
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
package setup
import (
"context"
"fmt"
"os"
"regexp"
"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"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/testcontainers/testcontainers-go"
)
// ComposeSetup sets up environment according to e2e.yaml.
func ComposeSetup(e2eConfig *config.E2EConfig) error {
composeConfigPath := e2eConfig.Setup.GetFile()
if composeConfigPath == "" {
return fmt.Errorf("no compose config file was provided")
}
// build docker client
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return err
}
// setup docker compose
composeFilePaths := []string{
composeConfigPath,
}
identifier := GetIdentity()
compose := testcontainers.NewLocalDockerCompose(composeFilePaths, identifier)
// bind wait port
timeout := e2eConfig.Setup.Timeout
var waitTimeout time.Duration
if timeout <= 0 {
waitTimeout = constant.DefaultWaitTimeout
} else {
waitTimeout = time.Duration(timeout) * time.Second
}
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
}
containerPorts := container.Ports
for inx := range portList {
for _, containerPort := range containerPorts {
if int(containerPort.PrivatePort) != portList[inx] {
continue
}
// expose env config to env
// format: <service_name>_<port>
envKey := fmt.Sprintf("%s_%d", service, containerPort.PrivatePort)
envValue := fmt.Sprintf("%d", containerPort.PublicPort)
err = os.Setenv(envKey, envValue)
if err != nil {
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}
containers, err := c.ContainerList(context.Background(), containerListOptions)
if err != nil {
return nil, err
}
if len(containers) == 0 {
return nil, fmt.Errorf("could not found container: %s", instanceName)
}
return &containers[0], nil
}
func getInstanceName(serviceName string) string {
match, err := regexp.MatchString(".*_[0-9]+", serviceName)
if err != nil {
return serviceName
}
if !match {
return serviceName + "_1"
}
return serviceName
}