From c3bb2177be9a5be6ceea336a0ee83fe032eaf608 Mon Sep 17 00:00:00 2001 From: bostigger Date: Tue, 8 Aug 2023 21:03:00 +0000 Subject: [PATCH 1/5] add multiple docker cmds to one single file --- api/handlers.go | 62 +++++++++++++++++++++++++--------------------- api/server.go | 2 +- api/server_test.go | 26 +++++++++++++++++++ 3 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 api/server_test.go diff --git a/api/handlers.go b/api/handlers.go index 727a210..a49814e 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -3,12 +3,11 @@ package api import ( "encoding/json" "fmt" + "github.com/profclems/compozify/pkg/parser" "net/http" "path" "strings" "time" - - "github.com/profclems/compozify/pkg/parser" ) // Response is the response body for the ParseDockerCommand handler. @@ -16,15 +15,15 @@ type Response struct { Output string `json:"output"` } -// ParseDockerCommand parses a Docker command and returns the equivalent Docker Compose YAML. -func (server *Server) ParseDockerCommand(w http.ResponseWriter, r *http.Request) { - type DockerCommand struct { - Command string `json:"command"` +// ParseDockerCommands ParseDockerCommand parses a Docker command and returns the equivalent Docker Compose YAML. +func (server *Server) ParseDockerCommands(w http.ResponseWriter, r *http.Request) { + type DockerCommands struct { + Commands []string `json:"commands"` } - var dockerCmd DockerCommand + var dockerCmds DockerCommands - logger := server.logger.With().Str("handler", "ParseDockerCommand").Str("remoteAddr", r.RemoteAddr).Logger() + logger := server.logger.With().Str("handler", "ParseDockerCommands").Str("remoteAddr", r.RemoteAddr).Logger() logger.Info().Msgf("%s %s %s", r.Method, r.URL.Path, r.Proto) start := time.Now() @@ -38,35 +37,42 @@ func (server *Server) ParseDockerCommand(w http.ResponseWriter, r *http.Request) } log.Msgf("Returned %d in %v", code, time.Since(start)) }() - err := json.NewDecoder(r.Body).Decode(&dockerCmd) + err := json.NewDecoder(r.Body).Decode(&dockerCmds) if err != nil { errorMsg = fmt.Sprintf("Error decoding request body: %v", err) code = http.StatusBadRequest return } - // Validate the command. - if dockerCmd.Command == "" { - errorMsg = "Docker command cannot be empty" - code = http.StatusBadRequest - return - } + var p *parser.Parser + for _, cmd := range dockerCmds.Commands { + if cmd == "" { + errorMsg = "Docker command cannot be empty" + code = http.StatusBadRequest + return + } - // Create a new Parser - p, err := parser.New(dockerCmd.Command) - if err != nil { - errorMsg = fmt.Sprintf("Error creating parser: %v", err) - code = http.StatusBadRequest - return - } + // Create a new Parser or append to existing parser + if p == nil { + p, err = parser.New(cmd) + } else { + yamlBytes := []byte(p.String()) + p, err = parser.AppendToYAML(yamlBytes, cmd) + } - // Parse the Docker command - err = p.Parse() - if err != nil { - errorMsg = fmt.Sprintf("Error parsing Docker command: %v", err) - code = http.StatusBadRequest - return + if err != nil { + errorMsg = fmt.Sprintf("Error parsing Docker command: %v", err) + code = http.StatusBadRequest + return + } + + err = p.Parse() + if err != nil { + errorMsg = fmt.Sprintf("Error parsing Docker command: %v", err) + code = http.StatusBadRequest + return + } } dockerComposeYaml := p.String() diff --git a/api/server.go b/api/server.go index cbab691..0f64640 100644 --- a/api/server.go +++ b/api/server.go @@ -29,7 +29,7 @@ func NewServer(logger *zerolog.Logger, listener net.Listener, assets fs.FS) *Ser } r := mux.NewRouter() - r.HandleFunc("/api/parse", server.ParseDockerCommand).Methods("POST") + r.HandleFunc("/api/parse", server.ParseDockerCommands).Methods("POST") r.PathPrefix("/").HandlerFunc(server.appHandler) server.http = http.Server{ diff --git a/api/server_test.go b/api/server_test.go new file mode 100644 index 0000000..57dc815 --- /dev/null +++ b/api/server_test.go @@ -0,0 +1,26 @@ +package api + +import ( + "context" + "net" + "os" + "testing" + + "github.com/rs/zerolog" +) + +func TestServer(t *testing.T) { + logger := zerolog.New(os.Stdout).With().Timestamp().Logger() + listener, err := net.Listen("tcp", ":8080") + if err != nil { + t.Fatalf("Failed to create listener: %v", err) + } + + server := NewServer(&logger, listener, nil) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if err := server.Run(ctx); err != nil { + t.Fatalf("Server error: %v", err) + } +} From a28ac33fefb0af081de10eec4ac975ff5d9cc2e3 Mon Sep 17 00:00:00 2001 From: bostigger Date: Tue, 8 Aug 2023 21:57:12 +0000 Subject: [PATCH 2/5] add multiple docker cmds to one single file --- api/server_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/server_test.go b/api/server_test.go index 57dc815..b085c4c 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -5,6 +5,7 @@ import ( "net" "os" "testing" + "time" "github.com/rs/zerolog" ) @@ -17,10 +18,11 @@ func TestServer(t *testing.T) { } server := NewServer(&logger, listener, nil) - ctx, cancel := context.WithCancel(context.Background()) + + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - if err := server.Run(ctx); err != nil { + if err := server.Run(ctx); err != nil && err != context.Canceled && err != context.DeadlineExceeded { t.Fatalf("Server error: %v", err) } } From cb422a6903873ac17b77e7b0445e714b46e91355 Mon Sep 17 00:00:00 2001 From: bostigger Date: Tue, 8 Aug 2023 22:12:47 +0000 Subject: [PATCH 3/5] add multiple docker cmds to one single file --- api/handlers.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/handlers.go b/api/handlers.go index a49814e..b52d2f7 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -3,11 +3,12 @@ package api import ( "encoding/json" "fmt" - "github.com/profclems/compozify/pkg/parser" "net/http" "path" "strings" "time" + + "github.com/profclems/compozify/pkg/parser" ) // Response is the response body for the ParseDockerCommand handler. From 482960b138e20bc57b91a4cc7a7069953c55445f Mon Sep 17 00:00:00 2001 From: bostigger Date: Tue, 8 Aug 2023 23:19:35 +0000 Subject: [PATCH 4/5] add multiple docker cmds to one single file --- api/handlers.go | 60 +++++++++++++++++++++------------------------- api/server_test.go | 35 ++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/api/handlers.go b/api/handlers.go index b52d2f7..e346c79 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -3,6 +3,8 @@ package api import ( "encoding/json" "fmt" + "github.com/rs/zerolog" + "io" "net/http" "path" "strings" @@ -21,76 +23,68 @@ func (server *Server) ParseDockerCommands(w http.ResponseWriter, r *http.Request type DockerCommands struct { Commands []string `json:"commands"` } - var dockerCmds DockerCommands logger := server.logger.With().Str("handler", "ParseDockerCommands").Str("remoteAddr", r.RemoteAddr).Logger() logger.Info().Msgf("%s %s %s", r.Method, r.URL.Path, r.Proto) - start := time.Now() - code := http.StatusOK - errorMsg := "" - defer func() { - log := logger.Info() - if errorMsg != "" { - log = logger.Error() - http.Error(w, errorMsg, code) - } - log.Msgf("Returned %d in %v", code, time.Since(start)) - }() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + } + }(r.Body) err := json.NewDecoder(r.Body).Decode(&dockerCmds) if err != nil { - errorMsg = fmt.Sprintf("Error decoding request body: %v", err) - code = http.StatusBadRequest + writeError(w, logger, "Error decoding request body", err, http.StatusBadRequest) return } + start := time.Now() + defer logDuration(logger, start) + var p *parser.Parser for _, cmd := range dockerCmds.Commands { if cmd == "" { - errorMsg = "Docker command cannot be empty" - code = http.StatusBadRequest + writeError(w, logger, "Docker command cannot be empty", nil, http.StatusBadRequest) return } - // Create a new Parser or append to existing parser if p == nil { p, err = parser.New(cmd) } else { - yamlBytes := []byte(p.String()) - p, err = parser.AppendToYAML(yamlBytes, cmd) + p, err = parser.AppendToYAML([]byte(p.String()), cmd) } if err != nil { - errorMsg = fmt.Sprintf("Error parsing Docker command: %v", err) - code = http.StatusBadRequest + writeError(w, logger, "Error parsing Docker command", err, http.StatusBadRequest) return } - err = p.Parse() - if err != nil { - errorMsg = fmt.Sprintf("Error parsing Docker command: %v", err) - code = http.StatusBadRequest + if err := p.Parse(); err != nil { + writeError(w, logger, "Error parsing Docker command", err, http.StatusBadRequest) return } } - dockerComposeYaml := p.String() - - // Create the response resp := Response{ - Output: dockerComposeYaml, + Output: p.String(), } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(resp) - if err != nil { + if err := json.NewEncoder(w).Encode(resp); err != nil { logger.Err(err).Msg("Unable to write response") - return } } +func writeError(w http.ResponseWriter, logger zerolog.Logger, msg string, err error, code int) { + logger.Err(err).Msg(msg) + http.Error(w, fmt.Sprintf("%s: %v", msg, err), code) +} + +func logDuration(logger zerolog.Logger, start time.Time) { + logger.Info().Msgf("Returned in %v", time.Since(start)) +} + // appHandler is web app http handler function. func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) { staticServer := http.FileServer(http.FS(server.assets)) diff --git a/api/server_test.go b/api/server_test.go index b085c4c..0b56ec7 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -1,9 +1,10 @@ package api import ( + "bytes" "context" + "fmt" "net" - "os" "testing" "time" @@ -11,18 +12,40 @@ import ( ) func TestServer(t *testing.T) { - logger := zerolog.New(os.Stdout).With().Timestamp().Logger() - listener, err := net.Listen("tcp", ":8080") + var logBuffer bytes.Buffer + logger := zerolog.New(&logBuffer).With().Timestamp().Logger() + listener, err := net.Listen("tcp", ":0") if err != nil { t.Fatalf("Failed to create listener: %v", err) } + defer func() { + if err := listener.Close(); err != nil { + logger.Error().Err(err).Msg("Failed to close listener") + } + }() + + addr := listener.Addr().(*net.TCPAddr) + port := addr.Port + endpoint := fmt.Sprintf("http://localhost:%d/api/parse", port) + logger.Info().Msgf("Endpoint: %s", endpoint) server := NewServer(&logger, listener, nil) ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - if err := server.Run(ctx); err != nil && err != context.Canceled && err != context.DeadlineExceeded { - t.Fatalf("Server error: %v", err) - } + go func() { + if err := server.Run(ctx); err != nil && err != context.Canceled { + logger.Error().Err(err).Msg("Server error") + } + }() + + // Wait for the server to start + time.Sleep(1 * time.Second) + + fmt.Printf("Server is running on %s. You have 2 minutes to test manually.\n", endpoint) + + // Wait for 2 minutes to allow manual testing + time.Sleep(2 * time.Minute) + } From f1f571df29ed8faceabe4ed105c540f1357b5476 Mon Sep 17 00:00:00 2001 From: bostigger Date: Tue, 8 Aug 2023 23:26:34 +0000 Subject: [PATCH 5/5] add multiple docker cmds to one single file --- api/handlers.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/handlers.go b/api/handlers.go index e346c79..80870bb 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -31,9 +31,10 @@ func (server *Server) ParseDockerCommands(w http.ResponseWriter, r *http.Request defer func(Body io.ReadCloser) { err := Body.Close() if err != nil { - + logger.Err(err).Msg("Error closing request body") } }(r.Body) + err := json.NewDecoder(r.Body).Decode(&dockerCmds) if err != nil { writeError(w, logger, "Error decoding request body", err, http.StatusBadRequest)