drone/app/gitspace/orchestrator/devcontainer/exec.go
Dhruv Dhruv b1b4111271 fix: [CDE-229]: Updating code-repo mgmt inside gitspace container. (#2460)
* feat: [CDE-230]: Cleaning logs from command exec inside gitspace container.
* fix: [CDE-229]: Updating code-repo mgmt inside gitspace container.
2024-08-11 05:22:58 +00:00

134 lines
3.1 KiB
Go

// 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 devcontainer
import (
"bytes"
"context"
"fmt"
"io"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
)
const RootUser = "root"
type Exec struct {
ContainerName string
DockerClient *client.Client
HomeDir string
UserIdentifier string
}
type execResult struct {
StdOut []byte
StdErr []byte
}
func (e *Exec) ExecuteCommand(
ctx context.Context,
command string,
root bool,
detach bool,
workingDir string,
) ([]byte, error) {
user := e.UserIdentifier
if root {
user = RootUser
}
cmd := []string{"/bin/sh", "-c", command}
execConfig := container.ExecOptions{
User: user,
AttachStdout: true,
AttachStderr: true,
Cmd: cmd,
Detach: detach,
WorkingDir: workingDir,
}
execID, err := e.DockerClient.ContainerExecCreate(ctx, e.ContainerName, execConfig)
if err != nil {
return nil, fmt.Errorf("failed to create docker exec for container %s: %w", e.ContainerName, err)
}
attachResult, err := e.attachExec(ctx, execID.ID, detach)
if err != nil && err.Error() != "unable to upgrade to tcp, received 200" {
return nil, fmt.Errorf("failed to start docker exec for container %s: %w", e.ContainerName, err)
}
var stdOutput []byte
if attachResult != nil {
stdOutput = attachResult.StdOut
}
return stdOutput, nil
}
func (e *Exec) ExecuteCommandInHomeDirectory(
ctx context.Context,
command string,
root bool,
detach bool,
) ([]byte, error) {
return e.ExecuteCommand(ctx, command, root, detach, e.HomeDir)
}
func (e *Exec) attachExec(ctx context.Context, id string, detach bool) (*execResult, error) {
resp, attachErr := e.DockerClient.ContainerExecAttach(ctx, id, container.ExecStartOptions{Detach: detach})
if attachErr != nil {
return nil, attachErr
}
defer resp.Close()
var outBuf, errBuf bytes.Buffer
copyErr := make(chan error)
go func() {
// StdCopy demultiplexes the stream into two buffers
_, err := stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader)
copyErr <- err
}()
select {
case err := <-copyErr:
if err != nil {
return nil, err
}
break
case <-ctx.Done():
return nil, ctx.Err()
}
stdout, err := io.ReadAll(&outBuf)
if err != nil {
return nil, fmt.Errorf("failed to read stdout of exec for container %s: %w", e.ContainerName, err)
}
stderr, err := io.ReadAll(&errBuf)
if err != nil {
return nil, fmt.Errorf("failed to read stderr of exec for container %s: %w", e.ContainerName, err)
}
return &execResult{
StdOut: stdout,
StdErr: stderr,
}, nil
}