diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ee6d355d..de55a843 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -67,10 +67,9 @@ jobs: go-version: "1.23" - name: Lint code with golangci-lint 🚨 - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v6 with: - version: "v1.60.3" - skip-pkg-cache: true + version: "latest" install-mode: "goinstall" - name: Lint Bash script with shellcheck 🚨 @@ -82,7 +81,7 @@ jobs: dockerfile: Dockerfile - name: Run Go tests 🔬 - run: go test -p 1 -cover -covermode atomic -coverprofile=profile.cov -v ./... + run: go test -tags embed_plugin_template -p 1 -cover -covermode atomic -coverprofile=profile.cov -v ./... env: GITHUB_AUTH_TOKEN: ${{ secrets.INTEGRATION }} diff --git a/.golangci.yaml b/.golangci.yaml index 4a617b33..ddab1d5b 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -91,6 +91,7 @@ linters-settings: - "github.com/testcontainers/testcontainers-go" - "github.com/redis/go-redis/v9" - "github.com/docker/go-connections/nat" + - "github.com/codingsince1985/checksum" tagalign: align: false sort: false diff --git a/Makefile b/Makefile index b3bf09f1..78fe74d5 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ clean: @rm -rf dist test: - @go test -v -cover -coverprofile=c.out ./... + @go test -tags embed_plugin_template -v -cover -coverprofile=c.out ./... test-race: @go test -race -v ./... diff --git a/cmd/cmd_helpers_test.go b/cmd/cmd_helpers_test.go index 375d87a3..a3fbc0ac 100644 --- a/cmd/cmd_helpers_test.go +++ b/cmd/cmd_helpers_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "os" + "os/exec" "path/filepath" "runtime" @@ -44,3 +45,10 @@ func mustPullPlugin() (string, error) { return filepath.Abs(fileName) //nolint:wrapcheck } + +// runCommand runs a command in a given directory. +func runCommand(dir string, command string, args ...string) error { + cmd := exec.Command(command, args...) + cmd.Dir = dir + return cmd.Run() //nolint:wrapcheck +} diff --git a/cmd/plugin_scaffold_test.go b/cmd/plugin_scaffold_test.go new file mode 100644 index 00000000..3a3cc183 --- /dev/null +++ b/cmd/plugin_scaffold_test.go @@ -0,0 +1,128 @@ +package cmd + +import ( + "context" + "os" + "path/filepath" + "sync" + "testing" + "time" + + "github.com/codingsince1985/checksum" + "github.com/gatewayd-io/gatewayd/config" + "github.com/gatewayd-io/gatewayd/plugin" + "github.com/gatewayd-io/gatewayd/testhelpers" + "github.com/spf13/cast" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + yamlv3 "gopkg.in/yaml.v3" +) + +func Test_pluginScaffoldCmd(t *testing.T) { + // Start the test containers. + ctx := context.Background() + postgresHostIP1, postgresMappedPort1 := testhelpers.SetupPostgreSQLTestContainer(ctx, t) + postgresHostIP2, postgresMappedPort2 := testhelpers.SetupPostgreSQLTestContainer(ctx, t) + postgresAddress1 := postgresHostIP1 + ":" + postgresMappedPort1.Port() + t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgresAddress1) + postgresAddress2 := postgresHostIP2 + ":" + postgresMappedPort2.Port() + t.Setenv("GATEWAYD_CLIENTS_TEST_WRITE_ADDRESS", postgresAddress2) + + globalTestConfigFile := filepath.Join("testdata", "gatewayd.yaml") + plugin.IsPluginTemplateEmbedded() + pluginTestScaffoldInputFile := "./testdata/scaffold_input.yaml" + + tempDir := t.TempDir() + + output, err := executeCommandC( + rootCmd, "plugin", "scaffold", "-i", pluginTestScaffoldInputFile, "-o", tempDir) + require.NoError(t, err, "plugin scaffold should not return an error") + assert.Contains(t, output, "scaffold done") + assert.Contains(t, output, "created files:") + assert.Contains(t, output, "test-gatewayd-plugin/.github/issue_template.md") + assert.Contains(t, output, "test-gatewayd-plugin/.github/pull_request_template.md") + assert.Contains(t, output, "test-gatewayd-plugin/.github/workflows/commits-signed.yaml") + + pluginName := "test-gatewayd-plugin" + pluginDir := filepath.Join(tempDir, pluginName) + + pluginsConfig, err := os.ReadFile(filepath.Join(pluginDir, "gatewayd_plugin.yaml")) + require.NoError(t, err, "reading plugins config file should not return an error") + + var localPluginsConfig map[string]interface{} + err = yamlv3.Unmarshal(pluginsConfig, &localPluginsConfig) + require.NoError(t, err, "unmarshalling yaml file should not return error") + + err = runCommand(pluginDir, "go", "mod", "tidy") + require.NoError(t, err, "running go mod tidy should not return an error") + err = runCommand(pluginDir, "make", "build-dev") + require.NoError(t, err, "running make build-dev should not return an error") + + pluginBinaryPath := filepath.Join(pluginDir, pluginName) + + _, err = os.Stat(pluginBinaryPath) + require.NoError(t, err, "plugin binary file should exist") + + pluginsList := cast.ToSlice(localPluginsConfig["plugins"]) + plugin := cast.ToStringMap(pluginsList[0]) + plugin["localPath"] = filepath.Join("cmd", pluginDir, pluginName) + sum, err := checksum.SHA256sum(pluginBinaryPath) + require.NoError(t, err, "marshalling yaml file should not return error") + plugin["checksum"] = sum + + pluginsList[0] = plugin + plugins := make(map[string]interface{}) + plugins["plugins"] = pluginsList + + updatedPluginConfig, err := yamlv3.Marshal(plugins) + require.NoError(t, err, "marshalling yaml file should not return error") + + err = os.WriteFile( + filepath.Join(pluginDir, "gatewayd_plugins.yaml"), + updatedPluginConfig, FilePermissions) + require.NoError(t, err, "writingh to yaml file should not return error") + + pluginTestConfigFile := filepath.Join(pluginDir, "gatewayd_plugins.yaml") + + stopChan = make(chan struct{}) + + var waitGroup sync.WaitGroup + + waitGroup.Add(1) + go func(waitGroup *sync.WaitGroup) { + // Test run command. + output, err := executeCommandC( + rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile) + require.NoError(t, err, "run command should not have returned an error") + + // Print the output for debugging purposes. + runCmd.Print(output) + // Check if GatewayD started and stopped correctly. + assert.Contains(t, output, "GatewayD is running") + assert.Contains(t, output, "Stopped all servers") + + waitGroup.Done() + }(&waitGroup) + + waitGroup.Add(1) + go func(waitGroup *sync.WaitGroup) { + time.Sleep(waitBeforeStop) + + StopGracefully( + context.Background(), + nil, + nil, + metricsServer, + nil, + loggers[config.Default], + servers, + stopChan, + nil, + nil, + ) + + waitGroup.Done() + }(&waitGroup) + + waitGroup.Wait() +} diff --git a/cmd/run_test.go b/cmd/run_test.go index 823189cc..534d203a 100644 --- a/cmd/run_test.go +++ b/cmd/run_test.go @@ -20,8 +20,8 @@ var ( func Test_runCmd(t *testing.T) { postgresHostIP, postgresMappedPort := testhelpers.SetupPostgreSQLTestContainer(context.Background(), t) - postgredAddress := postgresHostIP + ":" + postgresMappedPort.Port() - t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgredAddress) + postgresAddress := postgresHostIP + ":" + postgresMappedPort.Port() + t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgresAddress) globalTestConfigFile := "./test_global_runCmd.yaml" pluginTestConfigFile := "./test_plugins_runCmd.yaml" @@ -84,8 +84,8 @@ func Test_runCmd(t *testing.T) { // Test_runCmdWithTLS tests the run command with TLS enabled on the server. func Test_runCmdWithTLS(t *testing.T) { postgresHostIP, postgresMappedPort := testhelpers.SetupPostgreSQLTestContainer(context.Background(), t) - postgredAddress := postgresHostIP + ":" + postgresMappedPort.Port() - t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgredAddress) + postgresAddress := postgresHostIP + ":" + postgresMappedPort.Port() + t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgresAddress) globalTLSTestConfigFile := "./testdata/gatewayd_tls.yaml" pluginTestConfigFile := "./test_plugins_runCmdWithTLS.yaml" @@ -144,11 +144,11 @@ func Test_runCmdWithTLS(t *testing.T) { // Test_runCmdWithMultiTenancy tests the run command with multi-tenancy enabled. func Test_runCmdWithMultiTenancy(t *testing.T) { postgresHostIP, postgresMappedPort := testhelpers.SetupPostgreSQLTestContainer(context.Background(), t) - postgredAddress := postgresHostIP + ":" + postgresMappedPort.Port() - t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgredAddress) + postgresAddress := postgresHostIP + ":" + postgresMappedPort.Port() + t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgresAddress) postgresHostIP2, postgresMappedPort2 := testhelpers.SetupPostgreSQLTestContainer(context.Background(), t) - postgredAddress2 := postgresHostIP2 + ":" + postgresMappedPort2.Port() - t.Setenv("GATEWAYD_CLIENTS_TEST_WRITE_ADDRESS", postgredAddress2) + postgresAddress2 := postgresHostIP2 + ":" + postgresMappedPort2.Port() + t.Setenv("GATEWAYD_CLIENTS_TEST_WRITE_ADDRESS", postgresAddress2) globalTestConfigFile := "./testdata/gatewayd.yaml" pluginTestConfigFile := "./test_plugins_runCmdWithMultiTenancy.yaml" @@ -208,8 +208,8 @@ func Test_runCmdWithMultiTenancy(t *testing.T) { func Test_runCmdWithCachePlugin(t *testing.T) { postgresHostIP, postgresMappedPort := testhelpers.SetupPostgreSQLTestContainer(context.Background(), t) - postgredAddress := postgresHostIP + ":" + postgresMappedPort.Port() - t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgredAddress) + postgresAddress := postgresHostIP + ":" + postgresMappedPort.Port() + t.Setenv("GATEWAYD_CLIENTS_DEFAULT_WRITES_ADDRESS", postgresAddress) globalTestConfigFile := "./test_global_runCmdWithCachePlugin.yaml" pluginTestConfigFile := "./test_plugins_runCmdWithCachePlugin.yaml" diff --git a/cmd/testdata/scaffold_input.yaml b/cmd/testdata/scaffold_input.yaml new file mode 100644 index 00000000..db04542d --- /dev/null +++ b/cmd/testdata/scaffold_input.yaml @@ -0,0 +1,6 @@ +remote_url: https://github.com/gatewayd-io/test-gatewayd-plugin +version: 0.1 +description: This is test plugin +license: MIT +authors: + - GatewayD Team \ No newline at end of file diff --git a/config/constants.go b/config/constants.go index 28dffe83..0fbbef89 100644 --- a/config/constants.go +++ b/config/constants.go @@ -61,7 +61,7 @@ const ( // Plugin constants. DefaultMinPort = 50000 DefaultMaxPort = 60000 - PluginPriorityStart = 1000 + PluginPriorityStart = uint(1000) DefaultPluginAddress = "http://plugins/metrics" DefaultMetricsMergerPeriod = 5 * time.Second DefaultPluginHealthCheckPeriod = 5 * time.Second diff --git a/network/roundrobin_test.go b/network/roundrobin_test.go index 259e8c56..9f98d6ac 100644 --- a/network/roundrobin_test.go +++ b/network/roundrobin_test.go @@ -74,7 +74,7 @@ func TestRoundRobin_ConcurrentAccess(t *testing.T) { waitGroup.Wait() nextIndex := roundRobin.next.Load() - if nextIndex != uint32(numGoroutines) { //nolint:gosec + if nextIndex != uint32(numGoroutines) { t.Errorf("expected next index to be %d, got %d", numGoroutines, nextIndex) } } diff --git a/network/server.go b/network/server.go index ec919d72..0858976c 100644 --- a/network/server.go +++ b/network/server.go @@ -241,7 +241,7 @@ func (s *Server) OnClose(conn *ConnWrapper, err error) Action { // Shutdown the server if there are no more connections and the server is stopped. // This is used to shut down the server gracefully. - if uint64(s.CountConnections()) == 0 && !s.IsRunning() { + if s.CountConnections() == 0 && !s.IsRunning() { span.AddEvent("Shutting down the server") return Shutdown } diff --git a/plugin/.template/input.example.yaml b/plugin/.template/input.example.yaml index 517e4bb5..db04542d 100644 --- a/plugin/.template/input.example.yaml +++ b/plugin/.template/input.example.yaml @@ -1,4 +1,4 @@ -remote_url: https://github.com/gatewayd/test-gatewayd-plugin +remote_url: https://github.com/gatewayd-io/test-gatewayd-plugin version: 0.1 description: This is test plugin license: MIT diff --git a/plugin/plugin_registry.go b/plugin/plugin_registry.go index f486bfe2..b5130d52 100644 --- a/plugin/plugin_registry.go +++ b/plugin/plugin_registry.go @@ -477,7 +477,8 @@ func (reg *Registry) LoadPlugins( // in the config file. Built-in plugins are loaded first, followed by user-defined // plugins. Built-in plugins have a priority of 0 to 999, and user-defined plugins // have a priority of 1000 or greater. - plugin.Priority = sdkPlugin.Priority(config.PluginPriorityStart + uint(priority)) + plugin.Priority = sdkPlugin.Priority( + config.PluginPriorityStart + uint(priority)) //nolint:gosec logAdapter := logging.NewHcLogAdapter(®.Logger, pCfg.Name) diff --git a/plugin/plugin_registry_test.go b/plugin/plugin_registry_test.go index 4a33d910..fcd66294 100644 --- a/plugin/plugin_registry_test.go +++ b/plugin/plugin_registry_test.go @@ -167,7 +167,7 @@ func BenchmarkHookRun(b *testing.B) { args.Fields["test"] = v1.NewStringValue("test1") return args, nil } - for priority := range 1000 { + for priority := range uint(1000) { reg.AddHook(v1.HookName_HOOK_NAME_ON_NEW_LOGGER, sdkPlugin.Priority(priority), hookFunction,