Skip to content

Commit

Permalink
added envoy version check when using consul connect envoy
Browse files Browse the repository at this point in the history
  • Loading branch information
wilkermichael committed Dec 16, 2022
1 parent c1b6c5f commit 5edb613
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 20 deletions.
101 changes: 81 additions & 20 deletions command/connect/envoy/envoy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net"
"os"
"os/exec"
"strconv"
"strings"

"github.com/mitchellh/cli"
Expand All @@ -20,6 +21,7 @@ import (
"github.com/hashicorp/consul/command/flags"
"github.com/hashicorp/consul/ipaddr"
"github.com/hashicorp/consul/tlsutil"
"github.com/hashicorp/go-version"
)

func New(ui cli.Ui) *cmd {
Expand All @@ -38,26 +40,27 @@ type cmd struct {
client *api.Client

// flags
meshGateway bool
gateway string
proxyID string
nodeName string
sidecarFor string
adminAccessLogPath string
adminBind string
envoyBin string
bootstrap bool
disableCentralConfig bool
grpcAddr string
grpcCAFile string
grpcCAPath string
envoyVersion string
prometheusBackendPort string
prometheusScrapePath string
prometheusCAFile string
prometheusCAPath string
prometheusCertFile string
prometheusKeyFile string
meshGateway bool
gateway string
proxyID string
nodeName string
sidecarFor string
adminAccessLogPath string
adminBind string
envoyBin string
bootstrap bool
disableCentralConfig bool
grpcAddr string
grpcCAFile string
grpcCAPath string
envoyVersion string
prometheusBackendPort string
prometheusScrapePath string
prometheusCAFile string
prometheusCAPath string
prometheusCertFile string
prometheusKeyFile string
ignoreEnvoyCompatibility bool

// mesh gateway registration information
register bool
Expand Down Expand Up @@ -201,6 +204,11 @@ func (c *cmd) init() {
c.flags.StringVar(&c.prometheusKeyFile, "prometheus-key-file", "",
"Path to a private key file for Envoy to use when serving TLS on the Prometheus metrics endpoint. "+
"Only applicable when envoy_prometheus_bind_addr is set in proxy config.")
c.flags.BoolVar(&c.ignoreEnvoyCompatibility, "ignore-envoy-compatibility", false,
"Specifies whether or not the program should check the envoy version for compatibility. "+
"If this flag is set to true, the program will not check the envoy version for compatibility. "+
"It is recommended in most situations to leave this flag set to false, as ensuring compatibility "+
"with the Envoy version can prevent potential issues.")

c.http = &flags.HTTPFlags{}
flags.Merge(c.flags, c.http.ClientFlags())
Expand Down Expand Up @@ -448,6 +456,27 @@ func (c *cmd) run(args []string) int {
return 1
}

// Check if envoy version is supported
if !c.ignoreEnvoyCompatibility {
v, err := execEnvoyVersion(binary)
if err != nil {
c.UI.Warn("Couldn't get envoy version for compatibility check: " + err.Error())
return 1
}

ok, err := checkEnvoyVersionCompatibility(v, proxysupport.UnsupportedEnvoyVersions)

if err != nil {
c.UI.Warn("There was an error checking the compatibility of the envoy version: " + err.Error())
} else if !ok {
c.UI.Error(fmt.Sprintf("Envoy version %s is not supported. If there is a reason you need to use "+
"this version of envoy use the ignore-envoy-compatibility flag. Using an unsupported version of Envoy "+
"is not recommended and your experience may vary. For more information on compatibility "+
"see https://developer.hashicorp.com/consul/docs/connect/proxies/envoy#envoy-and-consul-client-agent", v))
return 1
}
}

err = execEnvoy(binary, nil, args, bootstrapJson)
if err == errUnsupportedOS {
c.UI.Error("Directly running Envoy is only supported on linux and macOS " +
Expand Down Expand Up @@ -791,3 +820,35 @@ Usage: consul connect envoy [options] [-- pass-through options]
$ consul connect envoy -sidecar-for web -- --log-level debug
`
)

func checkEnvoyVersionCompatibility(envoyVersion string, unsupportedList []string) (bool, error) {
// Now compare the versions to the list of supported versions
v, err := version.NewVersion(envoyVersion)
if err != nil {
return false, err
}

var cs strings.Builder

// Add one to the max minor version so that we accept all patches
splitS := strings.Split(proxysupport.GetMaxEnvoyMajorVersion(), ".")
minor, err := strconv.Atoi(splitS[1])
if err != nil {
return false, err
}
minor++
maxSupported := fmt.Sprintf("%s.%d", splitS[0], minor)

// Build the constraint string, make sure that we are less than but not equal to maxSupported since we added 1
cs.WriteString(fmt.Sprintf(">= %s, < %s", proxysupport.GetMinEnvoyMajorVersion(), maxSupported))
for _, s := range unsupportedList {
cs.WriteString(fmt.Sprintf(", != %s", s))
}

constraints, err := version.NewConstraint(cs.String())
if err != nil {
return false, err
}

return constraints.Check(v), nil
}
83 changes: 83 additions & 0 deletions command/connect/envoy/envoy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ package envoy
import (
"encoding/json"
"flag"
"fmt"
"net"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strconv"
"strings"
"testing"

"github.com/hashicorp/consul/agent/xds/proxysupport"
"github.com/stretchr/testify/assert"

"github.com/mitchellh/cli"
Expand Down Expand Up @@ -1518,3 +1521,83 @@ func testMockAgentSelf(wantXDSPorts agent.GRPCPorts, agentSelf110 bool) http.Han
w.Write(selfJSON)
}
}

func TestCheckEnvoyVersionCompatibility(t *testing.T) {
tests := []struct {
name string
envoyVersion string
unsupportedList []string
expectedSupport bool
isErrorExpected bool
}{
{
name: "supported-using-proxy-support-defined",
envoyVersion: proxysupport.EnvoyVersions[1],
unsupportedList: proxysupport.UnsupportedEnvoyVersions,
expectedSupport: true,
},
{
name: "supported-at-max",
envoyVersion: proxysupport.GetMaxEnvoyMajorVersion(),
unsupportedList: proxysupport.UnsupportedEnvoyVersions,
expectedSupport: true,
},
{
name: "supported-minor-higher",
envoyVersion: addNPatchVersion(proxysupport.EnvoyVersions[0], 1),
unsupportedList: proxysupport.UnsupportedEnvoyVersions,
expectedSupport: true,
},
{
name: "not-supported-major-higher",
envoyVersion: addNMinorVersion(proxysupport.EnvoyVersions[0], 1),
unsupportedList: proxysupport.UnsupportedEnvoyVersions,
expectedSupport: false,
},
{
name: "not-supported-major-lower",
envoyVersion: addNMinorVersion(proxysupport.EnvoyVersions[len(proxysupport.EnvoyVersions)-1], -1),
unsupportedList: proxysupport.UnsupportedEnvoyVersions,
expectedSupport: false,
},
{
name: "not-supported-explicitly-unsupported-version",
envoyVersion: addNPatchVersion(proxysupport.EnvoyVersions[0], 1),
unsupportedList: []string{"1.23.1", addNPatchVersion(proxysupport.EnvoyVersions[0], 1)},
expectedSupport: false,
},
{
name: "error-bad-input",
envoyVersion: "1.abc.3",
unsupportedList: proxysupport.UnsupportedEnvoyVersions,
expectedSupport: false,
isErrorExpected: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
actual, err := checkEnvoyVersionCompatibility(tc.envoyVersion, tc.unsupportedList)
if tc.isErrorExpected {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tc.expectedSupport, actual)
})
}
}

func addNPatchVersion(s string, n int) string {
splitS := strings.Split(s, ".")
minor, _ := strconv.Atoi(splitS[2])
minor += n
return fmt.Sprintf("%s.%s.%d", splitS[0], splitS[1], minor)
}

func addNMinorVersion(s string, n int) string {
splitS := strings.Split(s, ".")
major, _ := strconv.Atoi(splitS[1])
major += n
return fmt.Sprintf("%s.%d.%s", splitS[0], major, splitS[2])
}

0 comments on commit 5edb613

Please sign in to comment.