// 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 }