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 5 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
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
18 changes: 15 additions & 3 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 Down Expand Up @@ -189,7 +199,7 @@ func (r *Record) GetCmd() *cobra.Command {
}

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 +216,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
32 changes: 18 additions & 14 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 All @@ -34,20 +34,20 @@ type colorConsoleEncoder struct {
*zapcore.EncoderConfig
zapcore.Encoder
}

func NewColorConsole(cfg zapcore.EncoderConfig) (enc zapcore.Encoder) {
return colorConsoleEncoder{
EncoderConfig: &cfg,
// Using the default ConsoleEncoder can avoid rewriting interfaces such as ObjectEncoder
Encoder: zapcore.NewConsoleEncoder(cfg),
}
}

// EncodeEntry overrides ConsoleEncoder's EncodeEntry
func (c colorConsoleEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (buf *buffer.Buffer, err error) {
buff, err := c.Encoder.EncodeEntry(ent, fields) // Utilize the existing implementation of zap
if err != nil {
return nil, err
return nil, err
}

bytesArr := bytes.Replace(buff.Bytes(), []byte("\\u001b"), []byte("\u001b"), -1)
Expand All @@ -61,30 +61,30 @@ 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
})
}

func setupLogger() *zap.Logger {
logCfg := zap.NewDevelopmentConfig()

logCfg.Encoding = "colorConsole"

// 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",
"./keploy-logs.txt",
}

if debugMode {
go func() {
defer utils.HandlePanic()
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
15 changes: 11 additions & 4 deletions cmd/serve.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package cmd

import (
"os"
"path/filepath"

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

func NewCmdServe(logger *zap.Logger) *Serve {
Expand Down Expand Up @@ -66,6 +66,13 @@ func (s *Serve) GetCmd() *cobra.Command {
return
}

buildDelay := 30 * time.Second

if err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

You can remove this code since there is no build-delay in the serve command. So no need to check for this error.

s.logger.Error("Failed to get the build-delay flag", zap.Error((err)))
return
}

pid, err := cmd.Flags().GetUint32("pid")

if err != nil {
Expand Down Expand Up @@ -107,7 +114,7 @@ func (s *Serve) GetCmd() *cobra.Command {
}
s.logger.Debug("the ports are", zap.Any("ports", ports))

s.server.Serve(path, proxyPort, testReportPath, delay, pid, port, language, ports, apiTimeout, appCmd)
s.server.Serve(path, proxyPort, testReportPath, delay, buildDelay, pid, port, language, ports, apiTimeout, appCmd)
},
}

Expand Down
26 changes: 24 additions & 2 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, testsets *[]string, appContainer, networkName *string, Delay *uint64, passThorughPorts *[]uint, apiTimeout *uint64, globalNoise *models.GlobalNoise, testSetNoise *models.TestsetNoise, configPath string) error {
func (t *Test) getTestConfig(path *string, proxyPort *uint32, appCmd *string, testsets *[]string, appContainer, networkName *string, Delay *uint64, buildDelay *time.Duration, passThorughPorts *[]uint, apiTimeout *uint64, globalNoise *models.GlobalNoise, testSetNoise *models.TestsetNoise, 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 *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
}
Expand Down Expand Up @@ -174,6 +178,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 @@ -201,7 +211,7 @@ func (t *Test) GetCmd() *cobra.Command {
globalNoise := make(models.GlobalNoise)
testsetNoise := make(models.TestsetNoise)

err = t.getTestConfig(&path, &proxyPort, &appCmd, &testSets, &appContainer, &networkName, &delay, &ports, &apiTimeout, &globalNoise, &testsetNoise, configPath)
err = t.getTestConfig(&path, &proxyPort, &appCmd, &testSets, &appContainer, &networkName, &delay, &buildDelay, &ports, &apiTimeout, &globalNoise, &testsetNoise, configPath)
if err != nil {
if err == errFileNotFound {
t.logger.Info("continuing without configuration file because file not found")
Expand Down Expand Up @@ -229,6 +239,15 @@ func (t *Test) GetCmd() *cobra.Command {
}
}

if buildDelay <= time.Duration(30)*time.Second {
Copy link
Member

Choose a reason for hiding this comment

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

The same if check should be in the record command as well. And don't mention the example usage of record in the test command. It should be in the record command.

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.

if isDockerCmd {
Copy link
Member

Choose a reason for hiding this comment

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

No need to check for isDockerCmd again.

fmt.Println("Example usage:\n", `keploy test -c "docker run -p 8080:808 --network myNetworkName myApplicationImageName" --buildDelay 35s\n`, "\nor\n", `keploy test -c "docker run -p 8080:808 --network myNetworkName myApplicationImageName" --buildDelay 1m\n`)
} else {
Copy link
Member

@gouravkrosx gouravkrosx Dec 5, 2023

Choose a reason for hiding this comment

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

Only mention test command usage here. And the port forwarding in the docker command should have port -p 8080:8080, please fix this if any other place also has this incorrect port.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for the confusion, what does 'Only mention test command usage here. ' mean , Shall I remove the warning message 🤔

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I didn't notice. You don't need to remove the command or any warning message. Just fix the port forwarding.

fmt.Println("Example usage:\n", cmd.Example)
Copy link
Member

Choose a reason for hiding this comment

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

Since this buildDelay flag is only in the case of the docker command, there is no need to add this example here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since this buildDelay flag is only in the case of the docker command, there is no need to add this example here.

By default, do we assume that the docker environment is built when the user uses the record command? Do we consider the situation where the user needs to rebuild when running the test command? Then the buildDelay parameter still needs to be checked

Copy link
Member

Choose a reason for hiding this comment

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

@IllTamer Whenever there is a docker env the isDockerCmd is true since the keploy Dockerfile contains this as an environment variable. So if you've checked that it is dockerCmd & the buildDelay is <=30 then just show the warning as the usage rather than showing another example.

}
}

//if user provides relative path
if len(path) > 0 && path[0] != '/' {
absPath, err := filepath.Abs(path)
Expand Down Expand Up @@ -279,6 +298,7 @@ func (t *Test) GetCmd() *cobra.Command {
AppNetwork: networkName,
MongoPassword: mongoPassword,
Delay: delay,
BuildDelay: buildDelay,
PassThroughPorts: ports,
ApiTimeout: apiTimeout,
ProxyPort: proxyPort,
Expand All @@ -303,6 +323,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
Loading
Loading