From 6b89527505821b46c2debab42bb0118bec5c9f2c Mon Sep 17 00:00:00 2001 From: Seth Hoenig Date: Fri, 13 Nov 2020 14:17:01 -0600 Subject: [PATCH] scheduler: enable upgrade path for bridge network finger print This PR enables users of Nomad < 0.12 to upgrade to Nomad 0.12 and beyond. Nomad 0.12 introduced a network fingerprinter for bridge networks, which is a contstraint checked for if bridge network is being used. If users upgrade servers first as is recommended, suddenly no clients running older versions of Nomad will satisfy the bridge network resource constraint. Instead, this change only enforces the constraint if the Nomad client version is also >= 0.12. Closes #8423 --- scheduler/feasible.go | 27 +++++++++++++++++++++ scheduler/feasible_test.go | 49 ++++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/scheduler/feasible.go b/scheduler/feasible.go index 0d720b3af..ce952663d 100644 --- a/scheduler/feasible.go +++ b/scheduler/feasible.go @@ -28,6 +28,21 @@ const ( FilterConstraintDevices = "missing devices" ) +var ( + // predatesBridgeFingerprint returns true if the constraint matches a version + // of nomad that predates the addition of the bridge network finger-printer, + // which was added in Nomad v0.12 + predatesBridgeFingerprint = mustBridgeConstraint() +) + +func mustBridgeConstraint() version.Constraints { + versionC, err := version.NewConstraint("< 0.12") + if err != nil { + panic(err) + } + return versionC +} + // FeasibleIterator is used to iteratively yield nodes that // match feasibility constraints. The iterators may manage // some state for performance optimizations. @@ -343,6 +358,18 @@ func (c *NetworkChecker) SetNetwork(network *structs.NetworkResource) { func (c *NetworkChecker) Feasible(option *structs.Node) bool { if !c.hasNetwork(option) { + + // special case - if the client is running a version older than 0.12 but + // the server is 0.12 or newer, we need to maintain an upgrade path for + // jobs looking for a bridge network that will not have been fingerprinted + // on the client (which was added in 0.12) + if c.networkMode == "bridge" { + sv, err := version.NewSemver(option.Attributes["nomad.version"]) + if err == nil && predatesBridgeFingerprint.Check(sv) { + return true + } + } + c.ctx.Metrics().FilterNode(option, "missing network") return false } diff --git a/scheduler/feasible_test.go b/scheduler/feasible_test.go index 2aef98b50..9caaa72cf 100644 --- a/scheduler/feasible_test.go +++ b/scheduler/feasible_test.go @@ -399,14 +399,19 @@ func TestCSIVolumeChecker(t *testing.T) { func TestNetworkChecker(t *testing.T) { _, ctx := testContext(t) - nodes := []*structs.Node{ - mock.Node(), - mock.Node(), - mock.Node(), + + node := func(mode string) *structs.Node { + n := mock.Node() + n.NodeResources.Networks = append(n.NodeResources.Networks, &structs.NetworkResource{Mode: mode}) + n.Attributes["nomad.version"] = "0.12.0" // mock version is 0.5.0 + return n + } + + nodes := []*structs.Node{ + node("bridge"), + node("bridge"), + node("cni/mynet"), } - nodes[0].NodeResources.Networks = append(nodes[0].NodeResources.Networks, &structs.NetworkResource{Mode: "bridge"}) - nodes[1].NodeResources.Networks = append(nodes[1].NodeResources.Networks, &structs.NetworkResource{Mode: "bridge"}) - nodes[2].NodeResources.Networks = append(nodes[2].NodeResources.Networks, &structs.NetworkResource{Mode: "cni/mynet"}) checker := NewNetworkChecker(ctx) cases := []struct { @@ -439,6 +444,36 @@ func TestNetworkChecker(t *testing.T) { } } +func TestNetworkChecker_bridge_upgrade_path(t *testing.T) { + _, ctx := testContext(t) + + t.Run("older client", func(t *testing.T) { + // Create a client that is still on v0.11, which does not have the bridge + // network finger-printer (and thus no bridge network resource) + oldClient := mock.Node() + oldClient.Attributes["nomad.version"] = "0.11.0" + + checker := NewNetworkChecker(ctx) + checker.SetNetwork(&structs.NetworkResource{Mode: "bridge"}) + + ok := checker.Feasible(oldClient) + require.True(t, ok) + }) + + t.Run("updated client", func(t *testing.T) { + // Create a client that is updated to 0.12, but did not detect a bridge + // network resource. + oldClient := mock.Node() + oldClient.Attributes["nomad.version"] = "0.12.0" + + checker := NewNetworkChecker(ctx) + checker.SetNetwork(&structs.NetworkResource{Mode: "bridge"}) + + ok := checker.Feasible(oldClient) + require.False(t, ok) + }) +} + func TestDriverChecker_DriverInfo(t *testing.T) { _, ctx := testContext(t) nodes := []*structs.Node{