diff --git a/cmd/botkube/main.go b/cmd/botkube/main.go index 4fe239779b..45a0bb0a3d 100644 --- a/cmd/botkube/main.go +++ b/cmd/botkube/main.go @@ -74,7 +74,6 @@ func run(ctx context.Context) error { // Load configuration intconfig.RegisterFlags(pflag.CommandLine) - // TODO: Try to clean it up somehow remoteCfgSyncEnabled := graphql.IsRemoteConfigEnabled() gqlClient := graphql.NewDefaultGqlClient() deployClient := intconfig.NewDeploymentClient(gqlClient) diff --git a/go.mod b/go.mod index 33dc0c2b99..c5a4ecaa5a 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,6 @@ require ( k8s.io/kubectl v0.25.4 k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 sigs.k8s.io/controller-runtime v0.13.1 - sigs.k8s.io/yaml v1.3.0 ) require ( @@ -196,6 +195,7 @@ require ( sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) go 1.19 diff --git a/internal/config/env_provider.go b/internal/config/env_provider.go index 788452fab0..4cd358c571 100644 --- a/internal/config/env_provider.go +++ b/internal/config/env_provider.go @@ -2,9 +2,10 @@ package config import ( "context" - "github.com/kubeshop/botkube/pkg/config" "os" "strings" + + "github.com/kubeshop/botkube/pkg/config" ) const ( diff --git a/internal/config/env_provider_test.go b/internal/config/env_provider_test.go index 46e9121156..3787846df4 100644 --- a/internal/config/env_provider_test.go +++ b/internal/config/env_provider_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestEnvProviderSuccess(t *testing.T) { @@ -14,19 +15,20 @@ func TestEnvProviderSuccess(t *testing.T) { // when p := NewEnvProvider() - configs, err := p.Configs(context.Background()) + configs, cfgVer, err := p.Configs(context.Background()) // then - assert.NoError(t, err) + require.NoError(t, err) content, err := os.ReadFile("testdata/TestEnvProviderSuccess/config.yaml") assert.NoError(t, err) assert.Equal(t, content, configs[0]) + assert.Equal(t, cfgVer, 0) } func TestEnvProviderErr(t *testing.T) { // when p := NewEnvProvider() - _, err := p.Configs(context.Background()) + _, _, err := p.Configs(context.Background()) // then assert.Equal(t, "while reading a file: read .: is a directory", err.Error()) diff --git a/internal/config/fs_provider.go b/internal/config/fs_provider.go index dab0ef8663..73b929012c 100644 --- a/internal/config/fs_provider.go +++ b/internal/config/fs_provider.go @@ -3,10 +3,11 @@ package config import ( "context" "fmt" - "github.com/kubeshop/botkube/pkg/config" "os" "path/filepath" "strings" + + "github.com/kubeshop/botkube/pkg/config" ) const specialConfigFileNamePrefix = "_" diff --git a/internal/config/fs_provider_test.go b/internal/config/fs_provider_test.go index 8fe2667f72..51c476638d 100644 --- a/internal/config/fs_provider_test.go +++ b/internal/config/fs_provider_test.go @@ -6,18 +6,20 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestStaticProviderSuccess(t *testing.T) { // when p := NewFileSystemProvider([]string{"testdata/TestStaticProviderSuccess/config.yaml"}) - configs, err := p.Configs(context.Background()) + configs, cfgVer, err := p.Configs(context.Background()) // then - assert.NoError(t, err) + require.NoError(t, err) content, err := os.ReadFile("testdata/TestStaticProviderSuccess/config.yaml") assert.NoError(t, err) assert.Equal(t, content, configs[0]) + assert.Equal(t, cfgVer, 0) } func TestSortCfgFiles(t *testing.T) { diff --git a/internal/config/gql_client.go b/internal/config/gql_client.go index 20960b1670..7a3f224166 100644 --- a/internal/config/gql_client.go +++ b/internal/config/gql_client.go @@ -28,7 +28,7 @@ func NewDeploymentClient(client *gql.Gql) *Gql { // Deployment returns deployment with Botkube configuration. type Deployment struct { ResourceVersion int - YAMLConfig string + YAMLConfig string } // GetDeployment retrieves deployment by id. diff --git a/internal/config/gql_client_test.go b/internal/config/gql_client_test.go index b006a45c16..f8742aa38f 100644 --- a/internal/config/gql_client_test.go +++ b/internal/config/gql_client_test.go @@ -16,7 +16,7 @@ import ( ) func TestGql_GetDeployment(t *testing.T) { - expectedBody := fmt.Sprintf(`{"query":"query ($id:ID!){deployment(id: $id){botkubeConfig}}","variables":{"id":"my-id"}}%s`, "\n") + expectedBody := fmt.Sprintf(`{"query":"query ($id:ID!){deployment(id: $id){resourceVersion,yamlConfig}}","variables":{"id":"my-id"}}%s`, "\n") file, err := os.ReadFile("testdata/gql_get_deployment_success.json") assert.NoError(t, err) svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -51,5 +51,5 @@ func TestGql_GetDeployment(t *testing.T) { g := NewDeploymentClient(gqlClient) deployment, err := g.GetDeployment(context.Background()) assert.NoError(t, err) - assert.NotNil(t, deployment.BotkubeConfig) + assert.NotNil(t, deployment.YAMLConfig) } diff --git a/internal/config/gql_provider.go b/internal/config/gql_provider.go index d1291e338c..dc3f7ce687 100644 --- a/internal/config/gql_provider.go +++ b/internal/config/gql_provider.go @@ -2,9 +2,10 @@ package config import ( "context" - "github.com/kubeshop/botkube/pkg/config" "github.com/pkg/errors" + + "github.com/kubeshop/botkube/pkg/config" ) // GqlProvider is GraphQL provider diff --git a/internal/config/gql_provider_test.go b/internal/config/gql_provider_test.go index 29af20b82b..b49b249c1a 100644 --- a/internal/config/gql_provider_test.go +++ b/internal/config/gql_provider_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var _ DeploymentClient = &fakeGqlClient{} @@ -13,9 +14,10 @@ var _ DeploymentClient = &fakeGqlClient{} type fakeGqlClient struct { } -func (f *fakeGqlClient) GetDeployment(ctx context.Context) (Deployment, error) { +func (f *fakeGqlClient) GetDeployment(context.Context) (Deployment, error) { return Deployment{ - BotkubeConfig: "{\"settings\":{\"clusterName\":\"qa\"},\"sources\":{\"kubernetes-info\":{\"displayName\":\"Kubernetes Information\",\"kubernetes\":{\"recommendations\":{\"pod\":{\"noLatestImageTag\":true,\"labelsSet\":true},\"ingress\":{\"backendServiceValid\":true,\"tlsSecretValid\":true}}}}},\"executors\":{\"kubectl-read-only\":{\"kubectl\":{\"namespaces\":{\"include\":[\".*\"]},\"enabled\":true,\"commands\":{\"verbs\":[\"api-resources\",\"api-versions\",\"cluster-info\",\"describe\",\"diff\",\"explain\",\"get\",\"logs\",\"top\",\"auth\"],\"resources\":[\"deployments\",\"pods\",\"namespaces\",\"daemonsets\",\"statefulsets\",\"storageclasses\",\"nodes\"]},\"defaultNamespace\":\"default\",\"restrictAccess\":false}}},\"communications\":{\"default-group\":{\"socketSlack\":{\"enabled\":true,\"channels\":{\"botkube-demo\":{\"name\":\"botkube-demo\",\"notification\":{\"disabled\":false},\"bindings\":{\"sources\":[\"kubernetes-info\"],\"executors\":[\"kubectl-read-only\"]}}},\"botToken\":\"xoxb-3933899240838\",\"appToken\":\"xapp-1-A047D1ZJ03B-4262138376928\"}}}}", + ResourceVersion: 3, + YAMLConfig: "communications:\n default-group:\n socketSlack:\n appToken: xapp-1-A047D1ZJ03B-4262138376928\n botToken: xoxb-3933899240838\n channels:\n botkube-demo:\n bindings:\n executors:\n - kubectl-read-only\n sources:\n - kubernetes-info\n name: botkube-demo\n notification:\n disabled: false\n enabled: true\nexecutors:\n kubectl-read-only:\n kubectl:\n commands:\n resources:\n - deployments\n - pods\n - namespaces\n - daemonsets\n - statefulsets\n - storageclasses\n - nodes\n verbs:\n - api-resources\n - api-versions\n - cluster-info\n - describe\n - diff\n - explain\n - get\n - logs\n - top\n - auth\n defaultNamespace: default\n enabled: true\n namespaces:\n include:\n - .*\n restrictAccess: false\nsettings:\n clusterName: qa\nsources:\n kubernetes-info:\n displayName: Kubernetes Information\n kubernetes:\n recommendations:\n ingress:\n backendServiceValid: true\n tlsSecretValid: true\n pod:\n labelsSet: true\n noLatestImageTag: true\n", }, nil } @@ -23,13 +25,14 @@ func TestGqlProviderSuccess(t *testing.T) { //given f := fakeGqlClient{} p := NewGqlProvider(&f) + expected, err := os.ReadFile("testdata/TestGqlProviderSuccess/config.yaml") + require.NoError(t, err) // when - configs, err := p.Configs(context.Background()) + configs, ver, err := p.Configs(context.Background()) // then assert.NoError(t, err) - content, err := os.ReadFile("testdata/TestGqlProviderSuccess/config.yaml") - assert.NoError(t, err) - assert.Equal(t, configs[0], content) + assert.Equal(t, string(expected), string(configs[0])) + assert.Equal(t, 3, ver) } diff --git a/internal/config/provider.go b/internal/config/provider.go index 2fd684f5da..b063c7dd29 100644 --- a/internal/config/provider.go +++ b/internal/config/provider.go @@ -1,8 +1,9 @@ package config import ( - "github.com/kubeshop/botkube/pkg/config" "os" + + "github.com/kubeshop/botkube/pkg/config" ) // GetProvider resolves and returns paths for config files. @@ -18,4 +19,3 @@ func GetProvider(remoteCfgSyncEnabled bool, deployClient DeploymentClient) confi return NewFileSystemProvider(configPathsFlag) } - diff --git a/internal/config/provider_test.go b/internal/config/provider_test.go new file mode 100644 index 0000000000..8d4eb06665 --- /dev/null +++ b/internal/config/provider_test.go @@ -0,0 +1,69 @@ +package config_test + +import ( + "context" + "os" + "testing" + + "github.com/spf13/pflag" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/kubeshop/botkube/internal/config" +) + +func TestGetProvider(t *testing.T) { + t.Run("from envs variable only", func(t *testing.T) { + // given + t.Setenv("BOTKUBE_CONFIG_PATHS", "testdata/TestGetProvider/first.yaml,testdata/TestGetProvider/second.yaml,testdata/TestGetProvider/third.yaml") + + // when + provider := config.GetProvider(false, nil) + gotConfigs, _, err := provider.Configs(context.Background()) + assert.NoError(t, err) + + // then + c, err := os.ReadFile("testdata/TestGetProvider/all.yaml") + assert.NoError(t, err) + assert.Equal(t, c, gotConfigs.Merge()) + }) + + t.Run("from CLI flag only", func(t *testing.T) { + // given + fSet := pflag.NewFlagSet("testing", pflag.ContinueOnError) + config.RegisterFlags(fSet) + err := fSet.Parse([]string{"--config=testdata/TestGetProvider/first.yaml,testdata/TestGetProvider/second.yaml", "--config", "testdata/TestGetProvider/third.yaml"}) + require.NoError(t, err) + + // when + provider := config.GetProvider(false, nil) + gotConfigs, _, err := provider.Configs(context.Background()) + assert.NoError(t, err) + + // then + c, err := os.ReadFile("testdata/TestGetProvider/all.yaml") + assert.NoError(t, err) + assert.Equal(t, c, gotConfigs.Merge()) + }) + + t.Run("should honor env variable over the CLI flag", func(t *testing.T) { + // given + fSet := pflag.NewFlagSet("testing", pflag.ContinueOnError) + config.RegisterFlags(fSet) + + err := fSet.Parse([]string{"--config=testdata/TestGetProvider/from-cli-flag.yaml,testdata/TestGetProvider/from-cli-flag-second.yaml"}) + require.NoError(t, err) + + t.Setenv("BOTKUBE_CONFIG_PATHS", "testdata/TestGetProvider/first.yaml,testdata/TestGetProvider/second.yaml,testdata/TestGetProvider/third.yaml") + + // when + provider := config.GetProvider(false, nil) + gotConfigs, _, err := provider.Configs(context.Background()) + assert.NoError(t, err) + + // then + c, err := os.ReadFile("testdata/TestGetProvider/all.yaml") + assert.NoError(t, err) + assert.Equal(t, c, gotConfigs.Merge()) + }) +} diff --git a/pkg/config/testdata/TestFromProvider/all.yaml b/internal/config/testdata/TestGetProvider/all.yaml similarity index 100% rename from pkg/config/testdata/TestFromProvider/all.yaml rename to internal/config/testdata/TestGetProvider/all.yaml diff --git a/pkg/config/testdata/TestFromProvider/first.yaml b/internal/config/testdata/TestGetProvider/first.yaml similarity index 100% rename from pkg/config/testdata/TestFromProvider/first.yaml rename to internal/config/testdata/TestGetProvider/first.yaml diff --git a/pkg/config/testdata/TestFromProvider/from-cli-flag-second.yaml b/internal/config/testdata/TestGetProvider/from-cli-flag-second.yaml similarity index 100% rename from pkg/config/testdata/TestFromProvider/from-cli-flag-second.yaml rename to internal/config/testdata/TestGetProvider/from-cli-flag-second.yaml diff --git a/pkg/config/testdata/TestFromProvider/from-cli-flag.yaml b/internal/config/testdata/TestGetProvider/from-cli-flag.yaml similarity index 100% rename from pkg/config/testdata/TestFromProvider/from-cli-flag.yaml rename to internal/config/testdata/TestGetProvider/from-cli-flag.yaml diff --git a/pkg/config/testdata/TestFromProvider/second.yaml b/internal/config/testdata/TestGetProvider/second.yaml similarity index 100% rename from pkg/config/testdata/TestFromProvider/second.yaml rename to internal/config/testdata/TestGetProvider/second.yaml diff --git a/pkg/config/testdata/TestFromProvider/third.yaml b/internal/config/testdata/TestGetProvider/third.yaml similarity index 100% rename from pkg/config/testdata/TestFromProvider/third.yaml rename to internal/config/testdata/TestGetProvider/third.yaml diff --git a/internal/config/testdata/gql_get_deployment_success.json b/internal/config/testdata/gql_get_deployment_success.json index 8025402a6c..039d89dc89 100644 --- a/internal/config/testdata/gql_get_deployment_success.json +++ b/internal/config/testdata/gql_get_deployment_success.json @@ -1,7 +1,8 @@ { "data": { "deployment": { - "botkubeConfig": "{\"settings\":{\"clusterName\":\"qa\"},\"sources\":{\"kubernetes-info\":{\"displayName\":\"Kubernetes Information\",\"kubernetes\":{\"recommendations\":{\"pod\":{\"noLatestImageTag\":true,\"labelsSet\":true},\"ingress\":{\"backendServiceValid\":true,\"tlsSecretValid\":true}}}}},\"executors\":{\"kubectl-read-only\":{\"kubectl\":{\"namespaces\":{\"include\":[\".*\"]},\"enabled\":true,\"commands\":{\"verbs\":[\"api-resources\",\"api-versions\",\"cluster-info\",\"describe\",\"diff\",\"explain\",\"get\",\"logs\",\"top\",\"auth\"],\"resources\":[\"deployments\",\"pods\",\"namespaces\",\"daemonsets\",\"statefulsets\",\"storageclasses\",\"nodes\"]},\"defaultNamespace\":\"default\",\"restrictAccess\":false}}},\"communications\":{\"default-group\":{\"socketSlack\":{\"enabled\":true,\"channels\":{\"botkube-demo\":{\"name\":\"botkube-demo\",\"notification\":{\"disabled\":false},\"bindings\":{\"sources\":[\"kubernetes-info\"],\"executors\":[\"kubectl-read-only\"]}}},\"botToken\":\"xoxb-3933899240838-4238355342083-2rUu0vkKDECUj27qECYPCqGd\",\"appToken\":\"xapp-1-A047D1ZJ03B-4262138376928-33d80c4792bb44c189b4f2948e0a00351316b3f694332fdec1ecdc61046320ab\"}}}}" + "resourceVersion": 3, + "yamlConfig": "actions: {}\\nsources:\\n botkube/kubernetes_TckMy:\\n displayName: Kubernetes\\n kubernetes:\\n recommendations:\\n ingress: {}\\n pod: {}\\n event:\\n reason:\\n include: []\\n message:\\n include: []\\n types: []\\n resources: []\\n namespaces:\\n include: []\\n annotations: {}\\n labels: {}\\n botkube/kubernetes:\\n enabled: true\\n config:\\n event:\\n types:\\n - create\\n - delete\\n - error\\n namespaces:\\n include:\\n - .*\\n recommendations:\\n ingress:\\n backendServiceValid: false\\n tlsSecretValid: false\\n resources:\\n - type: v1/services\\n context:\\n rbac:\\n user:\\n type: \\\"\\\"\\n static:\\n value: \\\"\\\"\\n prefix: \\\"\\\"\\n group:\\n type: \\\"\\\"\\n static:\\n values: []\\n prefix: \\\"\\\"\\n botkube/kubernetes_ig8kh:\\n displayName: Kubernetes\\n kubernetes:\\n recommendations:\\n ingress: {}\\n pod: {}\\n event:\\n reason:\\n include: []\\n message:\\n include: []\\n types: []\\n resources: []\\n namespaces:\\n include: []\\n annotations: {}\\n labels: {}\\n botkube/kubernetes:\\n enabled: true\\n config:\\n event:\\n types:\\n - create\\n - delete\\n - error\\n namespaces:\\n include:\\n - .*\\n recommendations:\\n ingress:\\n backendServiceValid: true\\n tlsSecretValid: true\\n resources:\\n - type: v1/configmaps\\n context:\\n rbac:\\n user:\\n type: \\\"\\\"\\n static:\\n value: \\\"\\\"\\n prefix: \\\"\\\"\\n group:\\n type: \\\"\\\"\\n static:\\n values: []\\n prefix: \\\"\\\"\\n botkube/kubernetes_rO7iL:\\n displayName: Kubernetes\\n kubernetes:\\n recommendations:\\n ingress: {}\\n pod: {}\\n event:\\n reason:\\n include: []\\n message:\\n include: []\\n types: []\\n resources: []\\n namespaces:\\n include: []\\n annotations: {}\\n labels: {}\\n botkube/kubernetes:\\n enabled: true\\n config:\\n event:\\n types:\\n - create\\n - delete\\n - error\\n namespaces:\\n include:\\n - .*\\n recommendations:\\n ingress:\\n backendServiceValid: true\\n tlsSecretValid: true\\n pod:\\n labelsSet: true\\n noLatestImageTag: true\\n resources:\\n - type: v1/pods\\n context:\\n rbac:\\n user:\\n type: \\\"\\\"\\n static:\\n value: \\\"\\\"\\n prefix: \\\"\\\"\\n group:\\n type: \\\"\\\"\\n static:\\n values: []\\n prefix: \\\"\\\"\\n botkube/kubernetes_sA8UA:\\n displayName: Kubernetes\\n kubernetes:\\n recommendations:\\n ingress: {}\\n pod: {}\\n event:\\n reason:\\n include: []\\n message:\\n include: []\\n types: []\\n resources: []\\n namespaces:\\n include: []\\n annotations: {}\\n labels: {}\\n botkube/kubernetes:\\n enabled: true\\n config:\\n event:\\n types:\\n - create\\n - delete\\n - error\\n namespaces:\\n include:\\n - .*\\n recommendations:\\n ingress:\\n backendServiceValid: false\\n tlsSecretValid: false\\n resources:\\n - type: apps/v1/deployments\\n context:\\n rbac:\\n user:\\n type: \\\"\\\"\\n static:\\n value: \\\"\\\"\\n prefix: \\\"\\\"\\n group:\\n type: \\\"\\\"\\n static:\\n values: []\\n prefix: \\\"\\\"\\nexecutors:\\n botkube/helm_gejC3:\\n kubectl:\\n enabled: false\\n botkube/helm:\\n enabled: true\\n config:\\n helmDriver: secret\\n context:\\n rbac:\\n user:\\n type: \\\"\\\"\\n static:\\n value: \\\"\\\"\\n prefix: \\\"\\\"\\n group:\\n type: \\\"\\\"\\n static:\\n values: []\\n prefix: \\\"\\\"\\n botkube/kubectl_QYWyO:\\n kubectl:\\n enabled: false\\n botkube/kubectl:\\n enabled: true\\n config:\\n defaultNamespace: default\\n context:\\n rbac:\\n user:\\n type: \\\"\\\"\\n static:\\n value: \\\"\\\"\\n prefix: \\\"\\\"\\n group:\\n type: \\\"\\\"\\n static:\\n values: []\\n prefix: \\\"\\\"\\naliases:\\n k:\\n command: kubectl\\n displayName: Kubectl alias\\n kc:\\n command: kubectl\\n displayName: Kubectl alias\\ncommunications:\\n group-0:\\n slack:\\n enabled: false\\n channels: {}\\n socketSlack:\\n enabled: true\\n channels:\\n 0e61e4a6-fc4b-4c74-9ac1-a4d901827242:\\n name: priv-channel\\n notification:\\n disabled: false\\n bindings:\\n sources:\\n - botkube/kubernetes_rO7iL\\n - botkube/kubernetes_sA8UA\\n executors:\\n - botkube/helm_gejC3\\n - botkube/kubectl_QYWyO\\n 5cf223ad-acaf-498e-b804-af8f071c0e86:\\n name: botkube-demo\\n notification:\\n disabled: false\\n bindings:\\n sources:\\n - botkube/kubernetes_rO7iL\\n - botkube/kubernetes_ig8kh\\n - botkube/kubernetes_TckMy\\n - botkube/kubernetes_sA8UA\\n executors:\\n - botkube/helm_gejC3\\n - botkube/kubectl_QYWyO\\n botToken: xoxb-3774107759943-4092891006434-aTQweggWeP0tKw7xtLo5MfzJ\\n appToken: xapp-1-A042J7Y9Z0E-4130500921351-02566a901b6d8b02800d3818f911fe7903b376b74b2c42bda9505580f731adc7\\n mattermost:\\n enabled: false\\n botName: \\\"\\\"\\n url: \\\"\\\"\\n token: \\\"\\\"\\n team: \\\"\\\"\\n channels: {}\\n discord:\\n enabled: true\\n token: OTc2Nzg2NzIyNzA2ODIxMTIw.GT8tb4.Z3UZzbEdj7pcBZxBNOHgb1U6A2MyBgt7or52_Q\\n botID: \\\"976786722706821120\\\"\\n channels:\\n db8aa8c1-5087-4a92-ab3b-588110c2ab22:\\n id: \\\"976789916447019068\\\"\\n notification:\\n disabled: false\\n bindings:\\n sources:\\n - botkube/kubernetes_rO7iL\\n - botkube/kubernetes_ig8kh\\n - botkube/kubernetes_TckMy\\n - botkube/kubernetes_sA8UA\\n executors:\\n - botkube/helm_gejC3\\n - botkube/kubectl_QYWyO\\n teams:\\n enabled: false\\n port: \\\"\\\"\\n bindings:\\n sources: []\\n executors: []\\n webhook:\\n enabled: false\\n url: \\\"\\\"\\n bindings:\\n sources: []\\n elasticsearch:\\n enabled: false\\n username: \\\"\\\"\\n password: \\\"\\\"\\n server: \\\"\\\"\\n skipTLSVerify: false\\n awsSigning:\\n enabled: false\\n awsRegion: \\\"\\\"\\n roleArn: \\\"\\\"\\n indices: {}\\n group-1:\\n slack:\\n enabled: false\\n channels: {}\\n socketSlack:\\n enabled: true\\n channels:\\n 3f732335-171b-4be0-9c20-51bf2bb08dc0:\\n name: priv-channel\\n notification:\\n disabled: false\\n bindings:\\n sources:\\n - botkube/kubernetes_rO7iL\\n - botkube/kubernetes_sA8UA\\n executors:\\n - botkube/helm_gejC3\\n - botkube/kubectl_QYWyO\\n ec853107-870f-4ecd-a698-2887721869e1:\\n name: botkube-demo\\n notification:\\n disabled: false\\n bindings:\\n sources:\\n - botkube/kubernetes_rO7iL\\n - botkube/kubernetes_ig8kh\\n - botkube/kubernetes_TckMy\\n - botkube/kubernetes_sA8UA\\n executors:\\n - botkube/helm_gejC3\\n - botkube/kubectl_QYWyO\\n botToken: xoxb-\\n appToken: xapp-\\n mattermost:\\n enabled: false\\n botName: \\\"\\\"\\n url: \\\"\\\"\\n token: \\\"\\\"\\n team: \\\"\\\"\\n channels: {}\\n discord:\\n enabled: true\\n token: token\\n botID: \\\"\\\"\\n channels:\\n fa3465e8-9ecb-4e9e-985f-dd76bb5593f7:\\n id: \\\"id1\\\"\\n notification:\\n disabled: false\\n bindings:\\n sources:\\n - botkube/kubernetes_rO7iL\\n - botkube/kubernetes_ig8kh\\n - botkube/kubernetes_TckMy\\n - botkube/kubernetes_sA8UA\\n executors:\\n - botkube/helm_gejC3\\n - botkube/kubectl_QYWyO\\n teams:\\n enabled: false\\n port: \\\"\\\"\\n bindings:\\n sources: []\\n executors: []\\n webhook:\\n enabled: false\\n url: \\\"\\\"\\n bindings:\\n sources: []\\n elasticsearch:\\n enabled: false\\n username: \\\"\\\"\\n password: \\\"\\\"\\n server: \\\"\\\"\\n skipTLSVerify: false\\n awsSigning:\\n enabled: false\\n awsRegion: \\\"\\\"\\n roleArn: \\\"\\\"\\n indices: {}\\nfilters:\\n kubernetes:\\n objectAnnotationChecker: false\\n nodeEventsChecker: false\\nanalytics:\\n disable: false\\nsettings:\\n clusterName: test\\n upgradeNotifier: true\\n systemConfigMap: {}\\n persistentConfig:\\n startup:\\n fileName: \\\"\\\"\\n configMap: {}\\n runtime:\\n fileName: \\\"\\\"\\n configMap: {}\\n metricsPort: \\\"2112\\\"\\n healthPort: \\\"2114\\\"\\n lifecycleServer:\\n enabled: false\\n port: 0\\n deployment: {}\\n log:\\n level: debug\\n disableColors: false\\n informersResyncPeriod: 0s\\n kubeconfig: ~/.kube/config\\nconfigWatcher:\\n enabled: true\\n initialSyncTimeout: 0s\\n tmpDir: \\\"\\\"\\nplugins:\\n cacheDir: /tmp\\n repositories:\\n botkube:\\n url: http://localhost:8080/plugins-index.yaml\\n" } } } diff --git a/internal/config/updater.go b/internal/config/updater.go index c28905fb20..4feff89795 100644 --- a/internal/config/updater.go +++ b/internal/config/updater.go @@ -3,11 +3,12 @@ package config import ( "context" "fmt" - "github.com/kubeshop/botkube/pkg/config" + "time" + "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" - "time" + "github.com/kubeshop/botkube/pkg/config" ) type ConfigUpdater interface { @@ -18,36 +19,34 @@ type ResourceVersionHolder interface { SetResourceVersion(int) } - func GetConfigUpdater(remoteCfgEnabled bool, log logrus.FieldLogger, interval time.Duration, deployCli DeploymentClient, resVerHolders ...ResourceVersionHolder) ConfigUpdater { if remoteCfgEnabled { return NewConfigUpdater(log, interval, deployCli, resVerHolders...) } - return nil + return &noopConfigUpdater{} } func NewConfigUpdater(log logrus.FieldLogger, interval time.Duration, deployCli DeploymentClient, resVerHolders ...ResourceVersionHolder) ConfigUpdater { return &GraphQLConfigUpdater{ - log: log, - interval: interval, - deployCli: deployCli, + log: log, + interval: interval, + deployCli: deployCli, resVerHolders: resVerHolders, } } type GraphQLConfigUpdater struct { - log logrus.FieldLogger - interval time.Duration + log logrus.FieldLogger + interval time.Duration resVerHolders []ResourceVersionHolder - latestCfg config.Config + latestCfg config.Config resVersion int deployCli DeploymentClient } - func (u *GraphQLConfigUpdater) Do(ctx context.Context) error { u.log.Info("Starting...") @@ -60,7 +59,7 @@ func (u *GraphQLConfigUpdater) Do(ctx context.Context) error { u.log.Info("Shutdown requested. Finishing...") return nil case <-ticker.C: - + u.log.Debug("Querying the latest configuration...") // Check periodically cfg, resVer, err := u.queryConfig(ctx) if err != nil { @@ -70,12 +69,12 @@ func (u *GraphQLConfigUpdater) Do(ctx context.Context) error { u.latestCfg = cfg u.setResourceVersionForAll(resVer) + u.log.Debugf("Successfully set config version %d.", resVer) } } } func (u *GraphQLConfigUpdater) queryConfig(ctx context.Context) (config.Config, int, error) { - deploy, err := u.deployCli.GetDeployment(ctx) if err != nil { return config.Config{}, 0, fmt.Errorf("while getting deployment: %w", err) @@ -96,3 +95,9 @@ func (u *GraphQLConfigUpdater) setResourceVersionForAll(resVersion int) { h.SetResourceVersion(u.resVersion) } } + +type noopConfigUpdater struct{} + +func (u *noopConfigUpdater) Do(ctx context.Context) error { + return nil +} diff --git a/internal/executor/kubectl/config.go b/internal/executor/kubectl/config.go index dd19020681..c2ce85bbfb 100644 --- a/internal/executor/kubectl/config.go +++ b/internal/executor/kubectl/config.go @@ -2,7 +2,6 @@ package kubectl import ( "fmt" - "github.com/kubeshop/botkube/pkg/config" "github.com/MakeNowJust/heredoc" "k8s.io/utils/strings/slices" @@ -10,13 +9,14 @@ import ( "github.com/kubeshop/botkube/internal/executor/kubectl/builder" "github.com/kubeshop/botkube/pkg/api" "github.com/kubeshop/botkube/pkg/api/executor" + "github.com/kubeshop/botkube/pkg/config" "github.com/kubeshop/botkube/pkg/pluginx" ) // Config holds Kubectl plugin configuration parameters. type Config struct { - Log config.Logger `yaml:"log"` - DefaultNamespace string `yaml:"defaultNamespace,omitempty"` + Log config.Logger `yaml:"log"` + DefaultNamespace string `yaml:"defaultNamespace,omitempty"` InteractiveBuilder builder.Config `yaml:"interactiveBuilder,omitempty"` } diff --git a/internal/loggerx/logger.go b/internal/loggerx/logger.go index 7c9d6669e3..474d5b1eeb 100644 --- a/internal/loggerx/logger.go +++ b/internal/loggerx/logger.go @@ -1,10 +1,11 @@ package loggerx import ( - "github.com/kubeshop/botkube/pkg/config" "os" "github.com/sirupsen/logrus" + + "github.com/kubeshop/botkube/pkg/config" ) // New returns a new logger based on a given configuration. diff --git a/internal/plugin/logger.go b/internal/plugin/logger.go index 4f21d99e25..b6b345e56a 100644 --- a/internal/plugin/logger.go +++ b/internal/plugin/logger.go @@ -2,7 +2,6 @@ package plugin import ( "fmt" - "github.com/kubeshop/botkube/pkg/config" "io" "os" "regexp" @@ -12,6 +11,7 @@ import ( "github.com/sirupsen/logrus" "github.com/kubeshop/botkube/internal/loggerx" + "github.com/kubeshop/botkube/pkg/config" ) var specialCharsPattern = regexp.MustCompile(`(?i:[^A-Z0-9_])`) diff --git a/internal/source/kubernetes/source.go b/internal/source/kubernetes/source.go index 43d68686fc..4b2345d2b0 100644 --- a/internal/source/kubernetes/source.go +++ b/internal/source/kubernetes/source.go @@ -24,6 +24,7 @@ import ( "github.com/kubeshop/botkube/pkg/api" "github.com/kubeshop/botkube/pkg/api/source" "github.com/kubeshop/botkube/pkg/bot/interactive" + pkgConfig "github.com/kubeshop/botkube/pkg/config" ) const ( @@ -79,7 +80,7 @@ func (*Source) Stream(ctx context.Context, input source.StreamInput) (source.Str startTime: time.Now(), eventCh: make(chan source.Event), config: cfg, - logger: loggerx.New(loggerx.Config{ + logger: loggerx.New(pkgConfig.Logger{ Level: cfg.Log.Level, }), clusterName: input.Context.ClusterName, diff --git a/internal/source/prometheus/source.go b/internal/source/prometheus/source.go index b57dc5ec78..587e914afe 100644 --- a/internal/source/prometheus/source.go +++ b/internal/source/prometheus/source.go @@ -3,7 +3,6 @@ package prometheus import ( "context" "fmt" - "github.com/kubeshop/botkube/pkg/config" "time" "github.com/MakeNowJust/heredoc" @@ -12,6 +11,7 @@ import ( "github.com/kubeshop/botkube/internal/loggerx" "github.com/kubeshop/botkube/pkg/api" "github.com/kubeshop/botkube/pkg/api/source" + "github.com/kubeshop/botkube/pkg/config" formatx "github.com/kubeshop/botkube/pkg/format" ) diff --git a/internal/source/scheduler_test.go b/internal/source/scheduler_test.go index 529865b977..e24db7e24c 100644 --- a/internal/source/scheduler_test.go +++ b/internal/source/scheduler_test.go @@ -11,7 +11,6 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" - intConfig "github.com/kubeshop/botkube/internal/config" "github.com/kubeshop/botkube/internal/loggerx" "github.com/kubeshop/botkube/pkg/api/source" "github.com/kubeshop/botkube/pkg/config" @@ -19,7 +18,7 @@ import ( func TestStartingUniqueProcesses(t *testing.T) { // given - files := intConfig.YAMLFiles{ + files := config.YAMLFiles{ readTestdataFile(t, "config.yaml"), } givenCfg, _, err := config.LoadWithDefaults(files) diff --git a/internal/status/gql_reporter.go b/internal/status/gql_reporter.go index 826c8600b9..fb8923115d 100644 --- a/internal/status/gql_reporter.go +++ b/internal/status/gql_reporter.go @@ -13,30 +13,30 @@ import ( var _ StatusReporter = (*GraphQLStatusReporter)(nil) type GraphQLStatusReporter struct { - log logrus.FieldLogger - gql *gql.Gql + log logrus.FieldLogger + gql *gql.Gql resourceVersion int - resVerMutex sync.RWMutex + resVerMutex sync.RWMutex } func newGraphQLStatusReporter(logger logrus.FieldLogger, client *gql.Gql, cfgVersion int) *GraphQLStatusReporter { return &GraphQLStatusReporter{ - log: logger, - gql: client, + log: logger, + gql: client, resourceVersion: cfgVersion, } } func (r *GraphQLStatusReporter) ReportDeploymentStartup(ctx context.Context) (bool, error) { r.log.WithFields(logrus.Fields{ - "deploymentID": r.gql.DeploymentID, - "resourceVersion": r.getResourceVersion(), + "deploymentID": r.gql.DeploymentID, + "resourceVersion": r.getResourceVersion(), }).Debugf("Reporting deployment startup...") var mutation struct { Success bool `graphql:"reportDeploymentStartup(id: $id, resourceVersion: $resourceVersion)"` } variables := map[string]interface{}{ - "id": graphql.ID(r.gql.DeploymentID), + "id": graphql.ID(r.gql.DeploymentID), "resourceVersion": r.getResourceVersion(), } if err := r.gql.Cli.Mutate(ctx, &mutation, variables); err != nil { @@ -47,14 +47,14 @@ func (r *GraphQLStatusReporter) ReportDeploymentStartup(ctx context.Context) (bo func (r *GraphQLStatusReporter) ReportDeploymentShutdown(ctx context.Context) (bool, error) { r.log.WithFields(logrus.Fields{ - "deploymentID": r.gql.DeploymentID, + "deploymentID": r.gql.DeploymentID, "resourceVersion": r.getResourceVersion(), }).Debugf("Reporting deployment shutdown...") var mutation struct { Success bool `graphql:"reportDeploymentShutdown(id: $id, resourceVersion: $resourceVersion)"` } variables := map[string]interface{}{ - "id": graphql.ID(r.gql.DeploymentID), + "id": graphql.ID(r.gql.DeploymentID), "resourceVersion": r.getResourceVersion(), } if err := r.gql.Cli.Mutate(ctx, &mutation, variables); err != nil { @@ -65,14 +65,14 @@ func (r *GraphQLStatusReporter) ReportDeploymentShutdown(ctx context.Context) (b func (r *GraphQLStatusReporter) ReportDeploymentFailed(ctx context.Context) (bool, error) { r.log.WithFields(logrus.Fields{ - "deploymentID": r.gql.DeploymentID, + "deploymentID": r.gql.DeploymentID, "resourceVersion": r.getResourceVersion(), }).Debugf("Reporting deployment failure...") var mutation struct { Success bool `graphql:"reportDeploymentFailed(id: $id, resourceVersion: $resourceVersion)"` } variables := map[string]interface{}{ - "id": graphql.ID(r.gql.DeploymentID), + "id": graphql.ID(r.gql.DeploymentID), "resourceVersion": r.getResourceVersion(), } if err := r.gql.Cli.Mutate(ctx, &mutation, variables); err != nil { diff --git a/internal/status/noop_reporter.go b/internal/status/noop_reporter.go index 39fc1e6377..3e1d9abb92 100644 --- a/internal/status/noop_reporter.go +++ b/internal/status/noop_reporter.go @@ -6,7 +6,7 @@ import ( var _ StatusReporter = (*NoopStatusReporter)(nil) -type NoopStatusReporter struct {} +type NoopStatusReporter struct{} func newNoopStatusReporter() *NoopStatusReporter { return &NoopStatusReporter{} @@ -23,4 +23,4 @@ func (r *NoopStatusReporter) ReportDeploymentFailed(context.Context) (bool, erro return true, nil } -func (r *NoopStatusReporter) SetResourceVersion(int) { } +func (r *NoopStatusReporter) SetResourceVersion(int) {} diff --git a/pkg/config/config.go b/pkg/config/config.go index ec9f9ff495..e232ec76fa 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -772,7 +772,6 @@ func LoadWithDefaults(configs [][]byte) (*Config, LoadWithDefaultsDetails, error }, nil } - func normalizeConfigEnvName(name string) string { name = strings.TrimPrefix(name, configEnvVariablePrefix) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 5417032c27..1b6c7bc239 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -1,19 +1,16 @@ package config_test import ( - "context" "os" "path/filepath" "testing" "github.com/MakeNowJust/heredoc" - "github.com/spf13/pflag" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" "gotest.tools/v3/golden" - intConfig "github.com/kubeshop/botkube/internal/config" "github.com/kubeshop/botkube/pkg/config" ) @@ -31,7 +28,7 @@ func TestLoadConfigSuccess(t *testing.T) { t.Setenv("BOTKUBE_PLUGINS_REPOSITORIES_BOTKUBE_URL", "http://localhost:3000/botkube.yaml") // when - files := intConfig.YAMLFiles{ + files := config.YAMLFiles{ readTestdataFile(t, "config-all.yaml"), readTestdataFile(t, "config-global.yaml"), readTestdataFile(t, "config-slack-override.yaml"), @@ -72,7 +69,7 @@ func TestLoadConfigWithPlugins(t *testing.T) { }, } - files := intConfig.YAMLFiles{ + files := config.YAMLFiles{ readTestdataFile(t, "config-all.yaml"), } @@ -87,62 +84,6 @@ func TestLoadConfigWithPlugins(t *testing.T) { assert.Equal(t, expExecutorPlugin, gotCfg.Executors["plugin-based"].Plugins) } -func TestFromProvider(t *testing.T) { - t.Run("from envs variable only", func(t *testing.T) { - // given - t.Setenv("BOTKUBE_CONFIG_PATHS", "testdata/TestFromProvider/first.yaml,testdata/TestFromProvider/second.yaml,testdata/TestFromProvider/third.yaml") - - // when - provider := config.GetProvider(nil) - gotConfigs, err := provider.Configs(context.Background()) - assert.NoError(t, err) - - // then - c, err := os.ReadFile("testdata/TestFromProvider/all.yaml") - assert.NoError(t, err) - assert.Equal(t, c, gotConfigs.Merge()) - }) - - t.Run("from CLI flag only", func(t *testing.T) { - // given - fSet := pflag.NewFlagSet("testing", pflag.ContinueOnError) - config.RegisterFlags(fSet) - err := fSet.Parse([]string{"--config=testdata/TestFromProvider/first.yaml,testdata/TestFromProvider/second.yaml", "--config", "testdata/TestFromProvider/third.yaml"}) - require.NoError(t, err) - - // when - provider := config.GetProvider(nil) - gotConfigs, err := provider.Configs(context.Background()) - assert.NoError(t, err) - - // then - c, err := os.ReadFile("testdata/TestFromProvider/all.yaml") - assert.NoError(t, err) - assert.Equal(t, c, gotConfigs.Merge()) - }) - - t.Run("should honor env variable over the CLI flag", func(t *testing.T) { - // given - fSet := pflag.NewFlagSet("testing", pflag.ContinueOnError) - config.RegisterFlags(fSet) - - err := fSet.Parse([]string{"--config=testdata/TestFromProvider/from-cli-flag.yaml,testdata/TestFromProvider/from-cli-flag-second.yaml"}) - require.NoError(t, err) - - t.Setenv("BOTKUBE_CONFIG_PATHS", "testdata/TestFromProvider/first.yaml,testdata/TestFromProvider/second.yaml,testdata/TestFromProvider/third.yaml") - - // when - provider := config.GetProvider(nil) - gotConfigs, err := provider.Configs(context.Background()) - assert.NoError(t, err) - - // then - c, err := os.ReadFile("testdata/TestFromProvider/all.yaml") - assert.NoError(t, err) - assert.Equal(t, c, gotConfigs.Merge()) - }) -} - func TestNormalizeConfigEnvName(t *testing.T) { // given tests := []struct { diff --git a/pkg/config/testdata/TestLoadConfigSuccess/config.golden.yaml b/pkg/config/testdata/TestLoadConfigSuccess/config.golden.yaml index 33043a5679..3b01bbac5a 100644 --- a/pkg/config/testdata/TestLoadConfigSuccess/config.golden.yaml +++ b/pkg/config/testdata/TestLoadConfigSuccess/config.golden.yaml @@ -326,23 +326,22 @@ sources: my-annotation: "true" labels: my-label: "true" - plugins: - botkube/keptn: - enabled: true - config: - field: value - context: - rbac: - user: - type: "" - static: - value: "" - prefix: "" - group: - type: "" - static: - values: [] - prefix: "" + botkube/keptn: + enabled: true + config: + field: value + context: + rbac: + user: + type: "" + static: + value: "" + prefix: "" + group: + type: "" + static: + values: [] + prefix: "" executors: kubectl-read-only: kubectl: @@ -376,27 +375,25 @@ executors: - nodes defaultNamespace: default restrictAccess: false - plugins: {} plugin-based: kubectl: enabled: false - plugins: - botkube/echo: - enabled: true - config: - changeResponseToUpperCase: true - context: - rbac: - user: - type: "" - static: - value: "" - prefix: "" - group: - type: "" - static: - values: [] - prefix: "" + botkube/echo: + enabled: true + config: + changeResponseToUpperCase: true + context: + rbac: + user: + type: "" + static: + value: "" + prefix: "" + group: + type: "" + static: + values: [] + prefix: "" aliases: {} communications: default-workspace: