From 7cd95847bb1eae480b76c4d649b165dfc7f349f9 Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Wed, 13 Aug 2025 01:09:34 +0300 Subject: [PATCH 1/8] feat(plugin): add `cockroach sql` support Adds the ability to read Database Credentials and inject for `cockroach sql`. Signed-off-by: Anton Antonov --- plugins/cockroachdb/cockroach.go | 26 +++ plugins/cockroachdb/database_credentials.go | 60 ++++++ .../cockroachdb/database_credentials_test.go | 174 ++++++++++++++++++ plugins/cockroachdb/plugin.go | 22 +++ 4 files changed, 282 insertions(+) create mode 100644 plugins/cockroachdb/cockroach.go create mode 100644 plugins/cockroachdb/database_credentials.go create mode 100644 plugins/cockroachdb/database_credentials_test.go create mode 100644 plugins/cockroachdb/plugin.go diff --git a/plugins/cockroachdb/cockroach.go b/plugins/cockroachdb/cockroach.go new file mode 100644 index 000000000..f550a0809 --- /dev/null +++ b/plugins/cockroachdb/cockroach.go @@ -0,0 +1,26 @@ +package cockroachdb + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/needsauth" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" +) + +func Cockroach() schema.Executable { + return schema.Executable{ + Name: "cockroach", + Runs: []string{"cockroach"}, + DocsURL: sdk.URL("https://www.cockroachlabs.com/docs/stable/cockroach-sql.html"), + NeedsAuth: needsauth.IfAll( + needsauth.NotForHelpOrVersion(), + needsauth.ForCommand("sql"), + needsauth.NotWithoutArgs(), + ), + Uses: []schema.CredentialUsage{ + { + Name: credname.DatabaseCredentials, + }, + }, + } +} diff --git a/plugins/cockroachdb/database_credentials.go b/plugins/cockroachdb/database_credentials.go new file mode 100644 index 000000000..f47eb3672 --- /dev/null +++ b/plugins/cockroachdb/database_credentials.go @@ -0,0 +1,60 @@ +package cockroachdb + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/importer" + "github.com/1Password/shell-plugins/sdk/provision" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func DatabaseCredentials() schema.CredentialType { + return schema.CredentialType{ + Name: credname.DatabaseCredentials, + DocsURL: sdk.URL("https://www.cockroachlabs.com/docs/stable/connection-parameters.html"), + ManagementURL: sdk.URL("https://cockroachlabs.cloud/"), + Fields: []schema.CredentialField{ + { + Name: fieldname.Host, + MarkdownDescription: "CockroachDB host to connect to.", + }, + { + Name: fieldname.Port, + MarkdownDescription: "Port used to connect to CockroachDB.", + Optional: true, + }, + { + Name: fieldname.User, + MarkdownDescription: "CockroachDB user to authenticate as.", + }, + { + Name: fieldname.Password, + MarkdownDescription: "Password used to authenticate to CockroachDB.", + Secret: true, + Optional: true, + }, + { + Name: fieldname.Database, + MarkdownDescription: "Database name to connect to. Defaults to 'defaultdb'.", + Optional: true, + }, + { + Name: "insecure", + MarkdownDescription: "Connect in insecure mode (skip TLS verification). Set to '1' to skip TLS verification.", + Optional: true, + }, + }, + DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), + Importer: importer.TryEnvVarPair(defaultEnvVarMapping), + } +} + +var defaultEnvVarMapping = map[string]sdk.FieldName{ + "COCKROACH_HOST": fieldname.Host, + "COCKROACH_PORT": fieldname.Port, + "COCKROACH_USER": fieldname.User, + "COCKROACH_PASSWORD": fieldname.Password, + "COCKROACH_DATABASE": fieldname.Database, + "COCKROACH_INSECURE": "insecure", +} diff --git a/plugins/cockroachdb/database_credentials_test.go b/plugins/cockroachdb/database_credentials_test.go new file mode 100644 index 000000000..f282478de --- /dev/null +++ b/plugins/cockroachdb/database_credentials_test.go @@ -0,0 +1,174 @@ +package cockroachdb + +import ( + "testing" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/plugintest" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func TestDatabaseCredentialsImporter(t *testing.T) { + plugintest.TestImporter(t, DatabaseCredentials().Importer, map[string]plugintest.ImportCase{ + "environment variables - complete": { + Environment: map[string]string{ + "COCKROACH_HOST": "localhost", + "COCKROACH_PORT": "26257", + "COCKROACH_USER": "root", + "COCKROACH_PASSWORD": "password123", + "COCKROACH_DATABASE": "defaultdb", + "COCKROACH_INSECURE": "1", + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.Host: "localhost", + fieldname.Port: "26257", + fieldname.User: "root", + fieldname.Password: "password123", + fieldname.Database: "defaultdb", + "insecure": "1", + }, + }, + }, + }, + "environment variables - minimal": { + Environment: map[string]string{ + "COCKROACH_HOST": "cockroach.example.com", + "COCKROACH_USER": "admin", + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.Host: "cockroach.example.com", + fieldname.User: "admin", + }, + }, + }, + }, + "environment variables - production secure": { + Environment: map[string]string{ + "COCKROACH_HOST": "prod-cluster.cockroachlabs.cloud", + "COCKROACH_PORT": "26257", + "COCKROACH_USER": "produser", + "COCKROACH_PASSWORD": "securepass123", + "COCKROACH_DATABASE": "proddb", + "COCKROACH_INSECURE": "0", + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.Host: "prod-cluster.cockroachlabs.cloud", + fieldname.Port: "26257", + fieldname.User: "produser", + fieldname.Password: "securepass123", + fieldname.Database: "proddb", + "insecure": "0", + }, + }, + }, + }, + }) +} + +func TestDatabaseCredentialsProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, DatabaseCredentials().DefaultProvisioner, map[string]plugintest.ProvisionCase{ + "local development - insecure": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Host: "localhost", + fieldname.Port: "26257", + fieldname.User: "root", + fieldname.Password: "password123", + fieldname.Database: "defaultdb", + "insecure": "1", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "COCKROACH_HOST": "localhost", + "COCKROACH_PORT": "26257", + "COCKROACH_USER": "root", + "COCKROACH_PASSWORD": "password123", + "COCKROACH_DATABASE": "defaultdb", + "COCKROACH_INSECURE": "1", + }, + }, + }, + "production - secure": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Host: "prod-cluster.cockroachlabs.cloud", + fieldname.Port: "26257", + fieldname.User: "produser", + fieldname.Password: "securepass123", + fieldname.Database: "proddb", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "COCKROACH_HOST": "prod-cluster.cockroachlabs.cloud", + "COCKROACH_PORT": "26257", + "COCKROACH_USER": "produser", + "COCKROACH_PASSWORD": "securepass123", + "COCKROACH_DATABASE": "proddb", + }, + }, + }, + "minimal configuration": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Host: "cockroach.example.com", + fieldname.User: "admin", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "COCKROACH_HOST": "cockroach.example.com", + "COCKROACH_USER": "admin", + }, + }, + }, + }) +} + +// TestCockroachSQLExecutable tests the cockroach sql executable configuration +func TestCockroachSQLExecutable(t *testing.T) { + plugin := New() + + // Find the cockroach sql executable + var cockroachSQL *schema.Executable + for _, exec := range plugin.Executables { + if exec.Name == "cockroach" { + cockroachSQL = &exec + break + } + } + + if cockroachSQL == nil { + t.Fatal("cockroach sql executable not found in plugin") + } + + // Test that it uses database credentials + if len(cockroachSQL.Uses) != 1 { + t.Errorf("Expected 1 credential usage, got %d", len(cockroachSQL.Uses)) + } + + if cockroachSQL.Uses[0].Name != credname.DatabaseCredentials { + t.Errorf("Expected DatabaseCredentials, got %s", cockroachSQL.Uses[0].Name) + } +} + +// TestPluginValidation tests that the plugin passes all validation checks +func TestPluginValidation(t *testing.T) { + plugin := New() + + // Basic plugin validation + if plugin.Name != "cockroachdb" { + t.Errorf("Expected plugin name 'cockroachdb', got '%s'", plugin.Name) + } + + if len(plugin.Credentials) != 1 { + t.Errorf("Expected 1 credential type, got %d", len(plugin.Credentials)) + } + + if len(plugin.Executables) != 1 { + t.Errorf("Expected 1 executable, got %d", len(plugin.Executables)) + } +} diff --git a/plugins/cockroachdb/plugin.go b/plugins/cockroachdb/plugin.go new file mode 100644 index 000000000..07132c6f0 --- /dev/null +++ b/plugins/cockroachdb/plugin.go @@ -0,0 +1,22 @@ +package cockroachdb + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema" +) + +func New() schema.Plugin { + return schema.Plugin{ + Name: "cockroachdb", + Platform: schema.PlatformInfo{ + Name: "CockroachDB", + Homepage: sdk.URL("https://www.cockroachlabs.com"), + }, + Credentials: []schema.CredentialType{ + DatabaseCredentials(), + }, + Executables: []schema.Executable{ + Cockroach(), + }, + } +} From d7853bdf6276e73a5f04b19b80c2e71ff7377b6d Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Wed, 23 Sep 2026 00:05:27 +0300 Subject: [PATCH 2/8] fix: update field names The names were updated upstream. Signed-off-by: Anton Antonov --- plugins/cockroachdb/database_credentials.go | 4 ++-- plugins/cockroachdb/database_credentials_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/cockroachdb/database_credentials.go b/plugins/cockroachdb/database_credentials.go index f47eb3672..342de7f00 100644 --- a/plugins/cockroachdb/database_credentials.go +++ b/plugins/cockroachdb/database_credentials.go @@ -40,7 +40,7 @@ func DatabaseCredentials() schema.CredentialType { Optional: true, }, { - Name: "insecure", + Name: "Insecure", MarkdownDescription: "Connect in insecure mode (skip TLS verification). Set to '1' to skip TLS verification.", Optional: true, }, @@ -56,5 +56,5 @@ var defaultEnvVarMapping = map[string]sdk.FieldName{ "COCKROACH_USER": fieldname.User, "COCKROACH_PASSWORD": fieldname.Password, "COCKROACH_DATABASE": fieldname.Database, - "COCKROACH_INSECURE": "insecure", + "COCKROACH_INSECURE": "Insecure", } diff --git a/plugins/cockroachdb/database_credentials_test.go b/plugins/cockroachdb/database_credentials_test.go index f282478de..4f962ae83 100644 --- a/plugins/cockroachdb/database_credentials_test.go +++ b/plugins/cockroachdb/database_credentials_test.go @@ -29,7 +29,7 @@ func TestDatabaseCredentialsImporter(t *testing.T) { fieldname.User: "root", fieldname.Password: "password123", fieldname.Database: "defaultdb", - "insecure": "1", + "Insecure": "1", }, }, }, @@ -65,7 +65,7 @@ func TestDatabaseCredentialsImporter(t *testing.T) { fieldname.User: "produser", fieldname.Password: "securepass123", fieldname.Database: "proddb", - "insecure": "0", + "Insecure": "0", }, }, }, @@ -82,7 +82,7 @@ func TestDatabaseCredentialsProvisioner(t *testing.T) { fieldname.User: "root", fieldname.Password: "password123", fieldname.Database: "defaultdb", - "insecure": "1", + "Insecure": "1", }, ExpectedOutput: sdk.ProvisionOutput{ Environment: map[string]string{ From 85a6d8133543fc24ee8a9a0f6ebc177fd65f711e Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Fri, 2 Oct 2026 21:34:42 +0300 Subject: [PATCH 3/8] fix(cockroachdb): omit unsupported management URL 1Password rejects URL fields on Database items, which prevents importing CockroachDB credentials. Signed-off-by: Anton Antonov --- plugins/cockroachdb/database_credentials.go | 5 ++--- plugins/cockroachdb/database_credentials_test.go | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/cockroachdb/database_credentials.go b/plugins/cockroachdb/database_credentials.go index 342de7f00..b61448e6f 100644 --- a/plugins/cockroachdb/database_credentials.go +++ b/plugins/cockroachdb/database_credentials.go @@ -11,9 +11,8 @@ import ( func DatabaseCredentials() schema.CredentialType { return schema.CredentialType{ - Name: credname.DatabaseCredentials, - DocsURL: sdk.URL("https://www.cockroachlabs.com/docs/stable/connection-parameters.html"), - ManagementURL: sdk.URL("https://cockroachlabs.cloud/"), + Name: credname.DatabaseCredentials, + DocsURL: sdk.URL("https://www.cockroachlabs.com/docs/stable/connection-parameters.html"), Fields: []schema.CredentialField{ { Name: fieldname.Host, diff --git a/plugins/cockroachdb/database_credentials_test.go b/plugins/cockroachdb/database_credentials_test.go index 4f962ae83..329bb9991 100644 --- a/plugins/cockroachdb/database_credentials_test.go +++ b/plugins/cockroachdb/database_credentials_test.go @@ -159,6 +159,11 @@ func TestCockroachSQLExecutable(t *testing.T) { func TestPluginValidation(t *testing.T) { plugin := New() + // Database items cannot store URL fields in the 1Password CLI. + if DatabaseCredentials().ManagementURL != nil { + t.Error("Database credentials must not set a management URL") + } + // Basic plugin validation if plugin.Name != "cockroachdb" { t.Errorf("Expected plugin name 'cockroachdb', got '%s'", plugin.Name) From 0f80aa041e5e40de826076cc5979d5cd39a4c26d Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Fri, 2 Oct 2026 21:35:50 +0300 Subject: [PATCH 4/8] fix(cockroachdb): use PGPASSWORD for SQL login The SQL CLI ignores COCKROACH_PASSWORD and prompts for a password. Use PGPASSWORD so provisioned credentials authenticate correctly. Signed-off-by: Anton Antonov --- plugins/cockroachdb/database_credentials.go | 2 +- plugins/cockroachdb/database_credentials_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/cockroachdb/database_credentials.go b/plugins/cockroachdb/database_credentials.go index b61448e6f..c13deb68b 100644 --- a/plugins/cockroachdb/database_credentials.go +++ b/plugins/cockroachdb/database_credentials.go @@ -53,7 +53,7 @@ var defaultEnvVarMapping = map[string]sdk.FieldName{ "COCKROACH_HOST": fieldname.Host, "COCKROACH_PORT": fieldname.Port, "COCKROACH_USER": fieldname.User, - "COCKROACH_PASSWORD": fieldname.Password, + "PGPASSWORD": fieldname.Password, "COCKROACH_DATABASE": fieldname.Database, "COCKROACH_INSECURE": "Insecure", } diff --git a/plugins/cockroachdb/database_credentials_test.go b/plugins/cockroachdb/database_credentials_test.go index 329bb9991..119d07e8f 100644 --- a/plugins/cockroachdb/database_credentials_test.go +++ b/plugins/cockroachdb/database_credentials_test.go @@ -17,7 +17,7 @@ func TestDatabaseCredentialsImporter(t *testing.T) { "COCKROACH_HOST": "localhost", "COCKROACH_PORT": "26257", "COCKROACH_USER": "root", - "COCKROACH_PASSWORD": "password123", + "PGPASSWORD": "password123", "COCKROACH_DATABASE": "defaultdb", "COCKROACH_INSECURE": "1", }, @@ -53,7 +53,7 @@ func TestDatabaseCredentialsImporter(t *testing.T) { "COCKROACH_HOST": "prod-cluster.cockroachlabs.cloud", "COCKROACH_PORT": "26257", "COCKROACH_USER": "produser", - "COCKROACH_PASSWORD": "securepass123", + "PGPASSWORD": "securepass123", "COCKROACH_DATABASE": "proddb", "COCKROACH_INSECURE": "0", }, @@ -89,7 +89,7 @@ func TestDatabaseCredentialsProvisioner(t *testing.T) { "COCKROACH_HOST": "localhost", "COCKROACH_PORT": "26257", "COCKROACH_USER": "root", - "COCKROACH_PASSWORD": "password123", + "PGPASSWORD": "password123", "COCKROACH_DATABASE": "defaultdb", "COCKROACH_INSECURE": "1", }, @@ -108,7 +108,7 @@ func TestDatabaseCredentialsProvisioner(t *testing.T) { "COCKROACH_HOST": "prod-cluster.cockroachlabs.cloud", "COCKROACH_PORT": "26257", "COCKROACH_USER": "produser", - "COCKROACH_PASSWORD": "securepass123", + "PGPASSWORD": "securepass123", "COCKROACH_DATABASE": "proddb", }, }, From a5ef8406a8799aabee5c7137b15a22cd5236e90b Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Fri, 2 Oct 2026 21:38:01 +0300 Subject: [PATCH 5/8] test(cockroachdb): verify real SQL authentication Run the importer and provisioner against CockroachDB in Docker. Check insecure and TLS connections, password failures, and the environment variable the SQL CLI accepts. Signed-off-by: Anton Antonov --- .github/workflows/test.yaml | 2 + .../database_credentials_integration_test.go | 165 ++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 plugins/cockroachdb/database_credentials_integration_test.go diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e4c481b3e..9303f3a96 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -32,6 +32,8 @@ jobs: go-version: ^1.18 - name: Test + env: + COCKROACHDB_INTEGRATION: "1" run: make test - name: Validate diff --git a/plugins/cockroachdb/database_credentials_integration_test.go b/plugins/cockroachdb/database_credentials_integration_test.go new file mode 100644 index 000000000..b252de065 --- /dev/null +++ b/plugins/cockroachdb/database_credentials_integration_test.go @@ -0,0 +1,165 @@ +package cockroachdb + +import ( + "context" + "maps" + "os" + "os/exec" + "strings" + "testing" + "time" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +// Run with COCKROACHDB_INTEGRATION=1 make test. Docker pulls the pinned image if needed. +func TestDatabaseCredentialsIntegration(t *testing.T) { + if os.Getenv("COCKROACHDB_INTEGRATION") != "1" { + t.Skip("Set COCKROACHDB_INTEGRATION=1 to test against CockroachDB in Docker") + } + + for _, secure := range []bool{false, true} { + name := "insecure" + if secure { + name = "TLS" + } + t.Run(name, func(t *testing.T) { + start := "exec cockroach start-single-node --listen-addr=localhost:26258 --store=type=mem,size=1GiB --cache=64MiB --max-sql-memory=64MiB " + adminArgs := []string{"--host=localhost:26258"} + if secure { + start = "mkdir /certs && cockroach cert create-ca --certs-dir=/certs --ca-key=/certs/ca.key && " + + "cockroach cert create-node localhost --certs-dir=/certs --ca-key=/certs/ca.key && " + + "cockroach cert create-client root --certs-dir=/certs --ca-key=/certs/ca.key && " + start + "--certs-dir=/certs" + adminArgs = append(adminArgs, "--certs-dir=/certs") + } else { + start += "--insecure" + adminArgs = append(adminArgs, "--insecure") + } + container, err := cockroachDocker("run", "--detach", "--entrypoint=sh", "cockroachdb/cockroach:v25.2.4", "-c", start) + if err != nil { + t.Fatalf("Start CockroachDB: %v\n%s", err, container) + } + container = strings.TrimSpace(container) + t.Cleanup(func() { + if output, err := cockroachDocker("rm", "--force", container); err != nil { + t.Errorf("Remove test container: %v\n%s", err, output) + } + }) + admin := append([]string{"exec", container, "cockroach", "sql"}, adminArgs...) + deadline := time.Now().Add(90 * time.Second) + for { + output, err := cockroachDocker(append(admin, "--execute=SELECT 1")...) + if err == nil { + break + } + running, inspectErr := cockroachDocker("inspect", "--format={{.State.Running}}", container) + if inspectErr != nil || strings.TrimSpace(running) != "true" || time.Now().After(deadline) { + logs, _ := cockroachDocker("logs", container) + t.Fatalf("CockroachDB did not become ready: %v\n%s\n%s", err, output, logs) + } + time.Sleep(time.Second) + } + setup := "CREATE DATABASE plugin_test; CREATE USER shell_plugin; GRANT ALL ON DATABASE plugin_test TO shell_plugin;" + if secure { + setup += "ALTER USER shell_plugin WITH PASSWORD 'integration-password';" + } + if output, err := cockroachDocker(append(admin, "--execute="+setup)...); err != nil { + t.Fatalf("Create test credentials: %v\n%s", err, output) + } + + fields := map[sdk.FieldName]string{ + fieldname.Host: "localhost", fieldname.Port: "26258", + fieldname.User: "shell_plugin", fieldname.Database: "plugin_test", + "Insecure": "1", + } + if secure { + fields["Insecure"] = "0" + fields[fieldname.Password] = "integration-password" + } + for name := range defaultEnvVarMapping { + t.Setenv(name, "") + } + for name, field := range defaultEnvVarMapping { + t.Setenv(name, fields[field]) + } + var imported sdk.ImportOutput + DatabaseCredentials().Importer(context.Background(), sdk.ImportInput{}, &imported) + candidates := imported.AllCandidates() + if len(imported.Errors()) != 0 || len(candidates) != 1 { + t.Fatalf("Expected one imported credential without errors, got %d candidates and %d errors", len(candidates), len(imported.Errors())) + } + provision := func(t *testing.T, fields map[sdk.FieldName]string) map[string]string { + t.Helper() + out := sdk.ProvisionOutput{Environment: make(map[string]string)} + DatabaseCredentials().DefaultProvisioner.Provision(context.Background(), sdk.ProvisionInput{ItemFields: fields}, &out) + if len(out.Diagnostics.Errors) != 0 { + t.Fatal("Credential provisioning failed") + } + return out.Environment + } + query := func(environment map[string]string) (string, error) { + args := []string{"exec"} + for name, value := range environment { + args = append(args, "--env", name+"="+value) + } + args = append(args, container, "cockroach", "sql", "--format=csv", "--execute=SELECT current_user, current_database()") + if secure { + args = append(args, "--certs-dir=/certs") + } + return cockroachDocker(args...) + } + checkQuery := func(t *testing.T, fields map[sdk.FieldName]string) { + t.Helper() + output, err := query(provision(t, fields)) + if err != nil || !strings.Contains(output, "\nshell_plugin,plugin_test\n") { + t.Fatalf("Query with imported and provisioned credentials: %v\n%s", err, output) + } + } + t.Run("imported_credentials_connect", func(t *testing.T) { + checkQuery(t, candidates[0].Fields) + }) + if secure { + t.Run("TLS_is_the_default", func(t *testing.T) { + fields := maps.Clone(candidates[0].Fields) + delete(fields, "Insecure") + checkQuery(t, fields) + }) + checkRejected := func(t *testing.T, environment map[string]string) { + t.Helper() + output, err := query(environment) + if err == nil || !strings.Contains(output, "password authentication failed") { + t.Fatalf("Expected password authentication failure: %v\n%s", err, output) + } + } + t.Run("missing_password_is_rejected", func(t *testing.T) { + fields := maps.Clone(candidates[0].Fields) + delete(fields, fieldname.Password) + checkRejected(t, provision(t, fields)) + }) + t.Run("incorrect_password_is_rejected", func(t *testing.T) { + fields := maps.Clone(candidates[0].Fields) + fields[fieldname.Password] = "wrong-password" + checkRejected(t, provision(t, fields)) + }) + t.Run("COCKROACH_PASSWORD_is_ignored", func(t *testing.T) { + environment := provision(t, candidates[0].Fields) + delete(environment, "PGPASSWORD") + environment["COCKROACH_PASSWORD"] = "integration-password" + checkRejected(t, environment) + }) + } + }) + } +} + +func cockroachDocker(args ...string) (string, error) { + timeout := 30 * time.Second + if args[0] == "run" { + timeout = 2 * time.Minute + } + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + output, err := exec.CommandContext(ctx, "docker", args...).CombinedOutput() + return string(output), err +} From 004c955bbe745d37152bc1b84a2558cf0601cf42 Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Fri, 2 Oct 2026 21:38:51 +0300 Subject: [PATCH 6/8] test(cockroachdb): cover schema and auth rules Signed-off-by: Anton Antonov --- .../cockroachdb/database_credentials_test.go | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/plugins/cockroachdb/database_credentials_test.go b/plugins/cockroachdb/database_credentials_test.go index 119d07e8f..62919f50a 100644 --- a/plugins/cockroachdb/database_credentials_test.go +++ b/plugins/cockroachdb/database_credentials_test.go @@ -11,7 +11,11 @@ import ( ) func TestDatabaseCredentialsImporter(t *testing.T) { + for name := range defaultEnvVarMapping { + t.Setenv(name, "") + } plugintest.TestImporter(t, DatabaseCredentials().Importer, map[string]plugintest.ImportCase{ + "no environment variables": {}, "environment variables - complete": { Environment: map[string]string{ "COCKROACH_HOST": "localhost", @@ -128,11 +132,9 @@ func TestDatabaseCredentialsProvisioner(t *testing.T) { }) } -// TestCockroachSQLExecutable tests the cockroach sql executable configuration func TestCockroachSQLExecutable(t *testing.T) { plugin := New() - // Find the cockroach sql executable var cockroachSQL *schema.Executable for _, exec := range plugin.Executables { if exec.Name == "cockroach" { @@ -145,9 +147,8 @@ func TestCockroachSQLExecutable(t *testing.T) { t.Fatal("cockroach sql executable not found in plugin") } - // Test that it uses database credentials if len(cockroachSQL.Uses) != 1 { - t.Errorf("Expected 1 credential usage, got %d", len(cockroachSQL.Uses)) + t.Fatalf("Expected 1 credential usage, got %d", len(cockroachSQL.Uses)) } if cockroachSQL.Uses[0].Name != credname.DatabaseCredentials { @@ -155,16 +156,20 @@ func TestCockroachSQLExecutable(t *testing.T) { } } -// TestPluginValidation tests that the plugin passes all validation checks func TestPluginValidation(t *testing.T) { plugin := New() + for _, report := range plugin.DeepValidate() { + if report.HasErrors() { + t.Errorf("Plugin validation failed: %+v", report) + } + } + // Database items cannot store URL fields in the 1Password CLI. if DatabaseCredentials().ManagementURL != nil { t.Error("Database credentials must not set a management URL") } - // Basic plugin validation if plugin.Name != "cockroachdb" { t.Errorf("Expected plugin name 'cockroachdb', got '%s'", plugin.Name) } @@ -177,3 +182,15 @@ func TestPluginValidation(t *testing.T) { t.Errorf("Expected 1 executable, got %d", len(plugin.Executables)) } } + +func TestCockroachNeedsAuth(t *testing.T) { + plugintest.TestNeedsAuth(t, Cockroach().NeedsAuth, map[string]plugintest.NeedsAuthCase{ + "interactive SQL": {Args: []string{"sql"}, ExpectedNeedsAuth: true}, + "SQL query": {Args: []string{"sql", "--execute", "SELECT 1"}, ExpectedNeedsAuth: true}, + "SQL help": {Args: []string{"sql", "--help"}}, + "help": {Args: []string{"help"}}, + "version": {Args: []string{"version"}}, + "start server": {Args: []string{"start-single-node", "--insecure"}}, + "no arguments": {}, + }) +} From c73e655fd98d7cc5fac9b37ebb887e56015dadbf Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Fri, 2 Oct 2026 21:47:08 +0300 Subject: [PATCH 7/8] docs(cockroachdb): clarify insecure mode Signed-off-by: Anton Antonov --- plugins/cockroachdb/database_credentials.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cockroachdb/database_credentials.go b/plugins/cockroachdb/database_credentials.go index c13deb68b..76b92dc3f 100644 --- a/plugins/cockroachdb/database_credentials.go +++ b/plugins/cockroachdb/database_credentials.go @@ -40,7 +40,7 @@ func DatabaseCredentials() schema.CredentialType { }, { Name: "Insecure", - MarkdownDescription: "Connect in insecure mode (skip TLS verification). Set to '1' to skip TLS verification.", + MarkdownDescription: "Connect without TLS. Set to '1' only for an insecure development cluster.", Optional: true, }, }, From fcd4fa4ca35ea4a1b5c2f8fe4cd1b4680b51997b Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Sun, 4 Oct 2026 12:47:53 +0300 Subject: [PATCH 8/8] test: surpress initial docker pull logs Also clean-up with `--rm` just in case the force cleanup doesn't work. Signed-off-by: Anton Antonov --- plugins/cockroachdb/database_credentials_integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cockroachdb/database_credentials_integration_test.go b/plugins/cockroachdb/database_credentials_integration_test.go index b252de065..dc2fc06ba 100644 --- a/plugins/cockroachdb/database_credentials_integration_test.go +++ b/plugins/cockroachdb/database_credentials_integration_test.go @@ -36,7 +36,7 @@ func TestDatabaseCredentialsIntegration(t *testing.T) { start += "--insecure" adminArgs = append(adminArgs, "--insecure") } - container, err := cockroachDocker("run", "--detach", "--entrypoint=sh", "cockroachdb/cockroach:v25.2.4", "-c", start) + container, err := cockroachDocker("run", "--rm", "--quiet", "--detach", "--entrypoint=sh", "cockroachdb/cockroach:v25.2.4", "-c", start) if err != nil { t.Fatalf("Start CockroachDB: %v\n%s", err, container) }