From fbb082cf3144f22038f43fc6a08fbf2ea8cbd59d Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Thu, 12 Nov 2015 15:59:18 -0800 Subject: [PATCH 01/26] Adding the port hcl object to example --- command/init.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/init.go b/command/init.go index 356337ae807..c911c95cfe9 100644 --- a/command/init.go +++ b/command/init.go @@ -123,6 +123,9 @@ job "example" { # Configure Docker driver with the image config { image = "redis:latest" + port_map { + db = 6379 + } } # We must specify the resources required for @@ -133,7 +136,8 @@ job "example" { memory = 256 # 256MB network { mbits = 10 - dynamic_ports = ["6379"] + port "db" { + } } } } From 7f6e940946b24f1b64e007b7494e37ed503f1c1a Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 13 Nov 2015 18:09:42 -0800 Subject: [PATCH 02/26] Implemented port labeling and driver configs --- client/driver/docker.go | 124 ++++++++++++++----------- client/driver/driver.go | 1 - client/driver/exec.go | 21 ++++- client/driver/executor/test_harness.go | 2 +- client/driver/java.go | 22 ++++- client/driver/qemu.go | 22 ++++- client/driver/raw_exec.go | 15 ++- client/driver/rkt.go | 16 +++- jobspec/parse.go | 51 ++++++---- nomad/mock/mock.go | 18 ++-- nomad/structs/network.go | 14 +-- nomad/structs/structs.go | 67 +++---------- 12 files changed, 208 insertions(+), 165 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index b89f31eb32d..bd0edf2acc3 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/nomad/client/driver/args" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad/structs" + "github.com/mitchellh/mapstructure" ) type DockerDriver struct { @@ -23,6 +24,28 @@ type DockerDriver struct { fingerprint.StaticFingerprinter } +type dockerDriverConfig struct { + ImageName string `mapstructure:"image"` + Command string `mapstructure:"command"` + Args string `mapstructure:"args"` + NetworkMode string `mapstructure:"network_mode"` + PortMap map[string]int `mapstructure:"port_map"` + UserName string `mapstructure:"auth.username"` + Password string `mapstructure:"auth.password` + Email string `mapstructure:"auth.email"` + ServerAddress string `mapstructure:"auth.server_address` + Privileged bool `mapstructure:"privileged"` + DNS string `mapstructure:"dns_server"` + SearchDomains string `mapstructure:"search_domains"` +} + +func (c *dockerDriverConfig) Validate() error { + if c.ImageName == "" { + return fmt.Errorf("Docker Driver needs an image name") + } + return nil +} + type dockerPID struct { ImageID string ContainerID string @@ -116,7 +139,7 @@ func (d *DockerDriver) containerBinds(alloc *allocdir.AllocDir, task *structs.Ta } // createContainer initializes a struct needed to call docker.client.CreateContainer() -func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (docker.CreateContainerOptions, error) { +func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, driverConfig *dockerDriverConfig) (docker.CreateContainerOptions, error) { var c docker.CreateContainerOptions if task.Resources == nil { d.logger.Printf("[ERR] driver.docker: task.Resources is empty") @@ -134,8 +157,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do env.SetTaskLocalDir(filepath.Join("/", allocdir.TaskLocal)) config := &docker.Config{ - Env: env.List(), - Image: task.Config["image"], + Image: driverConfig.ImageName, } hostConfig := &docker.HostConfig{ @@ -184,11 +206,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do return c, fmt.Errorf("Unable to parse docker.privileged.enabled: %s", err) } - if v, ok := task.Config["privileged"]; ok { - taskPrivileged, err := strconv.ParseBool(v) - if err != nil { - return c, fmt.Errorf("Unable to parse boolean value from task config option 'privileged': %v", err) - } + if taskPrivileged := driverConfig.Privileged; taskPrivileged { if taskPrivileged && !hostPrivileged { return c, fmt.Errorf(`Unable to set privileged flag since "docker.privileged.enabled" is false`) } @@ -197,9 +215,9 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do } // set DNS servers - dns, ok := task.Config["dns-servers"] + dns := driverConfig.DNS - if ok && dns != "" { + if dns != "" { for _, v := range strings.Split(dns, ",") { ip := strings.TrimSpace(v) if net.ParseIP(ip) != nil { @@ -211,16 +229,16 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do } // set DNS search domains - dnsSearch, ok := task.Config["search-domains"] + dnsSearch := driverConfig.SearchDomains - if ok && dnsSearch != "" { + if dnsSearch != "" { for _, v := range strings.Split(dnsSearch, ",") { hostConfig.DNSSearch = append(hostConfig.DNSSearch, strings.TrimSpace(v)) } } - mode, ok := task.Config["network_mode"] - if !ok || mode == "" { + mode := driverConfig.NetworkMode + if mode == "" { // docker default d.logger.Printf("[WARN] driver.docker: no mode specified for networking, defaulting to bridge") mode = "bridge" @@ -245,45 +263,38 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do publishedPorts := map[docker.Port][]docker.PortBinding{} exposedPorts := map[docker.Port]struct{}{} - for _, port := range network.ListStaticPorts() { - publishedPorts[docker.Port(strconv.Itoa(port)+"/tcp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port)}} - publishedPorts[docker.Port(strconv.Itoa(port)+"/udp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port)}} + for _, port := range network.ReservedPorts { + publishedPorts[docker.Port(strconv.Itoa(port.Value)+"/tcp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} + publishedPorts[docker.Port(strconv.Itoa(port.Value)+"/udp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (static)\n", network.IP, port, port) - exposedPorts[docker.Port(strconv.Itoa(port)+"/tcp")] = struct{}{} - exposedPorts[docker.Port(strconv.Itoa(port)+"/udp")] = struct{}{} + exposedPorts[docker.Port(strconv.Itoa(port.Value)+"/tcp")] = struct{}{} + exposedPorts[docker.Port(strconv.Itoa(port.Value)+"/udp")] = struct{}{} d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", port) } - for label, port := range network.MapDynamicPorts() { - // If the label is numeric we expect that there is a service - // listening on that port inside the container. In this case we'll - // setup a mapping from our random host port to the label port. - // - // Otherwise we'll setup a direct 1:1 mapping from the host port to - // the container, and assume that the process inside will read the - // environment variable and bind to the correct port. - if _, err := strconv.Atoi(label); err == nil { - publishedPorts[docker.Port(label+"/tcp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port)}} - publishedPorts[docker.Port(label+"/udp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port)}} - d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %s (mapped)", network.IP, port, label) - exposedPorts[docker.Port(label+"/tcp")] = struct{}{} - exposedPorts[docker.Port(label+"/udp")] = struct{}{} - d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", port) - } else { - publishedPorts[docker.Port(strconv.Itoa(port)+"/tcp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port)}} - publishedPorts[docker.Port(strconv.Itoa(port)+"/udp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port)}} - d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d for label %s\n", network.IP, port, port, label) - exposedPorts[docker.Port(strconv.Itoa(port)+"/tcp")] = struct{}{} - exposedPorts[docker.Port(strconv.Itoa(port)+"/udp")] = struct{}{} - d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", port) + containerToHostPortMap := make(map[string]int) + for _, port := range network.DynamicPorts { + containerPort, ok := driverConfig.PortMap[port.Label] + if !ok { + containerPort = port.Value } + cp := strconv.Itoa(containerPort) + hostPort := strconv.Itoa(port.Value) + publishedPorts[docker.Port(cp+"/tcp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPort}} + publishedPorts[docker.Port(cp+"/udp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPort}} + d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (mapped)", network.IP, port.Value, containerPort) + exposedPorts[docker.Port(cp+"/tcp")] = struct{}{} + exposedPorts[docker.Port(cp+"/udp")] = struct{}{} + d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", hostPort) + containerToHostPortMap[cp] = port.Value } + env.SetPorts(containerToHostPortMap) hostConfig.PortBindings = publishedPorts config.ExposedPorts = exposedPorts } - rawArgs, hasArgs := task.Config["args"] + rawArgs := driverConfig.Args parsedArgs, err := args.ParseAndReplace(rawArgs, env.Map()) if err != nil { return c, err @@ -291,16 +302,17 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do // If the user specified a custom command to run as their entrypoint, we'll // inject it here. - if command, ok := task.Config["command"]; ok { + if command := driverConfig.Command; command != "" { cmd := []string{command} - if hasArgs { + if driverConfig.Args != "" { cmd = append(cmd, parsedArgs...) } config.Cmd = cmd - } else if hasArgs { + } else if driverConfig.Args != "" { d.logger.Println("[DEBUG] driver.docker: ignoring args because command not specified") } + config.Env = env.List() return docker.CreateContainerOptions{ Config: config, HostConfig: hostConfig, @@ -308,10 +320,14 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do } func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - // Get the image from config - image, ok := task.Config["image"] - if !ok || image == "" { - return nil, fmt.Errorf("Image not specified") + var driverConfig dockerDriverConfig + if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { + return nil, err + } + image := driverConfig.ImageName + + if err := driverConfig.Validate(); err != nil { + return nil, err } if task.Resources == nil { return nil, fmt.Errorf("Resources are not specified") @@ -361,10 +377,10 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle } authOptions := docker.AuthConfiguration{ - Username: task.Config["auth.username"], - Password: task.Config["auth.password"], - Email: task.Config["auth.email"], - ServerAddress: task.Config["auth.server-address"], + Username: driverConfig.UserName, + Password: driverConfig.Password, + Email: driverConfig.Email, + ServerAddress: driverConfig.ServerAddress, } err = client.PullImage(pullOptions, authOptions) @@ -384,7 +400,7 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle d.logger.Printf("[DEBUG] driver.docker: using image %s", dockerImage.ID) d.logger.Printf("[INFO] driver.docker: identified image %s as %s", image, dockerImage.ID) - config, err := d.createContainer(ctx, task) + config, err := d.createContainer(ctx, task, &driverConfig) if err != nil { d.logger.Printf("[ERR] driver.docker: %s", err) return nil, fmt.Errorf("Failed to create container config for image %s", image) diff --git a/client/driver/driver.go b/client/driver/driver.go index e2739e2b8a2..08087269098 100644 --- a/client/driver/driver.go +++ b/client/driver/driver.go @@ -133,7 +133,6 @@ func TaskEnvironmentVariables(ctx *ExecContext, task *structs.Task) environment. if len(task.Resources.Networks) > 0 { network := task.Resources.Networks[0] env.SetTaskIp(network.IP) - env.SetPorts(network.MapDynamicPorts()) } } diff --git a/client/driver/exec.go b/client/driver/exec.go index 4de719c465a..11eff80aeb1 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/client/getter" "github.com/hashicorp/nomad/nomad/structs" + "github.com/mitchellh/mapstructure" ) // ExecDriver fork/execs tasks using as many of the underlying OS's isolation @@ -21,6 +22,12 @@ type ExecDriver struct { DriverContext fingerprint.StaticFingerprinter } +type execDriverConfig struct { + ArtifactSource string `mapstructure:"artifact_source` + Checksum string `mapstructure:"checksum"` + Command string `mapstructure:"command"` + Args string `mapstructure:"args"` +} // execHandle is returned from Start/Open as a handle to the PID type execHandle struct { @@ -49,9 +56,13 @@ func (d *ExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, } func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { + var driverConfig execDriverConfig + if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { + return nil, err + } // Get the command to be ran - command, ok := task.Config["command"] - if !ok || command == "" { + command := driverConfig.Command + if command == "" { return nil, fmt.Errorf("missing command for exec driver") } @@ -67,8 +78,8 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // Proceed to download an artifact to be executed. _, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), - task.Config["artifact_source"], - task.Config["checksum"], + driverConfig.ArtifactSource, + driverConfig.Checksum, d.logger, ) if err != nil { @@ -81,7 +92,7 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // Look for arguments var args []string - if argRaw, ok := task.Config["args"]; ok { + if argRaw := driverConfig.Args; argRaw != "" { args = append(args, argRaw) } diff --git a/client/driver/executor/test_harness.go b/client/driver/executor/test_harness.go index 1e37f8eff93..34ead751e0a 100644 --- a/client/driver/executor/test_harness.go +++ b/client/driver/executor/test_harness.go @@ -21,7 +21,7 @@ var ( Networks: []*structs.NetworkResource{ &structs.NetworkResource{ MBits: 50, - DynamicPorts: []string{"http"}, + DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, }, }, } diff --git a/client/driver/java.go b/client/driver/java.go index 1aa2c6d3f94..b87fdac794f 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/client/getter" "github.com/hashicorp/nomad/nomad/structs" + "github.com/mitchellh/mapstructure" ) // JavaDriver is a simple driver to execute applications packaged in Jars. @@ -25,6 +26,13 @@ type JavaDriver struct { fingerprint.StaticFingerprinter } +type javaDriverConfig struct { + JvmOpts string `mapstructure:"jvm_options"` + ArtifactSource string `mapstructure:"artifact_source` + Checksum string `mapstructure:"checksum"` + Args string `mapstructure:"args"` +} + // javaHandle is returned from Start/Open as a handle to the PID type javaHandle struct { cmd executor.Executor @@ -90,6 +98,10 @@ func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, } func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { + var driverConfig javaDriverConfig + if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { + return nil, err + } taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) @@ -98,8 +110,8 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // Proceed to download an artifact to be executed. path, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), - task.Config["artifact_source"], - task.Config["checksum"], + driverConfig.ArtifactSource, + driverConfig.Checksum, d.logger, ) if err != nil { @@ -113,15 +125,15 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, args := []string{} // Look for jvm options - jvm_options, ok := task.Config["jvm_options"] - if ok && jvm_options != "" { + jvm_options := driverConfig.JvmOpts + if jvm_options != "" { d.logger.Printf("[DEBUG] driver.java: found JVM options: %s", jvm_options) args = append(args, jvm_options) } // Build the argument list. args = append(args, "-jar", filepath.Join(allocdir.TaskLocal, jarName)) - if argRaw, ok := task.Config["args"]; ok { + if argRaw := driverConfig.Args; argRaw != "" { args = append(args, argRaw) } diff --git a/client/driver/qemu.go b/client/driver/qemu.go index 79193a217ce..23d3f4777bc 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/client/getter" "github.com/hashicorp/nomad/nomad/structs" + "github.com/mitchellh/mapstructure" ) var ( @@ -30,6 +31,13 @@ type QemuDriver struct { fingerprint.StaticFingerprinter } +type qemuDriverConfig struct { + ArtifactSource string `mapstructure:"artifact_source` + Checksum string `mapstructure:"checksum"` + Accelerator string `mapstructure:"accelerator"` + GuestPorts string `mapstructure:"guest_ports"` +} + // qemuHandle is returned from Start/Open as a handle to the PID type qemuHandle struct { cmd executor.Executor @@ -69,6 +77,10 @@ func (d *QemuDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, // Run an existing Qemu image. Start() will pull down an existing, valid Qemu // image and save it to the Drivers Allocation Dir func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { + var driverConfig qemuDriverConfig + if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { + return nil, err + } // Get the image source source, ok := task.Config["artifact_source"] if !ok || source == "" { @@ -90,8 +102,8 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // Proceed to download an artifact to be executed. vmPath, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), - task.Config["artifact_source"], - task.Config["checksum"], + driverConfig.ArtifactSource, + driverConfig.Checksum, d.logger, ) if err != nil { @@ -103,7 +115,7 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // Parse configuration arguments // Create the base arguments accelerator := "tcg" - if acc, ok := task.Config["accelerator"]; ok { + if acc := driverConfig.Accelerator; acc != "" { accelerator = acc } // TODO: Check a lower bounds, e.g. the default 128 of Qemu @@ -132,7 +144,7 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // the Reserved ports in the Task Resources // Users can supply guest_hosts as a list of posts to map on the guest vm. // These map 1:1 with the requested Reserved Ports from the hostmachine. - ports := strings.Split(task.Config["guest_ports"], ",") + ports := strings.Split(driverConfig.GuestPorts, ",") if len(ports) == 0 { return nil, fmt.Errorf("[ERR] driver.qemu: Error parsing required Guest Ports") } @@ -149,7 +161,7 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, reservedPorts := task.Resources.Networks[0].ReservedPorts var forwarding string for i, p := range ports { - forwarding = fmt.Sprintf("%s,hostfwd=tcp::%s-:%s", forwarding, strconv.Itoa(reservedPorts[i]), p) + forwarding = fmt.Sprintf("%s,hostfwd=tcp::%s-:%s", forwarding, strconv.Itoa(reservedPorts[i].Value), p) } if "" == forwarding { diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index 12e99b7f426..64c130378ec 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/client/getter" "github.com/hashicorp/nomad/nomad/structs" + "github.com/mitchellh/mapstructure" ) const ( @@ -56,6 +57,10 @@ func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (boo } func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { + var driverConfig execDriverConfig + if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { + return nil, err + } // Get the tasks local directory. taskName := d.DriverContext.taskName taskDir, ok := ctx.AllocDir.TaskDirs[taskName] @@ -64,8 +69,8 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl } // Get the command to be ran - command, ok := task.Config["command"] - if !ok || command == "" { + command := driverConfig.Command + if command == "" { return nil, fmt.Errorf("missing command for Raw Exec driver") } @@ -75,8 +80,8 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl // Proceed to download an artifact to be executed. _, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), - task.Config["artifact_source"], - task.Config["checksum"], + driverConfig.ArtifactSource, + driverConfig.Checksum, d.logger, ) if err != nil { @@ -89,7 +94,7 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl // Look for arguments var args []string - if argRaw, ok := task.Config["args"]; ok { + if argRaw := driverConfig.Args; argRaw != "" { args = append(args, argRaw) } diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 3f191253174..0911b12a728 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/nomad/client/driver/args" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad/structs" + "github.com/mitchellh/mapstructure" ) var ( @@ -34,6 +35,11 @@ type RktDriver struct { fingerprint.StaticFingerprinter } +type rktDriverConfig struct { + ImageName string `mapstructure:"image"` + Args string `mapstructure:"args"` +} + // rktHandle is returned from Start/Open as a handle to the PID type rktHandle struct { proc *os.Process @@ -83,9 +89,13 @@ func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, e // Run an existing Rkt image. func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { + var driverConfig rktDriverConfig + if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { + return nil, err + } // Validate that the config is valid. - img, ok := task.Config["image"] - if !ok || img == "" { + img := driverConfig.ImageName + if img == "" { return nil, fmt.Errorf("Missing ACI image for rkt") } @@ -139,7 +149,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e } // Add user passed arguments. - if userArgs, ok := task.Config["args"]; ok { + if userArgs := driverConfig.Args; userArgs != "" { parsed, err := args.ParseAndReplace(userArgs, envVars.Map()) if err != nil { return nil, err diff --git a/jobspec/parse.go b/jobspec/parse.go index 1b52adefb8b..d9d66479d6f 100644 --- a/jobspec/parse.go +++ b/jobspec/parse.go @@ -8,7 +8,6 @@ import ( "path/filepath" "regexp" "strconv" - "strings" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/ast" @@ -408,6 +407,7 @@ func parseTasks(result *[]*structs.Task, list *ast.ObjectList) error { if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } + if err := mapstructure.WeakDecode(m, &t.Config); err != nil { return err } @@ -496,21 +496,14 @@ func parseResources(result *structs.Resources, list *ast.ObjectList) error { return err } - // Keep track of labels we've already seen so we can ensure there - // are no collisions when we turn them into environment variables. - // lowercase:NomalCase so we can get the first for the error message - seenLabel := map[string]string{} - for _, label := range r.DynamicPorts { - if !reDynamicPorts.MatchString(label) { - return errDynamicPorts - } - first, seen := seenLabel[strings.ToLower(label)] - if seen { - return fmt.Errorf("Found a port label collision: `%s` overlaps with previous `%s`", label, first) - } else { - seenLabel[strings.ToLower(label)] = label - } - + var networkObj *ast.ObjectList + if ot, ok := o.Items[0].Val.(*ast.ObjectType); ok { + listVal = ot.List + } else { + return fmt.Errorf("resource: should be an object") + } + if err := parsePorts(networkObj, &r); err != nil { + return err } result.Networks = []*structs.NetworkResource{&r} @@ -519,6 +512,32 @@ func parseResources(result *structs.Resources, list *ast.ObjectList) error { return nil } +func parsePorts(networkObj *ast.ObjectList, nw *structs.NetworkResource) error { + portsObjList := networkObj.Filter("Port") + knownPortLabels := make(map[string]bool) + for _, port := range portsObjList.Items { + label := port.Keys[0].Token.Value().(string) + if knownPortLabels[label] { + return fmt.Errorf("Found a port label collision: %s", label) + } + var p map[string]interface{} + var res structs.Port + if err := hcl.DecodeObject(&p, port.Val); err != nil { + return err + } + if err := mapstructure.WeakDecode(p, &res); err != nil { + return err + } + res.Label = label + if res.Value > 0 { + nw.ReservedPorts = append(nw.ReservedPorts, res) + } else { + nw.DynamicPorts = append(nw.DynamicPorts, res) + } + } + return nil +} + func parseUpdate(result *structs.UpdateStrategy, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { diff --git a/nomad/mock/mock.go b/nomad/mock/mock.go index 329ecd87200..a618f787b73 100644 --- a/nomad/mock/mock.go +++ b/nomad/mock/mock.go @@ -37,7 +37,7 @@ func Node() *structs.Node { &structs.NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []int{22}, + ReservedPorts: []structs.Port{structs.Port{Label: "main", Value: 22}}, MBits: 1, }, }, @@ -83,7 +83,7 @@ func Job() *structs.Job { &structs.Task{ Name: "web", Driver: "exec", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/date", "args": "+%s", }, @@ -96,7 +96,7 @@ func Job() *structs.Job { Networks: []*structs.NetworkResource{ &structs.NetworkResource{ MBits: 50, - DynamicPorts: []string{"http"}, + DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, }, }, }, @@ -148,7 +148,7 @@ func SystemJob() *structs.Job { &structs.Task{ Name: "web", Driver: "exec", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/date", "args": "+%s", }, @@ -158,7 +158,7 @@ func SystemJob() *structs.Job { Networks: []*structs.NetworkResource{ &structs.NetworkResource{ MBits: 50, - DynamicPorts: []string{"http"}, + DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, }, }, }, @@ -200,9 +200,9 @@ func Alloc() *structs.Allocation { &structs.NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []int{12345}, + ReservedPorts: []structs.Port{structs.Port{Label: "main", Value: 12345}}, MBits: 100, - DynamicPorts: []string{"http"}, + DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, }, }, }, @@ -214,9 +214,9 @@ func Alloc() *structs.Allocation { &structs.NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []int{5000}, + ReservedPorts: []structs.Port{structs.Port{Label: "main", Value: 5000}}, MBits: 50, - DynamicPorts: []string{"http"}, + DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, }, }, }, diff --git a/nomad/structs/network.go b/nomad/structs/network.go index 9221adc7ac4..33327a50b8a 100644 --- a/nomad/structs/network.go +++ b/nomad/structs/network.go @@ -96,10 +96,10 @@ func (idx *NetworkIndex) AddReserved(n *NetworkResource) (collide bool) { idx.UsedPorts[n.IP] = used } for _, port := range n.ReservedPorts { - if _, ok := used[port]; ok { + if _, ok := used[port.Value]; ok { collide = true } else { - used[port] = struct{}{} + used[port.Value] = struct{}{} } } @@ -151,7 +151,7 @@ func (idx *NetworkIndex) AssignNetwork(ask *NetworkResource) (out *NetworkResour // Check if any of the reserved ports are in use for _, port := range ask.ReservedPorts { - if _, ok := idx.UsedPorts[ipStr][port]; ok { + if _, ok := idx.UsedPorts[ipStr][port.Value]; ok { err = fmt.Errorf("reserved port collision") return } @@ -179,10 +179,10 @@ func (idx *NetworkIndex) AssignNetwork(ask *NetworkResource) (out *NetworkResour if _, ok := idx.UsedPorts[ipStr][randPort]; ok { goto PICK } - if IntContains(offer.ReservedPorts, randPort) { + if isPortReserved(offer.ReservedPorts, randPort) { goto PICK } - offer.ReservedPorts = append(offer.ReservedPorts, randPort) + offer.DynamicPorts[i].Value = randPort } // Stop, we have an offer! @@ -194,9 +194,9 @@ func (idx *NetworkIndex) AssignNetwork(ask *NetworkResource) (out *NetworkResour } // IntContains scans an integer slice for a value -func IntContains(haystack []int, needle int) bool { +func isPortReserved(haystack []Port, needle int) bool { for _, item := range haystack { - if item == needle { + if item.Value == needle { return true } } diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index ce5007ac54c..1a8f3b13418 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -620,15 +620,20 @@ func (r *Resources) GoString() string { return fmt.Sprintf("*%#v", *r) } +type Port struct { + Label string + Value int `mapstructure:"static"` +} + // NetworkResource is used to represent available network // resources type NetworkResource struct { - Device string // Name of the device - CIDR string // CIDR block of addresses - IP string // IP address - MBits int // Throughput - ReservedPorts []int `mapstructure:"reserved_ports"` // Reserved ports - DynamicPorts []string `mapstructure:"dynamic_ports"` // Dynamically assigned ports + Device string // Name of the device + CIDR string // CIDR block of addresses + IP string // IP address + MBits int // Throughput + ReservedPorts []Port // Reserved ports + DynamicPorts []Port // Dynamically assigned ports } // Copy returns a deep copy of the network resource @@ -636,7 +641,7 @@ func (n *NetworkResource) Copy() *NetworkResource { newR := new(NetworkResource) *newR = *n if n.ReservedPorts != nil { - newR.ReservedPorts = make([]int, len(n.ReservedPorts)) + newR.ReservedPorts = make([]Port, len(n.ReservedPorts)) copy(newR.ReservedPorts, n.ReservedPorts) } return newR @@ -656,52 +661,6 @@ func (n *NetworkResource) GoString() string { return fmt.Sprintf("*%#v", *n) } -// MapDynamicPorts returns a mapping of Label:PortNumber for dynamic ports -// allocated on this NetworkResource. The ordering of Label:Port pairs is -// random. -// -// Details: -// -// The jobspec lets us ask for two types of ports: Reserved ports and Dynamic -// ports. Reserved ports are identified by the port number, while Dynamic ports -// are identified by a Label. -// -// When we ask nomad to run a job it checks to see if the Reserved ports we -// requested are available. If they are, it then tries to provision any Dynamic -// ports that we have requested. When available ports are found to satisfy our -// dynamic port requirements, they are APPENDED to the reserved ports list. In -// effect, the reserved ports list serves double-duty. First it indicates the -// ports we *want*, and then it indicates the ports we are *using*. -// -// After the the offer process is complete and the job is scheduled we want to -// see which ports were made available to us. To see the dynamic ports that -// were allocated to us we look at the last N ports in our reservation, where N -// is how many dynamic ports we requested. -// -// MapDynamicPorts matches these port numbers with their labels and gives you -// the port mapping. -// -// Also, be aware that this is intended to be called in the context of -// task.Resources after an offer has been made. If you call it in some other -// context the behavior is unspecified, including maybe crashing. So don't do that. -func (n *NetworkResource) MapDynamicPorts() map[string]int { - ports := n.ReservedPorts[len(n.ReservedPorts)-len(n.DynamicPorts):] - mapping := make(map[string]int, len(n.DynamicPorts)) - - for idx, label := range n.DynamicPorts { - mapping[label] = ports[idx] - } - - return mapping -} - -// ListStaticPorts returns the list of Static ports allocated to this -// NetworkResource. These are presumed to have known semantics so there is no -// mapping information. -func (n *NetworkResource) ListStaticPorts() []int { - return n.ReservedPorts[:len(n.ReservedPorts)-len(n.DynamicPorts)] -} - const ( // JobTypeNomad is reserved for internal system tasks and is // always handled by the CoreScheduler. @@ -1032,7 +991,7 @@ type Task struct { Driver string // Config is provided to the driver to initialize - Config map[string]string + Config map[string]interface{} // Map of environment variables to be used by the driver Env map[string]string From 4d16daff5f4726c6f695046bf4a1c27929c7794f Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 13 Nov 2015 18:35:49 -0800 Subject: [PATCH 03/26] DRYed the code --- nomad/mock/mock.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nomad/mock/mock.go b/nomad/mock/mock.go index a618f787b73..3fccc38e199 100644 --- a/nomad/mock/mock.go +++ b/nomad/mock/mock.go @@ -37,7 +37,7 @@ func Node() *structs.Node { &structs.NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []structs.Port{structs.Port{Label: "main", Value: 22}}, + ReservedPorts: []structs.Port{{Label: "main", Value: 22}}, MBits: 1, }, }, @@ -96,7 +96,7 @@ func Job() *structs.Job { Networks: []*structs.NetworkResource{ &structs.NetworkResource{ MBits: 50, - DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, + DynamicPorts: []structs.Port{{Label: "http"}}, }, }, }, @@ -158,7 +158,7 @@ func SystemJob() *structs.Job { Networks: []*structs.NetworkResource{ &structs.NetworkResource{ MBits: 50, - DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, + DynamicPorts: []structs.Port{{Label: "http"}}, }, }, }, @@ -200,9 +200,9 @@ func Alloc() *structs.Allocation { &structs.NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []structs.Port{structs.Port{Label: "main", Value: 12345}}, + ReservedPorts: []structs.Port{{Label: "main", Value: 12345}}, MBits: 100, - DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, + DynamicPorts: []structs.Port{{Label: "http"}}, }, }, }, @@ -214,9 +214,9 @@ func Alloc() *structs.Allocation { &structs.NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []structs.Port{structs.Port{Label: "main", Value: 5000}}, + ReservedPorts: []structs.Port{{Label: "main", Value: 5000}}, MBits: 50, - DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, + DynamicPorts: []structs.Port{{Label: "http"}}, }, }, }, From 4186e70320430ac491020e086f7d5873402c1fc2 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 13 Nov 2015 20:22:49 -0800 Subject: [PATCH 04/26] Making the config for drivers public --- client/driver/docker.go | 8 ++++---- client/driver/exec.go | 4 ++-- client/driver/java.go | 4 ++-- client/driver/qemu.go | 4 ++-- client/driver/raw_exec.go | 2 +- client/driver/rkt.go | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index bd0edf2acc3..d07317fad48 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -24,7 +24,7 @@ type DockerDriver struct { fingerprint.StaticFingerprinter } -type dockerDriverConfig struct { +type DockerDriverConfig struct { ImageName string `mapstructure:"image"` Command string `mapstructure:"command"` Args string `mapstructure:"args"` @@ -39,7 +39,7 @@ type dockerDriverConfig struct { SearchDomains string `mapstructure:"search_domains"` } -func (c *dockerDriverConfig) Validate() error { +func (c *DockerDriverConfig) Validate() error { if c.ImageName == "" { return fmt.Errorf("Docker Driver needs an image name") } @@ -139,7 +139,7 @@ func (d *DockerDriver) containerBinds(alloc *allocdir.AllocDir, task *structs.Ta } // createContainer initializes a struct needed to call docker.client.CreateContainer() -func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, driverConfig *dockerDriverConfig) (docker.CreateContainerOptions, error) { +func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, driverConfig *DockerDriverConfig) (docker.CreateContainerOptions, error) { var c docker.CreateContainerOptions if task.Resources == nil { d.logger.Printf("[ERR] driver.docker: task.Resources is empty") @@ -320,7 +320,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri } func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - var driverConfig dockerDriverConfig + var driverConfig DockerDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } diff --git a/client/driver/exec.go b/client/driver/exec.go index 11eff80aeb1..bcde063ed31 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -22,7 +22,7 @@ type ExecDriver struct { DriverContext fingerprint.StaticFingerprinter } -type execDriverConfig struct { +type ExecDriverConfig struct { ArtifactSource string `mapstructure:"artifact_source` Checksum string `mapstructure:"checksum"` Command string `mapstructure:"command"` @@ -56,7 +56,7 @@ func (d *ExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, } func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - var driverConfig execDriverConfig + var driverConfig ExecDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } diff --git a/client/driver/java.go b/client/driver/java.go index b87fdac794f..774d7524a25 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -26,7 +26,7 @@ type JavaDriver struct { fingerprint.StaticFingerprinter } -type javaDriverConfig struct { +type JavaDriverConfig struct { JvmOpts string `mapstructure:"jvm_options"` ArtifactSource string `mapstructure:"artifact_source` Checksum string `mapstructure:"checksum"` @@ -98,7 +98,7 @@ func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, } func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - var driverConfig javaDriverConfig + var driverConfig JavaDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } diff --git a/client/driver/qemu.go b/client/driver/qemu.go index 23d3f4777bc..e12387e98dc 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -31,7 +31,7 @@ type QemuDriver struct { fingerprint.StaticFingerprinter } -type qemuDriverConfig struct { +type QemuDriverConfig struct { ArtifactSource string `mapstructure:"artifact_source` Checksum string `mapstructure:"checksum"` Accelerator string `mapstructure:"accelerator"` @@ -77,7 +77,7 @@ func (d *QemuDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, // Run an existing Qemu image. Start() will pull down an existing, valid Qemu // image and save it to the Drivers Allocation Dir func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - var driverConfig qemuDriverConfig + var driverConfig QemuDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index 64c130378ec..f0bb1113cd1 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -57,7 +57,7 @@ func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (boo } func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - var driverConfig execDriverConfig + var driverConfig ExecDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 0911b12a728..045b981b697 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -35,7 +35,7 @@ type RktDriver struct { fingerprint.StaticFingerprinter } -type rktDriverConfig struct { +type RktDriverConfig struct { ImageName string `mapstructure:"image"` Args string `mapstructure:"args"` } @@ -89,7 +89,7 @@ func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, e // Run an existing Rkt image. func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - var driverConfig rktDriverConfig + var driverConfig RktDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } From e8236c008122fe8956ab167d6794baac90bdbfd3 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 13 Nov 2015 20:51:30 -0800 Subject: [PATCH 05/26] Fixed the parsing and encoding logic --- api/resources.go | 9 +++++++-- api/tasks.go | 4 ++-- command/run.go | 1 + jobspec/parse.go | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/api/resources.go b/api/resources.go index 0b49c496e54..11b00befc2b 100644 --- a/api/resources.go +++ b/api/resources.go @@ -10,12 +10,17 @@ type Resources struct { Networks []*NetworkResource } +type Port struct { + Label string + Value int +} + // NetworkResource is used to describe required network // resources of a given task. type NetworkResource struct { Public bool CIDR string - ReservedPorts []int - DynamicPorts []string + ReservedPorts []Port + DynamicPorts []Port MBits int } diff --git a/api/tasks.go b/api/tasks.go index 2535d5ec565..b46cf14e708 100644 --- a/api/tasks.go +++ b/api/tasks.go @@ -65,7 +65,7 @@ func (g *TaskGroup) AddTask(t *Task) *TaskGroup { type Task struct { Name string Driver string - Config map[string]string + Config map[string]interface{} Constraints []*Constraint Env map[string]string Resources *Resources @@ -84,7 +84,7 @@ func NewTask(name, driver string) *Task { // the task. func (t *Task) SetConfig(key, val string) *Task { if t.Config == nil { - t.Config = make(map[string]string) + t.Config = make(map[string]interface{}) } t.Config[key] = val return t diff --git a/command/run.go b/command/run.go index ab43a388654..0ec0212e898 100644 --- a/command/run.go +++ b/command/run.go @@ -123,6 +123,7 @@ func (c *RunCommand) Run(args []string) int { // convertJob is used to take a *structs.Job and convert it to an *api.Job. // This function is just a hammer and probably needs to be revisited. func convertJob(in *structs.Job) (*api.Job, error) { + gob.Register([]map[string]interface{}{}) var apiJob *api.Job buf := new(bytes.Buffer) if err := gob.NewEncoder(buf).Encode(in); err != nil { diff --git a/jobspec/parse.go b/jobspec/parse.go index d9d66479d6f..b9daab6a80f 100644 --- a/jobspec/parse.go +++ b/jobspec/parse.go @@ -498,7 +498,7 @@ func parseResources(result *structs.Resources, list *ast.ObjectList) error { var networkObj *ast.ObjectList if ot, ok := o.Items[0].Val.(*ast.ObjectType); ok { - listVal = ot.List + networkObj = ot.List } else { return fmt.Errorf("resource: should be an object") } From a52bff862dafa737745629a4bd98d3aacb4e8135 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sat, 14 Nov 2015 17:30:36 -0800 Subject: [PATCH 06/26] Fixed the api tests --- api/compose_test.go | 10 +++++----- api/tasks_test.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/compose_test.go b/api/compose_test.go index 2a509bc55ed..bc76895a90e 100644 --- a/api/compose_test.go +++ b/api/compose_test.go @@ -20,7 +20,7 @@ func TestCompose(t *testing.T) { &NetworkResource{ CIDR: "0.0.0.0/0", MBits: 100, - ReservedPorts: []int{80, 443}, + ReservedPorts: []Port{{"", 80}, {"", 443}}, }, }, }) @@ -83,9 +83,9 @@ func TestCompose(t *testing.T) { &NetworkResource{ CIDR: "0.0.0.0/0", MBits: 100, - ReservedPorts: []int{ - 80, - 443, + ReservedPorts: []Port{ + {"", 80}, + {"", 443}, }, }, }, @@ -97,7 +97,7 @@ func TestCompose(t *testing.T) { Operand: "=", }, }, - Config: map[string]string{ + Config: map[string]interface{}{ "foo": "bar", }, Meta: map[string]string{ diff --git a/api/tasks_test.go b/api/tasks_test.go index 945fdf9bf22..75f29996d84 100644 --- a/api/tasks_test.go +++ b/api/tasks_test.go @@ -130,7 +130,7 @@ func TestTask_SetConfig(t *testing.T) { // Set another config value task.SetConfig("baz", "zip") - expect := map[string]string{"foo": "bar", "baz": "zip"} + expect := map[string]interface{}{"foo": "bar", "baz": "zip"} if !reflect.DeepEqual(task.Config, expect) { t.Fatalf("expect: %#v, got: %#v", expect, task.Config) } @@ -171,7 +171,7 @@ func TestTask_Require(t *testing.T) { &NetworkResource{ CIDR: "0.0.0.0/0", MBits: 100, - ReservedPorts: []int{80, 443}, + ReservedPorts: []Port{{"", 80}, {"", 443}}, }, }, } From 5c95e7f0dc015abac7f3ca595d67e6efa824f482 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sat, 14 Nov 2015 22:16:32 -0800 Subject: [PATCH 07/26] Fixed the client tests --- client/task_runner_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/task_runner_test.go b/client/task_runner_test.go index 751d00fa9ec..67e098783ac 100644 --- a/client/task_runner_test.go +++ b/client/task_runner_test.go @@ -46,7 +46,7 @@ func testTaskRunner() (*MockTaskStateUpdater, *TaskRunner) { // Initialize the port listing. This should be done by the offer process but // we have a mock so that doesn't happen. - task.Resources.Networks[0].ReservedPorts = []int{80} + task.Resources.Networks[0].ReservedPorts = []structs.Port{{"", 80}} allocDir := allocdir.NewAllocDir(filepath.Join(conf.AllocDir, alloc.ID)) allocDir.Build([]*structs.Task{task}) From 51e3c9928b9584ba340eae9742e94b90d61ddc20 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sat, 14 Nov 2015 22:28:11 -0800 Subject: [PATCH 08/26] Fixing the scheduler tests --- scheduler/generic_sched_test.go | 3 ++- scheduler/system_sched_test.go | 3 ++- scheduler/util_test.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scheduler/generic_sched_test.go b/scheduler/generic_sched_test.go index dfb35cb3c5e..e3e87557804 100644 --- a/scheduler/generic_sched_test.go +++ b/scheduler/generic_sched_test.go @@ -397,9 +397,10 @@ func TestServiceSched_JobModify_InPlace(t *testing.T) { h.AssertEvalStatus(t, structs.EvalStatusComplete) // Verify the network did not change + rp := structs.Port{"main", 5000} for _, alloc := range out { for _, resources := range alloc.TaskResources { - if resources.Networks[0].ReservedPorts[0] != 5000 { + if resources.Networks[0].ReservedPorts[0] != rp { t.Fatalf("bad: %#v", alloc) } } diff --git a/scheduler/system_sched_test.go b/scheduler/system_sched_test.go index ece805057f1..c6f186ee3f5 100644 --- a/scheduler/system_sched_test.go +++ b/scheduler/system_sched_test.go @@ -463,9 +463,10 @@ func TestSystemSched_JobModify_InPlace(t *testing.T) { h.AssertEvalStatus(t, structs.EvalStatusComplete) // Verify the network did not change + rp := structs.Port{"main", 5000} for _, alloc := range out { for _, resources := range alloc.TaskResources { - if resources.Networks[0].ReservedPorts[0] != 5000 { + if resources.Networks[0].ReservedPorts[0] != rp { t.Fatalf("bad: %#v", alloc) } } diff --git a/scheduler/util_test.go b/scheduler/util_test.go index 7873c55c582..f7a5274f32b 100644 --- a/scheduler/util_test.go +++ b/scheduler/util_test.go @@ -343,7 +343,7 @@ func TestTasksUpdated(t *testing.T) { } j6 := mock.Job() - j6.TaskGroups[0].Tasks[0].Resources.Networks[0].DynamicPorts = []string{"http", "https", "admin"} + j6.TaskGroups[0].Tasks[0].Resources.Networks[0].DynamicPorts = []structs.Port{{"http", 0}, {"https", 0}, {"admin", 0}} if !tasksUpdated(j1.TaskGroups[0], j6.TaskGroups[0]) { t.Fatalf("bad") } From 87f49c4f7f54e18ca06421a09a835fb0dd07f8ae Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 00:10:48 -0800 Subject: [PATCH 09/26] Fixed the tests for jobspec --- jobspec/parse.go | 12 +++++++++--- jobspec/parse_test.go | 14 +++++++------- jobspec/test-fixtures/bad-ports.hcl | 13 +++++++++++-- jobspec/test-fixtures/basic.hcl | 14 ++++++++++++-- jobspec/test-fixtures/overlapping-ports.hcl | 14 ++++++++++++-- 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/jobspec/parse.go b/jobspec/parse.go index b9daab6a80f..24772364fda 100644 --- a/jobspec/parse.go +++ b/jobspec/parse.go @@ -8,6 +8,7 @@ import ( "path/filepath" "regexp" "strconv" + "strings" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/ast" @@ -16,7 +17,7 @@ import ( ) var reDynamicPorts *regexp.Regexp = regexp.MustCompile("^[a-zA-Z0-9_]+$") -var errDynamicPorts = fmt.Errorf("DynamicPort label does not conform to naming requirements %s", reDynamicPorts.String()) +var errPortLabel = fmt.Errorf("Port label does not conform to naming requirements %s", reDynamicPorts.String()) // Parse parses the job spec from the given io.Reader. // @@ -513,11 +514,15 @@ func parseResources(result *structs.Resources, list *ast.ObjectList) error { } func parsePorts(networkObj *ast.ObjectList, nw *structs.NetworkResource) error { - portsObjList := networkObj.Filter("Port") + portsObjList := networkObj.Filter("port") knownPortLabels := make(map[string]bool) for _, port := range portsObjList.Items { label := port.Keys[0].Token.Value().(string) - if knownPortLabels[label] { + if !reDynamicPorts.MatchString(label) { + return errPortLabel + } + l := strings.ToLower(label) + if knownPortLabels[l] { return fmt.Errorf("Found a port label collision: %s", label) } var p map[string]interface{} @@ -534,6 +539,7 @@ func parsePorts(networkObj *ast.ObjectList, nw *structs.NetworkResource) error { } else { nw.DynamicPorts = append(nw.DynamicPorts, res) } + knownPortLabels[l] = true } return nil } diff --git a/jobspec/parse_test.go b/jobspec/parse_test.go index 2ce66b7b4d3..0814aa0f6ef 100644 --- a/jobspec/parse_test.go +++ b/jobspec/parse_test.go @@ -57,7 +57,7 @@ func TestParse(t *testing.T) { &structs.Task{ Name: "outside", Driver: "java", - Config: map[string]string{ + Config: map[string]interface{}{ "jar": "s3://my-cool-store/foo.jar", }, Meta: map[string]string{ @@ -91,7 +91,7 @@ func TestParse(t *testing.T) { &structs.Task{ Name: "binstore", Driver: "docker", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "hashicorp/binstore", }, Env: map[string]string{ @@ -104,8 +104,8 @@ func TestParse(t *testing.T) { Networks: []*structs.NetworkResource{ &structs.NetworkResource{ MBits: 100, - ReservedPorts: []int{1, 2, 3}, - DynamicPorts: []string{"http", "https", "admin"}, + ReservedPorts: []structs.Port{{"one", 1}, {"two", 2}, {"three", 3}}, + DynamicPorts: []structs.Port{{"http", 0}, {"https", 0}, {"admin", 0}}, }, }, }, @@ -113,7 +113,7 @@ func TestParse(t *testing.T) { &structs.Task{ Name: "storagelocker", Driver: "java", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "hashicorp/storagelocker", }, Resources: &structs.Resources{ @@ -256,8 +256,8 @@ func TestBadPorts(t *testing.T) { _, err = ParseFile(path) - if !strings.Contains(err.Error(), errDynamicPorts.Error()) { - t.Fatalf("\nExpected error\n %s\ngot\n %v", errDynamicPorts, err) + if !strings.Contains(err.Error(), errPortLabel.Error()) { + t.Fatalf("\nExpected error\n %s\ngot\n %v", errPortLabel, err) } } diff --git a/jobspec/test-fixtures/bad-ports.hcl b/jobspec/test-fixtures/bad-ports.hcl index 33c5f1c9319..2928f109508 100644 --- a/jobspec/test-fixtures/bad-ports.hcl +++ b/jobspec/test-fixtures/bad-ports.hcl @@ -42,8 +42,17 @@ job "binstore-storagelocker" { network { mbits = "100" - reserved_ports = [1,2,3] - dynamic_ports = ["this_is_aport", "this#is$not-a!port"] + port "one" { + static = 1 + } + port "two" { + static = 2 + } + port "three" { + static = 3 + } + port "this_is_aport" {} + port ""{} } } } diff --git a/jobspec/test-fixtures/basic.hcl b/jobspec/test-fixtures/basic.hcl index bf81a6ae7dc..236f4829aef 100644 --- a/jobspec/test-fixtures/basic.hcl +++ b/jobspec/test-fixtures/basic.hcl @@ -51,8 +51,18 @@ job "binstore-storagelocker" { network { mbits = "100" - reserved_ports = [1,2,3] - dynamic_ports = ["http", "https", "admin"] + port "one" { + static = 1 + } + port "two" { + static = 2 + } + port "three" { + static = 3 + } + port "http" {} + port "https" {} + port "admin" {} } } } diff --git a/jobspec/test-fixtures/overlapping-ports.hcl b/jobspec/test-fixtures/overlapping-ports.hcl index 3bf9f3c34cd..2a09947725c 100644 --- a/jobspec/test-fixtures/overlapping-ports.hcl +++ b/jobspec/test-fixtures/overlapping-ports.hcl @@ -42,8 +42,18 @@ job "binstore-storagelocker" { network { mbits = "100" - reserved_ports = [1,2,3] - dynamic_ports = ["Http", "http", "HTTP"] + port "one" { + static = 1 + } + port "two" { + static = 2 + } + port "three" { + static = 3 + } + port "Http" {} + port "http" {} + port "HTTP" {} } } } From 0d697788731f6ad7bc91ced50e17329d5635bbd8 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 00:37:00 -0800 Subject: [PATCH 10/26] Fixed compilation issues with driver tests --- client/driver/docker_test.go | 28 ++++++++++++++-------------- client/driver/driver_test.go | 8 ++++---- client/driver/exec_test.go | 12 ++++++------ client/driver/java_test.go | 6 +++--- client/driver/qemu_test.go | 6 +++--- client/driver/raw_exec_test.go | 12 ++++++------ client/driver/rkt_test.go | 8 ++++---- 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 872c2419b72..ed3b877bd7d 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -94,7 +94,7 @@ func TestDockerDriver_StartOpen_Wait(t *testing.T) { task := &structs.Task{ Name: "redis-demo", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "redis", }, Resources: basicResources, @@ -131,7 +131,7 @@ func TestDockerDriver_Start_Wait(t *testing.T) { task := &structs.Task{ Name: "redis-demo", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "redis", "command": "redis-server", "args": "-v", @@ -184,7 +184,7 @@ func TestDockerDriver_Start_Wait_AllocDir(t *testing.T) { file := "output.txt" task := &structs.Task{ Name: "redis-demo", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "redis", "command": "/bin/bash", "args": fmt.Sprintf(`-c "sleep 1; echo -n %s > $%s/%s"`, string(exp), environment.AllocDir, file), @@ -237,7 +237,7 @@ func TestDockerDriver_Start_Kill_Wait(t *testing.T) { task := &structs.Task{ Name: "redis-demo", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "redis", "command": "/bin/sleep", "args": "10", @@ -280,7 +280,7 @@ func TestDockerDriver_Start_Kill_Wait(t *testing.T) { func taskTemplate() *structs.Task { return &structs.Task{ Name: "redis-demo", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "redis", }, Resources: &structs.Resources{ @@ -289,8 +289,8 @@ func taskTemplate() *structs.Task { Networks: []*structs.NetworkResource{ &structs.NetworkResource{ IP: "127.0.0.1", - ReservedPorts: []int{11110}, - DynamicPorts: []string{"REDIS"}, + ReservedPorts: []structs.Port{{"main", 11110}}, + DynamicPorts: []structs.Port{{"REDIS", 0}}, }, }, }, @@ -303,13 +303,13 @@ func TestDocker_StartN(t *testing.T) { } task1 := taskTemplate() - task1.Resources.Networks[0].ReservedPorts[0] = 11111 + task1.Resources.Networks[0].ReservedPorts[0] = structs.Port{"main", 11110} task2 := taskTemplate() - task2.Resources.Networks[0].ReservedPorts[0] = 22222 + task2.Resources.Networks[0].ReservedPorts[0] = structs.Port{"main", 22222} task3 := taskTemplate() - task3.Resources.Networks[0].ReservedPorts[0] = 33333 + task3.Resources.Networks[0].ReservedPorts[0] = structs.Port{"main", 33333} taskList := []*structs.Task{task1, task2, task3} @@ -355,15 +355,15 @@ func TestDocker_StartNVersions(t *testing.T) { task1 := taskTemplate() task1.Config["image"] = "redis" - task1.Resources.Networks[0].ReservedPorts[0] = 11111 + task1.Resources.Networks[0].ReservedPorts[0] = structs.Port{"main", 11110} task2 := taskTemplate() task2.Config["image"] = "redis:latest" - task2.Resources.Networks[0].ReservedPorts[0] = 22222 + task2.Resources.Networks[0].ReservedPorts[0] = structs.Port{"main", 22222} task3 := taskTemplate() task3.Config["image"] = "redis:3.0" - task3.Resources.Networks[0].ReservedPorts[0] = 33333 + task3.Resources.Networks[0].ReservedPorts[0] = structs.Port{"main", 33333} taskList := []*structs.Task{task1, task2, task3} @@ -409,7 +409,7 @@ func TestDockerHostNet(t *testing.T) { task := &structs.Task{ Name: "redis-demo", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "redis", "network_mode": "host", }, diff --git a/client/driver/driver_test.go b/client/driver/driver_test.go index 2623e433e64..76877d133e6 100644 --- a/client/driver/driver_test.go +++ b/client/driver/driver_test.go @@ -18,8 +18,8 @@ var basicResources = &structs.Resources{ Networks: []*structs.NetworkResource{ &structs.NetworkResource{ IP: "0.0.0.0", - ReservedPorts: []int{12345}, - DynamicPorts: []string{"HTTP"}, + ReservedPorts: []structs.Port{{"main", 12345}}, + DynamicPorts: []structs.Port{{"HTTP", 0}}, }, }, } @@ -60,8 +60,8 @@ func TestDriver_TaskEnvironmentVariables(t *testing.T) { Networks: []*structs.NetworkResource{ &structs.NetworkResource{ IP: "1.2.3.4", - ReservedPorts: []int{80, 443, 8080, 12345}, - DynamicPorts: []string{"admin", "5000"}, + ReservedPorts: []structs.Port{{"one", 80}, {"two", 443}, {"three", 8080}, {"four", 12345}}, + DynamicPorts: []structs.Port{{"admin", 8081}, {"web", 8086}}, }, }, }, diff --git a/client/driver/exec_test.go b/client/driver/exec_test.go index 06c7107456a..2256e6c2ade 100644 --- a/client/driver/exec_test.go +++ b/client/driver/exec_test.go @@ -37,7 +37,7 @@ func TestExecDriver_StartOpen_Wait(t *testing.T) { ctestutils.ExecCompatible(t) task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/sleep", "args": "5", }, @@ -71,7 +71,7 @@ func TestExecDriver_Start_Wait(t *testing.T) { ctestutils.ExecCompatible(t) task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/sleep", "args": "2", }, @@ -115,7 +115,7 @@ func TestExecDriver_Start_Artifact_basic(t *testing.T) { task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": fmt.Sprintf("https://dl.dropboxusercontent.com/u/47675/jar_thing/%s?checksum=%s", file, checksum), "command": filepath.Join("$NOMAD_TASK_DIR", file), }, @@ -158,7 +158,7 @@ func TestExecDriver_Start_Artifact_expanded(t *testing.T) { task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": fmt.Sprintf("https://dl.dropboxusercontent.com/u/47675/jar_thing/%s", file), "command": "/bin/bash", "args": fmt.Sprintf("-c '/bin/sleep 1 && %s'", filepath.Join("$NOMAD_TASK_DIR", file)), @@ -202,7 +202,7 @@ func TestExecDriver_Start_Wait_AllocDir(t *testing.T) { file := "output.txt" task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/bash", "args": fmt.Sprintf("-c \"sleep 1; echo -n %s > $%s/%s\"", string(exp), environment.AllocDir, file), }, @@ -248,7 +248,7 @@ func TestExecDriver_Start_Kill_Wait(t *testing.T) { ctestutils.ExecCompatible(t) task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/sleep", "args": "1", }, diff --git a/client/driver/java_test.go b/client/driver/java_test.go index 0c3490d0e15..cad75c55d12 100644 --- a/client/driver/java_test.go +++ b/client/driver/java_test.go @@ -49,7 +49,7 @@ func TestJavaDriver_StartOpen_Wait(t *testing.T) { ctestutils.JavaCompatible(t) task := &structs.Task{ Name: "demo-app", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": "https://dl.dropboxusercontent.com/u/47675/jar_thing/demoapp.jar", "jvm_options": "-Xmx2048m -Xms256m", "checksum": "sha256:58d6e8130308d32e197c5108edd4f56ddf1417408f743097c2e662df0f0b17c8", @@ -95,7 +95,7 @@ func TestJavaDriver_Start_Wait(t *testing.T) { ctestutils.JavaCompatible(t) task := &structs.Task{ Name: "demo-app", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": "https://dl.dropboxusercontent.com/u/47675/jar_thing/demoapp.jar", "jvm_options": "-Xmx2048m -Xms256m", "checksum": "sha256:58d6e8130308d32e197c5108edd4f56ddf1417408f743097c2e662df0f0b17c8", @@ -142,7 +142,7 @@ func TestJavaDriver_Start_Kill_Wait(t *testing.T) { ctestutils.JavaCompatible(t) task := &structs.Task{ Name: "demo-app", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": "https://dl.dropboxusercontent.com/u/47675/jar_thing/demoapp.jar", }, Resources: basicResources, diff --git a/client/driver/qemu_test.go b/client/driver/qemu_test.go index 0ab60f86d0b..543bf247b9f 100644 --- a/client/driver/qemu_test.go +++ b/client/driver/qemu_test.go @@ -37,7 +37,7 @@ func TestQemuDriver_StartOpen_Wait(t *testing.T) { // TODO: use test server to load from a fixture task := &structs.Task{ Name: "linux", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": "https://dl.dropboxusercontent.com/u/47675/jar_thing/linux-0.2.img", "checksum": "sha256:a5e836985934c3392cbbd9b26db55a7d35a8d7ae1deb7ca559dd9c0159572544", "accelerator": "tcg", @@ -48,7 +48,7 @@ func TestQemuDriver_StartOpen_Wait(t *testing.T) { MemoryMB: 512, Networks: []*structs.NetworkResource{ &structs.NetworkResource{ - ReservedPorts: []int{22000, 80}, + ReservedPorts: []structs.Port{{"main", 22000}, {"web", 80}}, }, }, }, @@ -87,7 +87,7 @@ func TestQemuDriver_RequiresMemory(t *testing.T) { // TODO: use test server to load from a fixture task := &structs.Task{ Name: "linux", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": "https://dl.dropboxusercontent.com/u/47675/jar_thing/linux-0.2.img", "accelerator": "tcg", "host_port": "8080", diff --git a/client/driver/raw_exec_test.go b/client/driver/raw_exec_test.go index 053f29337b0..f4193021ffc 100644 --- a/client/driver/raw_exec_test.go +++ b/client/driver/raw_exec_test.go @@ -51,7 +51,7 @@ func TestRawExecDriver_Fingerprint(t *testing.T) { func TestRawExecDriver_StartOpen_Wait(t *testing.T) { task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/sleep", "args": "1", }, @@ -100,7 +100,7 @@ func TestRawExecDriver_Start_Artifact_basic(t *testing.T) { task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": fmt.Sprintf("https://dl.dropboxusercontent.com/u/47675/jar_thing/%s", file), "command": filepath.Join("$NOMAD_TASK_DIR", file), "checksum": checksum, @@ -148,7 +148,7 @@ func TestRawExecDriver_Start_Artifact_expanded(t *testing.T) { task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "artifact_source": fmt.Sprintf("https://dl.dropboxusercontent.com/u/47675/jar_thing/%s", file), "command": "/bin/bash", "args": fmt.Sprintf("-c '/bin/sleep 1 && %s'", filepath.Join("$NOMAD_TASK_DIR", file)), @@ -188,7 +188,7 @@ func TestRawExecDriver_Start_Artifact_expanded(t *testing.T) { func TestRawExecDriver_Start_Wait(t *testing.T) { task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/sleep", "args": "1", }, @@ -230,7 +230,7 @@ func TestRawExecDriver_Start_Wait_AllocDir(t *testing.T) { file := "output.txt" task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/bash", "args": fmt.Sprintf(`-c "sleep 1; echo -n %s > $%s/%s"`, string(exp), environment.AllocDir, file), }, @@ -275,7 +275,7 @@ func TestRawExecDriver_Start_Wait_AllocDir(t *testing.T) { func TestRawExecDriver_Start_Kill_Wait(t *testing.T) { task := &structs.Task{ Name: "sleep", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/sleep", "args": "1", }, diff --git a/client/driver/rkt_test.go b/client/driver/rkt_test.go index 53ea2a4277c..9ecfd0d790a 100644 --- a/client/driver/rkt_test.go +++ b/client/driver/rkt_test.go @@ -75,7 +75,7 @@ func TestRktDriver_Start(t *testing.T) { // TODO: use test server to load from a fixture task := &structs.Task{ Name: "etcd", - Config: map[string]string{ + Config: map[string]interface{}{ "trust_prefix": "coreos.com/etcd", "image": "coreos.com/etcd:v2.0.4", "command": "/etcd", @@ -114,7 +114,7 @@ func TestRktDriver_Start_Wait(t *testing.T) { ctestutils.RktCompatible(t) task := &structs.Task{ Name: "etcd", - Config: map[string]string{ + Config: map[string]interface{}{ "trust_prefix": "coreos.com/etcd", "image": "coreos.com/etcd:v2.0.4", "command": "/etcd", @@ -156,7 +156,7 @@ func TestRktDriver_Start_Wait_Skip_Trust(t *testing.T) { ctestutils.RktCompatible(t) task := &structs.Task{ Name: "etcd", - Config: map[string]string{ + Config: map[string]interface{}{ "image": "coreos.com/etcd:v2.0.4", "command": "/etcd", "args": "--version", @@ -197,7 +197,7 @@ func TestRktDriver_Start_Wait_Logs(t *testing.T) { ctestutils.RktCompatible(t) task := &structs.Task{ Name: "etcd", - Config: map[string]string{ + Config: map[string]interface{}{ "trust_prefix": "coreos.com/etcd", "image": "coreos.com/etcd:v2.0.4", "command": "/etcd", From 05d21c52e4afda4c83cd5c4ce6ef4fcb0b3926f4 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 00:41:56 -0800 Subject: [PATCH 11/26] Fixed the allocdir tests --- client/allocdir/alloc_dir_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/allocdir/alloc_dir_test.go b/client/allocdir/alloc_dir_test.go index f63073012ff..326e772b904 100644 --- a/client/allocdir/alloc_dir_test.go +++ b/client/allocdir/alloc_dir_test.go @@ -15,7 +15,7 @@ var ( t1 = &structs.Task{ Name: "web", Driver: "exec", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/date", "args": "+%s", }, @@ -27,7 +27,7 @@ var ( t2 = &structs.Task{ Name: "web2", Driver: "exec", - Config: map[string]string{ + Config: map[string]interface{}{ "command": "/bin/date", "args": "+%s", }, From 72f82a7056e7f270bddba923e217b0dda0628b03 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 01:13:42 -0800 Subject: [PATCH 12/26] Fixed the test related to setting env variables in tasks --- client/driver/driver.go | 1 + client/driver/driver_test.go | 8 ++++++-- nomad/structs/structs.go | 9 +++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/client/driver/driver.go b/client/driver/driver.go index 08087269098..7fc76863fc1 100644 --- a/client/driver/driver.go +++ b/client/driver/driver.go @@ -133,6 +133,7 @@ func TaskEnvironmentVariables(ctx *ExecContext, task *structs.Task) environment. if len(task.Resources.Networks) > 0 { network := task.Resources.Networks[0] env.SetTaskIp(network.IP) + env.SetPorts(network.MapLabelToValues()) } } diff --git a/client/driver/driver_test.go b/client/driver/driver_test.go index 76877d133e6..7065153a148 100644 --- a/client/driver/driver_test.go +++ b/client/driver/driver_test.go @@ -76,8 +76,12 @@ func TestDriver_TaskEnvironmentVariables(t *testing.T) { "NOMAD_CPU_LIMIT": "1000", "NOMAD_MEMORY_LIMIT": "500", "NOMAD_IP": "1.2.3.4", - "NOMAD_PORT_admin": "8080", - "NOMAD_PORT_5000": "12345", + "NOMAD_PORT_one": "80", + "NOMAD_PORT_two": "443", + "NOMAD_PORT_three": "8080", + "NOMAD_PORT_four": "12345", + "NOMAD_PORT_admin": "8081", + "NOMAD_PORT_web": "8086", "NOMAD_META_CHOCOLATE": "cake", "NOMAD_META_STRAWBERRY": "icecream", "HELLO": "world", diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 1a8f3b13418..b9a326e0fd4 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -661,6 +661,15 @@ func (n *NetworkResource) GoString() string { return fmt.Sprintf("*%#v", *n) } +func (n *NetworkResource) MapLabelToValues() map[string]int { + labelValues := make(map[string]int) + ports := append(n.ReservedPorts, n.DynamicPorts...) + for _, port := range ports { + labelValues[port.Label] = port.Value + } + return labelValues +} + const ( // JobTypeNomad is reserved for internal system tasks and is // always handled by the CoreScheduler. From 76a005b0f16a7f8777a3d5e6d0a9579674efbc2e Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 01:24:30 -0800 Subject: [PATCH 13/26] Fixed the exec driver config --- client/driver/exec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/driver/exec.go b/client/driver/exec.go index bcde063ed31..6abcb7fe912 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -23,7 +23,7 @@ type ExecDriver struct { fingerprint.StaticFingerprinter } type ExecDriverConfig struct { - ArtifactSource string `mapstructure:"artifact_source` + ArtifactSource string `mapstructure:"artifact_source"` Checksum string `mapstructure:"checksum"` Command string `mapstructure:"command"` Args string `mapstructure:"args"` From b6e3b8d151d40a33b7b6bba90ac12c8b8807220e Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 01:56:21 -0800 Subject: [PATCH 14/26] Fixed the structs test --- nomad/structs/funcs_test.go | 6 +- nomad/structs/network_test.go | 50 +++++++------ nomad/structs/structs_test.go | 130 ++-------------------------------- 3 files changed, 38 insertions(+), 148 deletions(-) diff --git a/nomad/structs/funcs_test.go b/nomad/structs/funcs_test.go index ea3488ff835..d156394dccd 100644 --- a/nomad/structs/funcs_test.go +++ b/nomad/structs/funcs_test.go @@ -60,7 +60,7 @@ func TestAllocsFit_PortsOvercommitted(t *testing.T) { Device: "eth0", IP: "10.0.0.1", MBits: 50, - ReservedPorts: []int{8000}, + ReservedPorts: []Port{{"main", 8000}}, }, }, }, @@ -111,7 +111,7 @@ func TestAllocsFit(t *testing.T) { Device: "eth0", IP: "10.0.0.1", MBits: 50, - ReservedPorts: []int{80}, + ReservedPorts: []Port{{"main", 80}}, }, }, }, @@ -128,7 +128,7 @@ func TestAllocsFit(t *testing.T) { Device: "eth0", IP: "10.0.0.1", MBits: 50, - ReservedPorts: []int{8000}, + ReservedPorts: []Port{{"main", 8000}}, }, }, }, diff --git a/nomad/structs/network_test.go b/nomad/structs/network_test.go index ed84c35326d..3ec453df52b 100644 --- a/nomad/structs/network_test.go +++ b/nomad/structs/network_test.go @@ -14,7 +14,7 @@ func TestNetworkIndex_Overcommitted(t *testing.T) { Device: "eth0", IP: "192.168.0.100", MBits: 505, - ReservedPorts: []int{8000, 9000}, + ReservedPorts: []Port{{"one", 8000}, {"two", 9000}}, } collide := idx.AddReserved(reserved) if collide { @@ -65,7 +65,7 @@ func TestNetworkIndex_SetNode(t *testing.T) { &NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []int{22}, + ReservedPorts: []Port{{"ssh", 22}}, MBits: 1, }, }, @@ -101,7 +101,7 @@ func TestNetworkIndex_AddAllocs(t *testing.T) { Device: "eth0", IP: "192.168.0.100", MBits: 20, - ReservedPorts: []int{8000, 9000}, + ReservedPorts: []Port{{"one", 8000}, {"two", 9000}}, }, }, }, @@ -115,7 +115,7 @@ func TestNetworkIndex_AddAllocs(t *testing.T) { Device: "eth0", IP: "192.168.0.100", MBits: 50, - ReservedPorts: []int{10000}, + ReservedPorts: []Port{{"one", 10000}}, }, }, }, @@ -148,7 +148,7 @@ func TestNetworkIndex_AddReserved(t *testing.T) { Device: "eth0", IP: "192.168.0.100", MBits: 20, - ReservedPorts: []int{8000, 9000}, + ReservedPorts: []Port{{"one", 8000}, {"two", 9000}}, } collide := idx.AddReserved(reserved) if collide { @@ -189,7 +189,7 @@ func TestNetworkIndex_yieldIP(t *testing.T) { &NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []int{22}, + ReservedPorts: []Port{{"ssh", 22}}, MBits: 1, }, }, @@ -227,7 +227,7 @@ func TestNetworkIndex_AssignNetwork(t *testing.T) { &NetworkResource{ Device: "eth0", IP: "192.168.0.100", - ReservedPorts: []int{22}, + ReservedPorts: []Port{{"ssh", 22}}, MBits: 1, }, }, @@ -244,7 +244,7 @@ func TestNetworkIndex_AssignNetwork(t *testing.T) { Device: "eth0", IP: "192.168.0.100", MBits: 20, - ReservedPorts: []int{8000, 9000}, + ReservedPorts: []Port{{"one", 8000}, {"two", 9000}}, }, }, }, @@ -258,7 +258,7 @@ func TestNetworkIndex_AssignNetwork(t *testing.T) { Device: "eth0", IP: "192.168.0.100", MBits: 50, - ReservedPorts: []int{10000}, + ReservedPorts: []Port{{"main", 10000}}, }, }, }, @@ -269,7 +269,7 @@ func TestNetworkIndex_AssignNetwork(t *testing.T) { // Ask for a reserved port ask := &NetworkResource{ - ReservedPorts: []int{8000}, + ReservedPorts: []Port{{"main", 8000}}, } offer, err := idx.AssignNetwork(ask) if err != nil { @@ -281,13 +281,14 @@ func TestNetworkIndex_AssignNetwork(t *testing.T) { if offer.IP != "192.168.0.101" { t.Fatalf("bad: %#v", offer) } - if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != 8000 { + rp := Port{"main", 8000} + if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != rp { t.Fatalf("bad: %#v", offer) } // Ask for dynamic ports ask = &NetworkResource{ - DynamicPorts: []string{"http", "https", "admin"}, + DynamicPorts: []Port{{"http", 0}, {"https", 0}, {"admin", 0}}, } offer, err = idx.AssignNetwork(ask) if err != nil { @@ -299,14 +300,19 @@ func TestNetworkIndex_AssignNetwork(t *testing.T) { if offer.IP != "192.168.0.100" { t.Fatalf("bad: %#v", offer) } - if len(offer.ReservedPorts) != 3 { - t.Fatalf("bad: %#v", offer) + if len(offer.DynamicPorts) != 3 { + t.Fatalf("There should be three dynamic ports") + } + for _, port := range offer.DynamicPorts { + if port.Value == 0 { + t.Fatalf("Dynamic Port: %v should have been assigned a host port", port.Label) + } } // Ask for reserved + dynamic ports ask = &NetworkResource{ - ReservedPorts: []int{12345}, - DynamicPorts: []string{"http", "https", "admin"}, + ReservedPorts: []Port{{"main", 2345}}, + DynamicPorts: []Port{{"http", 0}, {"https", 0}, {"admin", 0}}, } offer, err = idx.AssignNetwork(ask) if err != nil { @@ -318,7 +324,9 @@ func TestNetworkIndex_AssignNetwork(t *testing.T) { if offer.IP != "192.168.0.100" { t.Fatalf("bad: %#v", offer) } - if len(offer.ReservedPorts) != 4 || offer.ReservedPorts[0] != 12345 { + + rp = Port{"main", 2345} + if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != rp { t.Fatalf("bad: %#v", offer) } @@ -336,14 +344,14 @@ func TestNetworkIndex_AssignNetwork(t *testing.T) { } func TestIntContains(t *testing.T) { - l := []int{1, 2, 10, 20} - if IntContains(l, 50) { + l := []Port{{"one", 1}, {"two", 2}, {"ten", 10}, {"twenty", 20}} + if isPortReserved(l, 50) { t.Fatalf("bad") } - if !IntContains(l, 20) { + if !isPortReserved(l, 20) { t.Fatalf("bad") } - if !IntContains(l, 1) { + if !isPortReserved(l, 1) { t.Fatalf("bad") } } diff --git a/nomad/structs/structs_test.go b/nomad/structs/structs_test.go index 1f107b09520..61102a4bd8e 100644 --- a/nomad/structs/structs_test.go +++ b/nomad/structs/structs_test.go @@ -246,7 +246,7 @@ func TestResource_Add(t *testing.T) { &NetworkResource{ CIDR: "10.0.0.0/8", MBits: 100, - ReservedPorts: []int{22}, + ReservedPorts: []Port{{"ssh", 22}}, }, }, } @@ -259,7 +259,7 @@ func TestResource_Add(t *testing.T) { &NetworkResource{ IP: "10.0.0.1", MBits: 50, - ReservedPorts: []int{80}, + ReservedPorts: []Port{{"web", 80}}, }, }, } @@ -278,7 +278,7 @@ func TestResource_Add(t *testing.T) { &NetworkResource{ CIDR: "10.0.0.0/8", MBits: 150, - ReservedPorts: []int{22, 80}, + ReservedPorts: []Port{{"ssh", 22}, {"web", 80}}, }, }, } @@ -294,7 +294,7 @@ func TestResource_Add_Network(t *testing.T) { Networks: []*NetworkResource{ &NetworkResource{ MBits: 50, - DynamicPorts: []string{"http", "https"}, + DynamicPorts: []Port{{"http", 0}, {"https", 0}}, }, }, } @@ -302,7 +302,7 @@ func TestResource_Add_Network(t *testing.T) { Networks: []*NetworkResource{ &NetworkResource{ MBits: 25, - DynamicPorts: []string{"admin"}, + DynamicPorts: []Port{{"admin", 0}}, }, }, } @@ -320,7 +320,7 @@ func TestResource_Add_Network(t *testing.T) { Networks: []*NetworkResource{ &NetworkResource{ MBits: 75, - DynamicPorts: []string{"http", "https", "admin"}, + DynamicPorts: []Port{{"http", 0}, {"https", 0}, {"admin", 0}}, }, }, } @@ -330,124 +330,6 @@ func TestResource_Add_Network(t *testing.T) { } } -func TestMapDynamicPorts(t *testing.T) { - resources := &NetworkResource{ - ReservedPorts: []int{80, 443, 3306, 8080}, - DynamicPorts: []string{"mysql", "admin"}, - } - - expected := map[string]int{ - "mysql": 3306, - "admin": 8080, - } - actual := resources.MapDynamicPorts() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v; found %#v", expected, actual) - } -} - -func TestMapDynamicPortsEmpty(t *testing.T) { - resources := &NetworkResource{ - ReservedPorts: []int{}, - DynamicPorts: []string{}, - } - - expected := map[string]int{} - actual := resources.MapDynamicPorts() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v; found %#v", expected, actual) - } -} - -func TestMapDynamicPortsStaticOnly(t *testing.T) { - resources := &NetworkResource{ - ReservedPorts: []int{80, 443}, - DynamicPorts: []string{}, - } - - expected := map[string]int{} - actual := resources.MapDynamicPorts() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v; found %#v", expected, actual) - } -} - -func TestMapDynamicPortsOnly(t *testing.T) { - resources := &NetworkResource{ - ReservedPorts: []int{3306, 8080}, - DynamicPorts: []string{"mysql", "admin"}, - } - - expected := map[string]int{ - "mysql": 3306, - "admin": 8080, - } - actual := resources.MapDynamicPorts() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v; found %#v", expected, actual) - } -} - -func TestListStaticPorts(t *testing.T) { - resources := &NetworkResource{ - ReservedPorts: []int{80, 443, 3306, 8080}, - DynamicPorts: []string{"mysql", "admin"}, - } - - expected := []int{80, 443} - actual := resources.ListStaticPorts() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v; found %#v", expected, actual) - } -} - -func TestListStaticPortsEmpty(t *testing.T) { - resources := &NetworkResource{ - ReservedPorts: []int{}, - DynamicPorts: []string{}, - } - - expected := []int{} - actual := resources.ListStaticPorts() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v; found %#v", expected, actual) - } -} - -func TestListStaticPortsOnly(t *testing.T) { - resources := &NetworkResource{ - ReservedPorts: []int{80, 443}, - DynamicPorts: []string{}, - } - - expected := []int{80, 443} - actual := resources.ListStaticPorts() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v; found %#v", expected, actual) - } -} - -func TestListStaticPortsDynamicOnly(t *testing.T) { - resources := &NetworkResource{ - ReservedPorts: []int{3306, 8080}, - DynamicPorts: []string{"mysql", "admin"}, - } - - expected := []int{} - actual := resources.ListStaticPorts() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v; found %#v", expected, actual) - } -} - func TestEncodeDecode(t *testing.T) { type FooRequest struct { Foo string From 7b2e3ee55cb055ef7ee5b2c38fd5842b55ad713a Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 02:58:46 -0800 Subject: [PATCH 15/26] Making sure that there is only one port_map block in the docker driver config --- client/driver/docker.go | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index d07317fad48..b1d6b96ba47 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -25,24 +25,30 @@ type DockerDriver struct { } type DockerDriverConfig struct { - ImageName string `mapstructure:"image"` - Command string `mapstructure:"command"` - Args string `mapstructure:"args"` - NetworkMode string `mapstructure:"network_mode"` - PortMap map[string]int `mapstructure:"port_map"` - UserName string `mapstructure:"auth.username"` - Password string `mapstructure:"auth.password` - Email string `mapstructure:"auth.email"` - ServerAddress string `mapstructure:"auth.server_address` - Privileged bool `mapstructure:"privileged"` - DNS string `mapstructure:"dns_server"` - SearchDomains string `mapstructure:"search_domains"` + ImageName string `mapstructure:"image"` + Command string `mapstructure:"command"` + Args string `mapstructure:"args"` + NetworkMode string `mapstructure:"network_mode"` + PortMap []map[string]int `mapstructure:"port_map"` + UserName string `mapstructure:"auth.username"` + Password string `mapstructure:"auth.password` + Email string `mapstructure:"auth.email"` + ServerAddress string `mapstructure:"auth.server_address` + Privileged bool `mapstructure:"privileged"` + DNS string `mapstructure:"dns_server"` + SearchDomains string `mapstructure:"search_domains"` } func (c *DockerDriverConfig) Validate() error { if c.ImageName == "" { return fmt.Errorf("Docker Driver needs an image name") } + + fmt.Printf("[DIPTANU] Portmap %#v \n", c.PortMap) + + if len(c.PortMap) > 1 { + return fmt.Errorf("Only one port_map block is allowed in the docker driver config") + } return nil } @@ -274,7 +280,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri containerToHostPortMap := make(map[string]int) for _, port := range network.DynamicPorts { - containerPort, ok := driverConfig.PortMap[port.Label] + containerPort, ok := driverConfig.PortMap[0][port.Label] if !ok { containerPort = port.Value } From 7ebe23519a8d6f3a207e46b9cc6dfe0356e323d1 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 17:42:17 -0500 Subject: [PATCH 16/26] Fixed the java driver config --- client/driver/java.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/driver/java.go b/client/driver/java.go index 774d7524a25..10a2e805727 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -28,7 +28,7 @@ type JavaDriver struct { type JavaDriverConfig struct { JvmOpts string `mapstructure:"jvm_options"` - ArtifactSource string `mapstructure:"artifact_source` + ArtifactSource string `mapstructure:"artifact_source"` Checksum string `mapstructure:"checksum"` Args string `mapstructure:"args"` } From 042532f56175bb72ea323d875db7f31a6d811a89 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Sun, 15 Nov 2015 15:03:40 -0800 Subject: [PATCH 17/26] RawToSting true for structs codec. Needed for encoding strings in nil interfaces --- nomad/structs/structs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index b9a326e0fd4..70fea0cee0e 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -1543,7 +1543,7 @@ func (p *PlanResult) FullCommit(plan *Plan) (bool, int, int) { } // msgpackHandle is a shared handle for encoding/decoding of structs -var msgpackHandle = &codec.MsgpackHandle{} +var msgpackHandle = &codec.MsgpackHandle{RawToString: true} // Decode is used to decode a MsgPack encoded object func Decode(buf []byte, out interface{}) error { From 363fced30ba3be087c4132487c2b0f1549edd445 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Sun, 15 Nov 2015 19:05:35 -0800 Subject: [PATCH 18/26] mapstructure close quotes --- client/driver/docker.go | 4 ++-- client/driver/qemu.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index b1d6b96ba47..337d228a624 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -31,9 +31,9 @@ type DockerDriverConfig struct { NetworkMode string `mapstructure:"network_mode"` PortMap []map[string]int `mapstructure:"port_map"` UserName string `mapstructure:"auth.username"` - Password string `mapstructure:"auth.password` + Password string `mapstructure:"auth.password"` Email string `mapstructure:"auth.email"` - ServerAddress string `mapstructure:"auth.server_address` + ServerAddress string `mapstructure:"auth.server_address"` Privileged bool `mapstructure:"privileged"` DNS string `mapstructure:"dns_server"` SearchDomains string `mapstructure:"search_domains"` diff --git a/client/driver/qemu.go b/client/driver/qemu.go index e12387e98dc..9865f600202 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -32,7 +32,7 @@ type QemuDriver struct { } type QemuDriverConfig struct { - ArtifactSource string `mapstructure:"artifact_source` + ArtifactSource string `mapstructure:"artifact_source"` Checksum string `mapstructure:"checksum"` Accelerator string `mapstructure:"accelerator"` GuestPorts string `mapstructure:"guest_ports"` From 174ac10a8ab4fa557b15c1f03a1046108795c2aa Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 23:25:57 -0500 Subject: [PATCH 19/26] Adding comments to fields in the docker driver config --- client/driver/docker.go | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 337d228a624..c6ac5c89b37 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -24,18 +24,23 @@ type DockerDriver struct { fingerprint.StaticFingerprinter } +type DockerAuthConfig struct { + UserName string `mapstructure:"auth.username"` // user name of the registry + Password string `mapstructure:"auth.password` // password to access the registry + Email string `mapstructure:"auth.email"` // email address of the user who is allowed to access the registry + ServerAddress string `mapstructure:"auth.server_address` // server address of the registry + +} + type DockerDriverConfig struct { - ImageName string `mapstructure:"image"` - Command string `mapstructure:"command"` - Args string `mapstructure:"args"` - NetworkMode string `mapstructure:"network_mode"` - PortMap []map[string]int `mapstructure:"port_map"` - UserName string `mapstructure:"auth.username"` - Password string `mapstructure:"auth.password"` - Email string `mapstructure:"auth.email"` - ServerAddress string `mapstructure:"auth.server_address"` - Privileged bool `mapstructure:"privileged"` - DNS string `mapstructure:"dns_server"` + DockerAuthConfig + ImageName string `mapstructure:"image"` // Container's Image Name + Command string `mapstructure:"command"` // The Command/Entrypoint to run when the container starts up + Args string `mapstructure:"args"` // The arguments to the Command/Entrypoint + NetworkMode string `mapstructure:"network_mode"` // The network mode of the container - host, net and none + PortMap []map[string]int `mapstructure:"port_map"` // A map of host port labels and the ports exposed on the container + Privileged bool `mapstructure:"privileged"` // Flag to run the container in priviledged mode + DNS string `mapstructure:"dns_server"` // DNS Server for containers SearchDomains string `mapstructure:"search_domains"` } @@ -44,8 +49,6 @@ func (c *DockerDriverConfig) Validate() error { return fmt.Errorf("Docker Driver needs an image name") } - fmt.Printf("[DIPTANU] Portmap %#v \n", c.PortMap) - if len(c.PortMap) > 1 { return fmt.Errorf("Only one port_map block is allowed in the docker driver config") } From 041f48dc715c81bb16d183a9164d478397da4fe9 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 23:34:05 -0500 Subject: [PATCH 20/26] Fixed the docker auth config --- client/driver/docker.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index c6ac5c89b37..eba4341bb6f 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -25,10 +25,10 @@ type DockerDriver struct { } type DockerAuthConfig struct { - UserName string `mapstructure:"auth.username"` // user name of the registry - Password string `mapstructure:"auth.password` // password to access the registry - Email string `mapstructure:"auth.email"` // email address of the user who is allowed to access the registry - ServerAddress string `mapstructure:"auth.server_address` // server address of the registry + UserName string `mapstructure:"auth.username"` // user name of the registry + Password string `mapstructure:"auth.password"` // password to access the registry + Email string `mapstructure:"auth.email"` // email address of the user who is allowed to access the registry + ServerAddress string `mapstructure:"auth.server_address"` // server address of the registry } From c01f2a38f554e1e167f81df6181876efaf7c7232 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sun, 15 Nov 2015 23:53:04 -0500 Subject: [PATCH 21/26] Made the code more DRY --- client/driver/docker.go | 9 ++++----- client/driver/exec.go | 4 ++-- client/driver/executor/test_harness.go | 2 +- client/driver/java.go | 4 ++-- client/driver/qemu.go | 4 ++-- client/driver/raw_exec.go | 4 ++-- client/driver/rkt.go | 4 ++-- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index eba4341bb6f..74f72009e3b 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -215,12 +215,12 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri return c, fmt.Errorf("Unable to parse docker.privileged.enabled: %s", err) } - if taskPrivileged := driverConfig.Privileged; taskPrivileged { - if taskPrivileged && !hostPrivileged { + if driverConfig.Privileged { + if !hostPrivileged { return c, fmt.Errorf(`Unable to set privileged flag since "docker.privileged.enabled" is false`) } - hostConfig.Privileged = taskPrivileged + hostConfig.Privileged = driverConfig.Privileged } // set DNS servers @@ -303,8 +303,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri config.ExposedPorts = exposedPorts } - rawArgs := driverConfig.Args - parsedArgs, err := args.ParseAndReplace(rawArgs, env.Map()) + parsedArgs, err := args.ParseAndReplace(driverConfig.Args, env.Map()) if err != nil { return c, err } diff --git a/client/driver/exec.go b/client/driver/exec.go index 6abcb7fe912..073b4680a84 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -92,8 +92,8 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // Look for arguments var args []string - if argRaw := driverConfig.Args; argRaw != "" { - args = append(args, argRaw) + if driverConfig.Args != "" { + args = append(args, driverConfig.Args) } // Setup the command diff --git a/client/driver/executor/test_harness.go b/client/driver/executor/test_harness.go index 34ead751e0a..c564373ea8e 100644 --- a/client/driver/executor/test_harness.go +++ b/client/driver/executor/test_harness.go @@ -21,7 +21,7 @@ var ( Networks: []*structs.NetworkResource{ &structs.NetworkResource{ MBits: 50, - DynamicPorts: []structs.Port{structs.Port{Label: "http"}}, + DynamicPorts: []structs.Port{{Label: "http"}}, }, }, } diff --git a/client/driver/java.go b/client/driver/java.go index 10a2e805727..c7ea987563b 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -133,8 +133,8 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // Build the argument list. args = append(args, "-jar", filepath.Join(allocdir.TaskLocal, jarName)) - if argRaw := driverConfig.Args; argRaw != "" { - args = append(args, argRaw) + if driverConfig.Args != "" { + args = append(args, driverConfig.Args) } // Setup the command diff --git a/client/driver/qemu.go b/client/driver/qemu.go index 9865f600202..2b2583fe4cb 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -115,8 +115,8 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // Parse configuration arguments // Create the base arguments accelerator := "tcg" - if acc := driverConfig.Accelerator; acc != "" { - accelerator = acc + if driverConfig.Accelerator != "" { + accelerator = driverConfig.Accelerator } // TODO: Check a lower bounds, e.g. the default 128 of Qemu mem := fmt.Sprintf("%dM", task.Resources.MemoryMB) diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index f0bb1113cd1..57f7536a57f 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -94,8 +94,8 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl // Look for arguments var args []string - if argRaw := driverConfig.Args; argRaw != "" { - args = append(args, argRaw) + if driverConfig.Args != "" { + args = append(args, driverConfig.Args) } // Setup the command diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 045b981b697..1ce53916771 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -149,8 +149,8 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e } // Add user passed arguments. - if userArgs := driverConfig.Args; userArgs != "" { - parsed, err := args.ParseAndReplace(userArgs, envVars.Map()) + if driverConfig.Args != "" { + parsed, err := args.ParseAndReplace(driverConfig.Args, envVars.Map()) if err != nil { return nil, err } From bc2efb78241d6b031604cbc59d183195fc84c598 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 16 Nov 2015 10:00:06 -0800 Subject: [PATCH 22/26] jobspec: add test for types of nested configs --- jobspec/parse_test.go | 37 ++++++++++++++++++++ jobspec/test-fixtures/task-nested-config.hcl | 10 ++++++ 2 files changed, 47 insertions(+) create mode 100644 jobspec/test-fixtures/task-nested-config.hcl diff --git a/jobspec/parse_test.go b/jobspec/parse_test.go index 0814aa0f6ef..125127de578 100644 --- a/jobspec/parse_test.go +++ b/jobspec/parse_test.go @@ -225,6 +225,43 @@ func TestParse(t *testing.T) { }, false, }, + + { + "task-nested-config.hcl", + &structs.Job{ + Region: "global", + ID: "foo", + Name: "foo", + Type: "service", + Priority: 50, + + TaskGroups: []*structs.TaskGroup{ + &structs.TaskGroup{ + Name: "bar", + Count: 1, + RestartPolicy: &structs.RestartPolicy{ + Attempts: 2, + Interval: 1 * time.Minute, + Delay: 15 * time.Second, + }, + Tasks: []*structs.Task{ + &structs.Task{ + Name: "bar", + Driver: "docker", + Config: map[string]interface{}{ + "port_map": []map[string]interface{}{ + map[string]interface{}{ + "db": 1234, + }, + }, + }, + }, + }, + }, + }, + }, + false, + }, } for _, tc := range cases { diff --git a/jobspec/test-fixtures/task-nested-config.hcl b/jobspec/test-fixtures/task-nested-config.hcl new file mode 100644 index 00000000000..428c90e2ba0 --- /dev/null +++ b/jobspec/test-fixtures/task-nested-config.hcl @@ -0,0 +1,10 @@ +job "foo" { + task "bar" { + driver = "docker" + config { + port_map { + db = 1234 + } + } + } +} From 3d4fdbba8a10c65de8052fb7d959069c53c1a560 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Nov 2015 14:25:19 -0800 Subject: [PATCH 23/26] Avoid map[interface{}]interface{} conversions in the msgpack codec --- nomad/structs/structs.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 70fea0cee0e..ddc1c326f4d 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "reflect" "regexp" "strings" "time" @@ -1543,7 +1544,15 @@ func (p *PlanResult) FullCommit(plan *Plan) (bool, int, int) { } // msgpackHandle is a shared handle for encoding/decoding of structs -var msgpackHandle = &codec.MsgpackHandle{RawToString: true} +var msgpackHandle = func() *codec.MsgpackHandle { + h := &codec.MsgpackHandle{RawToString: true} + + // Sets the default type for decoding a map into a nil interface{}. + // This is necessary in particular because we store the driver configs as a + // nil interface{}. + h.MapType = reflect.TypeOf(map[string]interface{}(nil)) + return h +}() // Decode is used to decode a MsgPack encoded object func Decode(buf []byte, out interface{}) error { From 28757ca175f4663393b7f72ddfdb41c137492332 Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Mon, 16 Nov 2015 16:05:28 -0800 Subject: [PATCH 24/26] Change logging port as %d to logging port.Value --- client/driver/docker.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 51936e08d63..f218a911f40 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -14,10 +14,10 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/args" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad/structs" "github.com/mitchellh/mapstructure" - cstructs "github.com/hashicorp/nomad/client/driver/structs" ) type DockerDriver struct { @@ -276,10 +276,10 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri for _, port := range network.ReservedPorts { publishedPorts[docker.Port(strconv.Itoa(port.Value)+"/tcp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} publishedPorts[docker.Port(strconv.Itoa(port.Value)+"/udp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} - d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (static)\n", network.IP, port, port) + d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (static)\n", network.IP, port.Value, port.Value) exposedPorts[docker.Port(strconv.Itoa(port.Value)+"/tcp")] = struct{}{} exposedPorts[docker.Port(strconv.Itoa(port.Value)+"/udp")] = struct{}{} - d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", port) + d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", port.Value) } containerToHostPortMap := make(map[string]int) @@ -295,7 +295,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (mapped)", network.IP, port.Value, containerPort) exposedPorts[docker.Port(cp+"/tcp")] = struct{}{} exposedPorts[docker.Port(cp+"/udp")] = struct{}{} - d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", hostPort) + d.logger.Printf("[DEBUG] driver.docker: exposed port %s\n", hostPort) containerToHostPortMap[cp] = port.Value } From 640337547a206b6bc3a0662d5d2b35f7e13a3491 Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Mon, 16 Nov 2015 16:23:03 -0800 Subject: [PATCH 25/26] go fmt --- client/driver/exec.go | 3 +-- client/driver/java.go | 3 +-- client/driver/qemu.go | 3 +-- client/driver/raw_exec.go | 3 +-- client/driver/rkt.go | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/client/driver/exec.go b/client/driver/exec.go index 3fae312ef8c..1f9e5b7d94f 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -10,12 +10,11 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/executor" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/client/getter" "github.com/hashicorp/nomad/nomad/structs" "github.com/mitchellh/mapstructure" - cstructs "github.com/hashicorp/nomad/client/driver/structs" - ) // ExecDriver fork/execs tasks using as many of the underlying OS's isolation diff --git a/client/driver/java.go b/client/driver/java.go index 774a73989e5..1f51976475f 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -13,12 +13,11 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/executor" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/client/getter" "github.com/hashicorp/nomad/nomad/structs" "github.com/mitchellh/mapstructure" - cstructs "github.com/hashicorp/nomad/client/driver/structs" - ) // JavaDriver is a simple driver to execute applications packaged in Jars. diff --git a/client/driver/qemu.go b/client/driver/qemu.go index 9ad13ae217f..f51907f7528 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -13,12 +13,11 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/executor" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/client/getter" "github.com/hashicorp/nomad/nomad/structs" "github.com/mitchellh/mapstructure" - cstructs "github.com/hashicorp/nomad/client/driver/structs" - ) var ( diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index 6d61ba5f4da..d5202fc39a2 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -9,12 +9,11 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/executor" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/client/getter" "github.com/hashicorp/nomad/nomad/structs" "github.com/mitchellh/mapstructure" - cstructs "github.com/hashicorp/nomad/client/driver/structs" - ) const ( diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 10fa34593d3..d09eac1dbdf 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -17,11 +17,10 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/args" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad/structs" "github.com/mitchellh/mapstructure" - cstructs "github.com/hashicorp/nomad/client/driver/structs" - ) var ( From b8d468f5ad01c6055f7298649d02207fca557f00 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 16 Nov 2015 22:29:06 -0500 Subject: [PATCH 26/26] Addressed some review comments --- client/driver/docker.go | 20 ++++++++++---------- client/driver/java.go | 7 +++---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index f218a911f40..f2d25f9ba9f 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -35,14 +35,14 @@ type DockerAuthConfig struct { type DockerDriverConfig struct { DockerAuthConfig - ImageName string `mapstructure:"image"` // Container's Image Name - Command string `mapstructure:"command"` // The Command/Entrypoint to run when the container starts up - Args string `mapstructure:"args"` // The arguments to the Command/Entrypoint - NetworkMode string `mapstructure:"network_mode"` // The network mode of the container - host, net and none - PortMap []map[string]int `mapstructure:"port_map"` // A map of host port labels and the ports exposed on the container - Privileged bool `mapstructure:"privileged"` // Flag to run the container in priviledged mode - DNS string `mapstructure:"dns_server"` // DNS Server for containers - SearchDomains string `mapstructure:"search_domains"` + ImageName string `mapstructure:"image"` // Container's Image Name + Command string `mapstructure:"command"` // The Command/Entrypoint to run when the container starts up + Args string `mapstructure:"args"` // The arguments to the Command/Entrypoint + NetworkMode string `mapstructure:"network_mode"` // The network mode of the container - host, net and none + PortMap []map[string]int `mapstructure:"port_map"` // A map of host port labels and the ports exposed on the container + Privileged bool `mapstructure:"privileged"` // Flag to run the container in priviledged mode + DNS string `mapstructure:"dns_server"` // DNS Server for containers + SearchDomains string `mapstructure:"search_domains"` // DNS Search domains for containers } func (c *DockerDriverConfig) Validate() error { @@ -311,8 +311,8 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri // If the user specified a custom command to run as their entrypoint, we'll // inject it here. - if command := driverConfig.Command; command != "" { - cmd := []string{command} + if driverConfig.Command != "" { + cmd := []string{driverConfig.Command} if driverConfig.Args != "" { cmd = append(cmd, parsedArgs...) } diff --git a/client/driver/java.go b/client/driver/java.go index 1f51976475f..eb2930a2809 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -126,10 +126,9 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, args := []string{} // Look for jvm options - jvm_options := driverConfig.JvmOpts - if jvm_options != "" { - d.logger.Printf("[DEBUG] driver.java: found JVM options: %s", jvm_options) - args = append(args, jvm_options) + if driverConfig.JvmOpts != "" { + d.logger.Printf("[DEBUG] driver.java: found JVM options: %s", driverConfig.JvmOpts) + args = append(args, driverConfig.JvmOpts) } // Build the argument list.