Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions router/core/graph_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"errors"
"fmt"
"github.com/wundergraph/cosmo/router/pkg/execution_config"
otelmetric "go.opentelemetry.io/otel/metric"
"net/http"
"net/url"
Expand Down Expand Up @@ -102,6 +103,16 @@ type (
// newGraphServer creates a new server instance.
func newGraphServer(ctx context.Context, r *Router, routerConfig *nodev1.RouterConfig, proxy ProxyFunc) (*graphServer, error) {

/* Older versions of composition will not populate a compatibility version.
* Currently, all "old" router execution configurations are compatible as there have been no breaking
* changes.
* Upon the first breaking change to the execution config, an unpopulated compatibility version will
* also be unsupported (and the logic for IsRouterCompatibleWithExecutionConfig will need to be updated).
*/
if !execution_config.IsRouterCompatibleWithExecutionConfig(r.logger, routerConfig.CompatibilityVersion) {
return nil, fmt.Errorf(`the compatibility version "%s" is not compatible with this router version`, routerConfig.CompatibilityVersion)
Comment thread
StarpTech marked this conversation as resolved.
}

ctx, cancel := context.WithCancel(ctx)
s := &graphServer{
context: ctx,
Expand Down
10 changes: 0 additions & 10 deletions router/core/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -1105,16 +1105,6 @@ func (r *Router) Start(ctx context.Context) error {
return nil
}

/* Older versions of composition will not populate a compatibility version.
* Currently, all "old" router execution configurations are compatible as there have been no breaking
* changes.
* Upon the first breaking change to the execution config, an unpopulated compatibility version will
* also be unsupported (and the logic for IsRouterCompatibleWithExecutionConfig will need to be updated).
*/
if !execution_config.IsRouterCompatibleWithExecutionConfig(r.logger, cfg.CompatibilityVersion) {
return nil
}

if err := r.newServer(ctx, cfg); err != nil {
r.logger.Error("Failed to update server with new config", zap.Error(err))
return nil
Expand Down
36 changes: 18 additions & 18 deletions router/pkg/execution_config/compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ import (
)

const (
// ExecutionConfigVersionThreshold should ONLY be updated if there is a breaking change in the router execution config.
ExecutionConfigVersionThreshold = 1
compatibilityVersionParseErrorMessage = "Failed to parse compatibility version."
executionConfigVersionParseErrorMessage = "Failed to parse router execution config version of compatibility version."
// RouterCompatibilityVersionThreshold should ONLY be updated if there is a breaking change in the router execution config.
RouterCompatibilityVersionThreshold = 1
compatibilityVersionParseErrorMessage = "Failed to parse compatibility version."
routerCompatibilityVersionParseErrorMessage = "Failed to parse router execution config version of compatibility version."
)

func IsRouterCompatibleWithExecutionConfig(logger *zap.Logger, compatibilityVersion string) bool {
if compatibilityVersion == "" {
return true
}
/* A compatibility version is composed thus: <router execution configuration version>:<composition package version>
* A router version supports a maximum router execution configuration version (ExecutionConfigVersionThreshold).
* In the event the execution config version exceeds ExecutionConfigVersionThreshold, an error will request for
* the router version be upgraded.
/* A compatibility version is composed thus: <router compatibility version>:<composition package version>
* A router version supports a maximum router compatibility version (RouterCompatibilityVersionThreshold).
* In the event the execution config version exceeds RouterCompatibilityVersionThreshold, an error will request the
* router version be upgraded.
* If the router version requires a newer router execution configuration version, a warning will explain that some
* new features may be unavailable or functionality/behaviour may have changed.
*/
Expand All @@ -30,25 +30,25 @@ func IsRouterCompatibleWithExecutionConfig(logger *zap.Logger, compatibilityVers
logger.Error(compatibilityVersionParseErrorMessage, zap.String("compatibility_version", compatibilityVersion))
return false
}
routerExecutionVersion, err := strconv.ParseInt(segments[0], 10, 32)
routerCompatibilityVersion, err := strconv.ParseInt(segments[0], 10, 32)
if err != nil {
logger.Error(executionConfigVersionParseErrorMessage, zap.String("compatibility_version", compatibilityVersion))
logger.Error(routerCompatibilityVersionParseErrorMessage, zap.String("compatibility_version", compatibilityVersion))
return false
}
switch {
case routerExecutionVersion == ExecutionConfigVersionThreshold:
case routerCompatibilityVersion == RouterCompatibilityVersionThreshold:
return true
case routerExecutionVersion > ExecutionConfigVersionThreshold:
case routerCompatibilityVersion > RouterCompatibilityVersionThreshold:
logger.Error(
executionConfigVersionThresholdExceededError(routerExecutionVersion),
zap.Int64("execution_config_version", routerExecutionVersion),
executionConfigVersionThresholdExceededError(routerCompatibilityVersion),
zap.Int64("router_compatibility_version", routerCompatibilityVersion),
zap.String("composition_package_version", segments[1]),
)
return false
default:
logger.Warn(
executionConfigVersionInsufficientWarning(routerExecutionVersion),
zap.Int64("execution_config_version", routerExecutionVersion),
executionConfigVersionInsufficientWarning(routerCompatibilityVersion),
zap.Int64("router_compatibility_version", routerCompatibilityVersion),
zap.String("composition_package_version", segments[1]),
)
return true
Expand All @@ -58,15 +58,15 @@ func IsRouterCompatibleWithExecutionConfig(logger *zap.Logger, compatibilityVers
func executionConfigVersionThresholdExceededError(executionConfigVersion int64) string {
return fmt.Sprintf(
"This router version supports a router execution config version up to %d. The router execution config version supplied is %d. Please upgrade your router version.",
ExecutionConfigVersionThreshold,
RouterCompatibilityVersionThreshold,
executionConfigVersion,
)
}

func executionConfigVersionInsufficientWarning(executionConfigVersion int64) string {
return fmt.Sprintf(
"This router version requires a minimum router execution config version of %d to support all functionality. The router execution config version supplied is %d. Please create a new execution configuration.",
ExecutionConfigVersionThreshold,
RouterCompatibilityVersionThreshold,
executionConfigVersion,
)
}
18 changes: 9 additions & 9 deletions router/pkg/execution_config/compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestExecutionConfiguration(t *testing.T) {
t.Run("same compatibility version is supported", func(t *testing.T) {
observed, logs := observer.New(zapcore.DebugLevel)
logger := newLogger(observed)
assert.True(t, IsRouterCompatibleWithExecutionConfig(logger, fmt.Sprintf("%d:0.1.0", ExecutionConfigVersionThreshold)))
assert.True(t, IsRouterCompatibleWithExecutionConfig(logger, fmt.Sprintf("%d:0.1.0", RouterCompatibilityVersionThreshold)))
assert.Equal(t, 0, len(logs.All()))
})

Expand Down Expand Up @@ -51,23 +51,23 @@ func TestExecutionConfiguration(t *testing.T) {
assert.Equal(t, zap.String("compatibility_version", compatibilityVersion), logsSlice[0].Context[0])
})

t.Run("return an error if execution config version is unparsable", func(t *testing.T) {
t.Run("return an error if the router compatibility version is unparsable", func(t *testing.T) {
observed, logs := observer.New(zapcore.DebugLevel)
logger := newLogger(observed)
compatibilityVersion := "a:0.1.0"
assert.False(t, IsRouterCompatibleWithExecutionConfig(logger, compatibilityVersion))
logsSlice := logs.All()
assert.Equal(t, 1, len(logsSlice))
assert.Equal(t, executionConfigVersionParseErrorMessage, logsSlice[0].Message)
assert.Equal(t, routerCompatibilityVersionParseErrorMessage, logsSlice[0].Message)
assert.Equal(t, zapcore.ErrorLevel, logsSlice[0].Level)
assert.Equal(t, 1, len(logsSlice[0].Context))
assert.Equal(t, zap.String("compatibility_version", compatibilityVersion), logsSlice[0].Context[0])
})

t.Run("return an error if the maximum execution config version threshold of the router is exceeded", func(t *testing.T) {
t.Run("return an error if the maximum router compatibility version threshold of the router is exceeded", func(t *testing.T) {
observed, logs := observer.New(zapcore.DebugLevel)
logger := newLogger(observed)
nextVersion := int64(ExecutionConfigVersionThreshold + 1)
nextVersion := int64(RouterCompatibilityVersionThreshold + 1)
compVersion := "0.1.0"
compatibilityVersion := fmt.Sprintf("%d:%s", nextVersion, compVersion)
assert.False(t, IsRouterCompatibleWithExecutionConfig(logger, compatibilityVersion))
Expand All @@ -76,14 +76,14 @@ func TestExecutionConfiguration(t *testing.T) {
assert.Equal(t, executionConfigVersionThresholdExceededError(nextVersion), logsSlice[0].Message)
assert.Equal(t, zapcore.ErrorLevel, logsSlice[0].Level)
assert.Equal(t, 2, len(logsSlice[0].Context))
assert.Equal(t, zap.Int64("execution_config_version", nextVersion), logsSlice[0].Context[0])
assert.Equal(t, zap.Int64("router_compatibility_version", nextVersion), logsSlice[0].Context[0])
assert.Equal(t, zap.String("composition_package_version", compVersion), logsSlice[0].Context[1])
})

t.Run("return a warning if the execution config version is insufficient", func(t *testing.T) {
t.Run("return a warning if the router compatibility version is insufficient", func(t *testing.T) {
observed, logs := observer.New(zapcore.DebugLevel)
logger := newLogger(observed)
previousVersion := int64(ExecutionConfigVersionThreshold - 1)
previousVersion := int64(RouterCompatibilityVersionThreshold - 1)
compVersion := "0.1.0"
compatibilityVersion := fmt.Sprintf("%d:%s", previousVersion, compVersion)
assert.True(t, IsRouterCompatibleWithExecutionConfig(logger, compatibilityVersion))
Expand All @@ -92,7 +92,7 @@ func TestExecutionConfiguration(t *testing.T) {
assert.Equal(t, executionConfigVersionInsufficientWarning(previousVersion), logsSlice[0].Message)
assert.Equal(t, zapcore.WarnLevel, logsSlice[0].Level)
assert.Equal(t, 2, len(logsSlice[0].Context))
assert.Equal(t, zap.Int64("execution_config_version", previousVersion), logsSlice[0].Context[0])
assert.Equal(t, zap.Int64("router_compatibility_version", previousVersion), logsSlice[0].Context[0])
assert.Equal(t, zap.String("composition_package_version", compVersion), logsSlice[0].Context[1])
})
}
Expand Down