From 24adb3673c3acf29229c56c5dbf54ada6558a780 Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Wed, 13 Aug 2025 01:09:34 +0300 Subject: [PATCH] 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 00000000..f550a080 --- /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 00000000..f47eb367 --- /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 00000000..f282478d --- /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 00000000..07132c6f --- /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(), + }, + } +}