diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcffcf1..de04cb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,11 +16,12 @@ concurrency: jobs: extension-ci: - uses: steadybit/extension-kit/.github/workflows/reusable-extension-ci.yml@main + uses: steadybit/extension-kit/.github/workflows/reusable-extension-ci.yml@main # NOSONAR githubactions:S7637 - our own action with: go_version: '1.24' runs_on: steadybit_runner_ubuntu_latest_4cores_16GB build_linux_packages: true + run_make_prepare_audit: true VERSION_BUMPER_APPID: ${{ vars.GH_APP_STEADYBIT_APP_ID }} secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} @@ -41,12 +42,12 @@ jobs: packages: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Log in to the container registry - uses: docker/login-action@v3 + uses: docker/login-action@v3 # NOSONAR githubactions:S7637 - verified action creator with: registry: ghcr.io username: ${{ github.actor }} @@ -54,7 +55,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f with: images: ghcr.io/${{ github.repository }}/dummyconsumer diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index cb8bff3..6830199 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -7,6 +7,6 @@ on: jobs: call-workflow: - uses: steadybit/.github/.github/workflows/cla.yml@main + uses: steadybit/.github/.github/workflows/cla.yml@main # NOSONAR githubactions:S7637 - our own action secrets: PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN_USED_BY_CLA_FROM_ANSGAR }} diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 92190a3..7ebfc26 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Dependabot metadata id: metadata - uses: dependabot/fetch-metadata@v2 + uses: dependabot/fetch-metadata@v2 # NOSONAR githubactions:S7637 - verified action creator with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs diff --git a/.github/workflows/ghcr-cleanup.yml b/.github/workflows/ghcr-cleanup.yml index 2063c17..0106770 100644 --- a/.github/workflows/ghcr-cleanup.yml +++ b/.github/workflows/ghcr-cleanup.yml @@ -7,6 +7,6 @@ on: jobs: ghcr-cleanup: - uses: steadybit/extension-kit/.github/workflows/reusable-ghcr-cleanup.yml@main + uses: steadybit/extension-kit/.github/workflows/reusable-ghcr-cleanup.yml@main # NOSONAR githubactions:S7637 - our own action secrets: token: ${{ secrets.GHCR_CLEANUP_PAT }} diff --git a/CHANGELOG.md b/CHANGELOG.md index e36d989..d96c729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,67 @@ # Changelog -## v1.0.5 (next) +## v1.2.4 + +- Support changing IO and network thread count values with huge increments or decrements +- Update dependencies + +## v1.2.3 + +- Bump app version + +## v1.2.2 + +- Bump app version + +## v1.2.1 + +- Add cluster name to topic and consumergroup targets + +## v1.2.0 + +- Add cluster name to broker target attributes +- Better target ID for brokers in case of multiple clusters +- Add min/max validations +- Update dependencies + +## v1.1.1 + +- Make extension-kafka compatible with AWS MSK SCRAM-SHA-512 Auth +- Add TLS for compatibility with SASL_SSL security protocol +- Update to go 1.24 +- Update dependencies + +## v1.1.0 + +- Fix log line for check error +- Change metric colors behavior +- Change name of kafka config for certs + +## v1.0.9 + +- Fix log line for check error +- Fix metric ID for broker check + +## v1.0.8 + +- Add pod and container enrichment + +## v1.0.7 + +- Fix action ID + +## v1.0.6 + +- Add controller information to target attributes +- Add new broker check +- Add TLS connection support +- Update dependencies + +## v1.0.5 -- update dependencies - Use uid instead of name for user statement in Dockerfile +- Fix data race issue +- Update dependencies ## v1.0.0 diff --git a/Makefile b/Makefile index 27cf7e3..d9bad2a 100755 --- a/Makefile +++ b/Makefile @@ -32,6 +32,12 @@ tidy: go fmt ./... go mod tidy -v +## prepare_audit: install required kafkactl command for e2e tests, only intended for the CI runner +.PHONY: prepare_audit +prepare_audit: + wget https://github.com/deviceinsight/kafkactl/releases/download/v5.11.1/kafkactl_5.11.1_linux_amd64.deb + sudo dpkg -i kafkactl_5.11.1_linux_amd64.deb + ## audit: run quality control checks .PHONY: audit audit: diff --git a/charts/steadybit-extension-kafka/Chart.yaml b/charts/steadybit-extension-kafka/Chart.yaml index 7f0b6b5..4cb9f3b 100644 --- a/charts/steadybit-extension-kafka/Chart.yaml +++ b/charts/steadybit-extension-kafka/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 name: steadybit-extension-kafka description: Steadybit scaffold extension Helm chart for Kubernetes. -version: 1.0.25 -appVersion: v1.2.3 +version: 1.0.26 +appVersion: v1.2.4 home: https://www.steadybit.com/ icon: https://steadybit-website-assets.s3.amazonaws.com/logo-symbol-transparent.png maintainers: diff --git a/config/config.go b/config/config.go index aedb914..5a7ab29 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,5 @@ -/* - * Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package config diff --git a/e2e/integration_test.go b/e2e/integration_test.go index 5fd9ffd..7faeaf5 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -1,15 +1,14 @@ -/* - * Copyright 2024 steadybit GmbH. All rights reserved. - */ - // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package e2e import ( "context" "fmt" + "github.com/rs/zerolog/log" + "github.com/steadybit/action-kit/go/action_kit_api/v2" + "github.com/steadybit/action-kit/go/action_kit_test/client" "github.com/steadybit/action-kit/go/action_kit_test/e2e" actValidate "github.com/steadybit/action-kit/go/action_kit_test/validate" "github.com/steadybit/discovery-kit/go/discovery_kit_api" @@ -17,19 +16,29 @@ import ( "github.com/steadybit/extension-kit/extlogging" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "io" + "os" "os/exec" + "path/filepath" + "strconv" + "strings" "testing" "time" ) +var kafkactl func(ctx context.Context, commands ...string) (string, error) +var kafkactlStop func() + func TestWithMinikube(t *testing.T) { extlogging.InitZeroLog() extFactory := e2e.HelmExtensionFactory{ Name: "extension-kafka", Port: 8083, + ExtraArgs: func(m *e2e.Minikube) []string { return []string{ + "--set", "logging.lever=debug", "--set", "kafka.seedBrokers='my-kafka.default.svc.cluster.local:9092'", "--set", "kafka.auth.saslMechanism=PLAIN", "--set", "kafka.auth.saslUser=user1", @@ -38,7 +47,13 @@ func TestWithMinikube(t *testing.T) { }, } - e2e.WithMinikube(t, e2e.DefaultMinikubeOpts().AfterStart(helmInstallLocalStack), &extFactory, []e2e.WithMinikubeTestCase{ + defer func() { + if kafkactlStop != nil { + kafkactlStop() + } + }() + + e2e.WithMinikube(t, e2e.DefaultMinikubeOpts().AfterStart(helmInstallLocalStack).AfterStart(setupKafkactl), &extFactory, []e2e.WithMinikubeTestCase{ { Name: "validate discovery", Test: validateDiscovery, @@ -51,6 +66,22 @@ func TestWithMinikube(t *testing.T) { Name: "validate Actions", Test: validateActions, }, + { + Name: "alter num io threads", + Test: testAlterNumIoThreads, + }, + { + Name: "alter num network threads", + Test: testAlterNumNetworkThreads, + }, + { + Name: "alter limit connection creation rate", + Test: testAlterLimitConnectionCreationRate, + }, + { + Name: "alter max message bytes", + Test: testAlterMaxMessageBytes, + }, }) } @@ -59,7 +90,7 @@ func validateDiscovery(t *testing.T, _ *e2e.Minikube, e *e2e.Extension) { } func testDiscovery(t *testing.T, _ *e2e.Minikube, e *e2e.Extension) { - ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 120*time.Second) defer cancel() target, err := e2e.PollForTarget(ctx, e, "com.steadybit.extension_kafka.broker", func(target discovery_kit_api.Target) bool { @@ -76,6 +107,174 @@ func validateActions(t *testing.T, _ *e2e.Minikube, e *e2e.Extension) { assert.NoError(t, actValidate.ValidateEndpointReferences("/", e.Client)) } +func testAlterNumIoThreads(t *testing.T, _ *e2e.Minikube, e *e2e.Extension) { + target := &action_kit_api.Target{ + Name: "test_broker", + Attributes: map[string][]string{ + "kafka.broker.node-id": {"0"}, + }, + } + + config := struct { + Duration int `json:"duration"` + IoThreads float32 `json:"io_threads"` + }{ + Duration: 20000, + IoThreads: 1.0, + } + + // Reduce + increaseThreadsAction, err := e.RunAction("com.steadybit.extension_kafka.broker.limit-io-threads", target, config, &action_kit_api.ExecutionContext{}) + require.NoError(t, err) + defer func() { _ = increaseThreadsAction.Cancel() }() + + require.EventuallyWithT(t, func(c *assert.CollectT) { + brokerConfig, err := kafkactl(t.Context(), "describe", "broker", "0") + assert.NoError(c, err, "Failed to describe broker config") + assert.Regexp(c, `num\.io\.threads\s+1`, brokerConfig, "property not found") + }, 20*time.Second, 1*time.Second, "num.io.threads should be set to 1") + + require.NoError(t, increaseThreadsAction.Wait()) + require.NotEmpty(t, t, increaseThreadsAction.Messages()) + require.NotEmpty(t, t, increaseThreadsAction.Metrics()) + + // Increase + config.IoThreads = 100.0 + decreaseThreadsAction, err := e.RunAction("com.steadybit.extension_kafka.broker.limit-io-threads", target, config, &action_kit_api.ExecutionContext{}) + require.NoError(t, err) + defer func() { _ = decreaseThreadsAction.Cancel() }() + + require.EventuallyWithT(t, func(c *assert.CollectT) { + brokerConfig, err := kafkactl(t.Context(), "describe", "broker", "0") + assert.NoError(c, err, "Failed to describe broker config") + assert.Regexp(c, `num\.io\.threads\s+1`, brokerConfig, "property not found") + }, 20*time.Second, 1*time.Second, "num.io.threads should be set to 100") + + require.NoError(t, increaseThreadsAction.Wait()) + require.NotEmpty(t, t, decreaseThreadsAction.Messages()) + require.NotEmpty(t, t, decreaseThreadsAction.Metrics()) +} + +func testAlterNumNetworkThreads(t *testing.T, _ *e2e.Minikube, e *e2e.Extension) { + target := &action_kit_api.Target{ + Name: "test_broker", + Attributes: map[string][]string{ + "kafka.broker.node-id": {"0"}, + }, + } + + config := struct { + Duration int `json:"duration"` + NetworkThreads int `json:"network_threads"` + }{ + Duration: 20000, + NetworkThreads: 1, + } + + // Reduce + increaseThreadsAction, err := e.RunAction("com.steadybit.extension_kafka.broker.limit-network-threads", target, config, &action_kit_api.ExecutionContext{}) + require.NoError(t, err) + defer func() { _ = increaseThreadsAction.Cancel() }() + + require.EventuallyWithT(t, func(c *assert.CollectT) { + brokerConfig, err := kafkactl(t.Context(), "describe", "broker", "0") + assert.NoError(c, err, "Failed to describe broker config") + assert.Regexp(c, `num\.network\.threads\s+1`, brokerConfig, "property not found") + }, 20*time.Second, 1*time.Second, "num.network.threads should be set to 1") + + require.NoError(t, increaseThreadsAction.Wait()) + require.NotEmpty(t, t, increaseThreadsAction.Messages()) + require.NotEmpty(t, t, increaseThreadsAction.Metrics()) + + // Increase + config.NetworkThreads = 100.0 + decreaseThreadsAction, err := e.RunAction("com.steadybit.extension_kafka.broker.limit-network-threads", target, config, &action_kit_api.ExecutionContext{}) + require.NoError(t, err) + defer func() { _ = decreaseThreadsAction.Cancel() }() + + require.EventuallyWithT(t, func(c *assert.CollectT) { + brokerConfig, err := kafkactl(t.Context(), "describe", "broker", "0") + assert.NoError(c, err, "Failed to describe broker config") + assert.Regexp(c, `num\.network\.threads\s+1`, brokerConfig, "property not found") + }, 20*time.Second, 1*time.Second, "num.network.threads should be set to 1") + + require.NoError(t, increaseThreadsAction.Wait()) + require.NotEmpty(t, t, decreaseThreadsAction.Messages()) + require.NotEmpty(t, t, decreaseThreadsAction.Metrics()) +} + +func testAlterLimitConnectionCreationRate(t *testing.T, _ *e2e.Minikube, e *e2e.Extension) { + target := &action_kit_api.Target{ + Name: "test_broker", + Attributes: map[string][]string{ + "kafka.broker.node-id": {"0"}, + }, + } + + config := struct { + Duration int `json:"duration"` + ConnectionRate int `json:"connection_rate"` + }{ + Duration: 20000, + ConnectionRate: 1, + } + + action, err := e.RunAction("com.steadybit.extension_kafka.broker.limit-connection-creation", target, config, &action_kit_api.ExecutionContext{}) + require.NoError(t, err) + defer func() { _ = action.Cancel() }() + + // Testing this setting by opening up too many connections does not work with the current setup, + // as executing "kubectl" commands is too slow, and it does not support parallel invocations. + require.EventuallyWithT(t, func(c *assert.CollectT) { + brokerConfig, err := kafkactl(t.Context(), "describe", "broker", "0") + assert.NoError(c, err, "Failed to describe broker config") + assert.Regexp(c, `max\.connection\.creation\.rate\s+1`, brokerConfig, "property not found") + }, 20*time.Second, 1*time.Second, "max.connection.creation.rate should be set to 1") + + require.NoError(t, action.Wait()) + require.NotEmpty(t, t, action.Messages()) + require.NotEmpty(t, t, action.Metrics()) +} + +func testAlterMaxMessageBytes(t *testing.T, _ *e2e.Minikube, e *e2e.Extension) { + message := fmt.Sprintf("{\"a\": \"%s\"}", strings.Repeat("x", 1000)) + out, err := kafkactl(t.Context(), "produce", "foo", "-v", message) + require.NoError(t, err, out) + + config := struct { + Duration int `json:"duration"` + MaxBytes int `json:"max_bytes"` + }{ + Duration: 20000, + MaxBytes: 100, + } + + // Change message size setting on all nodes + var action client.ActionExecution + for i := 0; i < 3; i++ { + target := &action_kit_api.Target{ + Name: "test_broker", + Attributes: map[string][]string{ + "kafka.broker.node-id": {strconv.Itoa(i)}, + }, + } + action, err = e.RunAction("com.steadybit.extension_kafka.broker.reduce-message-max-bytes", target, config, &action_kit_api.ExecutionContext{}) + require.NoError(t, err) + //goland:noinspection ALL + defer func(a client.ActionExecution) { _ = a.Cancel() }(action) + } + + require.EventuallyWithT(t, func(c *assert.CollectT) { + out, err = kafkactl(t.Context(), "produce", "foo", "-v", message) + require.Error(t, err, out) + }, 20*time.Second, 1*time.Second, "long messages should be rejected") + + //goland:noinspection GoDfaNilDereference + require.NoError(t, action.Wait()) + require.NotEmpty(t, t, action.Messages()) + require.NotEmpty(t, t, action.Metrics()) +} + func helmInstallLocalStack(minikube *e2e.Minikube) error { out, err := exec.Command("helm", "repo", "add", "bitnami", "https://charts.bitnami.com/bitnami").CombinedOutput() if err != nil { @@ -85,6 +284,11 @@ func helmInstallLocalStack(minikube *e2e.Minikube) error { "upgrade", "--install", "--kube-context", minikube.Profile, "--set", "sasl.client.passwords=steadybit", + "--set", "provisioning.enabled=true", + "--set", "provisioning.topics[0].name=foo", + "--set", "image.repository=bitnamilegacy/kafka", + "--set", "image.tag=4.0.0-debian-12-r10", + "--set", "global.security.allowInsecureImages=true", "--namespace=default", "--timeout=15m0s", "my-kafka", "bitnami/kafka ", "--wait").CombinedOutput() @@ -93,3 +297,76 @@ func helmInstallLocalStack(minikube *e2e.Minikube) error { } return nil } + +func setupKafkactl(m *e2e.Minikube) error { + configPath := filepath.Join(os.Getenv("HOME"), ".config", "kafkactl", "config.yml") + if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil { + return fmt.Errorf("failed to create config directory: %w", err) + } + + backupPath := configPath + ".backup" + if _, err := os.Stat(configPath); err == nil { + if err := copyFile(configPath, backupPath); err != nil { + return fmt.Errorf("failed to backup config: %w", err) + } + } + + stop := func() { + if err := os.Rename(backupPath, configPath); err != nil { + log.Error().Err(err).Msg("Failed to restore original config") + } + } + + configContent := fmt.Sprintf(`contexts: + e2e: + brokers: + - my-kafka.default.svc.cluster.local:9092 + tls: + enabled: false + sasl: + enabled: true + username: user1 + password: steadybit + kubernetes: + enabled: true + kubecontext: %s + namespace: default +`, m.Profile) + + if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil { + stop() + return fmt.Errorf("failed to write temporary config: %w", err) + } + + kafkactl = func(ctx context.Context, commands ...string) (string, error) { + cmd := exec.CommandContext(ctx, "kafkactl", append(commands, "--context", "e2e")...) + output, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("kafkactl command failed: %w, output: %s", err, string(output)) + } + return string(output), nil + } + kafkactlStop = stop + return nil +} + +func copyFile(src, dst string) error { + sourceFile, err := os.Open(src) + if err != nil { + return err + } + defer func(sourceFile *os.File) { + _ = sourceFile.Close() + }(sourceFile) + + destFile, err := os.Create(dst) + if err != nil { + return err + } + defer func(destFile *os.File) { + _ = destFile.Close() + }(destFile) + + _, err = io.Copy(destFile, sourceFile) + return err +} diff --git a/extkafka/alter_actions_test.go b/extkafka/alter_actions_test.go new file mode 100644 index 0000000..d58338d --- /dev/null +++ b/extkafka/alter_actions_test.go @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH + +package extkafka + +import ( + "fmt" + "github.com/steadybit/extension-kit/extutil" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestAlterActions_Describe(t *testing.T) { + t.Run("AlterLimitConnectionCreate", func(t *testing.T) { + //Given + action := AlterLimitConnectionCreateRateAttack{} + //When + response := action.Describe() + + //Then + assert.Equal(t, "Limit the Connection Creation Rate", response.Description) + assert.Equal(t, "Limit Connection Creation Rate", response.Label) + assert.Equal(t, kafkaBrokerTargetId, response.TargetSelection.TargetType) + assert.Equal(t, fmt.Sprintf("%s.limit-connection-creation", kafkaBrokerTargetId), response.Id) + assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) + }) + + t.Run("AlterMessageMaxBytes", func(t *testing.T) { + //Given + action := AlterMessageMaxBytesAttack{} + //When + response := action.Describe() + + //Then + assert.Equal(t, "Reduce the max bytes allowed per message", response.Description) + assert.Equal(t, "Reduce Message Batch Size", response.Label) + assert.Equal(t, kafkaBrokerTargetId, response.TargetSelection.TargetType) + assert.Equal(t, fmt.Sprintf("%s.reduce-message-max-bytes", kafkaBrokerTargetId), response.Id) + assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) + }) + + t.Run("AlterNumberIOThreads", func(t *testing.T) { + //Given + action := AlterNumberIOThreadsAttack{} + //When + response := action.Describe() + + //Then + assert.Equal(t, "Limit the number of IO threads", response.Description) + assert.Equal(t, "Limit IO Threads", response.Label) + assert.Equal(t, kafkaBrokerTargetId, response.TargetSelection.TargetType) + assert.Equal(t, fmt.Sprintf("%s.limit-io-threads", kafkaBrokerTargetId), response.Id) + assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) + }) + + t.Run("AlterNumberNetworkThreads", func(t *testing.T) { + //Given + action := AlterNumberNetworkThreadsAttack{} + //When + response := action.Describe() + + //Then + assert.Equal(t, "Limit the number of network threads", response.Description) + assert.Equal(t, "Limit Network Threads", response.Label) + assert.Equal(t, kafkaBrokerTargetId, response.TargetSelection.TargetType) + assert.Equal(t, fmt.Sprintf("%s.limit-network-threads", kafkaBrokerTargetId), response.Id) + assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) + }) +} diff --git a/extkafka/alter_limit_connection_creation_rate.go b/extkafka/alter_limit_connection_creation_rate.go index 0e0dc99..0357c35 100644 --- a/extkafka/alter_limit_connection_creation_rate.go +++ b/extkafka/alter_limit_connection_creation_rate.go @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka @@ -65,40 +65,36 @@ func (k *AlterLimitConnectionCreateRateAttack) Describe() action_kit_api.ActionD } } -func (k *AlterLimitConnectionCreateRateAttack) Prepare(_ context.Context, state *AlterState, request action_kit_api.PrepareActionRequestBody) (*action_kit_api.PrepareResult, error) { +func (k *AlterLimitConnectionCreateRateAttack) Prepare(ctx context.Context, state *AlterState, request action_kit_api.PrepareActionRequestBody) (*action_kit_api.PrepareResult, error) { + var err error state.BrokerID = extutil.ToInt32(request.Target.Attributes["kafka.broker.node-id"][0]) - state.BrokerConfigValue = fmt.Sprintf("%.0f", request.Config["connection_rate"]) state.BrokerHosts = strings.Split(config.Config.SeedBrokers, ",") - - return nil, nil + state.TargetBrokerConfigValue = extutil.ToInt(request.Config["connection_rate"]) + state.InitialBrokerConfigValue, err = describeConfigInt(ctx, state.BrokerHosts, LimitConnectionRate, state.BrokerID) + return nil, err } func (k *AlterLimitConnectionCreateRateAttack) Start(ctx context.Context, state *AlterState) (*action_kit_api.StartResult, error) { - var err error - state.InitialBrokerConfigValue, err = describeConfig(ctx, state.BrokerHosts, LimitConnectionRate, state.BrokerID) - if err != nil { - return nil, err - } - - err = alterConfig(ctx, state.BrokerHosts, LimitConnectionRate, state.BrokerConfigValue, state.BrokerID) - if err != nil { + if err := alterConfigInt(ctx, state.BrokerHosts, LimitConnectionRate, state.TargetBrokerConfigValue, state.BrokerID); err != nil { return nil, err } - return &action_kit_api.StartResult{ Messages: &[]action_kit_api.Message{{ Level: extutil.Ptr(action_kit_api.Info), - Message: fmt.Sprintf("Alter config %s with value %s (initial value was: %s) for broker node-id: %v", LimitConnectionRate, state.BrokerConfigValue, state.InitialBrokerConfigValue, state.BrokerID), + Message: fmt.Sprintf("Alter config %s with value %d (initial value was: %d) for broker node-id: %v", LimitConnectionRate, state.TargetBrokerConfigValue, state.InitialBrokerConfigValue, state.BrokerID), }}, }, nil - } func (k *AlterLimitConnectionCreateRateAttack) Stop(ctx context.Context, state *AlterState) (*action_kit_api.StopResult, error) { - err := alterConfig(ctx, state.BrokerHosts, LimitConnectionRate, state.InitialBrokerConfigValue, state.BrokerID) + err := alterConfigInt(ctx, state.BrokerHosts, LimitConnectionRate, state.InitialBrokerConfigValue, state.BrokerID) if err != nil { return nil, err } - - return nil, nil + return &action_kit_api.StopResult{ + Messages: &[]action_kit_api.Message{{ + Level: extutil.Ptr(action_kit_api.Info), + Message: fmt.Sprintf("Alter config %s back to initial value %d for broker node-id: %v", LimitConnectionRate, state.InitialBrokerConfigValue, state.BrokerID), + }}, + }, nil } diff --git a/extkafka/alter_max_message_bytes.go b/extkafka/alter_max_message_bytes.go index c9db345..acc3bc7 100644 --- a/extkafka/alter_max_message_bytes.go +++ b/extkafka/alter_max_message_bytes.go @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka @@ -65,40 +65,35 @@ func (k *AlterMessageMaxBytesAttack) Describe() action_kit_api.ActionDescription } } -func (k *AlterMessageMaxBytesAttack) Prepare(_ context.Context, state *AlterState, request action_kit_api.PrepareActionRequestBody) (*action_kit_api.PrepareResult, error) { +func (k *AlterMessageMaxBytesAttack) Prepare(ctx context.Context, state *AlterState, request action_kit_api.PrepareActionRequestBody) (*action_kit_api.PrepareResult, error) { + var err error state.BrokerID = extutil.ToInt32(request.Target.Attributes["kafka.broker.node-id"][0]) - state.BrokerConfigValue = fmt.Sprintf("%.0f", request.Config["max_bytes"]) state.BrokerHosts = strings.Split(config.Config.SeedBrokers, ",") - - return nil, nil + state.TargetBrokerConfigValue = extutil.ToInt(request.Config["max_bytes"]) + state.InitialBrokerConfigValue, err = describeConfigInt(ctx, state.BrokerHosts, MessageMaxBytes, state.BrokerID) + return nil, err } func (k *AlterMessageMaxBytesAttack) Start(ctx context.Context, state *AlterState) (*action_kit_api.StartResult, error) { - var err error - state.InitialBrokerConfigValue, err = describeConfig(ctx, state.BrokerHosts, MessageMaxBytes, state.BrokerID) - if err != nil { + if err := alterConfigInt(ctx, state.BrokerHosts, MessageMaxBytes, state.TargetBrokerConfigValue, state.BrokerID); err != nil { return nil, err } - - err = alterConfig(ctx, state.BrokerHosts, MessageMaxBytes, state.BrokerConfigValue, state.BrokerID) - if err != nil { - return nil, err - } - return &action_kit_api.StartResult{ Messages: &[]action_kit_api.Message{{ Level: extutil.Ptr(action_kit_api.Info), - Message: fmt.Sprintf("Alter config %s with value %s (initial value was: %s) for broker node-id: %v", MessageMaxBytes, state.BrokerConfigValue, state.InitialBrokerConfigValue, state.BrokerID), + Message: fmt.Sprintf("Alter config %s with value %d (initial value was: %d) for broker node-id: %v", MessageMaxBytes, state.TargetBrokerConfigValue, state.InitialBrokerConfigValue, state.BrokerID), }}, }, nil - } func (k *AlterMessageMaxBytesAttack) Stop(ctx context.Context, state *AlterState) (*action_kit_api.StopResult, error) { - err := alterConfig(ctx, state.BrokerHosts, MessageMaxBytes, state.InitialBrokerConfigValue, state.BrokerID) - if err != nil { + if err := alterConfigInt(ctx, state.BrokerHosts, MessageMaxBytes, state.InitialBrokerConfigValue, state.BrokerID); err != nil { return nil, err } - - return nil, nil + return &action_kit_api.StopResult{ + Messages: &[]action_kit_api.Message{{ + Level: extutil.Ptr(action_kit_api.Info), + Message: fmt.Sprintf("Alter config %s back to initial value %d for broker node-id: %v", MessageMaxBytes, state.InitialBrokerConfigValue, state.BrokerID), + }}, + }, nil } diff --git a/extkafka/alter_num_io_threads.go b/extkafka/alter_num_io_threads.go index 9e3048c..4764238 100644 --- a/extkafka/alter_num_io_threads.go +++ b/extkafka/alter_num_io_threads.go @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka @@ -11,7 +11,6 @@ import ( "github.com/steadybit/extension-kafka/config" "github.com/steadybit/extension-kit/extbuild" "github.com/steadybit/extension-kit/extutil" - "strconv" "strings" ) @@ -66,45 +65,35 @@ func (k *AlterNumberIOThreadsAttack) Describe() action_kit_api.ActionDescription } } -func (k *AlterNumberIOThreadsAttack) Prepare(_ context.Context, state *AlterState, request action_kit_api.PrepareActionRequestBody) (*action_kit_api.PrepareResult, error) { +func (k *AlterNumberIOThreadsAttack) Prepare(ctx context.Context, state *AlterState, request action_kit_api.PrepareActionRequestBody) (*action_kit_api.PrepareResult, error) { + var err error state.BrokerID = extutil.ToInt32(request.Target.Attributes["kafka.broker.node-id"][0]) - state.BrokerConfigValue = fmt.Sprintf("%.0f", request.Config["io_threads"]) state.BrokerHosts = strings.Split(config.Config.SeedBrokers, ",") - - return nil, nil + state.TargetBrokerConfigValue = extutil.ToInt(request.Config["io_threads"]) + state.InitialBrokerConfigValue, err = describeConfigInt(ctx, state.BrokerHosts, NumberIOThreads, state.BrokerID) + return nil, err } func (k *AlterNumberIOThreadsAttack) Start(ctx context.Context, state *AlterState) (*action_kit_api.StartResult, error) { - var err error - if state.InitialBrokerConfigValue, err = describeConfig(ctx, state.BrokerHosts, NumberIOThreads, state.BrokerID); err != nil { - return nil, err - } - - targetValue, err := strconv.Atoi(state.BrokerConfigValue) - if err != nil { - return nil, err - } - - if err := adjustThreads(ctx, state.BrokerHosts, NumberIOThreads, targetValue, state.BrokerID); err != nil { + if err := adjustThreads(ctx, state.BrokerHosts, NumberIOThreads, state.TargetBrokerConfigValue, state.BrokerID); err != nil { return nil, err } return &action_kit_api.StartResult{ Messages: &[]action_kit_api.Message{{ Level: extutil.Ptr(action_kit_api.Info), - Message: fmt.Sprintf("Alter config %s with value %s (initial value was: %s) for broker node-id: %v", NumberIOThreads, state.BrokerConfigValue, state.InitialBrokerConfigValue, state.BrokerID), + Message: fmt.Sprintf("Alter config %s with value %d (initial value was: %d) for broker node-id: %v", NumberIOThreads, state.TargetBrokerConfigValue, state.InitialBrokerConfigValue, state.BrokerID), }}, }, nil } func (k *AlterNumberIOThreadsAttack) Stop(ctx context.Context, state *AlterState) (*action_kit_api.StopResult, error) { - targetValue, err := strconv.Atoi(state.InitialBrokerConfigValue) - if err != nil { + if err := adjustThreads(ctx, state.BrokerHosts, NumberIOThreads, state.InitialBrokerConfigValue, state.BrokerID); err != nil { return nil, err } - - if err := adjustThreads(ctx, state.BrokerHosts, NumberIOThreads, targetValue, state.BrokerID); err != nil { - return nil, err - } - - return nil, nil + return &action_kit_api.StopResult{ + Messages: &[]action_kit_api.Message{{ + Level: extutil.Ptr(action_kit_api.Info), + Message: fmt.Sprintf("Alter config %s back to initial value %d for broker node-id: %v", NumberIOThreads, state.InitialBrokerConfigValue, state.BrokerID), + }}, + }, nil } diff --git a/extkafka/alter_num_network_threads.go b/extkafka/alter_num_network_threads.go index 6c76242..5636395 100644 --- a/extkafka/alter_num_network_threads.go +++ b/extkafka/alter_num_network_threads.go @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2023 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka @@ -11,7 +11,6 @@ import ( "github.com/steadybit/extension-kafka/config" "github.com/steadybit/extension-kit/extbuild" "github.com/steadybit/extension-kit/extutil" - "strconv" "strings" ) @@ -66,51 +65,35 @@ func (k *AlterNumberNetworkThreadsAttack) Describe() action_kit_api.ActionDescri } } -func (k *AlterNumberNetworkThreadsAttack) Prepare(_ context.Context, state *AlterState, request action_kit_api.PrepareActionRequestBody) (*action_kit_api.PrepareResult, error) { +func (k *AlterNumberNetworkThreadsAttack) Prepare(ctx context.Context, state *AlterState, request action_kit_api.PrepareActionRequestBody) (*action_kit_api.PrepareResult, error) { + var err error state.BrokerID = extutil.ToInt32(request.Target.Attributes["kafka.broker.node-id"][0]) - state.BrokerConfigValue = fmt.Sprintf("%.0f", request.Config["network_threads"]) state.BrokerHosts = strings.Split(config.Config.SeedBrokers, ",") - - return nil, nil + state.TargetBrokerConfigValue = extutil.ToInt(request.Config["network_threads"]) + state.InitialBrokerConfigValue, err = describeConfigInt(ctx, state.BrokerHosts, NumberNetworkThreads, state.BrokerID) + return nil, err } func (k *AlterNumberNetworkThreadsAttack) Start(ctx context.Context, state *AlterState) (*action_kit_api.StartResult, error) { - var err error - if state.InitialBrokerConfigValue, err = describeConfig(ctx, state.BrokerHosts, NumberNetworkThreads, state.BrokerID); err != nil { - return nil, err - } - - targetValue, err := strconv.Atoi(state.BrokerConfigValue) - if err != nil { - return nil, err - } - - if err := adjustThreads(ctx, state.BrokerHosts, NumberNetworkThreads, targetValue, state.BrokerID); err != nil { + if err := adjustThreads(ctx, state.BrokerHosts, NumberNetworkThreads, state.TargetBrokerConfigValue, state.BrokerID); err != nil { return nil, err } - return &action_kit_api.StartResult{ Messages: &[]action_kit_api.Message{{ Level: extutil.Ptr(action_kit_api.Info), - Message: fmt.Sprintf("Alter config %s with value %s (initial value was: %s) for broker node-id: %v", NumberNetworkThreads, state.BrokerConfigValue, state.InitialBrokerConfigValue, state.BrokerID), + Message: fmt.Sprintf("Alter config %s with value %d (initial value was: %d) for broker node-id: %v", NumberNetworkThreads, state.TargetBrokerConfigValue, state.InitialBrokerConfigValue, state.BrokerID), }}, }, nil } func (k *AlterNumberNetworkThreadsAttack) Stop(ctx context.Context, state *AlterState) (*action_kit_api.StopResult, error) { - targetValue, err := strconv.Atoi(state.InitialBrokerConfigValue) - if err != nil { + if err := adjustThreads(ctx, state.BrokerHosts, NumberNetworkThreads, state.InitialBrokerConfigValue, state.BrokerID); err != nil { return nil, err } - - if err := adjustThreads(ctx, state.BrokerHosts, NumberNetworkThreads, targetValue, state.BrokerID); err != nil { - return nil, err - } - return &action_kit_api.StopResult{ Messages: &[]action_kit_api.Message{{ Level: extutil.Ptr(action_kit_api.Info), - Message: fmt.Sprintf("Alter config %s with value %s for broker node-id: %v", NumberNetworkThreads, state.BrokerConfigValue, state.BrokerID), + Message: fmt.Sprintf("Alter config %s back to initial value %d for broker node-id: %v", NumberNetworkThreads, state.InitialBrokerConfigValue, state.BrokerID), }}, }, nil } diff --git a/extkafka/alter_test.go b/extkafka/alter_test.go deleted file mode 100644 index d2d1807..0000000 --- a/extkafka/alter_test.go +++ /dev/null @@ -1,187 +0,0 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ - -package extkafka - -import ( - "context" - "fmt" - "github.com/google/uuid" - "github.com/steadybit/action-kit/go/action_kit_api/v2" - "github.com/steadybit/extension-kit/extutil" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestAlterLimit_Describe(t *testing.T) { - tests := []struct { - name string - requestBody action_kit_api.PrepareActionRequestBody - wantedError error - wantedState *AlterState - }{ - { - name: "Should return description", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - //Given - action := AlterLimitConnectionCreateRateAttack{} - //When - response := action.Describe() - - //Then - assert.Equal(t, "Limit the Connection Creation Rate", response.Description) - assert.Equal(t, "Limit Connection Creation Rate", response.Label) - assert.Equal(t, kafkaBrokerTargetId, response.TargetSelection.TargetType) - assert.Equal(t, fmt.Sprintf("%s.limit-connection-creation", kafkaBrokerTargetId), response.Id) - assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) - }) - t.Run(tt.name, func(t *testing.T) { - //Given - action := AlterMessageMaxBytesAttack{} - //When - response := action.Describe() - - //Then - assert.Equal(t, "Reduce the max bytes allowed per message", response.Description) - assert.Equal(t, "Reduce Message Batch Size", response.Label) - assert.Equal(t, kafkaBrokerTargetId, response.TargetSelection.TargetType) - assert.Equal(t, fmt.Sprintf("%s.reduce-message-max-bytes", kafkaBrokerTargetId), response.Id) - assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) - }) - t.Run(tt.name, func(t *testing.T) { - //Given - action := AlterNumberIOThreadsAttack{} - //When - response := action.Describe() - - //Then - assert.Equal(t, "Limit the number of IO threads", response.Description) - assert.Equal(t, "Limit IO Threads", response.Label) - assert.Equal(t, kafkaBrokerTargetId, response.TargetSelection.TargetType) - assert.Equal(t, fmt.Sprintf("%s.limit-io-threads", kafkaBrokerTargetId), response.Id) - assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) - }) - t.Run(tt.name, func(t *testing.T) { - //Given - action := AlterNumberNetworkThreadsAttack{} - //When - response := action.Describe() - - //Then - assert.Equal(t, "Limit the number of network threads", response.Description) - assert.Equal(t, "Limit Network Threads", response.Label) - assert.Equal(t, kafkaBrokerTargetId, response.TargetSelection.TargetType) - assert.Equal(t, fmt.Sprintf("%s.limit-network-threads", kafkaBrokerTargetId), response.Id) - assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) - }) - } -} - -func TestAlter_Prepare(t *testing.T) { - tests := []struct { - name string - requestBody action_kit_api.PrepareActionRequestBody - wantedError error - wantedState *AlterState - }{ - { - name: "Should return config", - requestBody: extutil.JsonMangle(action_kit_api.PrepareActionRequestBody{ - Target: &action_kit_api.Target{ - Attributes: map[string][]string{ - "kafka.broker.node-id": {"1"}, - }, - }, - Config: map[string]interface{}{ - "network_threads": 2, - "io_threads": 1, - "max_bytes": 256, - "connection_rate": 2, - "duration": 10000, - }, - ExecutionId: uuid.New(), - }), - - wantedState: &AlterState{ - BrokerConfigValue: "network_threads", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - //Given - action := AlterNumberNetworkThreadsAttack{} - state := AlterState{} - request := tt.requestBody - //When - _, err := action.Prepare(context.TODO(), &state, request) - - //Then - if tt.wantedError != nil { - assert.EqualError(t, err, tt.wantedError.Error()) - } - if tt.wantedState != nil { - assert.NoError(t, err) - assert.Equal(t, "network_threads", tt.wantedState.BrokerConfigValue) - } - }) - t.Run(tt.name, func(t *testing.T) { - //Given - action := AlterNumberIOThreadsAttack{} - state := AlterState{} - request := tt.requestBody - tt.wantedState.BrokerConfigValue = "io_threads" - //When - _, err := action.Prepare(context.TODO(), &state, request) - - //Then - if tt.wantedError != nil { - assert.EqualError(t, err, tt.wantedError.Error()) - } - if tt.wantedState != nil { - assert.NoError(t, err) - assert.Equal(t, "io_threads", tt.wantedState.BrokerConfigValue) - } - }) - t.Run(tt.name, func(t *testing.T) { - //Given - action := AlterMessageMaxBytesAttack{} - state := AlterState{} - request := tt.requestBody - tt.wantedState.BrokerConfigValue = "max_bytes" - //When - _, err := action.Prepare(context.TODO(), &state, request) - - //Then - if tt.wantedError != nil { - assert.EqualError(t, err, tt.wantedError.Error()) - } - if tt.wantedState != nil { - assert.NoError(t, err) - assert.Equal(t, "max_bytes", tt.wantedState.BrokerConfigValue) - } - }) - t.Run(tt.name, func(t *testing.T) { - //Given - action := AlterLimitConnectionCreateRateAttack{} - state := AlterState{} - request := tt.requestBody - tt.wantedState.BrokerConfigValue = "connection_rate" - //When - _, err := action.Prepare(context.TODO(), &state, request) - - //Then - if tt.wantedError != nil { - assert.EqualError(t, err, tt.wantedError.Error()) - } - if tt.wantedState != nil { - assert.NoError(t, err) - assert.Equal(t, "connection_rate", tt.wantedState.BrokerConfigValue) - } - }) - } -} diff --git a/extkafka/check_brokers.go b/extkafka/check_brokers.go index 97eb7a5..457c33b 100644 --- a/extkafka/check_brokers.go +++ b/extkafka/check_brokers.go @@ -1,7 +1,3 @@ -/* -* Copyright 2025 steadybit GmbH. All rights reserved. - */ - // SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2022 Steadybit GmbH diff --git a/extkafka/check_brokers_test.go b/extkafka/check_brokers_test.go index b35874e..816fe0e 100644 --- a/extkafka/check_brokers_test.go +++ b/extkafka/check_brokers_test.go @@ -1,6 +1,5 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka @@ -12,6 +11,7 @@ import ( "github.com/steadybit/extension-kafka/config" "github.com/steadybit/extension-kit/extutil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/twmb/franz-go/pkg/kfake" "github.com/twmb/franz-go/pkg/kgo" "strings" @@ -20,30 +20,17 @@ import ( ) func TestCheckBrokers_Describe(t *testing.T) { - tests := []struct { - name string - requestBody action_kit_api.PrepareActionRequestBody - wantedError error - wantedState *CheckBrokersState - }{ - { - name: "Should return description", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - //Given - action := CheckBrokersAction{} - //When - response := action.Describe() + //Given + action := CheckBrokersAction{} - //Then - assert.Equal(t, "Check activity of brokers.", response.Description) - assert.Equal(t, "Check Brokers", response.Label) - assert.Equal(t, fmt.Sprintf("%s.check", kafkaBrokerTargetId), response.Id) - assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) - }) - } + //When + response := action.Describe() + + //Then + assert.Equal(t, "Check activity of brokers.", response.Description) + assert.Equal(t, "Check Brokers", response.Label) + assert.Equal(t, fmt.Sprintf("%s.check", kafkaBrokerTargetId), response.Id) + assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) } func TestCheckBrokers_Prepare(t *testing.T) { @@ -51,9 +38,7 @@ func TestCheckBrokers_Prepare(t *testing.T) { kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(3), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer c.Close() seeds := c.ListenAddrs() @@ -70,7 +55,7 @@ func TestCheckBrokers_Prepare(t *testing.T) { requestBody: extutil.JsonMangle(action_kit_api.PrepareActionRequestBody{ Config: map[string]interface{}{ "expectedChanges": []string{"test"}, - "stateCheckMode": "test", + "changeCheckMode": "allTheTime", "duration": 10000, }, ExecutionId: uuid.New(), @@ -78,7 +63,7 @@ func TestCheckBrokers_Prepare(t *testing.T) { wantedState: &CheckBrokersState{ ExpectedChanges: []string{"test"}, - StateCheckMode: "test", + StateCheckMode: "allTheTime", StateCheckSuccess: false, }, }, @@ -98,8 +83,8 @@ func TestCheckBrokers_Prepare(t *testing.T) { } if tt.wantedState != nil { assert.NoError(t, err) - assert.Equal(t, "test", tt.wantedState.StateCheckMode) - assert.Equal(t, []string{"test"}, state.ExpectedChanges) + assert.Equal(t, tt.wantedState.ExpectedChanges, state.ExpectedChanges) + assert.Equal(t, tt.wantedState.StateCheckMode, state.StateCheckMode) assert.Equal(t, tt.wantedState.StateCheckSuccess, state.StateCheckSuccess) assert.NotNil(t, state.End) } @@ -112,9 +97,7 @@ func TestCheckBrokers_Status(t *testing.T) { kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(3), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer c.Close() seeds := c.ListenAddrs() @@ -126,19 +109,19 @@ func TestCheckBrokers_Status(t *testing.T) { kgo.ConsumerGroup("steadybit"), kgo.ConsumeTopics("steadybit"), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer cl.Close() tests := []struct { name string + killNode *int requestBody action_kit_api.PrepareActionRequestBody wantedError error wantedState *CheckBrokersState }{ { - name: "Should return status ok", + name: "Should return status ok", + killNode: extutil.Ptr(1), requestBody: extutil.JsonMangle(action_kit_api.PrepareActionRequestBody{ Target: &action_kit_api.Target{ Attributes: map[string][]string{ @@ -159,7 +142,8 @@ func TestCheckBrokers_Status(t *testing.T) { }, }, { - name: "Should return status ok with all the time check mode", + name: "Should return status ok with all the time check mode", + killNode: extutil.Ptr(2), requestBody: extutil.JsonMangle(action_kit_api.PrepareActionRequestBody{ Target: &action_kit_api.Target{ Attributes: map[string][]string{ @@ -189,31 +173,32 @@ func TestCheckBrokers_Status(t *testing.T) { state := CheckBrokersState{} request := tt.requestBody //When - _, errPrepare := action.Prepare(context.TODO(), &state, request) - statusResult, errStatus := action.Status(context.TODO(), &state) - time.Sleep(6 * time.Second) - err := c.RemoveNode(1) - if err != nil { - return - } + _, errPrepare := action.Prepare(t.Context(), &state, request) + statusResult, errStatus := action.Status(t.Context(), &state) //Then if tt.wantedState != nil { assert.NoError(t, errPrepare) assert.NoError(t, errStatus) assert.Equal(t, tt.wantedState.StateCheckMode, state.StateCheckMode) - assert.Equal(t, false, statusResult.Completed) + assert.False(t, statusResult.Completed) assert.NotNil(t, state.End) } + if tt.wantedError != nil { + err := c.RemoveNode(int32(*tt.killNode)) + require.NoError(t, err) + } + time.Sleep(6 * time.Second) + // Completed - statusResult, errStatus = action.Status(context.TODO(), &state) + statusResult, errStatus = action.Status(t.Context(), &state) //Then if tt.wantedState != nil { assert.NoError(t, errPrepare) assert.NoError(t, errStatus) assert.Equal(t, tt.wantedState.StateCheckMode, state.StateCheckMode) - assert.Equal(t, true, statusResult.Completed) + assert.True(t, statusResult.Completed) assert.NotNil(t, state.End) } }) diff --git a/extkafka/check_consumer_group.go b/extkafka/check_consumer_group.go index f20707a..fe87331 100644 --- a/extkafka/check_consumer_group.go +++ b/extkafka/check_consumer_group.go @@ -1,9 +1,5 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ - // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka diff --git a/extkafka/check_consumer_group_test.go b/extkafka/check_consumer_group_test.go index d5d235b..889c78d 100644 --- a/extkafka/check_consumer_group_test.go +++ b/extkafka/check_consumer_group_test.go @@ -1,11 +1,9 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka import ( - "context" "fmt" "github.com/google/uuid" "github.com/steadybit/action-kit/go/action_kit_api/v2" @@ -13,6 +11,7 @@ import ( extension_kit "github.com/steadybit/extension-kit" "github.com/steadybit/extension-kit/extutil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/twmb/franz-go/pkg/kfake" "github.com/twmb/franz-go/pkg/kgo" "strings" @@ -21,31 +20,18 @@ import ( ) func TestCheckConsumerGroup_Describe(t *testing.T) { - tests := []struct { - name string - requestBody action_kit_api.PrepareActionRequestBody - wantedError error - wantedState *ConsumerGroupCheckState - }{ - { - name: "Should return description", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - //Given - action := ConsumerGroupCheckAction{} - //When - response := action.Describe() - - //Then - assert.Equal(t, "Check the consumer state", response.Description) - assert.Equal(t, "Check Consumer State", response.Label) - assert.Equal(t, kafkaConsumerTargetId, response.TargetSelection.TargetType) - assert.Equal(t, fmt.Sprintf("%s.check", kafkaConsumerTargetId), response.Id) - assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) - }) - } + //Given + action := ConsumerGroupCheckAction{} + + //When + response := action.Describe() + + //Then + assert.Equal(t, "Check the consumer state", response.Description) + assert.Equal(t, "Check Consumer State", response.Label) + assert.Equal(t, kafkaConsumerTargetId, response.TargetSelection.TargetType) + assert.Equal(t, fmt.Sprintf("%s.check", kafkaConsumerTargetId), response.Id) + assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) } func TestCheckConsumerGroup_Prepare(t *testing.T) { @@ -102,8 +88,9 @@ func TestCheckConsumerGroup_Prepare(t *testing.T) { action := ConsumerGroupCheckAction{} state := ConsumerGroupCheckState{} request := tt.requestBody + //When - _, err := action.Prepare(context.TODO(), &state, request) + _, err := action.Prepare(t.Context(), &state, request) //Then if tt.wantedError != nil { @@ -111,10 +98,10 @@ func TestCheckConsumerGroup_Prepare(t *testing.T) { } if tt.wantedState != nil { assert.NoError(t, err) - assert.Equal(t, "test", tt.wantedState.StateCheckMode) - assert.Equal(t, "steadybit", state.ConsumerGroupName) - assert.Equal(t, []string{"test"}, state.ExpectedState) - assert.Equal(t, false, state.StateCheckSuccess) + assert.Equal(t, tt.wantedState.StateCheckMode, state.StateCheckMode) + assert.Equal(t, tt.wantedState.ConsumerGroupName, state.ConsumerGroupName) + assert.Equal(t, tt.wantedState.ExpectedState, state.ExpectedState) + assert.False(t, state.StateCheckSuccess) assert.NotNil(t, state.End) } }) @@ -126,9 +113,7 @@ func TestCheckConsumerGroup_Status(t *testing.T) { kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(3), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer c.Close() seeds := c.ListenAddrs() @@ -140,9 +125,7 @@ func TestCheckConsumerGroup_Status(t *testing.T) { kgo.ConsumerGroup("steadybit"), kgo.ConsumeTopics("steadybit"), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer cl.Close() tests := []struct { @@ -166,7 +149,6 @@ func TestCheckConsumerGroup_Status(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedState: &ConsumerGroupCheckState{ ConsumerGroupName: "steadybit", ExpectedState: []string{"Dead"}, @@ -190,7 +172,6 @@ func TestCheckConsumerGroup_Status(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedState: &ConsumerGroupCheckState{ ConsumerGroupName: "steadybit", ExpectedState: []string{"Dead"}, @@ -206,10 +187,10 @@ func TestCheckConsumerGroup_Status(t *testing.T) { action := ConsumerGroupCheckAction{} state := ConsumerGroupCheckState{} request := tt.requestBody + //When - _, errPrepare := action.Prepare(context.TODO(), &state, request) - statusResult, errStatus := action.Status(context.TODO(), &state) - time.Sleep(6 * time.Second) + _, errPrepare := action.Prepare(t.Context(), &state, request) + statusResult, errStatus := action.Status(t.Context(), &state) //Then if tt.wantedState != nil { @@ -217,82 +198,24 @@ func TestCheckConsumerGroup_Status(t *testing.T) { assert.NoError(t, errStatus) assert.Equal(t, tt.wantedState.StateCheckMode, state.StateCheckMode) assert.Equal(t, tt.wantedState.ConsumerGroupName, state.ConsumerGroupName) - assert.Equal(t, false, statusResult.Completed) + assert.False(t, statusResult.Completed) assert.NotNil(t, state.End) } + time.Sleep(6 * time.Second) + // Completed - statusResult, errStatus = action.Status(context.TODO(), &state) + statusResult, errStatus = action.Status(t.Context(), &state) + //Then if tt.wantedState != nil { assert.NoError(t, errPrepare) assert.NoError(t, errStatus) assert.Equal(t, tt.wantedState.StateCheckMode, state.StateCheckMode) assert.Equal(t, tt.wantedState.ConsumerGroupName, state.ConsumerGroupName) - assert.Equal(t, true, statusResult.Completed) + assert.True(t, statusResult.Completed) assert.NotNil(t, state.End) } }) } } - -//func TestAction_Stop(t *testing.T) { -// -// tests := []struct { -// name string -// requestBody action_kit_api.StopActionRequestBody -// state *KafkaBrokerAttackState -// executionRunData *ExecutionRunData -// wantedError error -// }{ -// { -// name: "Should successfully stop the action", -// requestBody: extutil.JsonMangle(action_kit_api.StopActionRequestBody{}), -// state: &KafkaBrokerAttackState{ -// ExecutionID: uuid.New(), -// SuccessRate: 40, -// }, -// executionRunData: getExecutionRunData(5, 10), -// wantedError: nil, -// }, { -// name: "Should fail because of low success rate", -// requestBody: extutil.JsonMangle(action_kit_api.StopActionRequestBody{}), -// state: &KafkaBrokerAttackState{ -// ExecutionID: uuid.New(), -// SuccessRate: 100, -// }, -// executionRunData: getExecutionRunData(4, 11), -// wantedError: extension_kit.ToError("Success Rate (36.36%) was below 100%", nil), -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// //Given -// saveExecutionRunData(tt.state.ExecutionID, tt.executionRunData) -// //When -// result, err := stop(tt.state) -// -// //Then -// if tt.wantedError != nil && result.Error == nil { -// assert.EqualError(t, err, tt.wantedError.Error()) -// } else if tt.wantedError != nil && result.Error != nil { -// assert.Equal(t, tt.wantedError.Error(), result.Error.Title) -// } else if tt.wantedError == nil && result.Error != nil { -// assert.Fail(t, "Should not have error", result.Error.Title) -// } else { -// assert.NoError(t, err) -// } -// }) -// } -//} -// -//func getExecutionRunData(successCounter uint64, counter uint64) *ExecutionRunData { -// data := &ExecutionRunData{ -// requestSuccessCounter: atomic.Uint64{}, -// requestCounter: atomic.Uint64{}, -// } -// data.requestCounter.Store(counter) -// data.requestSuccessCounter.Store(successCounter) -// return data -// -//} diff --git a/extkafka/check_partitions.go b/extkafka/check_partitions.go index a2fd2b5..cd17fbf 100644 --- a/extkafka/check_partitions.go +++ b/extkafka/check_partitions.go @@ -1,9 +1,5 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ - // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka diff --git a/extkafka/check_topic_lag_for_consumer.go b/extkafka/check_topic_lag_for_consumer.go index c4b5c8a..326cd23 100644 --- a/extkafka/check_topic_lag_for_consumer.go +++ b/extkafka/check_topic_lag_for_consumer.go @@ -1,9 +1,5 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ - // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka diff --git a/extkafka/check_topic_lag_for_consumer_test.go b/extkafka/check_topic_lag_for_consumer_test.go index 1d710af..38450c8 100644 --- a/extkafka/check_topic_lag_for_consumer_test.go +++ b/extkafka/check_topic_lag_for_consumer_test.go @@ -1,6 +1,5 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka @@ -13,6 +12,7 @@ import ( extension_kit "github.com/steadybit/extension-kit" "github.com/steadybit/extension-kit/extutil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/twmb/franz-go/pkg/kfake" "github.com/twmb/franz-go/pkg/kgo" "strings" @@ -21,31 +21,18 @@ import ( ) func TestCheckTopicLag_Describe(t *testing.T) { - tests := []struct { - name string - requestBody action_kit_api.PrepareActionRequestBody - wantedError error - wantedState *ConsumerGroupLagCheckState - }{ - { - name: "Should return description", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - //Given - action := ConsumerGroupLagCheckAction{} - //When - response := action.Describe() - - //Then - assert.Equal(t, "Check the consumer lag for a given topic (lag is calculated by the difference between topic offset and consumer offset)", response.Description) - assert.Equal(t, "Check Topic Lag", response.Label) - assert.Equal(t, kafkaConsumerTargetId, response.TargetSelection.TargetType) - assert.Equal(t, fmt.Sprintf("%s.check-lag", kafkaConsumerTargetId), response.Id) - assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) - }) - } + //Given + action := ConsumerGroupLagCheckAction{} + + //When + response := action.Describe() + + //Then + assert.Equal(t, "Check the consumer lag for a given topic (lag is calculated by the difference between topic offset and consumer offset)", response.Description) + assert.Equal(t, "Check Topic Lag", response.Label) + assert.Equal(t, kafkaConsumerTargetId, response.TargetSelection.TargetType) + assert.Equal(t, fmt.Sprintf("%s.check-lag", kafkaConsumerTargetId), response.Id) + assert.Equal(t, extutil.Ptr("Kafka"), response.Technology) } func TestCheckConsumerGroupLag_Prepare(t *testing.T) { @@ -70,7 +57,6 @@ func TestCheckConsumerGroupLag_Prepare(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedState: &ConsumerGroupLagCheckState{ ConsumerGroupName: "steadybit", StateCheckSuccess: true, @@ -91,7 +77,6 @@ func TestCheckConsumerGroupLag_Prepare(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedError: extension_kit.ToError("the target is missing the kafka.consumer-group.name attribute", nil), }, } @@ -101,8 +86,9 @@ func TestCheckConsumerGroupLag_Prepare(t *testing.T) { action := ConsumerGroupLagCheckAction{} state := ConsumerGroupLagCheckState{} request := tt.requestBody + //When - _, err := action.Prepare(context.TODO(), &state, request) + _, err := action.Prepare(t.Context(), &state, request) //Then if tt.wantedError != nil { @@ -110,10 +96,10 @@ func TestCheckConsumerGroupLag_Prepare(t *testing.T) { } if tt.wantedState != nil { assert.NoError(t, err) - assert.Equal(t, int64(1), tt.wantedState.AcceptableLag) - assert.Equal(t, "steadybit", state.ConsumerGroupName) - assert.Equal(t, "steadybit", state.Topic) - assert.Equal(t, false, state.StateCheckSuccess) + assert.Equal(t, tt.wantedState.AcceptableLag, state.AcceptableLag) + assert.Equal(t, state.ConsumerGroupName, state.ConsumerGroupName) + assert.Equal(t, state.Topic, state.Topic) + assert.False(t, state.StateCheckSuccess) assert.NotNil(t, state.End) } }) @@ -125,9 +111,7 @@ func TestCheckConsumerGroupLag_Status(t *testing.T) { kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(3), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer c.Close() seeds := c.ListenAddrs() @@ -140,9 +124,7 @@ func TestCheckConsumerGroupLag_Status(t *testing.T) { kgo.DefaultProduceTopic("steadybit"), kgo.ConsumeTopics("steadybit"), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer cl.Close() // produce messages for lags @@ -171,7 +153,6 @@ func TestCheckConsumerGroupLag_Status(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedState: &ConsumerGroupLagCheckState{ ConsumerGroupName: "steadybit", AcceptableLag: int64(15), @@ -194,7 +175,6 @@ func TestCheckConsumerGroupLag_Status(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedState: &ConsumerGroupLagCheckState{ ConsumerGroupName: "steadybit", AcceptableLag: int64(1), @@ -209,10 +189,10 @@ func TestCheckConsumerGroupLag_Status(t *testing.T) { action := ConsumerGroupLagCheckAction{} state := ConsumerGroupLagCheckState{} request := tt.requestBody + //When - _, errPrepare := action.Prepare(context.TODO(), &state, request) - statusResult, errStatus := action.Status(context.TODO(), &state) - time.Sleep(6 * time.Second) + _, errPrepare := action.Prepare(t.Context(), &state, request) + statusResult, errStatus := action.Status(t.Context(), &state) //Then if tt.wantedState != nil { @@ -221,12 +201,15 @@ func TestCheckConsumerGroupLag_Status(t *testing.T) { assert.Equal(t, tt.wantedState.AcceptableLag, state.AcceptableLag) assert.Equal(t, tt.wantedState.Topic, state.Topic) assert.Equal(t, tt.wantedState.ConsumerGroupName, state.ConsumerGroupName) - assert.Equal(t, false, statusResult.Completed) + assert.False(t, statusResult.Completed) assert.NotNil(t, state.End) } + time.Sleep(6 * time.Second) + // Completed - _, errStatus = action.Status(context.TODO(), &state) + _, errStatus = action.Status(t.Context(), &state) + //Then if tt.wantedState != nil { assert.NoError(t, errPrepare) @@ -239,64 +222,3 @@ func TestCheckConsumerGroupLag_Status(t *testing.T) { }) } } - -//func TestAction_Stop(t *testing.T) { -// -// tests := []struct { -// name string -// requestBody action_kit_api.StopActionRequestBody -// state *KafkaBrokerAttackState -// executionRunData *ExecutionRunData -// wantedError error -// }{ -// { -// name: "Should successfully stop the action", -// requestBody: extutil.JsonMangle(action_kit_api.StopActionRequestBody{}), -// state: &KafkaBrokerAttackState{ -// ExecutionID: uuid.New(), -// SuccessRate: 40, -// }, -// executionRunData: getExecutionRunData(5, 10), -// wantedError: nil, -// }, { -// name: "Should fail because of low success rate", -// requestBody: extutil.JsonMangle(action_kit_api.StopActionRequestBody{}), -// state: &KafkaBrokerAttackState{ -// ExecutionID: uuid.New(), -// SuccessRate: 100, -// }, -// executionRunData: getExecutionRunData(4, 11), -// wantedError: extension_kit.ToError("Success Rate (36.36%) was below 100%", nil), -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// //Given -// saveExecutionRunData(tt.state.ExecutionID, tt.executionRunData) -// //When -// result, err := stop(tt.state) -// -// //Then -// if tt.wantedError != nil && result.Error == nil { -// assert.EqualError(t, err, tt.wantedError.Error()) -// } else if tt.wantedError != nil && result.Error != nil { -// assert.Equal(t, tt.wantedError.Error(), result.Error.Title) -// } else if tt.wantedError == nil && result.Error != nil { -// assert.Fail(t, "Should not have error", result.Error.Title) -// } else { -// assert.NoError(t, err) -// } -// }) -// } -//} -// -//func getExecutionRunData(successCounter uint64, counter uint64) *ExecutionRunData { -// data := &ExecutionRunData{ -// requestSuccessCounter: atomic.Uint64{}, -// requestCounter: atomic.Uint64{}, -// } -// data.requestCounter.Store(counter) -// data.requestSuccessCounter.Store(successCounter) -// return data -// -//} diff --git a/extkafka/common.go b/extkafka/common.go index fb0a708..60f4f4e 100644 --- a/extkafka/common.go +++ b/extkafka/common.go @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH + package extkafka import ( @@ -52,10 +55,10 @@ type KafkaBrokerAttackState struct { } type AlterState struct { - BrokerConfigValue string - BrokerID int32 - InitialBrokerConfigValue string BrokerHosts []string + BrokerID int32 + InitialBrokerConfigValue int + TargetBrokerConfigValue int } var ( @@ -185,47 +188,73 @@ func createNewAdminClient(brokers []string) (*kadm.Client, error) { return kadm.NewClient(client), nil } -func describeConfig(ctx context.Context, brokers []string, configName string, brokerID int32) (string, error) { - var initialValue string +func describeConfigInt(ctx context.Context, brokers []string, configName string, brokerID int32) (int, error) { + value, err := describeConfigStr(ctx, brokers, configName, brokerID) + if err != nil { + return -1, err + } + return strconv.Atoi(value) +} + +func describeConfigStr(ctx context.Context, brokers []string, configName string, brokerID int32) (string, error) { adminClient, err := createNewAdminClient(brokers) if err != nil { return "", err } - // Get the initial value + return describeConfigOf(ctx, adminClient, configName, brokerID) +} + +func describeConfigOf(ctx context.Context, adminClient *kadm.Client, configName string, brokerID int32) (configValue string, err error) { configs, err := adminClient.DescribeBrokerConfigs(ctx, brokerID) if err != nil { return "", err } - _, err = configs.On(strconv.FormatInt(int64(brokerID), 10), func(resourceConfig *kadm.ResourceConfig) error { + _, err = configs.On(strconv.FormatInt(int64(brokerID), 10), func(resourceConfig *kadm.ResourceConfig) error { for i := range resourceConfig.Configs { if resourceConfig.Configs[i].Key == configName { - initialValue = resourceConfig.Configs[i].MaybeValue() - // Found! - break + configValue = resourceConfig.Configs[i].MaybeValue() + return nil } } - return err + var values []string + for _, c := range resourceConfig.Configs { + values = append(values, fmt.Sprintf("%s: %s", c.Key, *c.Value)) + } + log.Debug().Strs("configs", values).Msgf("Configuration property %s could not be found", configName) + + return nil }) if err != nil { return "", err } - if initialValue == "" { + + if configValue == "" { log.Warn().Msgf("No value found for configuration key: %s, for broker node-id: %d", configName, brokerID) + } else { + log.Debug().Msgf("Configuration value for key %s: %s, for broker node-id: %d", configName, configValue, brokerID) } - return initialValue, nil + return configValue, nil } -func alterConfig(ctx context.Context, brokers []string, configName string, configValue string, brokerID int32) error { +func alterConfigInt(ctx context.Context, brokers []string, configName string, configValue int, brokerID int32) error { + return alterConfigStr(ctx, brokers, configName, strconv.Itoa(configValue), brokerID) +} + +func alterConfigStr(ctx context.Context, brokers []string, configName string, configValue string, brokerID int32) error { adminClient, err := createNewAdminClient(brokers) if err != nil { return err } defer adminClient.Close() - responses, err := adminClient.AlterBrokerConfigs(ctx, []kadm.AlterConfig{{Name: configName, Value: extutil.Ptr(configValue)}}, brokerID) + op := kadm.SetConfig + if configValue == "" { + op = kadm.DeleteConfig + } + responses, err := adminClient.AlterBrokerConfigs(ctx, []kadm.AlterConfig{{Name: configName, Value: extutil.Ptr(configValue), Op: op}}, brokerID) if err != nil { return err } @@ -239,25 +268,35 @@ func alterConfig(ctx context.Context, brokers []string, configName string, confi if len(errs) > 0 { return errors.Join(errs...) } - return nil -} -func adjustThreads(ctx context.Context, hosts []string, configName string, targetValue int, brokerId int32) error { - currentConfig, err := describeConfig(ctx, hosts, configName, brokerId) - if err != nil { - return err + // Changes may take time to be applied, wait accordingly + now := time.Now() + for { + if time.Since(now).Seconds() > 5 { + return fmt.Errorf("configuration change of %s to %s was not applied in time", configName, configValue) + } + value, err := describeConfigOf(ctx, adminClient, configName, brokerID) + if err != nil { + return err + } + if value == configValue { + return nil + } + log.Debug().Msgf("Configuration change of %s to %s was not applied yet, waiting", configName, configValue) } +} - currentValue, err := strconv.Atoi(currentConfig) +func adjustThreads(ctx context.Context, hosts []string, configName string, targetValue int, brokerId int32) error { + currentValue, err := describeConfigInt(ctx, hosts, configName, brokerId) if err != nil { return err } - //as kafka does not allow to more than double or halve the number of threads, we use an iterative approach to get to that value + // As kafka does not allow to more than double or halve the number of threads, we use an iterative approach to get to that value for currentValue != targetValue { nextValue := max(min(targetValue, currentValue*2), currentValue/2) - if err := alterConfig(ctx, hosts, configName, strconv.Itoa(nextValue), brokerId); err != nil { + if err := alterConfigInt(ctx, hosts, configName, nextValue, brokerId); err != nil { return err } else { currentValue = nextValue diff --git a/extkafka/consumergroup_discovery_test.go b/extkafka/consumergroup_discovery_test.go index e4c4ee9..a3a0a9b 100644 --- a/extkafka/consumergroup_discovery_test.go +++ b/extkafka/consumergroup_discovery_test.go @@ -1,11 +1,13 @@ // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka import ( "fmt" - "reflect" + "github.com/steadybit/discovery-kit/go/discovery_kit_api" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "testing" "github.com/twmb/franz-go/pkg/kadm" @@ -16,46 +18,25 @@ import ( "strings" ) -// Test Describe() func TestDescribe(t *testing.T) { - d := &kafkaTopicDiscovery{} - desc := d.Describe() - if desc.Id != kafkaTopicTargetId { - t.Errorf("Describe().Id = %q; want %q", desc.Id, kafkaTopicTargetId) - } - if desc.Discover.CallInterval == nil { - t.Error("Describe().Discover.CallInterval = nil; want non-nil") - } + desc := (&kafkaTopicDiscovery{}).Describe() + assert.Equal(t, kafkaTopicTargetId, desc.Id) + assert.NotNil(t, desc.Discover.CallInterval) } -// Test DescribeTarget() func TestDescribeTarget(t *testing.T) { d := &kafkaTopicDiscovery{} td := d.DescribeTarget() - - if td.Id != kafkaTopicTargetId { - t.Errorf("DescribeTarget().Id = %q; want %q", td.Id, kafkaTopicTargetId) - } - if td.Label.One != "Kafka topic" || td.Label.Other != "Kafka topics" { - t.Errorf("DescribeTarget().Label = %+v; want One=\"Kafka topic\", Other=\"Kafka topics\"", td.Label) - } - if td.Category == nil || *td.Category != "kafka" { - t.Errorf("DescribeTarget().Category = %v; want pointer to \"kafka\"", td.Category) - } - if len(td.Table.Columns) != 6 { - t.Errorf("DescribeTarget().Table.Columns length = %d; want 6", len(td.Table.Columns)) - } - if len(td.Table.OrderBy) != 1 { - t.Errorf("DescribeTarget().Table.OrderBy length = %d; want 1", len(td.Table.OrderBy)) - } else { - ob := td.Table.OrderBy[0] - if ob.Attribute != "steadybit.label" || ob.Direction != "ASC" { - t.Errorf("DescribeTarget().OrderBy[0] = %+v; want Attribute=\"steadybit.label\", Direction=\"ASC\"", ob) - } - } + assert.Equal(t, kafkaTopicTargetId, td.Id) + assert.Equal(t, "Kafka topic", td.Label.One) + assert.Equal(t, "Kafka topics", td.Label.Other) + assert.Equal(t, "kafka", *td.Category) + assert.Len(t, td.Table.Columns, 6) + require.Len(t, td.Table.OrderBy, 1) + assert.Equal(t, "steadybit.label", td.Table.OrderBy[0].Attribute) + assert.Equal(t, discovery_kit_api.OrderByDirection("ASC"), td.Table.OrderBy[0].Direction) } -// Test DescribeAttributes() func TestDescribeAttributes(t *testing.T) { d := &kafkaTopicDiscovery{} attrs := d.DescribeAttributes() @@ -67,11 +48,7 @@ func TestDescribeAttributes(t *testing.T) { "kafka.topic.partitions-isr", "kafka.topic.replication-factor", } - - if len(attrs) != len(expected) { - t.Fatalf("DescribeAttributes() len = %d; want %d", len(attrs), len(expected)) - } - + require.Len(t, attrs, len(expected)) for _, want := range expected { found := false for _, a := range attrs { @@ -80,13 +57,10 @@ func TestDescribeAttributes(t *testing.T) { break } } - if !found { - t.Errorf("DescribeAttributes() missing %q", want) - } + assert.Truef(t, found, "DescribeAttributes() missing %q", want) } } -// Test toTopicTarget() func TestToTopicTarget(t *testing.T) { td := kadm.TopicDetail{ Topic: "my-topic", @@ -96,32 +70,19 @@ func TestToTopicTarget(t *testing.T) { }, } cluster := "cluster-42" - tgt := toTopicTarget(td, cluster) // Basic fields - if want := "my-topic-cluster-42"; tgt.Id != want { - t.Errorf("Id = %q; want %q", tgt.Id, want) - } - if tgt.Label != "my-topic" { - t.Errorf("Label = %q; want %q", tgt.Label, "my-topic") - } - if tgt.TargetType != kafkaTopicTargetId { - t.Errorf("TargetType = %q; want %q", tgt.TargetType, kafkaTopicTargetId) - } + assert.Equal(t, "my-topic-cluster-42", tgt.Id) + assert.Equal(t, "my-topic", tgt.Label) + assert.Equal(t, kafkaTopicTargetId, tgt.TargetType) // Attributes attr := tgt.Attributes - check := func(key string, want []string) { v, ok := attr[key] - if !ok { - t.Errorf("missing attribute %q", key) - return - } - if !reflect.DeepEqual(v, want) { - t.Errorf("%s = %v; want %v", key, v, want) - } + assert.True(t, ok, "missing attribute %q", key) + assert.Equal(t, want, v) } check("kafka.cluster.name", []string{cluster}) @@ -154,9 +115,7 @@ func TestDiscoverTargetsClusterName(t *testing.T) { kfake.NumBrokers(1), kfake.ClusterID("test"), ) - if err != nil { - t.Fatalf("failed to create fake cluster: %v", err) - } + require.NoError(t, err) defer c.Close() // Configure seed brokers for discovery @@ -169,34 +128,21 @@ func TestDiscoverTargetsClusterName(t *testing.T) { // Discover targets ctx := context.Background() targets, err := getAllTopics(ctx) - if err != nil { - t.Fatalf("getAllTopics error: %v", err) - } - if len(targets) == 0 { - t.Fatal("expected at least one discovered topic") - } + require.NoError(t, err) + require.NotEmpty(t, targets) // Retrieve expected cluster name from metadata client, err := createNewAdminClient(strings.Split(config.Config.SeedBrokers, ",")) - if err != nil { - t.Fatalf("createNewAdminClient error: %v", err) - } + require.NoError(t, err) defer client.Close() meta, err := client.BrokerMetadata(ctx) - if err != nil { - t.Fatalf("BrokerMetadata error: %v", err) - } + require.NoError(t, err) expected := meta.Cluster // Assert each discovered target has the correct cluster name attribute for _, tgt := range targets { values, ok := tgt.Attributes["kafka.cluster.name"] - if !ok { - t.Errorf("missing kafka.cluster.name for target %s", tgt.Id) - continue - } - if len(values) != 1 || values[0] != expected { - t.Errorf("kafka.cluster.name = %v; want [%s]", values, expected) - } + require.True(t, ok, "missing kafka.cluster.name for target %s", tgt.Id) + require.Equal(t, []string{expected}, values) } } diff --git a/extkafka/produce.go b/extkafka/produce.go index 037cdf1..bc358b5 100644 --- a/extkafka/produce.go +++ b/extkafka/produce.go @@ -1,6 +1,5 @@ -/* - * Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka diff --git a/extkafka/produceFixAmount.go b/extkafka/produceFixAmount.go index bdc72d9..343a862 100644 --- a/extkafka/produceFixAmount.go +++ b/extkafka/produceFixAmount.go @@ -1,6 +1,5 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka diff --git a/extkafka/produceFixAmount_test.go b/extkafka/produceFixAmount_test.go index 6f02208..8510b3a 100644 --- a/extkafka/produceFixAmount_test.go +++ b/extkafka/produceFixAmount_test.go @@ -1,16 +1,15 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka import ( - "context" "github.com/google/uuid" "github.com/steadybit/action-kit/go/action_kit_api/v2" "github.com/steadybit/extension-kafka/config" "github.com/steadybit/extension-kit/extutil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/twmb/franz-go/pkg/kfake" "strings" "testing" @@ -19,7 +18,6 @@ import ( func TestNewHTTPCheckActionFixedAmount_Prepare(t *testing.T) { action := produceMessageActionFixedAmount{} - tests := []struct { name string requestBody action_kit_api.PrepareActionRequestBody @@ -46,7 +44,6 @@ func TestNewHTTPCheckActionFixedAmount_Prepare(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedState: &KafkaBrokerAttackState{ ConsumerGroup: "", Topic: "steadybit", @@ -63,8 +60,9 @@ func TestNewHTTPCheckActionFixedAmount_Prepare(t *testing.T) { //Given state := action.NewEmptyState() request := tt.requestBody + //When - _, err := action.Prepare(context.Background(), &state, request) + _, err := action.Prepare(t.Context(), &state, request) //Then if tt.wantedError != nil { @@ -89,14 +87,12 @@ func TestNewHTTPCheckActionFixedAmount_All_Success(t *testing.T) { kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(1), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer c.Close() seeds := c.ListenAddrs() config.Config.SeedBrokers = strings.Join(seeds, ",") - //prepare the action + action := produceMessageActionFixedAmount{} state := action.NewEmptyState() prepareActionRequestBody := extutil.JsonMangle(action_kit_api.PrepareActionRequestBody{ @@ -120,7 +116,7 @@ func TestNewHTTPCheckActionFixedAmount_All_Success(t *testing.T) { }) // Prepare - prepareResult, err := action.Prepare(context.Background(), &state, prepareActionRequestBody) + prepareResult, err := action.Prepare(t.Context(), &state, prepareActionRequestBody) assert.NoError(t, err) assert.Nil(t, prepareResult) assert.Greater(t, state.DelayBetweenRequestsInMS, extutil.ToInt64(0)) @@ -128,24 +124,28 @@ func TestNewHTTPCheckActionFixedAmount_All_Success(t *testing.T) { executionRunData, err := action.getExecutionRunData(state.ExecutionID) assert.NoError(t, err) assert.NotNil(t, executionRunData) + // Start - startResult, err := action.Start(context.Background(), &state) + startResult, err := action.Start(t.Context(), &state) assert.NoError(t, err) assert.Nil(t, startResult) // Status - statusResult, err := action.Status(context.Background(), &state) + statusResult, err := action.Status(t.Context(), &state) assert.NoError(t, err) assert.NotNil(t, statusResult.Metrics) + time.Sleep(10 * time.Second) + // Status completed - statusResult, err = action.Status(context.Background(), &state) + statusResult, err = action.Status(t.Context(), &state) assert.NoError(t, err) assert.Equal(t, true, statusResult.Completed) assert.Equal(t, uint64(10), executionRunData.requestCounter.Load()) + // Stop - stopResult, err := action.Stop(context.Background(), &state) + stopResult, err := action.Stop(t.Context(), &state) assert.NoError(t, err) assert.NotNil(t, stopResult.Metrics) assert.Nil(t, stopResult.Error) @@ -157,14 +157,12 @@ func TestNewHTTPCheckActionFixedAmount_All_Failure(t *testing.T) { kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(1), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer c.Close() seeds := c.ListenAddrs() config.Config.SeedBrokers = strings.Join(seeds, ",") - //prepare the action + action := produceMessageActionFixedAmount{} state := action.NewEmptyState() prepareActionRequestBody := extutil.JsonMangle(action_kit_api.PrepareActionRequestBody{ @@ -188,23 +186,25 @@ func TestNewHTTPCheckActionFixedAmount_All_Failure(t *testing.T) { }) // Prepare - prepareResult, err := action.Prepare(context.Background(), &state, prepareActionRequestBody) + prepareResult, err := action.Prepare(t.Context(), &state, prepareActionRequestBody) assert.NoError(t, err) assert.Nil(t, prepareResult) assert.Greater(t, state.DelayBetweenRequestsInMS, extutil.ToInt64(0)) // Start - startResult, err := action.Start(context.Background(), &state) + startResult, err := action.Start(t.Context(), &state) assert.NoError(t, err) assert.Nil(t, startResult) // Status - statusResult, err := action.Status(context.Background(), &state) + statusResult, err := action.Status(t.Context(), &state) assert.NoError(t, err) assert.NotNil(t, statusResult.Metrics) + time.Sleep(5 * time.Second) + // Status completed - statusResult, err = action.Status(context.Background(), &state) + statusResult, err = action.Status(t.Context(), &state) assert.NoError(t, err) assert.Equal(t, true, statusResult.Completed) @@ -212,7 +212,7 @@ func TestNewHTTPCheckActionFixedAmount_All_Failure(t *testing.T) { assert.NoError(t, err) assert.Greater(t, executionRunData.requestCounter.Load(), uint64(0)) // Stop - stopResult, err := action.Stop(context.Background(), &state) + stopResult, err := action.Stop(t.Context(), &state) assert.NoError(t, err) assert.NotNil(t, stopResult.Metrics) assert.NotNil(t, stopResult.Error) diff --git a/extkafka/producePeriodically.go b/extkafka/producePeriodically.go index d2c9fcb..53fbc2d 100644 --- a/extkafka/producePeriodically.go +++ b/extkafka/producePeriodically.go @@ -1,6 +1,5 @@ -/* - * Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka diff --git a/extkafka/producePeriodically_test.go b/extkafka/producePeriodically_test.go index 7487ec7..60b909b 100644 --- a/extkafka/producePeriodically_test.go +++ b/extkafka/producePeriodically_test.go @@ -1,17 +1,16 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka import ( - "context" "github.com/google/uuid" "github.com/steadybit/action-kit/go/action_kit_api/v2" "github.com/steadybit/extension-kafka/config" extension_kit "github.com/steadybit/extension-kit" "github.com/steadybit/extension-kit/extutil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/twmb/franz-go/pkg/kfake" "strings" "testing" @@ -57,7 +56,6 @@ func TestNewProduceMessageActionPeriodically_Prepare(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedState: &KafkaBrokerAttackState{ ConsumerGroup: "", Topic: "steadybit", @@ -86,7 +84,6 @@ func TestNewProduceMessageActionPeriodically_Prepare(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedError: extension_kit.ToError("the target is missing the kafka.topic.name attribute", nil), }, } @@ -95,8 +92,9 @@ func TestNewProduceMessageActionPeriodically_Prepare(t *testing.T) { //Given state := action.NewEmptyState() request := tt.requestBody + //When - _, err := action.Prepare(context.Background(), &state, request) + _, err := action.Prepare(t.Context(), &state, request) //Then if tt.wantedError != nil { @@ -121,14 +119,12 @@ func TestNewHTTPCheckActionPeriodically_All_Success(t *testing.T) { kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(1), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer c.Close() seeds := c.ListenAddrs() config.Config.SeedBrokers = strings.Join(seeds, ",") - //prepare the action + action := produceMessageActionPeriodically{} state := action.NewEmptyState() prepareActionRequestBody := extutil.JsonMangle(action_kit_api.PrepareActionRequestBody{ @@ -151,7 +147,7 @@ func TestNewHTTPCheckActionPeriodically_All_Success(t *testing.T) { }) // Prepare - prepareResult, err := action.Prepare(context.Background(), &state, prepareActionRequestBody) + prepareResult, err := action.Prepare(t.Context(), &state, prepareActionRequestBody) assert.NoError(t, err) assert.Nil(t, prepareResult) assert.Greater(t, state.DelayBetweenRequestsInMS, extutil.ToInt64(0)) @@ -159,22 +155,26 @@ func TestNewHTTPCheckActionPeriodically_All_Success(t *testing.T) { executionRunData, err := action.getExecutionRunData(state.ExecutionID) assert.NoError(t, err) assert.NotNil(t, executionRunData) + // Start - startResult, err := action.Start(context.Background(), &state) + startResult, err := action.Start(t.Context(), &state) assert.NoError(t, err) assert.Nil(t, startResult) // Status - statusResult, err := action.Status(context.Background(), &state) + statusResult, err := action.Status(t.Context(), &state) assert.NoError(t, err) assert.NotNil(t, statusResult.Metrics) + time.Sleep(10 * time.Second) + // Status completed - statusResult, err = action.Status(context.Background(), &state) + statusResult, err = action.Status(t.Context(), &state) assert.NoError(t, err) assert.Equal(t, false, statusResult.Completed) + // Stop - stopResult, err := action.Stop(context.Background(), &state) + stopResult, err := action.Stop(t.Context(), &state) assert.NoError(t, err) assert.NotNil(t, stopResult.Metrics) assert.Nil(t, stopResult.Error) @@ -186,14 +186,12 @@ func TestNewHTTPCheckActionPeriodically_All_Failure(t *testing.T) { kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(1), ) - if err != nil { - panic(err) - } + require.NoError(t, err) defer c.Close() seeds := c.ListenAddrs() config.Config.SeedBrokers = strings.Join(seeds, ",") - //prepare the action + action := produceMessageActionPeriodically{} state := action.NewEmptyState() prepareActionRequestBody := extutil.JsonMangle(action_kit_api.PrepareActionRequestBody{ @@ -218,31 +216,34 @@ func TestNewHTTPCheckActionPeriodically_All_Failure(t *testing.T) { }) // Prepare - prepareResult, err := action.Prepare(context.Background(), &state, prepareActionRequestBody) + prepareResult, err := action.Prepare(t.Context(), &state, prepareActionRequestBody) assert.NoError(t, err) assert.Nil(t, prepareResult) assert.Greater(t, state.DelayBetweenRequestsInMS, extutil.ToInt64(0)) // Start - startResult, err := action.Start(context.Background(), &state) + startResult, err := action.Start(t.Context(), &state) assert.NoError(t, err) assert.Nil(t, startResult) // Status - statusResult, err := action.Status(context.Background(), &state) + statusResult, err := action.Status(t.Context(), &state) assert.NoError(t, err) assert.NotNil(t, statusResult.Metrics) + time.Sleep(5 * time.Second) + // Status completed - statusResult, err = action.Status(context.Background(), &state) + statusResult, err = action.Status(t.Context(), &state) assert.NoError(t, err) assert.Equal(t, statusResult.Completed, false) executionRunData, err := action.getExecutionRunData(state.ExecutionID) assert.NoError(t, err) assert.Greater(t, executionRunData.requestCounter.Load(), uint64(0)) + // Stop - stopResult, err := action.Stop(context.Background(), &state) + stopResult, err := action.Stop(t.Context(), &state) assert.NoError(t, err) assert.NotNil(t, stopResult.Metrics) assert.NotNil(t, stopResult.Error) diff --git a/extkafka/produce_test.go b/extkafka/produce_test.go index 5c218c3..f72188a 100644 --- a/extkafka/produce_test.go +++ b/extkafka/produce_test.go @@ -1,6 +1,5 @@ -/* -* Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka @@ -15,7 +14,6 @@ import ( ) func TestAction_Prepare(t *testing.T) { - tests := []struct { name string requestBody action_kit_api.PrepareActionRequestBody @@ -43,7 +41,6 @@ func TestAction_Prepare(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedState: &KafkaBrokerAttackState{ ConsumerGroup: "", Topic: "steadybit", @@ -69,7 +66,6 @@ func TestAction_Prepare(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedError: extension_kit.ToError("failed to interpret config value for recordHeaders as a key/value array", nil), }, { @@ -86,7 +82,6 @@ func TestAction_Prepare(t *testing.T) { }, ExecutionId: uuid.New(), }), - wantedError: extension_kit.ToError("max concurrent can't be zero", nil), }, } @@ -95,6 +90,7 @@ func TestAction_Prepare(t *testing.T) { //Given state := KafkaBrokerAttackState{} request := tt.requestBody + //When _, err := prepare(request, &state, func(executionRunData *ExecutionRunData, state *KafkaBrokerAttackState) bool { return false }) @@ -116,7 +112,6 @@ func TestAction_Prepare(t *testing.T) { } func TestAction_Stop(t *testing.T) { - tests := []struct { name string requestBody action_kit_api.StopActionRequestBody @@ -148,6 +143,7 @@ func TestAction_Stop(t *testing.T) { t.Run(tt.name, func(t *testing.T) { //Given saveExecutionRunData(tt.state.ExecutionID, tt.executionRunData) + //When result, err := stop(tt.state) diff --git a/extkafka/topic_discovery_test.go b/extkafka/topic_discovery_test.go index 9fa0037..328edc9 100644 --- a/extkafka/topic_discovery_test.go +++ b/extkafka/topic_discovery_test.go @@ -1,13 +1,16 @@ // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka import ( "fmt" + "github.com/steadybit/discovery-kit/go/discovery_kit_api" + "github.com/stretchr/testify/assert" "reflect" "testing" + "github.com/stretchr/testify/require" "github.com/twmb/franz-go/pkg/kadm" "context" @@ -16,49 +19,31 @@ import ( "strings" ) -// Test Describe() func TestDescribeTopic(t *testing.T) { - d := &kafkaTopicDiscovery{} - desc := d.Describe() - if desc.Id != kafkaTopicTargetId { - t.Errorf("Describe().Id = %q; want %q", desc.Id, kafkaTopicTargetId) - } - if desc.Discover.CallInterval == nil { - t.Error("Describe().Discover.CallInterval = nil; want non-nil") - } + desc := (&kafkaTopicDiscovery{}).Describe() + require.Equal(t, kafkaTopicTargetId, desc.Id) + require.NotNil(t, desc.Discover.CallInterval) } -// Test DescribeTarget() func TestDescribeTargetTopic(t *testing.T) { d := &kafkaTopicDiscovery{} td := d.DescribeTarget() - if td.Id != kafkaTopicTargetId { - t.Errorf("DescribeTarget().Id = %q; want %q", td.Id, kafkaTopicTargetId) - } - if td.Label.One != "Kafka topic" || td.Label.Other != "Kafka topics" { - t.Errorf("DescribeTarget().Label = %+v; want One=\"Kafka topic\", Other=\"Kafka topics\"", td.Label) - } - if td.Category == nil || *td.Category != "kafka" { - t.Errorf("DescribeTarget().Category = %v; want pointer to \"kafka\"", td.Category) - } - if len(td.Table.Columns) != 6 { - t.Errorf("DescribeTarget().Table.Columns length = %d; want 6", len(td.Table.Columns)) - } - if len(td.Table.OrderBy) != 1 { - t.Errorf("DescribeTarget().Table.OrderBy length = %d; want 1", len(td.Table.OrderBy)) - } else { - ob := td.Table.OrderBy[0] - if ob.Attribute != "steadybit.label" || ob.Direction != "ASC" { - t.Errorf("DescribeTarget().OrderBy[0] = %+v; want Attribute=\"steadybit.label\", Direction=\"ASC\"", ob) - } - } + require.Equal(t, kafkaTopicTargetId, td.Id) + require.Equal(t, "Kafka topic", td.Label.One) + require.Equal(t, "Kafka topics", td.Label.Other) + require.NotNil(t, td.Category) + require.Equal(t, "kafka", *td.Category) + require.Len(t, td.Table.Columns, 6) + require.Len(t, td.Table.OrderBy, 1) + + ob := td.Table.OrderBy[0] + require.Equal(t, "steadybit.label", ob.Attribute) + require.Equal(t, discovery_kit_api.OrderByDirection("ASC"), ob.Direction) } -// Test DescribeAttributes() func TestDescribeAttributesTopic(t *testing.T) { - d := &kafkaTopicDiscovery{} - attrs := d.DescribeAttributes() + attrs := (&kafkaTopicDiscovery{}).DescribeAttributes() expected := []string{ "kafka.topic.name", "kafka.topic.partitions", @@ -68,10 +53,7 @@ func TestDescribeAttributesTopic(t *testing.T) { "kafka.topic.replication-factor", } - if len(attrs) != len(expected) { - t.Fatalf("DescribeAttributes() len = %d; want %d", len(attrs), len(expected)) - } - + require.Len(t, attrs, len(expected)) for _, want := range expected { found := false for _, a := range attrs { @@ -80,13 +62,10 @@ func TestDescribeAttributesTopic(t *testing.T) { break } } - if !found { - t.Errorf("DescribeAttributes() missing %q", want) - } + assert.Truef(t, found, "DescribeAttributes() missing %q", want) } } -// Test toTopicTarget() func TestToTopicTargetTopic(t *testing.T) { td := kadm.TopicDetail{ Topic: "my-topic", @@ -96,32 +75,19 @@ func TestToTopicTargetTopic(t *testing.T) { }, } cluster := "cluster-42" - tgt := toTopicTarget(td, cluster) // Basic fields - if want := "my-topic-cluster-42"; tgt.Id != want { - t.Errorf("Id = %q; want %q", tgt.Id, want) - } - if tgt.Label != "my-topic" { - t.Errorf("Label = %q; want %q", tgt.Label, "my-topic") - } - if tgt.TargetType != kafkaTopicTargetId { - t.Errorf("TargetType = %q; want %q", tgt.TargetType, kafkaTopicTargetId) - } + assert.Equal(t, "my-topic-cluster-42", tgt.Id) + assert.Equal(t, "my-topic", tgt.Label) + assert.Equal(t, kafkaTopicTargetId, tgt.TargetType) // Attributes attr := tgt.Attributes - check := func(key string, want []string) { v, ok := attr[key] - if !ok { - t.Errorf("missing attribute %q", key) - return - } - if !reflect.DeepEqual(v, want) { - t.Errorf("%s = %v; want %v", key, v, want) - } + assert.True(t, ok, "missing attribute %q", key) + assert.True(t, reflect.DeepEqual(v, want), "%s = %v; want %v", key, v, want) } check("kafka.cluster.name", []string{cluster}) @@ -148,15 +114,12 @@ func TestToTopicTargetTopic(t *testing.T) { // TestDiscoverTargetsClusterName verifies that the kafka.cluster.name attribute // is correctly set when discovering topics against a fake Kafka cluster. func TestDiscoverTopicTargetsClusterName(t *testing.T) { - // Set up fake Kafka cluster with a topic "steadybit" c, err := kfake.NewCluster( kfake.SeedTopics(-1, "steadybit"), kfake.NumBrokers(1), kfake.ClusterID("test"), ) - if err != nil { - t.Fatalf("failed to create fake cluster: %v", err) - } + require.NoError(t, err) defer c.Close() // Configure seed brokers for discovery @@ -169,34 +132,23 @@ func TestDiscoverTopicTargetsClusterName(t *testing.T) { // Discover targets ctx := context.Background() targets, err := getAllTopics(ctx) - if err != nil { - t.Fatalf("getAllTopics error: %v", err) - } - if len(targets) == 0 { - t.Fatal("expected at least one discovered topic") - } + require.NoError(t, err) + require.NotEmpty(t, targets) // Retrieve expected cluster name from metadata client, err := createNewAdminClient(strings.Split(config.Config.SeedBrokers, ",")) - if err != nil { - t.Fatalf("createNewAdminClient error: %v", err) - } + require.NoError(t, err) defer client.Close() + meta, err := client.BrokerMetadata(ctx) - if err != nil { - t.Fatalf("BrokerMetadata error: %v", err) - } + require.NoError(t, err) expected := meta.Cluster // Assert each discovered target has the correct cluster name attribute for _, tgt := range targets { values, ok := tgt.Attributes["kafka.cluster.name"] - if !ok { - t.Errorf("missing kafka.cluster.name for target %s", tgt.Id) - continue - } - if len(values) != 1 || values[0] != expected { - t.Errorf("kafka.cluster.name = %v; want [%s]", values, expected) - } + require.True(t, ok, "missing kafka.cluster.name for target %s", tgt.Id) + require.Len(t, values, 1) + require.Equal(t, expected, values[0]) } } diff --git a/extkafka/util.go b/extkafka/util.go index 763ab88..3966979 100644 --- a/extkafka/util.go +++ b/extkafka/util.go @@ -1,6 +1,5 @@ -/* - * Copyright 2024 steadybit GmbH. All rights reserved. - */ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH package extkafka diff --git a/extkafka/util_test.go b/extkafka/util_test.go index 651ce60..8e5f7e3 100644 --- a/extkafka/util_test.go +++ b/extkafka/util_test.go @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH + package extkafka import ( diff --git a/go.mod b/go.mod index 49919b4..9260562 100644 --- a/go.mod +++ b/go.mod @@ -10,18 +10,18 @@ require ( github.com/kelseyhightower/envconfig v1.4.0 github.com/rs/zerolog v1.34.0 github.com/steadybit/action-kit/go/action_kit_api/v2 v2.10.0 - github.com/steadybit/action-kit/go/action_kit_sdk v1.2.0 + github.com/steadybit/action-kit/go/action_kit_sdk v1.3.0 github.com/steadybit/action-kit/go/action_kit_test v1.4.2 - github.com/steadybit/advice-kit/go/advice_kit_api v1.2.0 + github.com/steadybit/advice-kit/go/advice_kit_api v1.2.1 github.com/steadybit/discovery-kit/go/discovery_kit_api v1.7.0 github.com/steadybit/discovery-kit/go/discovery_kit_commons v0.3.0 - github.com/steadybit/discovery-kit/go/discovery_kit_sdk v1.3.0 + github.com/steadybit/discovery-kit/go/discovery_kit_sdk v1.3.1 github.com/steadybit/discovery-kit/go/discovery_kit_test v1.2.0 github.com/steadybit/event-kit/go/event_kit_api v1.6.0 github.com/steadybit/extension-kit v1.10.0 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 github.com/twmb/franz-go v1.19.5 - github.com/twmb/franz-go/pkg/kadm v1.16.0 + github.com/twmb/franz-go/pkg/kadm v1.16.1 github.com/twmb/franz-go/pkg/kfake v0.0.0-20250711145744-a849b8be17b7 go.uber.org/automaxprocs v1.6.0 ) @@ -29,15 +29,16 @@ require ( require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/elastic/go-sysinfo v1.15.3 // indirect + github.com/elastic/go-sysinfo v1.15.4 // indirect github.com/elastic/go-windows v1.0.2 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect - github.com/getkin/kin-openapi v0.132.0 // indirect + github.com/getkin/kin-openapi v0.133.0 // indirect github.com/go-logr/logr v1.4.2 // indirect - github.com/go-openapi/jsonpointer v0.21.1 // indirect + github.com/go-openapi/jsonpointer v0.22.1 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.1 // indirect + github.com/go-openapi/swag/jsonname v0.25.1 // indirect github.com/go-resty/resty/v2 v2.16.5 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect @@ -46,7 +47,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/mailru/easyjson v0.9.0 // indirect + github.com/mailru/easyjson v0.9.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/moby/spdystream v0.5.0 // indirect @@ -67,16 +68,17 @@ require ( github.com/rs/xid v1.6.0 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/twmb/franz-go/pkg/kmsg v1.11.2 // indirect + github.com/woodsbury/decimal128 v1.4.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect github.com/zmwangx/debounce v1.0.0 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/net v0.41.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.27.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect golang.org/x/time v0.11.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/go.sum b/go.sum index cc35dd7..b6a9c1c 100644 --- a/go.sum +++ b/go.sum @@ -11,24 +11,26 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/elastic/go-sysinfo v1.15.3 h1:W+RnmhKFkqPTCRoFq2VCTmsT4p/fwpo+3gKNQsn1XU0= -github.com/elastic/go-sysinfo v1.15.3/go.mod h1:K/cNrqYTDrSoMh2oDkYEMS2+a72GRxMvNP+GC+vRIlo= +github.com/elastic/go-sysinfo v1.15.4 h1:A3zQcunCxik14MgXu39cXFXcIw2sFXZ0zL886eyiv1Q= +github.com/elastic/go-sysinfo v1.15.4/go.mod h1:ZBVXmqS368dOn/jvijV/zHLfakWTYHBZPk3G244lHrU= github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI= github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= -github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= +github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= -github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= +github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= +github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= +github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -74,8 +76,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/madflojo/testcerts v1.4.0 h1:I09gN0C1ly9IgeVNcAqKk8RAKIJTe3QnFrrPBDyvzN4= github.com/madflojo/testcerts v1.4.0/go.mod h1:MW8sh39gLnkKh4K0Nc55AyHEDl9l/FBLDUsQhpmkuo0= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= +github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -138,18 +140,18 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/steadybit/action-kit/go/action_kit_api/v2 v2.10.0 h1:XIEeNvyXacUn52nONt7rMQDTvWUht+sX2RyrGxzcmts= github.com/steadybit/action-kit/go/action_kit_api/v2 v2.10.0/go.mod h1:kc36KasHIY867fB7rBJtxg2orPaCU7xbezsAA05qcA8= -github.com/steadybit/action-kit/go/action_kit_sdk v1.2.0 h1:Jr/RVP4rrE5bDYPzPMxGI038qEUjxFY51BCGvvrk3uk= -github.com/steadybit/action-kit/go/action_kit_sdk v1.2.0/go.mod h1:vFcl8fQMFbD+xw8CqtvixmITLToYDT4RRMqRwlVC3Zc= +github.com/steadybit/action-kit/go/action_kit_sdk v1.3.0 h1:Ql3rJ9m407gZPGZK93jmf7FSTNzir31y2Hi/hP+g0+4= +github.com/steadybit/action-kit/go/action_kit_sdk v1.3.0/go.mod h1:fQm+I4UT7GSr5mzNuJ6MxE/iHl1xwesYXN5NM8l8N1A= github.com/steadybit/action-kit/go/action_kit_test v1.4.2 h1:jCjnVMvJO1hzYKZgn4ivwzJNSWId2rRNhKvo9uGCKmk= github.com/steadybit/action-kit/go/action_kit_test v1.4.2/go.mod h1:7vqpIZI0kasNi50I29fe7QybNZ3AaJpbap24j6w8Ujw= -github.com/steadybit/advice-kit/go/advice_kit_api v1.2.0 h1:0M4tthKcuVyTn1kzyD5/Q3/XlRNGBnN8MXsV71XNeZs= -github.com/steadybit/advice-kit/go/advice_kit_api v1.2.0/go.mod h1:P9e6rqDUEgLiW+1ouboV2uN1Je7bubP3SWLTd6TRKz4= +github.com/steadybit/advice-kit/go/advice_kit_api v1.2.1 h1:fVZScd0xVSuGIn/+2BjJ/Uj4tCzY7s2E3AJcd1KfxpY= +github.com/steadybit/advice-kit/go/advice_kit_api v1.2.1/go.mod h1:lN4X8wOehdmDj87QAn2FaJ18tWwpYXieyQ09hBPkPMM= github.com/steadybit/discovery-kit/go/discovery_kit_api v1.7.0 h1:eT+zVqsb8oriixyDNF5DTuB5L83Z8q6vEAbfN6dbpJA= github.com/steadybit/discovery-kit/go/discovery_kit_api v1.7.0/go.mod h1:caJZz4j098TZelA84CTUDhT+/0zOti8mqSTMhkYj3yQ= github.com/steadybit/discovery-kit/go/discovery_kit_commons v0.3.0 h1:D3yk1Izl2tczk3UD60bcmaLnpCzZ03fFVrV27jgnBEQ= github.com/steadybit/discovery-kit/go/discovery_kit_commons v0.3.0/go.mod h1:vRvPsLTWvsR5Y5V/N/jtzRq03GNFVnbStgN6iKhqCqM= -github.com/steadybit/discovery-kit/go/discovery_kit_sdk v1.3.0 h1:F2n7hqq90wv/fN2SFP8ltyxbrlSH+e9zruJ30FgPcQ8= -github.com/steadybit/discovery-kit/go/discovery_kit_sdk v1.3.0/go.mod h1:fFHtqPbtKdVWXmGP11xuFvhGTT9yNeVnBVYV/C6X4v4= +github.com/steadybit/discovery-kit/go/discovery_kit_sdk v1.3.1 h1:Di/Gv6Wp+E2FBfx2ojf4cBQPJ+lwEk4mclql+lG3bOs= +github.com/steadybit/discovery-kit/go/discovery_kit_sdk v1.3.1/go.mod h1:z7Ma15GaX/UZMTUx/Uulwiz6k+2PwVqou6zP8TKW/Vw= github.com/steadybit/discovery-kit/go/discovery_kit_test v1.2.0 h1:Pdgl5y0/MkwRqgXVWeHYYlQucTZ6BEAAAZmvgO+jzh4= github.com/steadybit/discovery-kit/go/discovery_kit_test v1.2.0/go.mod h1:2Ox3Se50dEoG0kSuDhTvlLEWeBvtRmSly/OZCmtDiJk= github.com/steadybit/event-kit/go/event_kit_api v1.6.0 h1:MZbVVLOPQn46enR3RjWglgWfGR5Eq/18BAex3bH8OQw= @@ -160,18 +162,20 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/twmb/franz-go v1.19.5 h1:W7+o8D0RsQsedqib71OVlLeZ0zI6CbFra7yTYhZTs5Y= github.com/twmb/franz-go v1.19.5/go.mod h1:4kFJ5tmbbl7asgwAGVuyG1ZMx0NNpYk7EqflvWfPCpM= -github.com/twmb/franz-go/pkg/kadm v1.16.0 h1:STMs1t5lYR5mR974PSiwNzE5TvsosByTp+rKXLOhAjE= -github.com/twmb/franz-go/pkg/kadm v1.16.0/go.mod h1:MUdcUtnf9ph4SFBLLA/XxE29rvLhWYLM9Ygb8dfSCvw= +github.com/twmb/franz-go/pkg/kadm v1.16.1 h1:IEkrhTljgLHJ0/hT/InhXGjPdmWfFvxp7o/MR7vJ8cw= +github.com/twmb/franz-go/pkg/kadm v1.16.1/go.mod h1:Ue/ye1cc9ipsQFg7udFbbGiFNzQMqiH73fGC2y0rwyc= github.com/twmb/franz-go/pkg/kfake v0.0.0-20250711145744-a849b8be17b7 h1:SmVArSUtiB+bsqMjHtqemjL1YCj4L74NSiOxjtwAJ/o= github.com/twmb/franz-go/pkg/kfake v0.0.0-20250711145744-a849b8be17b7/go.mod h1:udxwmMC3r4xqjwrSrMi8p9jpqMDNpC2YwexpDSUmQtw= github.com/twmb/franz-go/pkg/kmsg v1.11.2 h1:hIw75FpwcAjgeyfIGFqivAvwC5uNIOWRGvQgZhH4mhg= github.com/twmb/franz-go/pkg/kmsg v1.11.2/go.mod h1:CFfkkLysDNmukPYhGzuUcDtf46gQSqCZHMW1T4Z+wDE= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQsBgUlc= +github.com/woodsbury/decimal128 v1.4.0/go.mod h1:BP46FUrVjVhdTbKT+XuQh2xfQaGki9LMIRJSFuh6THU= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= @@ -185,16 +189,16 @@ go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwE golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -208,22 +212,22 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/test-dataset/dummyconsumer/Dockerfile b/test-dataset/dummyconsumer/Dockerfile index f6bab81..b38de21 100644 --- a/test-dataset/dummyconsumer/Dockerfile +++ b/test-dataset/dummyconsumer/Dockerfile @@ -2,7 +2,7 @@ FROM golang:1.24 AS builder WORKDIR /app COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -o myapp +RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o myapp FROM alpine:3.14 diff --git a/test-dataset/dummyconsumer/go.mod b/test-dataset/dummyconsumer/go.mod index 43e670b..f97e194 100644 --- a/test-dataset/dummyconsumer/go.mod +++ b/test-dataset/dummyconsumer/go.mod @@ -3,15 +3,16 @@ module github.com/steadybit/extension-kafka/dummyconsummer go 1.24 require ( - github.com/rs/zerolog v1.33.0 - github.com/twmb/franz-go v1.18.0 + github.com/rs/zerolog v1.34.0 + github.com/twmb/franz-go v1.19.5 ) require ( - github.com/klauspost/compress v1.17.8 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect - github.com/twmb/franz-go/pkg/kmsg v1.9.0 // indirect - golang.org/x/sys v0.12.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/twmb/franz-go/pkg/kmsg v1.11.2 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/sys v0.35.0 // indirect ) diff --git a/test-dataset/dummyconsumer/go.sum b/test-dataset/dummyconsumer/go.sum index e50ea26..f608c93 100644 --- a/test-dataset/dummyconsumer/go.sum +++ b/test-dataset/dummyconsumer/go.sum @@ -1,25 +1,28 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/twmb/franz-go v1.18.0 h1:25FjMZfdozBywVX+5xrWC2W+W76i0xykKjTdEeD2ejw= -github.com/twmb/franz-go v1.18.0/go.mod h1:zXCGy74M0p5FbXsLeASdyvfLFsBvTubVqctIaa5wQ+I= -github.com/twmb/franz-go/pkg/kmsg v1.9.0 h1:JojYUph2TKAau6SBtErXpXGC7E3gg4vGZMv9xFU/B6M= -github.com/twmb/franz-go/pkg/kmsg v1.9.0/go.mod h1:CMbfazviCyY6HM0SXuG5t9vOwYDHRCSrJJyBAe5paqg= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/twmb/franz-go v1.19.5 h1:W7+o8D0RsQsedqib71OVlLeZ0zI6CbFra7yTYhZTs5Y= +github.com/twmb/franz-go v1.19.5/go.mod h1:4kFJ5tmbbl7asgwAGVuyG1ZMx0NNpYk7EqflvWfPCpM= +github.com/twmb/franz-go/pkg/kmsg v1.11.2 h1:hIw75FpwcAjgeyfIGFqivAvwC5uNIOWRGvQgZhH4mhg= +github.com/twmb/franz-go/pkg/kmsg v1.11.2/go.mod h1:CFfkkLysDNmukPYhGzuUcDtf46gQSqCZHMW1T4Z+wDE= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= diff --git a/test-dataset/dummyconsumer/main.go b/test-dataset/dummyconsumer/main.go index 34aab62..9ecaa5a 100644 --- a/test-dataset/dummyconsumer/main.go +++ b/test-dataset/dummyconsumer/main.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/rs/zerolog/log" "github.com/twmb/franz-go/pkg/kgo" - "github.com/twmb/franz-go/pkg/sasl/plain" + "github.com/twmb/franz-go/pkg/sasl/scram" "os" "strings" ) @@ -43,10 +43,10 @@ func main() { kgo.SeedBrokers(strings.Split(seeds, ",")...), kgo.ConsumerGroup(consumer), kgo.ConsumeTopics(topic), - kgo.SASL(plain.Auth{ + kgo.SASL(scram.Auth{ User: saslUser, Pass: saslPassword, - }.AsMechanism()), + }.AsSha512Mechanism()), ) log.Info().Msgf("Initiating consumer with kafka config: brokers %s, consumer name %s on topic %s with user %s", seeds, consumer, topic, saslUser) if err != nil {