Skip to content

Commit

Permalink
feat(harchery:swarm): improve compatibility with docker windows (#5636)
Browse files Browse the repository at this point in the history
Signed-off-by: richardlt <[email protected]>
  • Loading branch information
richardlt authored Jan 14, 2021
1 parent efbe00d commit d1af983
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 37 deletions.
6 changes: 5 additions & 1 deletion engine/hatchery/swarm/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ func (h *HatcherySwarm) SpawnWorker(ctx context.Context, spawnArgs hatchery.Spaw
serviceMemory = int64(i)
}
}
serviceMemorySwap := int64(-1)
if h.Config.DisableMemorySwap {
serviceMemorySwap = 0
}

var cmdArgs []string
if sa, ok := envm["CDS_SERVICE_ARGS"]; ok {
Expand Down Expand Up @@ -328,7 +332,6 @@ func (h *HatcherySwarm) SpawnWorker(ctx context.Context, spawnArgs hatchery.Spaw
labels[hatchery.LabelServiceJobID] = fmt.Sprintf("%d", spawnArgs.JobID)
labels[hatchery.LabelServiceID] = fmt.Sprintf("%d", r.ID)
labels[hatchery.LabelServiceReqName] = r.Name

}

//Start the services
Expand All @@ -341,6 +344,7 @@ func (h *HatcherySwarm) SpawnWorker(ctx context.Context, spawnArgs hatchery.Spaw
env: env,
labels: labels,
memory: serviceMemory,
memorySwap: serviceMemorySwap,
entryPoint: nil,
}

Expand Down
3 changes: 2 additions & 1 deletion engine/hatchery/swarm/swarm_util_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type containerArgs struct {
cmd, env []string
labels map[string]string
memory int64
memorySwap int64
dockerOpts dockerOpts
entryPoint strslice.StrSlice
}
Expand Down Expand Up @@ -86,7 +87,7 @@ func (h *HatcherySwarm) createAndStartContainer(ctx context.Context, dockerClien
}
hostConfig.Resources = container.Resources{
Memory: cArgs.memory * 1024 * 1024, //from MB to B
MemorySwap: -1,
MemorySwap: cArgs.memorySwap,
}

networkingConfig := &network.NetworkingConfig{
Expand Down
25 changes: 14 additions & 11 deletions engine/hatchery/swarm/swarm_util_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,12 @@ func Test_computeDockerOpts(t *testing.T) {
func TestHatcherySwarm_createAndStartContainer(t *testing.T) {
h := testSwarmHatchery(t)
args := containerArgs{
name: "my-nginx",
image: "nginx:latest",
env: []string{"FROM_CDS", "FROM_CDS"},
labels: map[string]string{"FROM_CDS": "FROM_CDS"},
memory: 256,
name: "my-nginx",
image: "nginx:latest",
env: []string{"FROM_CDS", "FROM_CDS"},
labels: map[string]string{"FROM_CDS": "FROM_CDS"},
memory: 256,
memorySwap: -1,
}

// RegisterOnly = true, this will pull image if image is not found
Expand All @@ -138,12 +139,13 @@ func TestHatcherySwarm_createAndStartContainer(t *testing.T) {
func TestHatcherySwarm_createAndStartContainerWithMount(t *testing.T) {
h := testSwarmHatchery(t)
args := containerArgs{
name: "my-nginx",
image: "nginx:latest",
cmd: []string{"uname"},
env: []string{"FROM_CDS", "FROM_CDS"},
labels: map[string]string{"FROM_CDS": "FROM_CDS"},
memory: 256,
name: "my-nginx",
image: "nginx:latest",
cmd: []string{"uname"},
env: []string{"FROM_CDS", "FROM_CDS"},
labels: map[string]string{"FROM_CDS": "FROM_CDS"},
memory: 256,
memorySwap: -1,
dockerOpts: dockerOpts{
mounts: []mount.Mount{
{
Expand Down Expand Up @@ -188,6 +190,7 @@ func TestHatcherySwarm_createAndStartContainerWithNetwork(t *testing.T) {
env: []string{"FROM_CDS", "FROM_CDS"},
labels: map[string]string{"FROM_CDS": "FROM_CDS"},
memory: 256,
memorySwap: -1,
network: "my-network",
networkAlias: "my-container",
}
Expand Down
5 changes: 2 additions & 3 deletions engine/hatchery/swarm/swarm_util_kill.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,18 @@ func (h *HatcherySwarm) killAndRemove(ctx context.Context, dockerClient *dockerC
Since: "10s",
}
var spawnErr = sdk.SpawnErrorForm{
Error: err.Error(),
Error: sdk.NewErrorFrom(err, "an error occurred when registering the model with container name: %s", container.Name).Error(),
}

logsReader, errL := dockerClient.ContainerLogs(ctx, container.ID, logsOpts)
if errL != nil {
log.Error(ctx, "hatchery> swarm> killAndRemove> cannot get logs from docker for containers service %s %v : %v", container.ID, container.Name, errL)
spawnErr.Logs = []byte(fmt.Sprintf("unable to get container logs: %v", errL))

} else if logsReader != nil {
defer logsReader.Close()
logs, errR := ioutil.ReadAll(logsReader)
if errR != nil {
log.Error(ctx, "hatchery> swarm> killAndRemove> cannot read logs for containers service %s %v : %v", container.ID, container.Name, errR)
spawnErr.Logs = []byte(fmt.Sprintf("unable to get read container logs: %v", errR))
} else if logs != nil {
spawnErr.Logs = logs
}
Expand Down
3 changes: 2 additions & 1 deletion engine/hatchery/swarm/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ type HatcheryConfiguration struct {
MaxContainers int `mapstructure:"maxContainers" toml:"maxContainers" default:"10" commented:"false" comment:"Max Containers on Host managed by this Hatchery" json:"maxContainers"`

// DefaultMemory Worker default memory
DefaultMemory int `mapstructure:"defaultMemory" toml:"defaultMemory" default:"1024" commented:"false" comment:"Worker default memory in Mo" json:"defaultMemory"`
DefaultMemory int `mapstructure:"defaultMemory" toml:"defaultMemory" default:"1024" commented:"false" comment:"Worker default memory in Mo" json:"defaultMemory"`
DisableMemorySwap bool `mapstructure:"disableMemorySwap" toml:"disableMemorySwap" default:"false" commented:"true" comment:"Set to true to disable memory swap" json:"disableMemorySwap"`

// WorkerTTL Worker TTL (minutes)
WorkerTTL int `mapstructure:"workerTTL" toml:"workerTTL" default:"10" commented:"false" comment:"Worker TTL (minutes)" json:"workerTTL"`
Expand Down
41 changes: 26 additions & 15 deletions engine/worker/internal/action/builtin_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,37 +30,39 @@ type script struct {
}

func prepareScriptContent(parameters []sdk.Parameter, basedir afero.Fs, workdir afero.File) (*script, error) {
var script = script{
shell: "/bin/sh",
var script = script{}

// Set default shell based on os
if isWindows() {
script.shell = "PowerShell"
script.opts = []string{"-ExecutionPolicy", "Bypass", "-Command"}
} else {
script.shell = "/bin/sh"
script.opts = []string{"-e"}
}

// Get script content
var scriptContent string
a := sdk.ParameterFind(parameters, "script")
scriptContent = a.Value

// except on windows where it's powershell
if isWindows() {
script.shell = "PowerShell"
script.opts = []string{"-ExecutionPolicy", "Bypass", "-Command"}
// on windows, we add ErrorActionPreference just below
} else if strings.HasPrefix(scriptContent, "#!") { // If user wants a specific shell, use it
if strings.HasPrefix(scriptContent, "#!") { // If user wants a specific shell, use it
t := strings.SplitN(scriptContent, "\n", 2)
script.shell = strings.TrimPrefix(t[0], "#!") // Find out the shebang
script.shell = strings.TrimRight(script.shell, " \t\r\n") // Remove all the trailing shit
splittedShell := strings.Split(script.shell, " ") // Split it to find options
script.shell = splittedShell[0]
script.opts = splittedShell[1:]
// if it's a shell, we add set -e to failed job when a command is failed
if isShell(script.shell) && len(splittedShell) == 1 {
script.opts = append(script.opts, "-e")
if !isWindows() && isShell(script.shell) && len(splittedShell) == 1 {
script.opts = []string{"-e"}
}
if isWindows() && isPowerShell(script.shell) && len(splittedShell) == 1 {
script.opts = []string{"-ExecutionPolicy", "Bypass", "-Command"}
}
if len(t) > 1 {
scriptContent = t[1]
}

} else {
script.opts = []string{"-e"}
}

script.content = []byte(scriptContent)
Expand Down Expand Up @@ -143,8 +145,8 @@ func writeScriptContent(ctx context.Context, script *script, fs afero.Fs, basedi
}
}

if isWindows() {
//This aims to stop a the very first error and return the right exit code
if isWindows() && isPowerShell(script.shell) {
// This aims to stop a the very first error and return the right exit code
psCommand := fmt.Sprintf("& { $ErrorActionPreference='Stop'; & %s ;exit $LastExitCode}", realScriptPath)
script.opts = append(script.opts, psCommand)
} else {
Expand Down Expand Up @@ -313,3 +315,12 @@ func isShell(in string) bool {
}
return false
}

func isPowerShell(in string) bool {
for _, v := range []string{"PowerShell", "pwsh.exe", "powershell.exe"} {
if strings.HasSuffix(in, v) {
return true
}
}
return false
}
8 changes: 3 additions & 5 deletions sdk/hatchery/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,13 @@ loopModels:
// CheckWorkerModelRegister checks if a model has been registered, if not it raises an error on the API
func CheckWorkerModelRegister(h Interface, modelPath string) error {
var sendError bool
var m *sdk.Model
for i := range models {
m = &models[i]
if m.Group.Name+"/"+m.Name == modelPath {
sendError = m.NeedRegistration
if models[i].Group.Name+"/"+models[i].Name == modelPath {
sendError = models[i].NeedRegistration
break
}
}
if m != nil && sendError {
if sendError {
return sdk.WithStack(sdk.ErrWorkerModelDeploymentFailed)
}
return nil
Expand Down

0 comments on commit d1af983

Please sign in to comment.