Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions dev-tools/jenkins_ci.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ $env:RACE_DETECTOR = "true"
# Install mage from vendor.
exec { go install github.com/elastic/beats/vendor/github.com/magefile/mage }

echo "Fetching testing dependencies"
# TODO (elastic/beats#5050): Use a vendored copy of this.
exec { go get github.com/docker/libcompose }

if (Test-Path "$env:beat") {
cd "$env:beat"
} else {
Expand Down
3 changes: 0 additions & 3 deletions libbeat/scripts/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ COVERAGE_DIR?=${BUILD_DIR}/coverage
COVERAGE_TOOL?=${BEAT_GOPATH}/bin/gotestcover
COVERAGE_TOOL_REPO?=github.com/elastic/beats/vendor/github.com/pierrre/gotestcover
TESTIFY_TOOL_REPO?=github.com/elastic/beats/vendor/github.com/stretchr/testify/assert
LIBCOMPOSE_TOOL_REPO?=github.com/docker/libcompose
NOW=$(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
GOBUILD_FLAGS?=-i -ldflags "-X github.com/elastic/beats/libbeat/version.buildTime=$(NOW) -X github.com/elastic/beats/libbeat/version.commit=$(COMMIT_ID)"
GOIMPORTS=goimports
Expand Down Expand Up @@ -172,8 +171,6 @@ prepare-tests:
go get ${COVERAGE_TOOL_REPO}
# testify is needed for unit and integration tests
go get ${TESTIFY_TOOL_REPO}
# libcompose is needed for integration tests
go get ${LIBCOMPOSE_TOOL_REPO}

.PHONY: unit-tests
unit-tests: ## @testing Runs the unit tests with coverage. Race is not enabled for unit tests because tests run much slower.
Expand Down
211 changes: 8 additions & 203 deletions libbeat/tests/compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,13 @@
package compose

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"

"strconv"

"github.com/docker/libcompose/docker"
"github.com/docker/libcompose/docker/ctx"
"github.com/docker/libcompose/project"
"github.com/docker/libcompose/project/options"
"testing"
)

// docker-compose project wrapper
type composeProject struct {
p project.APIProject
file string
}

type serviceInfo struct {
Name string
Running bool
Healthy bool
// Has been up for too long?:
Old bool
}

// Regexp matching state to flag container as old
var oldRegexp = regexp.MustCompile("minute")

// EnsureUp starts all the requested services (must be defined in docker-compose.yml)
// with a default timeout of 300 seconds
func EnsureUp(t *testing.T, services ...string) {
Expand Down Expand Up @@ -93,169 +65,7 @@ func EnsureUpWithTimeout(t *testing.T, timeout int, services ...string) {
}
}

// Start the container, unless it's running already
func (c *composeProject) Start(service string) error {
servicesStatus, err := c.getServices(service)
if err != nil {
return err
}

if servicesStatus[service] != nil {
if servicesStatus[service].Running {
// Someone is running it
return nil
}
}

c.Lock()
defer c.Unlock()

return c.p.Up(context.Background(), options.Up{
Create: options.Create{
ForceBuild: true,
},
}, service)
}

// Ensure all wanted services are healthy. Wait loop (60s timeout)
func (c *composeProject) Wait(seconds int, services ...string) error {
healthy := false
for !healthy && seconds > 0 {
healthy = true

servicesStatus, err := c.getServices(services...)
if err != nil {
return err
}

for _, s := range servicesStatus {
if !s.Healthy {
healthy = false
break
}
}

time.Sleep(1 * time.Second)
seconds--
}

if !healthy {
return errors.New("Timeout waiting for services to be healthy")
}
return nil
}

func (c *composeProject) Kill(service string) error {
c.Lock()
defer c.Unlock()

return c.p.Kill(context.Background(), "KILL", service)
}

func (c *composeProject) KillOld(except []string) error {
// Do not kill ourselves ;)
except = append(except, "beat")

// These services take very long to start up and stop. If they are stopped
// it can happen that an other package tries to start them at the same time
// which leads to a conflict. We need a better solution long term but that should
// solve the problem for now.
except = append(except, "elasticsearch", "kibana", "logstash", "kubernetes")

servicesStatus, err := c.getServices()
if err != nil {
return err
}

for _, s := range servicesStatus {
// Ignore the ones we want
if contains(except, s.Name) {
continue
}

if s.Old {
err = c.Kill(s.Name)
if err != nil {
return err
}
}
}

return nil
}

// Lock acquires the lock (300s) timeout
// Normally it should only be seconds that the lock is used, but in some cases it can take longer.
func (c *composeProject) Lock() {
seconds := 300
for seconds > 0 {
file, err := os.OpenFile(c.file+".lock", os.O_CREATE|os.O_EXCL, 0500)
file.Close()
if err != nil {
fmt.Println("docker-compose.yml is locked, waiting")
time.Sleep(1 * time.Second)
seconds--
continue
}
return
}

// This should rarely happen as we lock for start only, less than a second
panic(errors.New("Timeout waiting for lock, please remove docker-compose.yml.lock"))
}

func (c *composeProject) Unlock() {
os.Remove(c.file + ".lock")
}

func (c *composeProject) getServices(filter ...string) (map[string]*serviceInfo, error) {
c.Lock()
defer c.Unlock()

result := make(map[string]*serviceInfo)
services, err := c.p.Ps(context.Background(), filter...)
if err != nil {
return nil, err
}

containers, err := c.p.Containers(context.Background(), project.Filter{State: project.Running}, filter...)
if err != nil {
return nil, err
}

for _, c := range services {
name := strings.Split(c["Name"], "_")[1]
// In case of several (stopped) containers, always prefer info about running ones
if result[name] != nil {
if result[name].Running {
continue
}
}

service := &serviceInfo{
Name: name,
}
// fill details:
service.Healthy = strings.Contains(c["State"], "(healthy)")
service.Running = contains(containers, c["Id"])
if service.Healthy {
service.Old = oldRegexp.MatchString(c["State"])
}
result[name] = service
}
return result, nil
}

func contains(list []string, item string) bool {
for _, i := range list {
if item == i {
return true
}
}
return false
}

func getComposeProject() (*composeProject, error) {
func getComposeProject() (*Project, error) {
// find docker-compose
path, err := os.Getwd()
if err != nil {
Expand All @@ -266,22 +76,17 @@ func getComposeProject() (*composeProject, error) {
return nil, errors.New("docker-compose.yml not found")
}

if _, err = os.Stat(path + "/docker-compose.yml"); err != nil {
if _, err = os.Stat(filepath.Join(path, "docker-compose.yml")); err != nil {
path = filepath.Dir(path)
} else {
break
}
}

project, err := docker.NewProject(&ctx.Context{
Context: project.Context{
ProjectName: os.Getenv("DOCKER_COMPOSE_PROJECT_NAME"),
ComposeFiles: []string{path + "/docker-compose.yml"},
return NewProject(
os.Getenv("DOCKER_COMPOSE_PROJECT_NAME"),
[]string{
filepath.Join(path, "docker-compose.yml"),
},
}, nil)
if err != nil {
return nil, err
}

return &composeProject{project, path + "/docker-compose.yml"}, nil
)
}
Loading