Skip to content

Commit

Permalink
feat(konnect): add controller status accounting for konnect sync fail…
Browse files Browse the repository at this point in the history
…ures
  • Loading branch information
czeslavo committed May 17, 2023
1 parent d5cb05c commit 6baa1ee
Show file tree
Hide file tree
Showing 7 changed files with 368 additions and 50 deletions.
49 changes: 46 additions & 3 deletions internal/clients/config_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,59 @@ import (
type ConfigStatus int

const (
// ConfigStatusOK: no error happens in translation from k8s objects to kong configuration
// ConfigStatusOK no error happens in translation from k8s objects to kong configuration
// and succeeded to apply kong configuration to kong gateway.
ConfigStatusOK ConfigStatus = iota
// ConfigStatusTranslationErrorHappened: error happened in translation of k8s objects
// ConfigStatusTranslationErrorHappened error happened in translation of k8s objects
// but succeeded to apply kong configuration for remaining objects.
ConfigStatusTranslationErrorHappened
// ConfigStatusApplyFailed: failed to apply kong configurations.
// ConfigStatusApplyFailed failed to apply kong configurations.
ConfigStatusApplyFailed
// ConfigStatusOKKonnectApplyFailed no error happens in translation from k8s objects to kong configuration
// and succeeded to apply kong configuration to kong gateway.
ConfigStatusOKKonnectApplyFailed
// ConfigStatusTranslationErrorHappenedKonnectApplyFailed error happened in translation of k8s objects
// but succeeded to apply kong configuration for remaining objects.
ConfigStatusTranslationErrorHappenedKonnectApplyFailed
// ConfigStatusApplyFailedKonnectApplyFailed failed to apply kong configurations.
ConfigStatusApplyFailedKonnectApplyFailed
// ConfigStatusUnknown.
ConfigStatusUnknown
)

type CalculateConfigStatusInput struct {
// Any error occurred when syncing with Gateways.
GatewaysFailed bool

// Any error occurred when syncing with Konnect,
KonnectFailed bool

// translation of some of Kubernetes objects failed, and they were skipped.
TranslationFailuresOccurred bool
}

// CalculateConfigStatus calculates a clients.ConfigStatus that sums up the configuration synchronisation result as
// a single enumerated value.
func CalculateConfigStatus(i CalculateConfigStatusInput) ConfigStatus {
switch {
case !i.GatewaysFailed && !i.KonnectFailed && !i.TranslationFailuresOccurred:
return ConfigStatusOK
case !i.GatewaysFailed && !i.KonnectFailed && i.TranslationFailuresOccurred:
return ConfigStatusTranslationErrorHappened
case i.GatewaysFailed && !i.KonnectFailed:
return ConfigStatusApplyFailed
case !i.GatewaysFailed && i.KonnectFailed && !i.TranslationFailuresOccurred:
return ConfigStatusOKKonnectApplyFailed
case !i.GatewaysFailed && i.KonnectFailed && i.TranslationFailuresOccurred:
return ConfigStatusTranslationErrorHappenedKonnectApplyFailed
case i.GatewaysFailed && i.KonnectFailed:
return ConfigStatusApplyFailedKonnectApplyFailed
}

// shouldn't happen
return ConfigStatusUnknown
}

type ConfigStatusNotifier interface {
NotifyConfigStatus(context.Context, ConfigStatus)
}
Expand Down
64 changes: 61 additions & 3 deletions internal/clients/config_status_test.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package clients
package clients_test

import (
"context"
"testing"
"time"

"github.com/go-logr/logr/testr"
"github.com/stretchr/testify/require"

"github.com/kong/kubernetes-ingress-controller/v2/internal/clients"
)

func TestChannelConfigNotifier(t *testing.T) {
logger := testr.New(t)
n := NewChannelConfigNotifier(logger)
n := clients.NewChannelConfigNotifier(logger)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

ch := n.SubscribeConfigStatus()

// Call NotifyConfigStatus 5 times to make sure that the method is non-blocking.
for i := 0; i < 5; i++ {
n.NotifyConfigStatus(ctx, ConfigStatusOK)
n.NotifyConfigStatus(ctx, clients.ConfigStatusOK)
}

for i := 0; i < 5; i++ {
Expand All @@ -29,3 +32,58 @@ func TestChannelConfigNotifier(t *testing.T) {
}
}
}

func TestCalculateConfigStatus(t *testing.T) {
testCases := []struct {
name string

gatewayFailure bool
konnectFailure bool
translationFailures bool

expectedConfigStatus clients.ConfigStatus
}{
{
name: "success",
expectedConfigStatus: clients.ConfigStatusOK,
},
{
name: "gateway failure",
gatewayFailure: true,
expectedConfigStatus: clients.ConfigStatusApplyFailed,
},
{
name: "translation failures",
translationFailures: true,
expectedConfigStatus: clients.ConfigStatusTranslationErrorHappened,
},
{
name: "konnect failure",
konnectFailure: true,
expectedConfigStatus: clients.ConfigStatusOKKonnectApplyFailed,
},
{
name: "both gateway and konnect failure",
gatewayFailure: true,
konnectFailure: true,
expectedConfigStatus: clients.ConfigStatusApplyFailedKonnectApplyFailed,
},
{
name: "translation failures and konnect failure",
translationFailures: true,
konnectFailure: true,
expectedConfigStatus: clients.ConfigStatusTranslationErrorHappenedKonnectApplyFailed,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := clients.CalculateConfigStatus(clients.CalculateConfigStatusInput{
GatewaysFailed: tc.gatewayFailure,
KonnectFailed: tc.konnectFailure,
TranslationFailuresOccurred: tc.translationFailures,
})
require.Equal(t, tc.expectedConfigStatus, result)
})
}
}
42 changes: 23 additions & 19 deletions internal/dataplane/kong_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,21 +368,22 @@ func (c *KongClient) Update(ctx context.Context) error {
c.logger.Debug("successfully built data-plane configuration")
}

shas, err := c.sendOutToGatewayClients(ctx, parsingResult.KongState, c.kongConfig)
if err != nil {
c.configStatusNotifier.NotifyConfigStatus(ctx, clients.ConfigStatusApplyFailed)
return err
}

c.trySendOutToKonnectClient(ctx, parsingResult.KongState, c.kongConfig)

// succeeded to apply configuration to Kong gateway.
// notify the receiver of config status that translation error happened when there are translation errors,
// otherwise notify that config status is OK.
if len(parsingResult.TranslationFailures) > 0 {
c.configStatusNotifier.NotifyConfigStatus(ctx, clients.ConfigStatusTranslationErrorHappened)
} else {
c.configStatusNotifier.NotifyConfigStatus(ctx, clients.ConfigStatusOK)
shas, gatewaysSyncErr := c.sendOutToGatewayClients(ctx, parsingResult.KongState, c.kongConfig)
konnectSyncErr := c.maybeSendOutToKonnectClient(ctx, parsingResult.KongState, c.kongConfig)

// Taking into account the results of syncing configuration with Gateways and Konnect, and potential translation
// failures, calculate the config status and report it.
c.configStatusNotifier.NotifyConfigStatus(ctx, clients.CalculateConfigStatus(
clients.CalculateConfigStatusInput{
GatewaysFailed: gatewaysSyncErr != nil,
KonnectFailed: konnectSyncErr != nil,
TranslationFailuresOccurred: len(parsingResult.TranslationFailures) > 0,
},
))

// In case of a failure in syncing configuration with Gateways, propagate the error.
if gatewaysSyncErr != nil {
return gatewaysSyncErr
}

// report on configured Kubernetes objects if enabled
Expand Down Expand Up @@ -420,12 +421,13 @@ func (c *KongClient) sendOutToGatewayClients(
return previousSHAs, nil
}

// It will try to send ignore errors that are returned from Konnect client.
func (c *KongClient) trySendOutToKonnectClient(ctx context.Context, s *kongstate.KongState, config sendconfig.Config) {
// maybeSendOutToKonnectClient sends out the configuration to Konnect when KonnectClient is provided.
// It's a noop when Konnect integration is not enabled.
func (c *KongClient) maybeSendOutToKonnectClient(ctx context.Context, s *kongstate.KongState, config sendconfig.Config) error {
konnectClient := c.clientsProvider.KonnectClient()
// There's no KonnectClient configured, that's totally fine.
if konnectClient == nil {
return
return nil
}

if _, err := c.sendToClient(ctx, konnectClient, s, config); err != nil {
Expand All @@ -434,11 +436,13 @@ func (c *KongClient) trySendOutToKonnectClient(ctx context.Context, s *kongstate

if errors.Is(err, sendconfig.ErrUpdateSkippedDueToBackoffStrategy{}) {
c.logger.WithError(err).Warn("Skipped pushing configuration to Konnect")
return
}

c.logger.WithError(err).Warn("Failed pushing configuration to Konnect")
return err
}

return nil
}

func (c *KongClient) sendToClient(
Expand Down

0 comments on commit 6baa1ee

Please sign in to comment.