Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make docker compose startup time configurable #1166

Merged
merged 17 commits into from
Dec 10, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 10 additions & 3 deletions cmd/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,15 @@ Docker
-v /sys/kernel/debug:/sys/kernel/debug -v /sys/fs/bpf:/sys/fs/bpf -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/keploy/keploy'

Record:
keploy record -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage"
keploy record -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage" --buildDelay 35s
or
keploy record -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage" --buildDelay 1m

Test:
keploy test -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage" --delay 1
keploy test -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage" --delay 1 --buildDelay 35s
or
keploy test -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage" --delay 1 --buildDelay 1m

`

var exampleOneClickInstall = `
Expand Down Expand Up @@ -90,7 +95,9 @@ Docker
keploy record -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage"

Test:
keploy test -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage" --delay 1
keploy test -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage" --delay 1 --buildDelay 35s
or
keploy test -c "docker run -p 8080:8080 --name myContainerName --network myNetworkName myApplicationImage" --delay 1 --buildDelay 1m
`

type Example struct {
Expand Down
27 changes: 22 additions & 5 deletions cmd/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/spf13/cobra"
"go.keploy.io/server/pkg/models"
Expand Down Expand Up @@ -40,7 +41,7 @@ func readRecordConfig(configPath string) (*models.Record, error) {

var filters = *&models.Filters{}

func (t *Record) GetRecordConfig(path *string, proxyPort *uint32, appCmd *string, appContainer, networkName *string, Delay *uint64, passThroughPorts *[]uint, configPath string) error {
func (t *Record) GetRecordConfig(path *string, proxyPort *uint32, appCmd *string, appContainer, networkName *string, Delay *uint64, buildDelay *time.Duration, passThroughPorts *[]uint, configPath string) error {
configFilePath := filepath.Join(configPath, "keploy-config.yaml")
if isExist := utils.CheckFileExists(configFilePath); !isExist {
return errFileNotFound
Expand Down Expand Up @@ -68,6 +69,9 @@ func (t *Record) GetRecordConfig(path *string, proxyPort *uint32, appCmd *string
if *Delay == 5 {
*Delay = confRecord.Delay
}
if *buildDelay == 30*time.Second && confRecord.BuildDelay != 0 {
*buildDelay = confRecord.BuildDelay
}
if len(*passThroughPorts) == 0 {
*passThroughPorts = confRecord.PassThroughPorts
}
Expand Down Expand Up @@ -118,6 +122,12 @@ func (r *Record) GetCmd() *cobra.Command {
return err
}

buildDelay, err := cmd.Flags().GetDuration("buildDelay")
if err != nil {
r.logger.Error("Failed to get the build-delay flag", zap.Error((err)))
return err
}

ports, err := cmd.Flags().GetUintSlice("passThroughPorts")
if err != nil {
r.logger.Error("failed to read the ports of outgoing calls to be ignored")
Expand All @@ -136,7 +146,7 @@ func (r *Record) GetCmd() *cobra.Command {
return err
}

err = r.GetRecordConfig(&path, &proxyPort, &appCmd, &appContainer, &networkName, &delay, &ports, configPath)
err = r.GetRecordConfig(&path, &proxyPort, &appCmd, &appContainer, &networkName, &delay, &buildDelay, &ports, configPath)
if err != nil {
if err == errFileNotFound {
r.logger.Info("continuing without configuration file because file not found")
Expand All @@ -148,7 +158,7 @@ func (r *Record) GetCmd() *cobra.Command {
if appCmd == "" {
fmt.Println("Error: missing required -c flag or appCmd in config file")
if isDockerCmd {
fmt.Println("Example usage:\n", `keploy record -c "docker run -p 8080:808 --network myNetworkName myApplicationImageName" --delay 6\n`)
fmt.Println("Example usage:\n", `keploy record -c "docker run -p 8080:8080 --network myNetworkName myApplicationImageName" --delay 6\n`)
} else {
fmt.Println("Example usage:\n", cmd.Example)
}
Expand All @@ -172,6 +182,11 @@ func (r *Record) GetCmd() *cobra.Command {
// user provided the absolute path
}

if isDockerCmd && buildDelay <= 30*time.Second {
fmt.Printf("Warning: buildDelay is set to %d, incase your docker container takes more time to build use --buildDelay to set custom delay\n", buildDelay)
fmt.Println("Example usage:\n", `keploy record -c "docker-compose up --build" --buildDelay 35s\n`, "\nor\n", `keploy record -c "docker-compose up --build" --buildDelay 1m\n`)
}

path += "/keploy"

r.logger.Info("", zap.Any("keploy test and mock path", path))
Expand All @@ -183,13 +198,13 @@ func (r *Record) GetCmd() *cobra.Command {
}
if !hasContainerName && appContainer == "" {
fmt.Println("Error: missing required --containerName flag or containerName in config file")
fmt.Println("\nExample usage:\n", `keploy record -c "docker run -p 8080:808 --network myNetworkName myApplicationImageName" --delay 6`)
fmt.Println("\nExample usage:\n", `keploy record -c "docker run -p 8080:8080 --network myNetworkName myApplicationImageName" --delay 6`)
return errors.New("missing required --containerName flag or containerName in config file")
}
}

r.logger.Debug("the ports are", zap.Any("ports", ports))
r.recorder.CaptureTraffic(path, proxyPort, appCmd, appContainer, networkName, delay, ports, &filters)
r.recorder.CaptureTraffic(path, proxyPort, appCmd, appContainer, networkName, delay, buildDelay, ports, &filters)
return nil
},
}
Expand All @@ -206,6 +221,8 @@ func (r *Record) GetCmd() *cobra.Command {

recordCmd.Flags().Uint64P("delay", "d", 5, "User provided time to run its application")

recordCmd.Flags().DurationP("buildDelay", "", 30*time.Second, "User provided time to wait docker container build")

recordCmd.Flags().UintSlice("passThroughPorts", []uint{}, "Ports of Outgoing dependency calls to be ignored as mocks")

recordCmd.Flags().String("config-path", ".", "Path to the local directory where keploy configuration file is stored")
Expand Down
20 changes: 12 additions & 8 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package cmd

import (
"bytes"
"errors"
"log"
"net/http"
_ "net/http/pprof"
"os"
"time"
"errors"
"bytes"

"github.com/TheZeroSlave/zapsentry"
sentry "github.com/getsentry/sentry-go"
"github.com/spf13/cobra"
"go.keploy.io/server/utils"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
)

var Emoji = "\U0001F430" + " Keploy:"
Expand Down Expand Up @@ -61,13 +61,13 @@ func (c colorConsoleEncoder) Clone() zapcore.Encoder {
clone := c.Encoder.Clone()
return colorConsoleEncoder{
EncoderConfig: c.EncoderConfig,
Encoder: clone,
Encoder: clone,
}
}

func init() {
_ = zap.RegisterEncoder("colorConsole", func(config zapcore.EncoderConfig) (zapcore.Encoder, error) {
return NewColorConsole(config), nil
return NewColorConsole(config), nil
})
}

Expand All @@ -78,7 +78,7 @@ func setupLogger() *zap.Logger {

// Customize the encoder config to put the emoji at the beginning.
logCfg.EncoderConfig.EncodeTime = customTimeEncoder
logCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
logCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder

logCfg.OutputPaths = []string{
"stdout",
Expand Down Expand Up @@ -171,10 +171,14 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e

var rootExamples = `
Record:
keploy record -c "docker run -p 8080:8080 --name <containerName> --network keploy-network <applicationImage>" --containerName "<containerName>" --delay 1
keploy record -c "docker run -p 8080:8080 --name <containerName> --network keploy-network <applicationImage>" --containerName "<containerName>" --delay 1 --buildDelay 35s
or
keploy record -c "docker run -p 8080:8080 --name <containerName> --network keploy-network <applicationImage>" --containerName "<containerName>" --delay 1 --buildDelay 1m

Test:
keploy test --c "docker run -p 8080:8080 --name <containerName> --network keploy-network <applicationImage>" --delay 1
keploy test --c "docker run -p 8080:8080 --name <containerName> --network keploy-network <applicationImage>" --delay 1 --buildDelay 35s
or
keploy test --c "docker run -p 8080:8080 --name <containerName> --network keploy-network <applicationImage>" --delay 1 --buildDelay 1m

Generate-Config:
keploy generate-config -p "/path/to/localdir"
Expand Down
5 changes: 2 additions & 3 deletions cmd/serve.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package cmd

import (
"os"
"path/filepath"

"github.com/spf13/cobra"
"go.keploy.io/server/pkg/service/serve"
"go.uber.org/zap"
"os"
"path/filepath"
)

func NewCmdServe(logger *zap.Logger) *Serve {
Expand Down
32 changes: 25 additions & 7 deletions cmd/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/spf13/cobra"
"go.keploy.io/server/pkg/models"
Expand Down Expand Up @@ -38,7 +39,7 @@ func readTestConfig(configPath string) (*models.Test, error) {
return &doc.Test, nil
}

func (t *Test) getTestConfig(path *string, proxyPort *uint32, appCmd *string, tests *map[string][]string, appContainer, networkName *string, Delay *uint64, passThorughPorts *[]uint, apiTimeout *uint64, globalNoise *models.GlobalNoise, testSetNoise *models.TestsetNoise, coverageReportPath *string, withCoverage *bool, configPath string) error {
func (t *Test) getTestConfig(path *string, proxyPort *uint32, appCmd *string, tests *map[string][]string, appContainer, networkName *string, Delay *uint64, buildDelay *time.Duration, passThorughPorts *[]uint, apiTimeout *uint64, globalNoise *models.GlobalNoise, testSetNoise *models.TestsetNoise, coverageReportPath *string, withCoverage *bool, configPath string) error {
configFilePath := filepath.Join(configPath, "keploy-config.yaml")
if isExist := utils.CheckFileExists(configFilePath); !isExist {
return errFileNotFound
Expand Down Expand Up @@ -70,9 +71,12 @@ func (t *Test) getTestConfig(path *string, proxyPort *uint32, appCmd *string, te
if *Delay == 5 {
*Delay = confTest.Delay
}
if *buildDelay == 30*time.Second && confTest.BuildDelay != 0 {
*buildDelay = confTest.BuildDelay
}
if len(*passThorughPorts) == 0 {
*passThorughPorts = confTest.PassThroughPorts
}
}
if len(*coverageReportPath) == 0 {
*coverageReportPath = confTest.CoverageReportPath
}
Expand Down Expand Up @@ -184,6 +188,12 @@ func (t *Test) GetCmd() *cobra.Command {
return err
}

buildDelay, err := cmd.Flags().GetDuration("buildDelay")
if err != nil {
t.logger.Error("Failed to get the build-delay flag", zap.Error((err)))
return err
}

apiTimeout, err := cmd.Flags().GetUint64("apiTimeout")
if err != nil {
t.logger.Error("Failed to get the apiTimeout flag", zap.Error((err)))
Expand Down Expand Up @@ -223,7 +233,7 @@ func (t *Test) GetCmd() *cobra.Command {
globalNoise := make(models.GlobalNoise)
testsetNoise := make(models.TestsetNoise)

err = t.getTestConfig(&path, &proxyPort, &appCmd, &tests, &appContainer, &networkName, &delay, &ports, &apiTimeout, &globalNoise, &testsetNoise, &coverageReportPath, &withCoverage, configPath)
err = t.getTestConfig(&path, &proxyPort, &appCmd, &tests, &appContainer, &networkName, &delay, &buildDelay, &ports, &apiTimeout, &globalNoise, &testsetNoise, &coverageReportPath, &withCoverage, configPath)
if err != nil {
if err == errFileNotFound {
t.logger.Info("continuing without configuration file because file not found")
Expand All @@ -235,7 +245,7 @@ func (t *Test) GetCmd() *cobra.Command {
if appCmd == "" {
fmt.Println("Error: missing required -c flag or appCmd in config file")
if isDockerCmd {
fmt.Println("Example usage:\n", `keploy test -c "docker run -p 8080:808 --network myNetworkName myApplicationImageName" --delay 6\n`)
fmt.Println("Example usage:\n", `keploy test -c "docker run -p 8080:8080 --network myNetworkName myApplicationImageName" --delay 6\n`)
}
fmt.Println("Example usage:\n", cmd.Example)

Expand All @@ -245,12 +255,17 @@ func (t *Test) GetCmd() *cobra.Command {
if delay <= 5 {
fmt.Printf("Warning: delay is set to %d seconds, incase your app takes more time to start use --delay to set custom delay\n", delay)
if isDockerCmd {
fmt.Println("Example usage:\n", `keploy test -c "docker run -p 8080:808 --network myNetworkName myApplicationImageName" --delay 6\n`)
fmt.Println("Example usage:\n", `keploy test -c "docker run -p 8080:8080 --network myNetworkName myApplicationImageName" --delay 6\n`)
} else {
fmt.Println("Example usage:\n", cmd.Example)
}
}

if isDockerCmd && buildDelay <= 30*time.Second {
fmt.Printf("Warning: buildDelay is set to %d, incase your docker container takes more time to build use --buildDelay to set custom delay\n", buildDelay)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This warning should be printed only when there is docker command because we don't need the build-delay flag in case of native integration.

fmt.Println("Example usage:\n", `keploy test -c "docker-compose up --build" --buildDelay 35s\n`, "\nor\n", `keploy test -c "docker-compose up --build" --buildDelay 1m\n`)
}

//if user provides relative path
if len(path) > 0 && path[0] != '/' {
absPath, err := filepath.Abs(path)
Expand Down Expand Up @@ -281,7 +296,7 @@ func (t *Test) GetCmd() *cobra.Command {
}
if !hasContainerName && appContainer == "" {
fmt.Println("Error: missing required --containerName flag or containerName in config file")
fmt.Println("\nExample usage:\n", `keploy test -c "docker run -p 8080:808 --network myNetworkName myApplicationImageName" --delay 6`)
fmt.Println("\nExample usage:\n", `keploy test -c "docker run -p 8080:8080 --network myNetworkName myApplicationImageName" --delay 6`)
return errors.New("missing required --containerName flag or containerName in config file")
}
}
Expand All @@ -301,12 +316,13 @@ func (t *Test) GetCmd() *cobra.Command {
AppNetwork: networkName,
MongoPassword: mongoPassword,
Delay: delay,
BuildDelay: buildDelay,
PassThroughPorts: ports,
ApiTimeout: apiTimeout,
ProxyPort: proxyPort,
GlobalNoise: globalNoise,
TestsetNoise: testsetNoise,
WithCoverage: withCoverage,
WithCoverage: withCoverage,
CoverageReportPath: coverageReportPath,
})

Expand All @@ -327,6 +343,8 @@ func (t *Test) GetCmd() *cobra.Command {
testCmd.Flags().StringP("networkName", "n", "", "Name of the application's docker network")
testCmd.Flags().Uint64P("delay", "d", 5, "User provided time to run its application")

testCmd.Flags().DurationP("buildDelay", "", 30*time.Second, "User provided time to wait docker container build")

testCmd.Flags().Uint64("apiTimeout", 5, "User provided timeout for calling its application")

testCmd.Flags().UintSlice("passThroughPorts", []uint{}, "Ports of Outgoing dependency calls to be ignored as mocks")
Expand Down
1 change: 1 addition & 0 deletions pkg/hooks/keploy/keploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ package keploy
// Host string `default:"0.0.0.0"`
// Port string `validate:"required"`
// Delay time.Duration `default:"5s"`
// BuildDelay time.Duration `default:"30s"`
// Timeout time.Duration `default:"60s"`
// Filter Filter
// TestPath string `default:""`
Expand Down
10 changes: 5 additions & 5 deletions pkg/hooks/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var (
ErrFailedUnitTest = errors.New("test failure occured when running keploy tests along with unit tests")
)

func (h *Hook) LaunchUserApplication(appCmd, appContainer, appNetwork string, Delay uint64, isUnitTestIntegration bool) error {
func (h *Hook) LaunchUserApplication(appCmd, appContainer, appNetwork string, Delay uint64, buildDelay time.Duration, isUnitTestIntegration bool) error {
// Supports Native-Linux, Windows (WSL), Lima, Colima

if appCmd == "" {
Expand All @@ -56,7 +56,7 @@ func (h *Hook) LaunchUserApplication(appCmd, appContainer, appNetwork string, De

h.logger.Debug("User Application is running inside docker in isolation", zap.Any("Container", appContainer), zap.Any("Network", appNetwork))
//search for the container and process it
err := h.processDockerEnv("", appContainer, appNetwork)
err := h.processDockerEnv("", appContainer, appNetwork, buildDelay)
if err != nil {
return err
}
Expand Down Expand Up @@ -220,7 +220,7 @@ func (h *Hook) LaunchUserApplication(appCmd, appContainer, appNetwork string, De
}
}

err := h.processDockerEnv(appCmd, appContainer, appNetwork)
err := h.processDockerEnv(appCmd, appContainer, appNetwork, buildDelay)
if err != nil {
return err
}
Expand All @@ -243,7 +243,7 @@ func (h *Hook) LaunchUserApplication(appCmd, appContainer, appNetwork string, De
}
}

func (h *Hook) processDockerEnv(appCmd, appContainer, appNetwork string) error {
func (h *Hook) processDockerEnv(appCmd, appContainer, appNetwork string, buildDelay time.Duration) error {
// to notify the kernel hooks that the user application is related to Docker.
key := 0
value := true
Expand Down Expand Up @@ -282,7 +282,7 @@ func (h *Hook) processDockerEnv(appCmd, appContainer, appNetwork string) error {
h.logger.Debug("exiting from goroutine of docker daemon event listener")
}()

endTime := time.Now().Add(30 * time.Second)
endTime := time.Now().Add(buildDelay)
logTicker := time.NewTicker(1 * time.Second)
defer logTicker.Stop()

Expand Down