139 lines
3.0 KiB
Go
139 lines
3.0 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package jobspec
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
multierror "github.com/hashicorp/go-multierror"
|
|
"github.com/hashicorp/hcl"
|
|
"github.com/hashicorp/hcl/hcl/ast"
|
|
"github.com/hashicorp/nomad/api"
|
|
"github.com/mitchellh/mapstructure"
|
|
)
|
|
|
|
// ParseNetwork parses a collection containing exactly one NetworkResource
|
|
func ParseNetwork(o *ast.ObjectList) (*api.NetworkResource, error) {
|
|
if len(o.Items) > 1 {
|
|
return nil, fmt.Errorf("only one 'network' resource allowed")
|
|
}
|
|
|
|
// Check for invalid keys
|
|
valid := []string{
|
|
"mode",
|
|
"mbits",
|
|
"dns",
|
|
"port",
|
|
"hostname",
|
|
}
|
|
if err := checkHCLKeys(o.Items[0].Val, valid); err != nil {
|
|
return nil, multierror.Prefix(err, "network ->")
|
|
}
|
|
|
|
var r api.NetworkResource
|
|
var m map[string]interface{}
|
|
if err := hcl.DecodeObject(&m, o.Items[0].Val); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
delete(m, "dns")
|
|
if err := mapstructure.WeakDecode(m, &r); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var networkObj *ast.ObjectList
|
|
if ot, ok := o.Items[0].Val.(*ast.ObjectType); ok {
|
|
networkObj = ot.List
|
|
} else {
|
|
return nil, fmt.Errorf("should be an object")
|
|
}
|
|
if err := parsePorts(networkObj, &r); err != nil {
|
|
return nil, multierror.Prefix(err, "network, ports ->")
|
|
}
|
|
|
|
// Filter dns
|
|
if dns := networkObj.Filter("dns"); len(dns.Items) > 0 {
|
|
if len(dns.Items) > 1 {
|
|
return nil, multierror.Prefix(fmt.Errorf("cannot have more than 1 dns block"), "network ->")
|
|
}
|
|
|
|
d, err := parseDNS(dns.Items[0])
|
|
if err != nil {
|
|
return nil, multierror.Prefix(err, "network ->")
|
|
}
|
|
|
|
r.DNS = d
|
|
}
|
|
|
|
return &r, nil
|
|
}
|
|
|
|
func parsePorts(networkObj *ast.ObjectList, nw *api.NetworkResource) error {
|
|
portsObjList := networkObj.Filter("port")
|
|
knownPortLabels := make(map[string]bool)
|
|
for _, port := range portsObjList.Items {
|
|
if len(port.Keys) == 0 {
|
|
return fmt.Errorf("ports must be named")
|
|
}
|
|
|
|
// check for invalid keys
|
|
valid := []string{
|
|
"static",
|
|
"to",
|
|
"host_network",
|
|
}
|
|
if err := checkHCLKeys(port.Val, valid); err != nil {
|
|
return err
|
|
}
|
|
|
|
label := port.Keys[0].Token.Value().(string)
|
|
if !reDynamicPorts.MatchString(label) {
|
|
return errPortLabel
|
|
}
|
|
l := strings.ToLower(label)
|
|
if knownPortLabels[l] {
|
|
return fmt.Errorf("found a port label collision: %s", label)
|
|
}
|
|
|
|
var res api.Port
|
|
if err := hcl.DecodeObject(&res, port.Val); err != nil {
|
|
return err
|
|
}
|
|
|
|
res.Label = label
|
|
if res.Value > 0 {
|
|
nw.ReservedPorts = append(nw.ReservedPorts, res)
|
|
} else {
|
|
nw.DynamicPorts = append(nw.DynamicPorts, res)
|
|
}
|
|
knownPortLabels[l] = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseDNS(dns *ast.ObjectItem) (*api.DNSConfig, error) {
|
|
valid := []string{
|
|
"servers",
|
|
"searches",
|
|
"options",
|
|
}
|
|
|
|
if err := checkHCLKeys(dns.Val, valid); err != nil {
|
|
return nil, multierror.Prefix(err, "dns ->")
|
|
}
|
|
|
|
var dnsCfg api.DNSConfig
|
|
var m map[string]interface{}
|
|
if err := hcl.DecodeObject(&m, dns.Val); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := mapstructure.WeakDecode(m, &dnsCfg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &dnsCfg, nil
|
|
}
|