client: Add option to enable hairpinMode on Nomad bridge (#15961)
* Add `bridge_network_hairpin_mode` client config setting * Add node attribute: `nomad.bridge.hairpin_mode` * Changed format string to use `%q` to escape user provided data * Add test to validate template JSON for developer safety Co-authored-by: Daniel Bennett <dbennett@hashicorp.com>
This commit is contained in:
parent
37834dffda
commit
4caac1a92f
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
client: Add option to enable hairpinMode on Nomad bridge
|
||||||
|
```
|
|
@ -524,12 +524,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartAll(ev)
|
return ar.RestartAll(ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 1},
|
"main": {State: "running", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 1},
|
"prestart-oneshot": {State: "dead", Restarts: 1},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"prestart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 1},
|
"poststart-oneshot": {State: "dead", Restarts: 1},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"poststart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -538,12 +538,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartRunning(ev)
|
return ar.RestartRunning(ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 1},
|
"main": {State: "running", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"prestart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"poststart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -561,12 +561,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartAll(ev)
|
return ar.RestartAll(ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 1},
|
"main": {State: "running", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 1},
|
"prestart-oneshot": {State: "dead", Restarts: 1},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"prestart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 1},
|
"poststart-oneshot": {State: "dead", Restarts: 1},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"poststart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -584,12 +584,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartRunning(ev)
|
return ar.RestartRunning(ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 1},
|
"main": {State: "running", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"prestart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"poststart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -599,12 +599,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartAll(ev)
|
return ar.RestartAll(ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 1},
|
"main": {State: "running", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 1},
|
"prestart-oneshot": {State: "dead", Restarts: 1},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"prestart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 1},
|
"poststart-oneshot": {State: "dead", Restarts: 1},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"poststart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -616,12 +616,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "dead", Restarts: 0},
|
"main": {State: "dead", Restarts: 0},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "dead", Restarts: 0},
|
"poststop": {State: "dead", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -630,12 +630,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartTask("main", ev)
|
return ar.RestartTask("main", ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 1},
|
"main": {State: "running", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 0},
|
"prestart-sidecar": {State: "running", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 0},
|
"poststart-sidecar": {State: "running", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -645,12 +645,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartTask("main", ev)
|
return ar.RestartTask("main", ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 1},
|
"main": {State: "running", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 0},
|
"prestart-sidecar": {State: "running", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 0},
|
"poststart-sidecar": {State: "running", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -668,12 +668,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "dead", Restarts: 1},
|
"main": {State: "dead", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "dead", Restarts: 0},
|
"poststop": {State: "dead", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -692,12 +692,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "dead", Restarts: 1},
|
"main": {State: "dead", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "dead", Restarts: 0},
|
"poststop": {State: "dead", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -715,12 +715,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "dead", Restarts: 1},
|
"main": {State: "dead", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "dead", Restarts: 0},
|
"poststop": {State: "dead", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -738,12 +738,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "dead", Restarts: 1},
|
"main": {State: "dead", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "dead", Restarts: 0},
|
"poststop": {State: "dead", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -764,12 +764,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedErr: "Task not running",
|
expectedErr: "Task not running",
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "dead", Restarts: 1},
|
"main": {State: "dead", Restarts: 1},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-sidecar": {State: "dead", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "dead", Restarts: 0},
|
"poststop": {State: "dead", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -778,12 +778,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartTask("prestart-sidecar", ev)
|
return ar.RestartTask("prestart-sidecar", ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 0},
|
"main": {State: "running", Restarts: 0},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"prestart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 0},
|
"poststart-sidecar": {State: "running", Restarts: 0},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -792,12 +792,12 @@ func TestAllocRunner_Lifecycle_Restart(t *testing.T) {
|
||||||
return ar.RestartTask("poststart-sidecar", ev)
|
return ar.RestartTask("poststart-sidecar", ev)
|
||||||
},
|
},
|
||||||
expectedAfter: map[string]structs.TaskState{
|
expectedAfter: map[string]structs.TaskState{
|
||||||
"main": structs.TaskState{State: "running", Restarts: 0},
|
"main": {State: "running", Restarts: 0},
|
||||||
"prestart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"prestart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"prestart-sidecar": structs.TaskState{State: "running", Restarts: 0},
|
"prestart-sidecar": {State: "running", Restarts: 0},
|
||||||
"poststart-oneshot": structs.TaskState{State: "dead", Restarts: 0},
|
"poststart-oneshot": {State: "dead", Restarts: 0},
|
||||||
"poststart-sidecar": structs.TaskState{State: "running", Restarts: 1},
|
"poststart-sidecar": {State: "running", Restarts: 1},
|
||||||
"poststop": structs.TaskState{State: "pending", Restarts: 0},
|
"poststop": {State: "pending", Restarts: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ func newNetworkConfigurator(log hclog.Logger, alloc *structs.Allocation, config
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case netMode == "bridge":
|
case netMode == "bridge":
|
||||||
c, err := newBridgeNetworkConfigurator(log, config.BridgeNetworkName, config.BridgeNetworkAllocSubnet, config.CNIPath, ignorePortMappingHostIP)
|
c, err := newBridgeNetworkConfigurator(log, config.BridgeNetworkName, config.BridgeNetworkAllocSubnet, config.BridgeNetworkHairpinMode, config.CNIPath, ignorePortMappingHostIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package allocrunner
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
hclog "github.com/hashicorp/go-hclog"
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
|
@ -28,6 +29,8 @@ const (
|
||||||
cniAdminChainName = "NOMAD-ADMIN"
|
cniAdminChainName = "NOMAD-ADMIN"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var nomadBridgeTmpl = template.Must(template.New("cniConf").Parse(nomadCNIConfigTemplate))
|
||||||
|
|
||||||
// bridgeNetworkConfigurator is a NetworkConfigurator which adds the alloc to a
|
// bridgeNetworkConfigurator is a NetworkConfigurator which adds the alloc to a
|
||||||
// shared bridge, configures masquerading for egress traffic and port mapping
|
// shared bridge, configures masquerading for egress traffic and port mapping
|
||||||
// for ingress
|
// for ingress
|
||||||
|
@ -35,14 +38,16 @@ type bridgeNetworkConfigurator struct {
|
||||||
cni *cniNetworkConfigurator
|
cni *cniNetworkConfigurator
|
||||||
allocSubnet string
|
allocSubnet string
|
||||||
bridgeName string
|
bridgeName string
|
||||||
|
hairpinMode bool
|
||||||
|
|
||||||
logger hclog.Logger
|
logger hclog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBridgeNetworkConfigurator(log hclog.Logger, bridgeName, ipRange, cniPath string, ignorePortMappingHostIP bool) (*bridgeNetworkConfigurator, error) {
|
func newBridgeNetworkConfigurator(log hclog.Logger, bridgeName, ipRange string, hairpinMode bool, cniPath string, ignorePortMappingHostIP bool) (*bridgeNetworkConfigurator, error) {
|
||||||
b := &bridgeNetworkConfigurator{
|
b := &bridgeNetworkConfigurator{
|
||||||
bridgeName: bridgeName,
|
bridgeName: bridgeName,
|
||||||
allocSubnet: ipRange,
|
allocSubnet: ipRange,
|
||||||
|
hairpinMode: hairpinMode,
|
||||||
logger: log,
|
logger: log,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +59,7 @@ func newBridgeNetworkConfigurator(log hclog.Logger, bridgeName, ipRange, cniPath
|
||||||
b.allocSubnet = defaultNomadAllocSubnet
|
b.allocSubnet = defaultNomadAllocSubnet
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := newCNINetworkConfiguratorWithConf(log, cniPath, bridgeNetworkAllocIfPrefix, ignorePortMappingHostIP, buildNomadBridgeNetConfig(b.bridgeName, b.allocSubnet))
|
c, err := newCNINetworkConfiguratorWithConf(log, cniPath, bridgeNetworkAllocIfPrefix, ignorePortMappingHostIP, buildNomadBridgeNetConfig(*b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -134,8 +139,12 @@ func (b *bridgeNetworkConfigurator) Teardown(ctx context.Context, alloc *structs
|
||||||
return b.cni.Teardown(ctx, alloc, spec)
|
return b.cni.Teardown(ctx, alloc, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildNomadBridgeNetConfig(bridgeName, subnet string) []byte {
|
func buildNomadBridgeNetConfig(b bridgeNetworkConfigurator) []byte {
|
||||||
return []byte(fmt.Sprintf(nomadCNIConfigTemplate, bridgeName, subnet, cniAdminChainName))
|
return []byte(fmt.Sprintf(nomadCNIConfigTemplate,
|
||||||
|
b.bridgeName,
|
||||||
|
b.hairpinMode,
|
||||||
|
b.allocSubnet,
|
||||||
|
cniAdminChainName))
|
||||||
}
|
}
|
||||||
|
|
||||||
const nomadCNIConfigTemplate = `{
|
const nomadCNIConfigTemplate = `{
|
||||||
|
@ -147,16 +156,17 @@ const nomadCNIConfigTemplate = `{
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "bridge",
|
"type": "bridge",
|
||||||
"bridge": "%s",
|
"bridge": %q,
|
||||||
"ipMasq": true,
|
"ipMasq": true,
|
||||||
"isGateway": true,
|
"isGateway": true,
|
||||||
"forceAddress": true,
|
"forceAddress": true,
|
||||||
|
"hairpinMode": %v,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"ranges": [
|
"ranges": [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"subnet": "%s"
|
"subnet": %q
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
@ -168,7 +178,7 @@ const nomadCNIConfigTemplate = `{
|
||||||
{
|
{
|
||||||
"type": "firewall",
|
"type": "firewall",
|
||||||
"backend": "iptables",
|
"backend": "iptables",
|
||||||
"iptablesAdminChainName": "%s"
|
"iptablesAdminChainName": %q
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "portmap",
|
"type": "portmap",
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package allocrunner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/ci"
|
||||||
|
"github.com/shoenig/test/must"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_buildNomadBridgeNetConfig(t *testing.T) {
|
||||||
|
ci.Parallel(t)
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
b *bridgeNetworkConfigurator
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
b: &bridgeNetworkConfigurator{},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "hairpin",
|
||||||
|
b: &bridgeNetworkConfigurator{
|
||||||
|
bridgeName: defaultNomadBridgeName,
|
||||||
|
allocSubnet: defaultNomadAllocSubnet,
|
||||||
|
hairpinMode: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad_input",
|
||||||
|
b: &bridgeNetworkConfigurator{
|
||||||
|
bridgeName: `bad"`,
|
||||||
|
allocSubnet: defaultNomadAllocSubnet,
|
||||||
|
hairpinMode: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tc := tc
|
||||||
|
ci.Parallel(t)
|
||||||
|
bCfg := buildNomadBridgeNetConfig(*tc.b)
|
||||||
|
// Validate that the JSON created is rational
|
||||||
|
must.True(t, json.Valid(bCfg))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -261,6 +261,10 @@ type Config struct {
|
||||||
// networking mode. This defaults to 'nomad' if not set
|
// networking mode. This defaults to 'nomad' if not set
|
||||||
BridgeNetworkName string
|
BridgeNetworkName string
|
||||||
|
|
||||||
|
// BridgeNetworkHairpinMode is whether or not to enable hairpin mode on the
|
||||||
|
// internal bridge network
|
||||||
|
BridgeNetworkHairpinMode bool
|
||||||
|
|
||||||
// BridgeNetworkAllocSubnet is the IP subnet to use for address allocation
|
// BridgeNetworkAllocSubnet is the IP subnet to use for address allocation
|
||||||
// for allocations in bridge networking mode. Subnet must be in CIDR
|
// for allocations in bridge networking mode. Subnet must be in CIDR
|
||||||
// notation
|
// notation
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//go:build !linux
|
//go:build !linux
|
||||||
// +build !linux
|
|
||||||
|
|
||||||
package fingerprint
|
package fingerprint
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
package fingerprint
|
package fingerprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -5,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
|
@ -35,6 +38,9 @@ func (f *BridgeFingerprint) Fingerprint(req *FingerprintRequest, resp *Fingerpri
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp.AddAttribute("nomad.bridge.hairpin_mode",
|
||||||
|
strconv.FormatBool(req.Config.BridgeNetworkHairpinMode))
|
||||||
|
|
||||||
resp.Detected = true
|
resp.Detected = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -788,6 +788,7 @@ func convertClientConfig(agentConfig *Config) (*clientconfig.Config, error) {
|
||||||
conf.CNIConfigDir = agentConfig.Client.CNIConfigDir
|
conf.CNIConfigDir = agentConfig.Client.CNIConfigDir
|
||||||
conf.BridgeNetworkName = agentConfig.Client.BridgeNetworkName
|
conf.BridgeNetworkName = agentConfig.Client.BridgeNetworkName
|
||||||
conf.BridgeNetworkAllocSubnet = agentConfig.Client.BridgeNetworkSubnet
|
conf.BridgeNetworkAllocSubnet = agentConfig.Client.BridgeNetworkSubnet
|
||||||
|
conf.BridgeNetworkHairpinMode = agentConfig.Client.BridgeNetworkHairpinMode
|
||||||
|
|
||||||
for _, hn := range agentConfig.Client.HostNetworks {
|
for _, hn := range agentConfig.Client.HostNetworks {
|
||||||
conf.HostNetworks[hn.Name] = hn
|
conf.HostNetworks[hn.Name] = hn
|
||||||
|
|
|
@ -315,6 +315,10 @@ type ClientConfig struct {
|
||||||
// the host
|
// the host
|
||||||
BridgeNetworkSubnet string `hcl:"bridge_network_subnet"`
|
BridgeNetworkSubnet string `hcl:"bridge_network_subnet"`
|
||||||
|
|
||||||
|
// BridgeNetworkHairpinMode is whether or not to enable hairpin mode on the
|
||||||
|
// internal bridge network
|
||||||
|
BridgeNetworkHairpinMode bool `hcl:"bridge_network_hairpin_mode"`
|
||||||
|
|
||||||
// HostNetworks describes the different host networks available to the host
|
// HostNetworks describes the different host networks available to the host
|
||||||
// if the host uses multiple interfaces
|
// if the host uses multiple interfaces
|
||||||
HostNetworks []*structs.ClientHostNetworkConfig `hcl:"host_network"`
|
HostNetworks []*structs.ClientHostNetworkConfig `hcl:"host_network"`
|
||||||
|
@ -2137,6 +2141,10 @@ func (a *ClientConfig) Merge(b *ClientConfig) *ClientConfig {
|
||||||
result.BridgeNetworkSubnet = b.BridgeNetworkSubnet
|
result.BridgeNetworkSubnet = b.BridgeNetworkSubnet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.BridgeNetworkHairpinMode {
|
||||||
|
result.BridgeNetworkHairpinMode = true
|
||||||
|
}
|
||||||
|
|
||||||
result.HostNetworks = a.HostNetworks
|
result.HostNetworks = a.HostNetworks
|
||||||
|
|
||||||
if len(b.HostNetworks) != 0 {
|
if len(b.HostNetworks) != 0 {
|
||||||
|
|
|
@ -147,12 +147,17 @@ client {
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
- `bridge_network_name` `(string: "nomad")` - Sets the name of the bridge to be
|
- `bridge_network_name` `(string: "nomad")` - Sets the name of the bridge to be
|
||||||
created by nomad for allocations running with bridge networking mode on the
|
created by Nomad for allocations running with bridge networking mode on the
|
||||||
client.
|
client.
|
||||||
|
|
||||||
- `bridge_network_subnet` `(string: "172.26.64.0/20")` - Specifies the subnet
|
- `bridge_network_subnet` `(string: "172.26.64.0/20")` - Specifies the subnet
|
||||||
which the client will use to allocate IP addresses from.
|
which the client will use to allocate IP addresses from.
|
||||||
|
|
||||||
|
- `bridge_network_hairpin_mode` `(bool: false)` - Specifies if hairpin mode
|
||||||
|
is enabled on the network bridge created by Nomad for allocations running
|
||||||
|
with bridge networking mode on this client. You may use the corresponding
|
||||||
|
node attribute `nomad.bridge.hairpin_mode` in constraints.
|
||||||
|
|
||||||
- `artifact` <code>([Artifact](#artifact-parameters): varied)</code> -
|
- `artifact` <code>([Artifact](#artifact-parameters): varied)</code> -
|
||||||
Specifies controls on the behavior of task
|
Specifies controls on the behavior of task
|
||||||
[`artifact`](/nomad/docs/job-specification/artifact) blocks.
|
[`artifact`](/nomad/docs/job-specification/artifact) blocks.
|
||||||
|
|
Loading…
Reference in New Issue