Skip to content

Commit

Permalink
feat: improve docker compose coverage (#2320)
Browse files Browse the repository at this point in the history
## Description
This PR contains various improvements (and hacks) to docker compose
transpiler to improve it's coverage of docker composes, specifically in
the `awesome-compose` repo.

- support for per service `env_file` key
- better path support, notably turn filepaths referencing upstream or
home path to Persistent Directories
- use container name as service name if set (required for hostname
networking)
- support for volumes mounting files (before only supported directories
due to files artifact expansion limitations)
- converts invalid service names to ones that are RFC 1035

other fixes for compose to enable more docker compose coverage:
- allow uploading entire package contents into a files artifact
- allow compressing empty directories

## Is this change user facing?
NO

## References
#2043

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Ben Gazzard <ben@dartoxia.com>
Co-authored-by: Gyanendra Mishra <anomaly.the@gmail.com>
Co-authored-by: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com>
Co-authored-by: kurtosisbot <kurtosisbot@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Laurent Luce <laurentluce49@yahoo.com>
Co-authored-by: leoporoli <leandroporoli@gmail.com>
Co-authored-by: leovct <l009.vincent@gmail.com>
  • Loading branch information
9 people authored Mar 22, 2024
1 parent 7bfa28f commit 45bd59e
Show file tree
Hide file tree
Showing 15 changed files with 478 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package user_service_functions

import (
"context"
"fmt"
"strconv"
"strings"
"sync"

Expand Down Expand Up @@ -539,6 +541,7 @@ func createStartServiceOperation(
memoryAllocationMegabytes := serviceConfig.GetMemoryAllocationMegabytes()
privateIPAddrPlaceholder := serviceConfig.GetPrivateIPAddrPlaceholder()
user := serviceConfig.GetUser()
filesToBeMoved := serviceConfig.GetFilesToBeMoved()

// We replace the placeholder value with the actual private IP address
privateIPAddrStr := privateIpAddr.String()
Expand All @@ -552,6 +555,17 @@ func createStartServiceOperation(
envVars[key] = strings.Replace(envVars[key], privateIPAddrPlaceholder, privateIPAddrStr, unlimitedReplacements)
}

// TODO clean this hack up
// this path will only be hit if `files_to_be_moved` is set in Starlark; which is a hidden property
// used by compose transpilation
if len(filesToBeMoved) > 0 {
var err error
cmdArgs, entrypointArgs, err = getUpdatedEntrypointAndCmdFromFilesToBeMoved(ctx, dockerManager, containerImageName, cmdArgs, entrypointArgs, filesToBeMoved)
if err != nil {
return nil, stacktrace.Propagate(err, "an error occurred while handling files for compose")
}
}

volumeMounts := map[string]string{}
shouldDeleteVolumes := true
if filesArtifactsExpansion != nil {
Expand Down Expand Up @@ -753,6 +767,50 @@ func createStartServiceOperation(
}
}

// TODO - clean this up this is super janky, a way to handle compose & volume
func getUpdatedEntrypointAndCmdFromFilesToBeMoved(ctx context.Context, dockerManager *docker_manager.DockerManager, containerImageName string, cmdArgs []string, entrypointArgs []string, filesToBeMoved map[string]string) ([]string, []string, error) {
concatenatedFilesToBeMoved := []string{}
for source, destination := range filesToBeMoved {
// TODO improve this; the first condition handles files the other folders
concatenatedFilesToBeMoved = append(concatenatedFilesToBeMoved, fmt.Sprintf("mv %v %v", source, destination))
}

concatenatedFilesToBeMovedAsStr := strings.Join(concatenatedFilesToBeMoved, " && ")
originalEntrypointArgs, originalCmdArgs, err := dockerManager.GetEntryPointAndCommand(ctx, containerImageName)
if err != nil {
return nil, nil, stacktrace.Propagate(err, "an error occurred fetching data about image '%v'", containerImageName)
}

// we do this replacement as we want to keep the original command args as they are not overwritten
// it might be that none of the two are set
if len(cmdArgs) == 0 && len(originalCmdArgs) > 0 {
cmdArgs = originalCmdArgs
}
if len(entrypointArgs) == 0 && len(originalEntrypointArgs) > 0 {
entrypointArgs = originalEntrypointArgs
}

entryPointArgsAsStr := quoteAndJoinArgs(entrypointArgs)
cmdArgsAsStr := quoteAndJoinArgs(cmdArgs)

if len(cmdArgs) > 0 {
if len(entrypointArgs) > 0 {
cmdArgs = []string{"-c", concatenatedFilesToBeMovedAsStr + " && " + entryPointArgsAsStr + " " + cmdArgsAsStr}
} else {
cmdArgs = []string{"-c", concatenatedFilesToBeMovedAsStr + " && " + cmdArgsAsStr}
}
} else {
if len(entrypointArgs) > 0 {
cmdArgs = []string{"-c", concatenatedFilesToBeMovedAsStr + " && " + entryPointArgsAsStr}
} else {
// no entrypoint and no command; this shouldn't really happen
cmdArgs = []string{"-c", concatenatedFilesToBeMovedAsStr}
}
}
entrypointArgs = []string{"/bin/sh"}
return cmdArgs, entrypointArgs, nil
}

// Ensure that provided [privatePorts] and [publicPorts] are one to one by checking:
// - There is a matching publicPort for every portID in privatePorts
// - There are the same amount of private and public ports
Expand Down Expand Up @@ -845,3 +903,11 @@ func registerUserServices(

return successfulRegistrations, failedRegistrations, nil
}

func quoteAndJoinArgs(args []string) string {
var quotedArgs []string
for _, arg := range args {
quotedArgs = append(quotedArgs, strconv.Quote(arg))
}
return strings.Join(quotedArgs, " ")
}
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,17 @@ func (manager *DockerManager) getImagePlatform(ctx context.Context, imageName st
return imageInspect.Architecture, nil
}

func (manager *DockerManager) GetEntryPointAndCommand(ctx context.Context, imageName string) ([]string, []string, error) {
imageInspect, _, err := manager.dockerClient.ImageInspectWithRaw(ctx, imageName)
if err != nil {
return nil, nil, stacktrace.Propagate(err, "an error occurred while running image inspect on image '%v'", imageName)
}
if imageInspect.Config == nil {
return nil, nil, stacktrace.NewError("image inspect config was empty, can't geet entrypoint or cmd: %v", imageInspect)
}
return imageInspect.Config.Entrypoint, imageInspect.Config.Cmd, nil
}

/*
Creates a Docker-Container-To-Host Port mapping, defining how a Container's JSON RPC and service-specific ports are
mapped to the host ports.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ package service

import (
"encoding/json"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_download_mode"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_download_mode"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec"
Expand Down Expand Up @@ -70,6 +69,8 @@ type privateServiceConfig struct {
NodeSelectors map[string]string

ImageDownloadMode image_download_mode.ImageDownloadMode

FilesToBeMoved map[string]string
}

func CreateServiceConfig(
Expand Down Expand Up @@ -123,6 +124,7 @@ func CreateServiceConfig(
Tolerations: tolerations,
NodeSelectors: nodeSelectors,
ImageDownloadMode: imageDownloadMode,
FilesToBeMoved: map[string]string{},
}
return &ServiceConfig{internalServiceConfig}, nil
}
Expand Down Expand Up @@ -217,6 +219,14 @@ func (serviceConfig *ServiceConfig) GetNodeSelectors() map[string]string {
return serviceConfig.privateServiceConfig.NodeSelectors
}

func (serviceConfig *ServiceConfig) SetFilesToBeMoved(filesToBeMoved map[string]string) {
serviceConfig.privateServiceConfig.FilesToBeMoved = filesToBeMoved
}

func (serviceConfig *ServiceConfig) GetFilesToBeMoved() map[string]string {
return serviceConfig.privateServiceConfig.FilesToBeMoved
}

func (serviceConfig *ServiceConfig) UnmarshalJSON(data []byte) error {

// Suppressing exhaustruct requirement because we want an object with zero values
Expand Down
Loading

0 comments on commit 45bd59e

Please sign in to comment.