ctr: move tasks, run to commands package

Signed-off-by: Jess Valarezo <valarezo.jessica@gmail.com>
This commit is contained in:
Jess Valarezo 2017-10-31 11:57:41 -07:00
parent fe3d9a70fa
commit c3b70f1d0b
18 changed files with 271 additions and 244 deletions

View File

@ -1,13 +1,16 @@
package main package run
import ( import (
gocontext "context" gocontext "context"
"encoding/csv"
"fmt" "fmt"
"runtime" "runtime"
"strings"
"github.com/containerd/console" "github.com/containerd/console"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/tasks"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -15,10 +18,6 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
type resizer interface {
Resize(ctx gocontext.Context, w, h uint32) error
}
func withEnv(context *cli.Context) containerd.SpecOpts { func withEnv(context *cli.Context) containerd.SpecOpts {
return func(_ gocontext.Context, _ *containerd.Client, _ *containers.Container, s *specs.Spec) error { return func(_ gocontext.Context, _ *containerd.Client, _ *containers.Container, s *specs.Spec) error {
env := context.StringSlice("env") env := context.StringSlice("env")
@ -42,10 +41,84 @@ func withMounts(context *cli.Context) containerd.SpecOpts {
} }
} }
var runCommand = cli.Command{ // parseMountFlag parses a mount string in the form "type=foo,source=/path,destination=/target,options=rbind:rw"
func parseMountFlag(m string) (specs.Mount, error) {
mount := specs.Mount{}
r := csv.NewReader(strings.NewReader(m))
fields, err := r.Read()
if err != nil {
return mount, err
}
for _, field := range fields {
v := strings.Split(field, "=")
if len(v) != 2 {
return mount, fmt.Errorf("invalid mount specification: expected key=val")
}
key := v[0]
val := v[1]
switch key {
case "type":
mount.Type = val
case "source", "src":
mount.Source = val
case "destination", "dst":
mount.Destination = val
case "options":
mount.Options = strings.Split(val, ":")
default:
return mount, fmt.Errorf("mount option %q not supported", key)
}
}
return mount, nil
}
// replaceOrAppendEnvValues returns the defaults with the overrides either
// replaced by env key or appended to the list
func replaceOrAppendEnvValues(defaults, overrides []string) []string {
cache := make(map[string]int, len(defaults))
for i, e := range defaults {
parts := strings.SplitN(e, "=", 2)
cache[parts[0]] = i
}
for _, value := range overrides {
// Values w/o = means they want this env to be removed/unset.
if !strings.Contains(value, "=") {
if i, exists := cache[value]; exists {
defaults[i] = "" // Used to indicate it should be removed
}
continue
}
// Just do a normal set/update
parts := strings.SplitN(value, "=", 2)
if i, exists := cache[parts[0]]; exists {
defaults[i] = value
} else {
defaults = append(defaults, value)
}
}
// Now remove all entries that we want to "unset"
for i := 0; i < len(defaults); i++ {
if defaults[i] == "" {
defaults = append(defaults[:i], defaults[i+1:]...)
i--
}
}
return defaults
}
// Command runs a container
var Command = cli.Command{
Name: "run", Name: "run",
Usage: "run a container", Usage: "run a container",
ArgsUsage: "Image|RootFS ID [COMMAND] [ARG...]", ArgsUsage: "[flags] Image|RootFS ID [COMMAND] [ARG...]",
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "tty,t", Name: "tty,t",
@ -125,7 +198,7 @@ var runCommand = cli.Command{
if context.Bool("rm") && !detach { if context.Bool("rm") && !detach {
defer container.Delete(ctx, containerd.WithSnapshotCleanup) defer container.Delete(ctx, containerd.WithSnapshotCleanup)
} }
task, err := newTask(ctx, client, container, context.String("checkpoint"), tty, context.Bool("null-io")) task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), tty, context.Bool("null-io"))
if err != nil { if err != nil {
return err return err
} }
@ -151,7 +224,7 @@ var runCommand = cli.Command{
return nil return nil
} }
if tty { if tty {
if err := handleConsoleResize(ctx, task, con); err != nil { if err := tasks.HandleConsoleResize(ctx, task, con); err != nil {
logrus.WithError(err).Error("console resize") logrus.WithError(err).Error("console resize")
} }
} else { } else {

View File

@ -1,56 +1,23 @@
// +build !windows // +build !windows
package main package run
import ( import (
gocontext "context" gocontext "context"
"os"
"os/signal"
"golang.org/x/sys/unix"
"github.com/containerd/console"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
func init() { func init() {
runCommand.Flags = append(runCommand.Flags, cli.BoolFlag{ Command.Flags = append(Command.Flags, cli.BoolFlag{
Name: "rootfs", Name: "rootfs",
Usage: "use custom rootfs that is not managed by containerd snapshotter.", Usage: "use custom rootfs that is not managed by containerd snapshotter",
}) })
} }
func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
// do an initial resize of the console
size, err := con.Size()
if err != nil {
return err
}
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
logrus.WithError(err).Error("resize pty")
}
s := make(chan os.Signal, 16)
signal.Notify(s, unix.SIGWINCH)
go func() {
for range s {
size, err := con.Size()
if err != nil {
logrus.WithError(err).Error("get pty size")
continue
}
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
logrus.WithError(err).Error("resize pty")
}
}
}()
return nil
}
func withTTY() containerd.SpecOpts { func withTTY() containerd.SpecOpts {
return containerd.WithTTY return containerd.WithTTY
} }
@ -115,24 +82,3 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
cOpts = append([]containerd.NewContainerOpts{containerd.WithNewSpec(opts...)}, cOpts...) cOpts = append([]containerd.NewContainerOpts{containerd.WithNewSpec(opts...)}, cOpts...)
return client.NewContainer(ctx, id, cOpts...) return client.NewContainer(ctx, id, cOpts...)
} }
func newTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, tty, nullIO bool) (containerd.Task, error) {
if checkpoint == "" {
io := containerd.Stdio
if tty {
io = containerd.StdioTerminal
}
if nullIO {
if tty {
return nil, errors.New("tty and null-io cannot be used together")
}
io = containerd.NullIO
}
return container.NewTask(ctx, io)
}
im, err := client.GetImage(ctx, checkpoint)
if err != nil {
return nil, err
}
return container.NewTask(ctx, containerd.Stdio, containerd.WithTaskCheckpoint(im))
}

View File

@ -1,15 +1,13 @@
package main package run
import ( import (
gocontext "context" gocontext "context"
"time"
"github.com/containerd/console" "github.com/containerd/console"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -19,7 +17,7 @@ import (
const pipeRoot = `\\.\pipe` const pipeRoot = `\\.\pipe`
func init() { func init() {
runCommand.Flags = append(runCommand.Flags, cli.StringSliceFlag{ Command.Flags = append(Command.Flags, cli.StringSliceFlag{
Name: "layer", Name: "layer",
Usage: "HCSSHIM Layers to be used", Usage: "HCSSHIM Layers to be used",
}) })
@ -36,34 +34,6 @@ func withLayers(context *cli.Context) containerd.SpecOpts {
} }
} }
func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
// do an initial resize of the console
size, err := con.Size()
if err != nil {
return err
}
go func() {
prevSize := size
for {
time.Sleep(time.Millisecond * 250)
size, err := con.Size()
if err != nil {
log.G(ctx).WithError(err).Error("get pty size")
continue
}
if size.Width != prevSize.Width || size.Height != prevSize.Height {
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
logrus.WithError(err).Error("resize pty")
}
prevSize = size
}
}
}()
return nil
}
func withTTY(terminal bool) containerd.SpecOpts { func withTTY(terminal bool) containerd.SpecOpts {
if !terminal { if !terminal {
return func(ctx gocontext.Context, client *containerd.Client, c *containers.Container, s *specs.Spec) error { return func(ctx gocontext.Context, client *containerd.Client, c *containers.Container, s *specs.Spec) error {
@ -117,17 +87,3 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
// TODO(mlaventure): containerd.WithImage(image), // TODO(mlaventure): containerd.WithImage(image),
) )
} }
func newTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty, nullIO bool) (containerd.Task, error) {
io := containerd.Stdio
if tty {
io = containerd.StdioTerminal
}
if nullIO {
if tty {
return nil, errors.New("tty and null-io cannot be used together")
}
io = containerd.NullIO
}
return container.NewTask(ctx, io)
}

View File

@ -1,4 +1,4 @@
package main package tasks
import ( import (
"os" "os"
@ -10,7 +10,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var taskAttachCommand = cli.Command{ var attachCommand = cli.Command{
Name: "attach", Name: "attach",
Usage: "attach to the IO of a running container", Usage: "attach to the IO of a running container",
ArgsUsage: "CONTAINER", ArgsUsage: "CONTAINER",
@ -51,7 +51,7 @@ var taskAttachCommand = cli.Command{
} }
if tty { if tty {
if err := handleConsoleResize(ctx, task, con); err != nil { if err := HandleConsoleResize(ctx, task, con); err != nil {
logrus.WithError(err).Error("console resize") logrus.WithError(err).Error("console resize")
} }
} else { } else {

View File

@ -1,4 +1,4 @@
package main package tasks
import ( import (
"fmt" "fmt"
@ -9,10 +9,10 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var taskCheckpointCommand = cli.Command{ var checkpointCommand = cli.Command{
Name: "checkpoint", Name: "checkpoint",
Usage: "checkpoint a container", Usage: "checkpoint a container",
ArgsUsage: "CONTAINER", ArgsUsage: "[flags] CONTAINER",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "exit", Name: "exit",

View File

@ -1,11 +1,11 @@
package main package tasks
import ( import (
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var taskDeleteCommand = cli.Command{ var deleteCommand = cli.Command{
Name: "delete", Name: "delete",
Usage: "delete a task", Usage: "delete a task",
ArgsUsage: "CONTAINER", ArgsUsage: "CONTAINER",

View File

@ -1,4 +1,4 @@
package main package tasks
import ( import (
"errors" "errors"
@ -11,10 +11,10 @@ import (
) )
//TODO:(jessvalarezo) exec-id is optional here, update to required arg //TODO:(jessvalarezo) exec-id is optional here, update to required arg
var taskExecCommand = cli.Command{ var execCommand = cli.Command{
Name: "exec", Name: "exec",
Usage: "execute additional processes in an existing container", Usage: "execute additional processes in an existing container",
ArgsUsage: "CONTAINER CMD [ARG...]", ArgsUsage: "[flags] CONTAINER CMD [ARG...]",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "cwd", Name: "cwd",
@ -84,7 +84,7 @@ var taskExecCommand = cli.Command{
} }
} }
if tty { if tty {
if err := handleConsoleResize(ctx, process, con); err != nil { if err := HandleConsoleResize(ctx, process, con); err != nil {
logrus.WithError(err).Error("console resize") logrus.WithError(err).Error("console resize")
} }
} else { } else {

View File

@ -1,4 +1,4 @@
package main package tasks
import ( import (
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
@ -8,10 +8,10 @@ import (
// TODO:(jessvalarezo) the pid flag is not used here // TODO:(jessvalarezo) the pid flag is not used here
// update to be able to signal given pid // update to be able to signal given pid
var taskKillCommand = cli.Command{ var killCommand = cli.Command{
Name: "kill", Name: "kill",
Usage: "signal a container (default: SIGTERM)", Usage: "signal a container (default: SIGTERM)",
ArgsUsage: "CONTAINER", ArgsUsage: "[flags] CONTAINER",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "signal, s", Name: "signal, s",

View File

@ -1,4 +1,4 @@
package main package tasks
import ( import (
"fmt" "fmt"
@ -10,28 +10,11 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var tasksCommand = cli.Command{ var listCommand = cli.Command{
Name: "tasks", Name: "list",
Usage: "manage tasks", Usage: "list tasks",
Aliases: []string{"t"}, Aliases: []string{"ls"},
Subcommands: []cli.Command{ ArgsUsage: "[flags]",
taskAttachCommand,
taskCheckpointCommand,
taskExecCommand,
taskKillCommand,
taskPauseCommand,
taskPsCommand,
taskResumeCommand,
taskStartCommand,
taskDeleteCommand,
taskListCommand,
},
}
var taskListCommand = cli.Command{
Name: "list",
Usage: "list tasks",
Aliases: []string{"ls"},
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "quiet, q", Name: "quiet, q",

View File

@ -1,11 +1,11 @@
package main package tasks
import ( import (
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var taskPauseCommand = cli.Command{ var pauseCommand = cli.Command{
Name: "pause", Name: "pause",
Usage: "pause an existing container", Usage: "pause an existing container",
ArgsUsage: "CONTAINER", ArgsUsage: "CONTAINER",

View File

@ -1,4 +1,4 @@
package main package tasks
import ( import (
"fmt" "fmt"
@ -11,7 +11,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var taskPsCommand = cli.Command{ var psCommand = cli.Command{
Name: "ps", Name: "ps",
Usage: "list processes for container", Usage: "list processes for container",
ArgsUsage: "CONTAINER", ArgsUsage: "CONTAINER",

View File

@ -1,11 +1,11 @@
package main package tasks
import ( import (
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var taskResumeCommand = cli.Command{ var resumeCommand = cli.Command{
Name: "resume", Name: "resume",
Usage: "resume a paused container", Usage: "resume a paused container",
ArgsUsage: "CONTAINER", ArgsUsage: "CONTAINER",

View File

@ -1,4 +1,4 @@
package main package tasks
import ( import (
"github.com/containerd/console" "github.com/containerd/console"
@ -8,7 +8,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var taskStartCommand = cli.Command{ var startCommand = cli.Command{
Name: "start", Name: "start",
Usage: "start a container that have been created", Usage: "start a container that have been created",
ArgsUsage: "CONTAINER", ArgsUsage: "CONTAINER",
@ -43,7 +43,7 @@ var taskStartCommand = cli.Command{
tty := spec.Process.Terminal tty := spec.Process.Terminal
task, err := newTask(ctx, client, container, "", tty, context.Bool("null-io")) task, err := NewTask(ctx, client, container, "", tty, context.Bool("null-io"))
if err != nil { if err != nil {
return err return err
} }
@ -66,7 +66,7 @@ var taskStartCommand = cli.Command{
return err return err
} }
if tty { if tty {
if err := handleConsoleResize(ctx, task, con); err != nil { if err := HandleConsoleResize(ctx, task, con); err != nil {
logrus.WithError(err).Error("console resize") logrus.WithError(err).Error("console resize")
} }
} else { } else {

View File

@ -0,0 +1,30 @@
package tasks
import (
gocontext "context"
"github.com/urfave/cli"
)
type resizer interface {
Resize(ctx gocontext.Context, w, h uint32) error
}
// Command is the cli command for managing tasks
var Command = cli.Command{
Name: "tasks",
Usage: "manage tasks",
Aliases: []string{"t"},
Subcommands: []cli.Command{
attachCommand,
checkpointCommand,
deleteCommand,
execCommand,
listCommand,
killCommand,
pauseCommand,
psCommand,
resumeCommand,
startCommand,
},
}

View File

@ -0,0 +1,64 @@
// +build !windows
package tasks
import (
gocontext "context"
"os"
"os/signal"
"github.com/containerd/console"
"github.com/containerd/containerd"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
// HandleConsoleResize resizes the console
func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
// do an initial resize of the console
size, err := con.Size()
if err != nil {
return err
}
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
log.G(ctx).WithError(err).Error("resize pty")
}
s := make(chan os.Signal, 16)
signal.Notify(s, unix.SIGWINCH)
go func() {
for range s {
size, err := con.Size()
if err != nil {
log.G(ctx).WithError(err).Error("get pty size")
continue
}
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
log.G(ctx).WithError(err).Error("resize pty")
}
}
}()
return nil
}
// NewTask creates a new task
func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, tty, nullIO bool) (containerd.Task, error) {
if checkpoint == "" {
io := containerd.Stdio
if tty {
io = containerd.StdioTerminal
}
if nullIO {
if tty {
return nil, errors.New("tty and null-io cannot be used together")
}
io = containerd.NullIO
}
return container.NewTask(ctx, io)
}
im, err := client.GetImage(ctx, checkpoint)
if err != nil {
return nil, err
}
return container.NewTask(ctx, containerd.Stdio, containerd.WithTaskCheckpoint(im))
}

View File

@ -0,0 +1,55 @@
package tasks
import (
gocontext "context"
"time"
"github.com/containerd/console"
"github.com/containerd/containerd"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
)
// HandleConsoleResize resizes the console
func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
// do an initial resize of the console
size, err := con.Size()
if err != nil {
return err
}
go func() {
prevSize := size
for {
time.Sleep(time.Millisecond * 250)
size, err := con.Size()
if err != nil {
log.G(ctx).WithError(err).Error("get pty size")
continue
}
if size.Width != prevSize.Width || size.Height != prevSize.Height {
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
log.G(ctx).WithError(err).Error("resize pty")
}
prevSize = size
}
}
}()
return nil
}
// NewTask creates a new task
func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty, nullIO bool) (containerd.Task, error) {
io := containerd.Stdio
if tty {
io = containerd.StdioTerminal
}
if nullIO {
if tty {
return nil, errors.New("tty and null-io cannot be used together")
}
io = containerd.NullIO
}
return container.NewTask(ctx, io)
}

View File

@ -12,7 +12,9 @@ import (
"github.com/containerd/containerd/cmd/ctr/commands/images" "github.com/containerd/containerd/cmd/ctr/commands/images"
namespacesCmd "github.com/containerd/containerd/cmd/ctr/commands/namespaces" namespacesCmd "github.com/containerd/containerd/cmd/ctr/commands/namespaces"
"github.com/containerd/containerd/cmd/ctr/commands/plugins" "github.com/containerd/containerd/cmd/ctr/commands/plugins"
"github.com/containerd/containerd/cmd/ctr/commands/run"
"github.com/containerd/containerd/cmd/ctr/commands/snapshot" "github.com/containerd/containerd/cmd/ctr/commands/snapshot"
"github.com/containerd/containerd/cmd/ctr/commands/tasks"
versionCmd "github.com/containerd/containerd/cmd/ctr/commands/version" versionCmd "github.com/containerd/containerd/cmd/ctr/commands/version"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/server" "github.com/containerd/containerd/server"
@ -80,9 +82,9 @@ containerd CLI
images.Command, images.Command,
namespacesCmd.Command, namespacesCmd.Command,
pprofCommand, pprofCommand,
runCommand, run.Command,
snapshot.Command, snapshot.Command,
tasksCommand, tasks.Command,
}, extraCmds...) }, extraCmds...)
app.Before = func(context *cli.Context) error { app.Before = func(context *cli.Context) error {
if context.GlobalBool("debug") { if context.GlobalBool("debug") {

View File

@ -1,82 +0,0 @@
package main
import (
"encoding/csv"
"fmt"
"strings"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
// parseMountFlag parses a mount string in the form "type=foo,source=/path,destination=/target,options=rbind:rw"
func parseMountFlag(m string) (specs.Mount, error) {
mount := specs.Mount{}
r := csv.NewReader(strings.NewReader(m))
fields, err := r.Read()
if err != nil {
return mount, err
}
for _, field := range fields {
v := strings.Split(field, "=")
if len(v) != 2 {
return mount, fmt.Errorf("invalid mount specification: expected key=val")
}
key := v[0]
val := v[1]
switch key {
case "type":
mount.Type = val
case "source", "src":
mount.Source = val
case "destination", "dst":
mount.Destination = val
case "options":
mount.Options = strings.Split(val, ":")
default:
return mount, fmt.Errorf("mount option %q not supported", key)
}
}
return mount, nil
}
// replaceOrAppendEnvValues returns the defaults with the overrides either
// replaced by env key or appended to the list
func replaceOrAppendEnvValues(defaults, overrides []string) []string {
cache := make(map[string]int, len(defaults))
for i, e := range defaults {
parts := strings.SplitN(e, "=", 2)
cache[parts[0]] = i
}
for _, value := range overrides {
// Values w/o = means they want this env to be removed/unset.
if !strings.Contains(value, "=") {
if i, exists := cache[value]; exists {
defaults[i] = "" // Used to indicate it should be removed
}
continue
}
// Just do a normal set/update
parts := strings.SplitN(value, "=", 2)
if i, exists := cache[parts[0]]; exists {
defaults[i] = value
} else {
defaults = append(defaults, value)
}
}
// Now remove all entries that we want to "unset"
for i := 0; i < len(defaults); i++ {
if defaults[i] == "" {
defaults = append(defaults[:i], defaults[i+1:]...)
i--
}
}
return defaults
}