From d577102d5ec6ff771163ab886ee26182ee694d25 Mon Sep 17 00:00:00 2001 From: Dhruv Dhruv Date: Tue, 9 Jul 2024 21:13:47 +0000 Subject: [PATCH] feat: [CDE-127]: Adding changes to include logging in container orchestration. (#2178) * feat: [CDE-127]: Linting. * Merge branch 'refs/heads/main' into CDE-127 # Conflicts: # app/gitspace/orchestrator/container/embedded_docker.go # cmd/gitness/wire.go * feat: [CDE-127]: Addressing review comments. * feat: [CDE-127]: Removing streams map from StatefulLogger. Flushing the stream every time the invoking function is closed. * feat: [CDE-127]: Removing streams map from StatefulLogger. Flushing the stream every time the invoking function is closed. * feat: [CDE-127]: Removing streams map from StatefulLogger. Flushing the stream every time the invoking function is closed. * feat: [CDE-127]: Removing streams map from StatefulLogger. Flushing the stream every time the invoking function is closed. * feat: [CDE-127]: Adding flush stream logic to delete gitspace in orchestrator. Linting. * feat: [CDE-127]: Adding flush stream logic to delete gitspace in orchestrator. Linting. * feat: [CDE-127]: Addressing review comments. * feat: [CDE-127]: Adding changes to make logutil.StatefulLogger a wrapper on livelog.LogStream and adding initialisation and flush functions. * feat: [CDE-127]: Adding changes to make logutil.StatefulLogger a wrapper on livelog.LogStream and adding initialisation and flush functions. * feat: [CDE-127]: Adding changes to include logging in container orchestration's stop flow. * feat: [CDE-127]: Adding changes to include logging in container orchestration's start flow. --- app/gitspace/logutil/reusable_scanner.go | 50 +++ app/gitspace/logutil/stateful_logger.go | 106 ++++++ app/gitspace/logutil/wire.go | 29 ++ .../orchestrator/container/embedded_docker.go | 323 ++++++++++++++++-- app/gitspace/orchestrator/container/wire.go | 3 + cmd/gitness/wire.go | 4 +- cmd/gitness/wire_gen.go | 4 +- 7 files changed, 492 insertions(+), 27 deletions(-) create mode 100644 app/gitspace/logutil/reusable_scanner.go create mode 100644 app/gitspace/logutil/stateful_logger.go create mode 100644 app/gitspace/logutil/wire.go diff --git a/app/gitspace/logutil/reusable_scanner.go b/app/gitspace/logutil/reusable_scanner.go new file mode 100644 index 000000000..9e920c3a4 --- /dev/null +++ b/app/gitspace/logutil/reusable_scanner.go @@ -0,0 +1,50 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 logutil + +import ( + "bufio" + "fmt" + "strings" +) + +type reusableScanner struct { + scanner *bufio.Scanner + reader *strings.Reader +} + +func newReusableScanner() *reusableScanner { + reader := strings.NewReader("") + scanner := bufio.NewScanner(reader) + return &reusableScanner{ + scanner: scanner, + reader: reader, + } +} + +func (r *reusableScanner) scan(input string) ([]string, error) { + r.reader.Reset(input) + var lines []string + + for r.scanner.Scan() { + lines = append(lines, r.scanner.Text()) + } + + if err := r.scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading string %s: %w", input, err) + } + + return lines, nil +} diff --git a/app/gitspace/logutil/stateful_logger.go b/app/gitspace/logutil/stateful_logger.go new file mode 100644 index 000000000..4bf11d1f0 --- /dev/null +++ b/app/gitspace/logutil/stateful_logger.go @@ -0,0 +1,106 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 logutil + +import ( + "context" + "fmt" + "time" + + "github.com/harness/gitness/livelog" +) + +const offset int64 = 1000000000 + +// StatefulLogger is a wrapper on livelog.Logstream. It is used to create stateful instances of LogStreamInstance. +type StatefulLogger struct { + logz livelog.LogStream +} + +// LogStreamInstance is a stateful instance of the livelog.LogStream. It keeps track of the position & log key (id). +type LogStreamInstance struct { + ctx context.Context + id int64 + offsetID int64 + position int + scanner *reusableScanner + logz livelog.LogStream +} + +func NewStatefulLogger(logz livelog.LogStream) *StatefulLogger { + return &StatefulLogger{ + logz: logz, + } +} + +// GetLogStream returns an instance of LogStreamInstance tied to the given id. +func (s *StatefulLogger) CreateLogStream(ctx context.Context, id int64) (*LogStreamInstance, error) { + // TODO: As livelog.LogStreamInstance uses only a single id as key, conflicts are likely if pipelines and gitspaces + // are used in the same instance of Gitness. We need to update the underlying implementation to use another unique + // key. To avoid that, we offset the ID by offset (1000000000). + offsetID := offset + id + + // Create new logstream + err := s.logz.Create(ctx, offsetID) + if err != nil { + return nil, fmt.Errorf("error creating log stream for ID %d: %w", id, err) + } + + newStream := &LogStreamInstance{ + id: id, + offsetID: offsetID, + ctx: ctx, + scanner: newReusableScanner(), + logz: s.logz, + } + + return newStream, nil +} + +// Write writes the msg into the underlying log stream. +func (l *LogStreamInstance) Write(msg string) error { + lines, err := l.scanner.scan(msg) + if err != nil { + return fmt.Errorf("error parsing log lines %s: %w", msg, err) + } + + for _, line := range lines { + err = l.logz.Write( + l.ctx, + l.offsetID, + &livelog.Line{ + Number: l.position, + Message: line, + Timestamp: time.Now().UnixMilli(), + }) + if err != nil { + return fmt.Errorf("could not write log %s for ID %d at pos %d: %w", line, l.id, l.position, err) + } + + l.position++ + } + + return nil +} + +// Flush deletes the underlying stream. +func (l *LogStreamInstance) Flush() error { + err := l.logz.Delete(l.ctx, l.offsetID) + if err != nil { + return fmt.Errorf("failed to delete old log stream for ID %d: %w", l.id, err) + } + + return nil +} diff --git a/app/gitspace/logutil/wire.go b/app/gitspace/logutil/wire.go new file mode 100644 index 000000000..fbb075c6f --- /dev/null +++ b/app/gitspace/logutil/wire.go @@ -0,0 +1,29 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 logutil + +import ( + "github.com/harness/gitness/livelog" + + "github.com/google/wire" +) + +var WireSet = wire.NewSet( + ProvideStatefulLogger, +) + +func ProvideStatefulLogger(logz livelog.LogStream) *StatefulLogger { + return NewStatefulLogger(logz) +} diff --git a/app/gitspace/orchestrator/container/embedded_docker.go b/app/gitspace/orchestrator/container/embedded_docker.go index 569585eaa..567ccc102 100644 --- a/app/gitspace/orchestrator/container/embedded_docker.go +++ b/app/gitspace/orchestrator/container/embedded_docker.go @@ -21,6 +21,7 @@ import ( "os" "path/filepath" + "github.com/harness/gitness/app/gitspace/logutil" "github.com/harness/gitness/infraprovider" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" @@ -59,6 +60,7 @@ type EmbeddedDockerOrchestrator struct { vsCodeService *VSCode vsCodeWebService *VSCodeWeb config *Config + statefulLogger *logutil.StatefulLogger } func NewEmbeddedDockerOrchestrator( @@ -66,12 +68,14 @@ func NewEmbeddedDockerOrchestrator( vsCodeService *VSCode, vsCodeWebService *VSCodeWeb, config *Config, + statefulLogger *logutil.StatefulLogger, ) Orchestrator { return &EmbeddedDockerOrchestrator{ dockerClientFactory: dockerClientFactory, vsCodeService: vsCodeService, vsCodeWebService: vsCodeWebService, config: config, + statefulLogger: statefulLogger, } } @@ -135,6 +139,18 @@ func (e *EmbeddedDockerOrchestrator) StartGitspace( return nil, startErr } + logStreamInstance, loggerErr := e.statefulLogger.CreateLogStream(ctx, gitspaceConfig.ID) + if loggerErr != nil { + return nil, fmt.Errorf("error getting log stream for gitspace ID %d: %w", gitspaceConfig.ID, loggerErr) + } + + defer func() { + loggerErr = logStreamInstance.Flush() + if loggerErr != nil { + log.Warn().Err(loggerErr).Msgf("failed to flush log stream for gitspace ID %d", gitspaceConfig.ID) + } + }() + startErr = e.startGitspace( ctx, gitspaceConfig, @@ -142,6 +158,7 @@ func (e *EmbeddedDockerOrchestrator) StartGitspace( containerName, dockerClient, ideService, + logStreamInstance, ) if startErr != nil { return nil, fmt.Errorf("failed to start gitspace %s: %w", containerName, startErr) @@ -175,18 +192,19 @@ func (e *EmbeddedDockerOrchestrator) startGitspace( containerName string, dockerClient *client.Client, ideService IDE, + logStreamInstance *logutil.LogStreamInstance, ) error { var imageName = devcontainerConfig.Image if imageName == "" { imageName = e.config.DefaultBaseImage } - err := e.pullImage(ctx, imageName, dockerClient) + err := e.pullImage(ctx, imageName, dockerClient, logStreamInstance) if err != nil { return err } - err = e.createContainer(ctx, gitspaceConfig, dockerClient, imageName, containerName, ideService) + err = e.createContainer(ctx, gitspaceConfig, dockerClient, imageName, containerName, ideService, logStreamInstance) if err != nil { return err } @@ -197,24 +215,57 @@ func (e *EmbeddedDockerOrchestrator) startGitspace( WorkingDir: e.config.DefaultBindMountTargetPath, } - err = e.executePostCreateCommand(ctx, devcontainerConfig, devcontainer) + err = e.executePostCreateCommand(ctx, devcontainerConfig, devcontainer, logStreamInstance) if err != nil { return err } - err = e.cloneCode(ctx, gitspaceConfig, devcontainerConfig, devcontainer) + err = e.cloneCode(ctx, gitspaceConfig, devcontainerConfig, devcontainer, logStreamInstance) if err != nil { return err } - err = e.setupSSHServer(ctx, gitspaceConfig.GitspaceInstance, devcontainer) + err = e.setupSSHServer(ctx, gitspaceConfig.GitspaceInstance, devcontainer, logStreamInstance) if err != nil { return err } - err = ideService.Setup(ctx, devcontainer, gitspaceConfig.GitspaceInstance) + err = e.setupIDE(ctx, gitspaceConfig, devcontainer, ideService, logStreamInstance) if err != nil { - return fmt.Errorf("failed to setup IDE for gitspace %s: %w", containerName, err) + return err + } + + return nil +} + +func (e *EmbeddedDockerOrchestrator) setupIDE( + ctx context.Context, + gitspaceConfig *types.GitspaceConfig, + devcontainer *Devcontainer, + ideService IDE, + logStreamInstance *logutil.LogStreamInstance, +) error { + loggingErr := logStreamInstance.Write("Setting up IDE inside container: " + string(gitspaceConfig.IDE)) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + err := ideService.Setup(ctx, devcontainer, gitspaceConfig.GitspaceInstance) + if err != nil { + loggingErr = logStreamInstance.Write("Error while setting up IDE inside container: " + err.Error()) + + err = fmt.Errorf("failed to setup IDE for gitspace %s: %w", devcontainer.ContainerName, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("Successfully set up IDE inside container") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } return nil @@ -263,6 +314,7 @@ func (e *EmbeddedDockerOrchestrator) setupSSHServer( ctx context.Context, gitspaceInstance *types.GitspaceInstance, devcontainer *Devcontainer, + logStreamInstance *logutil.LogStreamInstance, ) error { sshServerScript, err := GenerateScriptFromTemplate( templateSetupSSHServer, &SetupSSHServerPayload{ @@ -275,9 +327,32 @@ func (e *EmbeddedDockerOrchestrator) setupSSHServer( "failed to generate scipt to setup ssh server from template %s: %w", templateSetupSSHServer, err) } - _, err = devcontainer.ExecuteCommand(ctx, sshServerScript, false) + loggingErr := logStreamInstance.Write("Installing ssh-server inside container") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + output, err := devcontainer.ExecuteCommand(ctx, sshServerScript, false) if err != nil { - return fmt.Errorf("failed to setup SSH server: %w", err) + loggingErr = logStreamInstance.Write("Error while installing ssh-server inside container: " + err.Error()) + + err = fmt.Errorf("failed to setup SSH server: %w", err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("SSH server installation output...\n" + string(*output)) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + loggingErr = logStreamInstance.Write("Successfully installed ssh-server inside container") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } return nil @@ -288,6 +363,7 @@ func (e *EmbeddedDockerOrchestrator) cloneCode( gitspaceConfig *types.GitspaceConfig, devcontainerConfig *types.DevcontainerConfig, devcontainer *Devcontainer, + logStreamInstance *logutil.LogStreamInstance, ) error { var devcontainerPresent = "true" if devcontainerConfig.Image == "" { @@ -304,9 +380,32 @@ func (e *EmbeddedDockerOrchestrator) cloneCode( return fmt.Errorf("failed to generate scipt to clone git from template %s: %w", templateCloneGit, err) } - _, err = devcontainer.ExecuteCommand(ctx, gitCloneScript, false) + loggingErr := logStreamInstance.Write("Cloning git repo inside container: " + gitspaceConfig.CodeRepoURL) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + output, err := devcontainer.ExecuteCommand(ctx, gitCloneScript, false) if err != nil { - return fmt.Errorf("failed to clone code: %w", err) + loggingErr = logStreamInstance.Write("Error while cloning git repo inside container: " + err.Error()) + + err = fmt.Errorf("failed to clone code: %w", err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("Cloning git repo output...\n" + string(*output)) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + loggingErr = logStreamInstance.Write("Successfully cloned git repo inside container") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } return nil @@ -316,14 +415,43 @@ func (e *EmbeddedDockerOrchestrator) executePostCreateCommand( ctx context.Context, devcontainerConfig *types.DevcontainerConfig, devcontainer *Devcontainer, + logStreamInstance *logutil.LogStreamInstance, ) error { if devcontainerConfig.PostCreateCommand == "" { + loggingErr := logStreamInstance.Write("No post-create command provided, skipping execution") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + return nil } - _, err := devcontainer.ExecuteCommand(ctx, devcontainerConfig.PostCreateCommand, false) + loggingErr := logStreamInstance.Write("Executing postCreate command: " + devcontainerConfig.PostCreateCommand) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + output, err := devcontainer.ExecuteCommand(ctx, devcontainerConfig.PostCreateCommand, false) if err != nil { - return fmt.Errorf("post create command failed %q: %w", devcontainerConfig.PostCreateCommand, err) + loggingErr = logStreamInstance.Write("Error while executing postCreate command") + + err = fmt.Errorf("failed to execute postCreate command %q: %w", devcontainerConfig.PostCreateCommand, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("Post create command execution output...\n" + string(*output)) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + loggingErr = logStreamInstance.Write("Successfully executed postCreate command") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } return nil @@ -336,6 +464,7 @@ func (e *EmbeddedDockerOrchestrator) createContainer( imageName string, containerName string, ideService IDE, + logStreamInstance *logutil.LogStreamInstance, ) error { portUsedByIDE := ideService.PortAndProtocol() @@ -372,10 +501,34 @@ func (e *EmbeddedDockerOrchestrator) createContainer( gitspaceConfig.SpacePath, gitspaceConfig.Identifier, ) + + loggingErr := logStreamInstance.Write("Creating bind mount source directory: " + bindMountSourcePath) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + err := os.MkdirAll(bindMountSourcePath, os.ModePerm) if err != nil { - return fmt.Errorf( + loggingErr = logStreamInstance.Write("Error while creating bind mount source directory: " + err.Error()) + + err = fmt.Errorf( "could not create bind mount source path %s: %w", bindMountSourcePath, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("Successfully created bind mount source directory") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + loggingErr = logStreamInstance.Write("Creating container: " + containerName) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } resp2, err := dockerClient.ContainerCreate(ctx, &container.Config{ @@ -394,12 +547,43 @@ func (e *EmbeddedDockerOrchestrator) createContainer( }, }, nil, containerName) if err != nil { - return fmt.Errorf("could not create container %s: %w", containerName, err) + loggingErr = logStreamInstance.Write("Error while creating container: " + err.Error()) + + err = fmt.Errorf("could not create container %s: %w", containerName, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("Successfully created container") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + loggingErr = logStreamInstance.Write("Starting container: " + containerName) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } err = dockerClient.ContainerStart(ctx, resp2.ID, dockerTypes.ContainerStartOptions{}) if err != nil { - return fmt.Errorf("could not start container %s: %w", containerName, err) + loggingErr = logStreamInstance.Write("Error while creating container: " + err.Error()) + + err = fmt.Errorf("could not start container %s: %w", containerName, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("Successfully started container") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } return nil @@ -409,20 +593,59 @@ func (e *EmbeddedDockerOrchestrator) pullImage( ctx context.Context, imageName string, dockerClient *client.Client, + logStreamInstance *logutil.LogStreamInstance, ) error { - resp, err := dockerClient.ImagePull(ctx, imageName, dockerTypes.ImagePullOptions{}) + loggingErr := logStreamInstance.Write("Pulling image: " + imageName) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + pullResponse, err := dockerClient.ImagePull(ctx, imageName, dockerTypes.ImagePullOptions{}) + defer func() { - closingErr := resp.Close() + closingErr := pullResponse.Close() if closingErr != nil { log.Warn().Err(closingErr).Msg("failed to close image pull response") } }() + if err != nil { - return fmt.Errorf("could not pull image %s: %w", imageName, err) + loggingErr = logStreamInstance.Write("Error while pulling image: " + err.Error()) + + err = fmt.Errorf("could not pull image %s: %w", imageName, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err } - // TODO: This is required to ensure the execution waits till the image is downloaded. - // Will be removed once logs PR is merged. - io.Copy(io.Discard, resp) // nolint:errcheck + + // NOTE: It is necessary to read all the data in pullResponse to ensure the image has been completely downloaded. + // If the execution proceeds before the response is completed, the container will not find the required image. + output, err := io.ReadAll(pullResponse) + if err != nil { + loggingErr = logStreamInstance.Write("Error while parsing image pull response: " + err.Error()) + + err = fmt.Errorf("error while parsing pull image output %s: %w", imageName, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write(string(output)) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + loggingErr = logStreamInstance.Write("Successfully pulled image") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + return nil } @@ -461,7 +684,20 @@ func (e EmbeddedDockerOrchestrator) StopGitspace( } log.Debug().Msg("stopping gitspace") - err = e.stopGitspace(ctx, containerName, dockerClient) + + logStreamInstance, loggerErr := e.statefulLogger.CreateLogStream(ctx, gitspaceConfig.ID) + if loggerErr != nil { + return fmt.Errorf("error getting log stream for gitspace ID %d: %w", gitspaceConfig.ID, loggerErr) + } + + defer func() { + loggerErr = logStreamInstance.Flush() + if loggerErr != nil { + log.Warn().Err(loggerErr).Msgf("failed to flush log stream for gitspace ID %d", gitspaceConfig.ID) + } + }() + + err = e.stopGitspace(ctx, containerName, dockerClient, logStreamInstance) if err != nil { return fmt.Errorf("failed to stop gitspace %s: %w", containerName, err) } @@ -475,15 +711,52 @@ func (e EmbeddedDockerOrchestrator) stopGitspace( ctx context.Context, containerName string, dockerClient *client.Client, + logStreamInstance *logutil.LogStreamInstance, ) error { + loggingErr := logStreamInstance.Write("Stopping container: " + containerName) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + err := dockerClient.ContainerStop(ctx, containerName, nil) if err != nil { - return fmt.Errorf("could not stop container %s: %w", containerName, err) + loggingErr = logStreamInstance.Write("Error while stopping container: " + err.Error()) + + err = fmt.Errorf("could not stop container %s: %w", containerName, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("Successfully stopped container") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) + } + + loggingErr = logStreamInstance.Write("Removing container: " + containerName) + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } err = dockerClient.ContainerRemove(ctx, containerName, dockerTypes.ContainerRemoveOptions{Force: true}) if err != nil { - return fmt.Errorf("could not remove container %s: %w", containerName, err) + loggingErr = logStreamInstance.Write("Error while removing container: " + err.Error()) + + err = fmt.Errorf("could not remove container %s: %w", containerName, err) + + if loggingErr != nil { + err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr) + } + + return err + } + + loggingErr = logStreamInstance.Write("Successfully removed container") + if loggingErr != nil { + return fmt.Errorf("logging error: %w", loggingErr) } return nil diff --git a/app/gitspace/orchestrator/container/wire.go b/app/gitspace/orchestrator/container/wire.go index 0bc6ae4a3..9a265c394 100644 --- a/app/gitspace/orchestrator/container/wire.go +++ b/app/gitspace/orchestrator/container/wire.go @@ -15,6 +15,7 @@ package container import ( + "github.com/harness/gitness/app/gitspace/logutil" "github.com/harness/gitness/infraprovider" "github.com/google/wire" @@ -31,12 +32,14 @@ func ProvideEmbeddedDockerOrchestrator( vsCodeService *VSCode, vsCodeWebService *VSCodeWeb, config *Config, + statefulLogger *logutil.StatefulLogger, ) Orchestrator { return NewEmbeddedDockerOrchestrator( dockerClientFactory, vsCodeService, vsCodeWebService, config, + statefulLogger, ) } diff --git a/cmd/gitness/wire.go b/cmd/gitness/wire.go index 647f5ac53..61ac1ae92 100644 --- a/cmd/gitness/wire.go +++ b/cmd/gitness/wire.go @@ -9,7 +9,6 @@ package main import ( "context" - "github.com/harness/gitness/app/services/gitspaceevent" checkcontroller "github.com/harness/gitness/app/api/controller/check" "github.com/harness/gitness/app/api/controller/connector" @@ -46,6 +45,7 @@ import ( pullreqevents "github.com/harness/gitness/app/events/pullreq" repoevents "github.com/harness/gitness/app/events/repo" infrastructure "github.com/harness/gitness/app/gitspace/infrastructure" + "github.com/harness/gitness/app/gitspace/logutil" "github.com/harness/gitness/app/gitspace/orchestrator" containerorchestrator "github.com/harness/gitness/app/gitspace/orchestrator/container" "github.com/harness/gitness/app/gitspace/scm" @@ -65,6 +65,7 @@ import ( "github.com/harness/gitness/app/services/codecomments" "github.com/harness/gitness/app/services/codeowners" "github.com/harness/gitness/app/services/exporter" + "github.com/harness/gitness/app/services/gitspaceevent" "github.com/harness/gitness/app/services/importer" "github.com/harness/gitness/app/services/keywordsearch" locker "github.com/harness/gitness/app/services/locker" @@ -221,6 +222,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e cliserver.ProvideDockerConfig, cliserver.ProvideGitspaceContainerOrchestratorConfig, cliserver.ProvideGitspaceEventConfig, + logutil.WireSet, ) return &cliserver.System{}, nil } diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index 66feea713..dc76859a6 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -44,6 +44,7 @@ import ( events3 "github.com/harness/gitness/app/events/pullreq" events2 "github.com/harness/gitness/app/events/repo" "github.com/harness/gitness/app/gitspace/infrastructure" + "github.com/harness/gitness/app/gitspace/logutil" "github.com/harness/gitness/app/gitspace/orchestrator" "github.com/harness/gitness/app/gitspace/orchestrator/container" "github.com/harness/gitness/app/gitspace/scm" @@ -338,7 +339,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro if err != nil { return nil, err } - containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, vsCode, vsCodeWeb, containerConfig) + statefulLogger := logutil.ProvideStatefulLogger(logStream) + containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, vsCode, vsCodeWeb, containerConfig, statefulLogger) orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator) gitspaceEventStore := database.ProvideGitspaceEventStore(db) gitspaceController := gitspace.ProvideController(authorizer, infraProviderResourceStore, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter3, orchestratorOrchestrator, gitspaceEventStore)