diff --git a/frontend/apps/web/components/connections/forms/sql-server/SqlServerForm.tsx b/frontend/apps/web/components/connections/forms/sql-server/SqlServerForm.tsx
index 862dde531c..1ca5688f4b 100644
--- a/frontend/apps/web/components/connections/forms/sql-server/SqlServerForm.tsx
+++ b/frontend/apps/web/components/connections/forms/sql-server/SqlServerForm.tsx
@@ -1,4 +1,7 @@
-import { buildConnectionConfigMssql } from '@/app/(mgmt)/[account]/connections/util';
+import {
+ buildConnectionConfigMssql,
+ getTunnelConfig,
+} from '@/app/(mgmt)/[account]/connections/util';
import Submit from '@/components/forms/Submit';
import { useAccount } from '@/components/providers/account-provider';
import { BaseStore } from '@/util/zustand.stores.util';
@@ -8,6 +11,8 @@ import { useMutation } from '@connectrpc/connect-query';
import {
CheckConnectionConfigByIdRequestSchema,
CheckConnectionConfigRequestSchema,
+ CheckSSHConnectionByIdRequestSchema,
+ CheckSSHConnectionRequestSchema,
ConnectionService,
} from '@neosync/sdk';
import { ReactElement, useEffect } from 'react';
@@ -184,6 +189,16 @@ export default function SqlServerForm(props: Props): ReactElement {
const values = await getValueWithSecrets?.();
return values?.tunnel;
}}
+ onCheckRequest={() => {
+ return createMessage(CheckSSHConnectionRequestSchema, {
+ tunnel: getTunnelConfig(formData.tunnel),
+ });
+ }}
+ onCheckIdRequest={() => {
+ return createMessage(CheckSSHConnectionByIdRequestSchema, {
+ id: connectionId ?? '',
+ });
+ }}
/>
diff --git a/internal/connection-manager/providers/sqlprovider/provider.go b/internal/connection-manager/providers/sqlprovider/provider.go
index 4859365a41..2b168ad052 100644
--- a/internal/connection-manager/providers/sqlprovider/provider.go
+++ b/internal/connection-manager/providers/sqlprovider/provider.go
@@ -31,7 +31,7 @@ func (s *sqlDbtxWrapper) Close() error {
return s.close()
}
-const defaultConnectionTimeoutSeconds = uint32(30)
+const defaultConnectionTimeoutSeconds = uint32(10)
func (p *Provider) GetConnectionClient(cc *mgmtv1alpha1.ConnectionConfig, logger *slog.Logger) (neosync_benthos_sql.SqlDbtx, error) {
container, err := p.connector.NewDbFromConnectionConfig(cc, logger, sqlconnect.WithConnectionTimeout(defaultConnectionTimeoutSeconds))
From af5326c7cdb09a87088e1c98f0db279adc8a3c0a Mon Sep 17 00:00:00 2001
From: Nick Z <2420177+nickzelei@users.noreply.github.com>
Date: Thu, 27 Mar 2025 13:50:43 -0700
Subject: [PATCH 07/11] removes fmt log
---
internal/sshtunnel/connectors/postgrestunconnector/connector.go | 2 --
1 file changed, 2 deletions(-)
diff --git a/internal/sshtunnel/connectors/postgrestunconnector/connector.go b/internal/sshtunnel/connectors/postgrestunconnector/connector.go
index 475d1e2362..f990619b54 100644
--- a/internal/sshtunnel/connectors/postgrestunconnector/connector.go
+++ b/internal/sshtunnel/connectors/postgrestunconnector/connector.go
@@ -4,7 +4,6 @@ import (
"context"
"crypto/tls"
"database/sql/driver"
- "fmt"
"log/slog"
"net"
@@ -112,7 +111,6 @@ func New(
}
func (c *Connector) Connect(_ context.Context) (driver.Conn, error) {
- fmt.Println("opening", c.connStr)
return c.driver.Open(c.connStr)
}
From b9216c91b19d6ff57d5f8c212e23fd452272dcd7 Mon Sep 17 00:00:00 2001
From: Nick Z <2420177+nickzelei@users.noreply.github.com>
Date: Thu, 27 Mar 2025 13:51:30 -0700
Subject: [PATCH 08/11] removes more formats
---
internal/sshtunnel/dialer.go | 2 --
1 file changed, 2 deletions(-)
diff --git a/internal/sshtunnel/dialer.go b/internal/sshtunnel/dialer.go
index c2c9bd0b1e..7255886363 100644
--- a/internal/sshtunnel/dialer.go
+++ b/internal/sshtunnel/dialer.go
@@ -113,7 +113,6 @@ const (
)
func (s *SSHDialer) getClient(ctx context.Context) (*ssh.Client, error) {
- s.logger.Debug("getting ssh client")
s.clientmu.Lock()
defer s.clientmu.Unlock()
@@ -144,7 +143,6 @@ func (s *SSHDialer) getClient(ctx context.Context) (*ssh.Client, error) {
s.client = client
s.startKeepAlive(client)
- s.logger.Debug("dialed ssh client")
return client, nil
}
From fb46e4cc301874354e93b037d2ff601573e1cc1b Mon Sep 17 00:00:00 2001
From: Nick Z <2420177+nickzelei@users.noreply.github.com>
Date: Thu, 27 Mar 2025 13:53:33 -0700
Subject: [PATCH 09/11] Adds debug for keepalive loop
---
internal/sshtunnel/dialer.go | 2 ++
1 file changed, 2 insertions(+)
diff --git a/internal/sshtunnel/dialer.go b/internal/sshtunnel/dialer.go
index 7255886363..8a01c1c833 100644
--- a/internal/sshtunnel/dialer.go
+++ b/internal/sshtunnel/dialer.go
@@ -208,6 +208,8 @@ func (s *SSHDialer) startKeepAlive(client *ssh.Client) {
s.logger.Error("keepalive failed", "error", err)
s.client = nil
client.Close()
+ } else {
+ s.logger.Debug("keepalive successful")
}
case <-ctx.Done():
s.logger.Error("keepalive timed out")
From 266c4f39759813172e06c5b6b376eeed1d763918 Mon Sep 17 00:00:00 2001
From: Nick Z <2420177+nickzelei@users.noreply.github.com>
Date: Thu, 27 Mar 2025 13:54:54 -0700
Subject: [PATCH 10/11] unexports ssh utils
---
internal/sshtunnel/utils.go | 12 ++++++------
internal/sshtunnel/utils_test.go | 30 +++++++++++++++---------------
2 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/internal/sshtunnel/utils.go b/internal/sshtunnel/utils.go
index a2082a930c..6129157c58 100644
--- a/internal/sshtunnel/utils.go
+++ b/internal/sshtunnel/utils.go
@@ -11,7 +11,7 @@ import (
"golang.org/x/crypto/ssh"
)
-func GetPrivateKeyAuthMethod(keyBytes []byte, passphrase *string) (ssh.AuthMethod, error) {
+func getPrivateKeyAuthMethod(keyBytes []byte, passphrase *string) (ssh.AuthMethod, error) {
if passphrase != nil && *passphrase != "" {
return getEncryptedPrivateKeyAuthMethod(keyBytes, []byte(*passphrase))
}
@@ -34,7 +34,7 @@ func getPlaintextPrivateKeyAuthMethod(keyBytes []byte) (ssh.AuthMethod, error) {
return ssh.PublicKeys(key), nil
}
-func ParseSshKey(keyString string) (ssh.PublicKey, error) {
+func parseSshKey(keyString string) (ssh.PublicKey, error) {
// Parse the key
publicKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(keyString)) //nolint
if err != nil {
@@ -46,7 +46,7 @@ func ParseSshKey(keyString string) (ssh.PublicKey, error) {
// Auth Method is optional and will return nil if there is no valid method.
// Will only return error if unable to parse the private key into an auth method
-func GetTunnelAuthMethodFromSshConfig(auth *mgmtv1alpha1.SSHAuthentication) (ssh.AuthMethod, error) {
+func getTunnelAuthMethodFromSshConfig(auth *mgmtv1alpha1.SSHAuthentication) (ssh.AuthMethod, error) {
if auth == nil {
return nil, nil
}
@@ -54,7 +54,7 @@ func GetTunnelAuthMethodFromSshConfig(auth *mgmtv1alpha1.SSHAuthentication) (ssh
case *mgmtv1alpha1.SSHAuthentication_Passphrase:
return ssh.Password(config.Passphrase.Value), nil
case *mgmtv1alpha1.SSHAuthentication_PrivateKey:
- authMethod, err := GetPrivateKeyAuthMethod([]byte(config.PrivateKey.Value), config.PrivateKey.Passphrase)
+ authMethod, err := getPrivateKeyAuthMethod([]byte(config.PrivateKey.Value), config.PrivateKey.Passphrase)
if err != nil {
return nil, err
}
@@ -80,7 +80,7 @@ func GetTunnelConfigFromSSHDto(tunnel *mgmtv1alpha1.SSHTunnel) (*DtoTunnelConfig
return nil, fmt.Errorf("unable to build host key callback: %w", err)
}
- authmethod, err := GetTunnelAuthMethodFromSshConfig(tunnel.GetAuthentication())
+ authmethod, err := getTunnelAuthMethodFromSshConfig(tunnel.GetAuthentication())
if err != nil {
return nil, fmt.Errorf("unable to parse ssh auth method: %w", err)
}
@@ -111,7 +111,7 @@ func getSshAddr(tunnel *mgmtv1alpha1.SSHTunnel) string {
func buildHostKeyCallback(tunnel *mgmtv1alpha1.SSHTunnel) (ssh.HostKeyCallback, error) {
if tunnel.GetKnownHostPublicKey() != "" {
- publickey, err := ParseSshKey(tunnel.GetKnownHostPublicKey())
+ publickey, err := parseSshKey(tunnel.GetKnownHostPublicKey())
if err != nil {
return nil, fmt.Errorf("unable to parse ssh known host public key: %w", err)
}
diff --git a/internal/sshtunnel/utils_test.go b/internal/sshtunnel/utils_test.go
index ac409cc791..68c8346612 100644
--- a/internal/sshtunnel/utils_test.go
+++ b/internal/sshtunnel/utils_test.go
@@ -28,24 +28,24 @@ UmTDjHp2ZBeXOtnQniimAAAAEHRlc3RAZXhhbXBsZS5jb20BAgMEBQ==
unencryptedPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJVer1BVE7oIuxR6Z5uP/yBwUmTDjHp2ZBeXOtnQniim test@example.com"
)
-func Test_GetPrivateKeyAuthMethod(t *testing.T) {
- out, err := GetPrivateKeyAuthMethod([]byte(encryptedPrivateKey), ptr(encryptedPrivateKeyPass))
+func Test_getPrivateKeyAuthMethod(t *testing.T) {
+ out, err := getPrivateKeyAuthMethod([]byte(encryptedPrivateKey), ptr(encryptedPrivateKeyPass))
assert.NoError(t, err)
assert.NotNil(t, out)
- out, err = GetPrivateKeyAuthMethod([]byte(encryptedPrivateKey), ptr("badpassword"))
+ out, err = getPrivateKeyAuthMethod([]byte(encryptedPrivateKey), ptr("badpassword"))
assert.Error(t, err)
assert.Nil(t, out)
- out, err = GetPrivateKeyAuthMethod([]byte("bad key"), ptr(encryptedPrivateKeyPass))
+ out, err = getPrivateKeyAuthMethod([]byte("bad key"), ptr(encryptedPrivateKeyPass))
assert.Error(t, err)
assert.Nil(t, out)
- out, err = GetPrivateKeyAuthMethod([]byte(unencryptedPrivateKey), nil)
+ out, err = getPrivateKeyAuthMethod([]byte(unencryptedPrivateKey), nil)
assert.NoError(t, err)
assert.NotNil(t, out)
- out, err = GetPrivateKeyAuthMethod([]byte("bad key"), nil)
+ out, err = getPrivateKeyAuthMethod([]byte("bad key"), nil)
assert.Error(t, err)
assert.Nil(t, out)
}
@@ -54,26 +54,26 @@ func ptr[T any](val T) *T {
return &val
}
-func Test_ParseSshKey(t *testing.T) {
- pk, err := ParseSshKey(unencryptedPublicKey)
+func Test_parseSshKey(t *testing.T) {
+ pk, err := parseSshKey(unencryptedPublicKey)
assert.NoError(t, err)
assert.NotNil(t, pk)
- pk, err = ParseSshKey("bad key")
+ pk, err = parseSshKey("bad key")
assert.Error(t, err)
assert.Nil(t, pk)
}
-func Test_GetTunnelAuthMethodFromSshConfig(t *testing.T) {
- out, err := GetTunnelAuthMethodFromSshConfig(nil)
+func Test_getTunnelAuthMethodFromSshConfig(t *testing.T) {
+ out, err := getTunnelAuthMethodFromSshConfig(nil)
assert.NoError(t, err)
assert.Nil(t, out)
- out, err = GetTunnelAuthMethodFromSshConfig(&mgmtv1alpha1.SSHAuthentication{})
+ out, err = getTunnelAuthMethodFromSshConfig(&mgmtv1alpha1.SSHAuthentication{})
assert.NoError(t, err)
assert.Nil(t, out)
- out, err = GetTunnelAuthMethodFromSshConfig(&mgmtv1alpha1.SSHAuthentication{
+ out, err = getTunnelAuthMethodFromSshConfig(&mgmtv1alpha1.SSHAuthentication{
AuthConfig: &mgmtv1alpha1.SSHAuthentication_Passphrase{
Passphrase: &mgmtv1alpha1.SSHPassphrase{
Value: "foo",
@@ -83,7 +83,7 @@ func Test_GetTunnelAuthMethodFromSshConfig(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, out)
- out, err = GetTunnelAuthMethodFromSshConfig(&mgmtv1alpha1.SSHAuthentication{
+ out, err = getTunnelAuthMethodFromSshConfig(&mgmtv1alpha1.SSHAuthentication{
AuthConfig: &mgmtv1alpha1.SSHAuthentication_PrivateKey{
PrivateKey: &mgmtv1alpha1.SSHPrivateKey{
Value: encryptedPrivateKey,
@@ -94,7 +94,7 @@ func Test_GetTunnelAuthMethodFromSshConfig(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, out)
- out, err = GetTunnelAuthMethodFromSshConfig(&mgmtv1alpha1.SSHAuthentication{
+ out, err = getTunnelAuthMethodFromSshConfig(&mgmtv1alpha1.SSHAuthentication{
AuthConfig: &mgmtv1alpha1.SSHAuthentication_PrivateKey{
PrivateKey: &mgmtv1alpha1.SSHPrivateKey{
Value: encryptedPrivateKey,
From 37caf46a837bc6a58838d45d69d831636ff2ce9d Mon Sep 17 00:00:00 2001
From: Nick Z <2420177+nickzelei@users.noreply.github.com>
Date: Thu, 27 Mar 2025 14:06:33 -0700
Subject: [PATCH 11/11] regens mocks
---
.../mock_ConnectionServiceClient.go | 118 ++++++++++++++++++
1 file changed, 118 insertions(+)
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_ConnectionServiceClient.go b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_ConnectionServiceClient.go
index a5fc440cd9..d73319b976 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_ConnectionServiceClient.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_ConnectionServiceClient.go
@@ -142,6 +142,124 @@ func (_c *MockConnectionServiceClient_CheckConnectionConfigById_Call) RunAndRetu
return _c
}
+// CheckSSHConnection provides a mock function with given fields: _a0, _a1
+func (_m *MockConnectionServiceClient) CheckSSHConnection(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.CheckSSHConnectionRequest]) (*connect.Response[mgmtv1alpha1.CheckSSHConnectionResponse], error) {
+ ret := _m.Called(_a0, _a1)
+
+ if len(ret) == 0 {
+ panic("no return value specified for CheckSSHConnection")
+ }
+
+ var r0 *connect.Response[mgmtv1alpha1.CheckSSHConnectionResponse]
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, *connect.Request[mgmtv1alpha1.CheckSSHConnectionRequest]) (*connect.Response[mgmtv1alpha1.CheckSSHConnectionResponse], error)); ok {
+ return rf(_a0, _a1)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, *connect.Request[mgmtv1alpha1.CheckSSHConnectionRequest]) *connect.Response[mgmtv1alpha1.CheckSSHConnectionResponse]); ok {
+ r0 = rf(_a0, _a1)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*connect.Response[mgmtv1alpha1.CheckSSHConnectionResponse])
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, *connect.Request[mgmtv1alpha1.CheckSSHConnectionRequest]) error); ok {
+ r1 = rf(_a0, _a1)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockConnectionServiceClient_CheckSSHConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckSSHConnection'
+type MockConnectionServiceClient_CheckSSHConnection_Call struct {
+ *mock.Call
+}
+
+// CheckSSHConnection is a helper method to define mock.On call
+// - _a0 context.Context
+// - _a1 *connect.Request[mgmtv1alpha1.CheckSSHConnectionRequest]
+func (_e *MockConnectionServiceClient_Expecter) CheckSSHConnection(_a0 interface{}, _a1 interface{}) *MockConnectionServiceClient_CheckSSHConnection_Call {
+ return &MockConnectionServiceClient_CheckSSHConnection_Call{Call: _e.mock.On("CheckSSHConnection", _a0, _a1)}
+}
+
+func (_c *MockConnectionServiceClient_CheckSSHConnection_Call) Run(run func(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.CheckSSHConnectionRequest])) *MockConnectionServiceClient_CheckSSHConnection_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(*connect.Request[mgmtv1alpha1.CheckSSHConnectionRequest]))
+ })
+ return _c
+}
+
+func (_c *MockConnectionServiceClient_CheckSSHConnection_Call) Return(_a0 *connect.Response[mgmtv1alpha1.CheckSSHConnectionResponse], _a1 error) *MockConnectionServiceClient_CheckSSHConnection_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockConnectionServiceClient_CheckSSHConnection_Call) RunAndReturn(run func(context.Context, *connect.Request[mgmtv1alpha1.CheckSSHConnectionRequest]) (*connect.Response[mgmtv1alpha1.CheckSSHConnectionResponse], error)) *MockConnectionServiceClient_CheckSSHConnection_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// CheckSSHConnectionById provides a mock function with given fields: _a0, _a1
+func (_m *MockConnectionServiceClient) CheckSSHConnectionById(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.CheckSSHConnectionByIdRequest]) (*connect.Response[mgmtv1alpha1.CheckSSHConnectionByIdResponse], error) {
+ ret := _m.Called(_a0, _a1)
+
+ if len(ret) == 0 {
+ panic("no return value specified for CheckSSHConnectionById")
+ }
+
+ var r0 *connect.Response[mgmtv1alpha1.CheckSSHConnectionByIdResponse]
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, *connect.Request[mgmtv1alpha1.CheckSSHConnectionByIdRequest]) (*connect.Response[mgmtv1alpha1.CheckSSHConnectionByIdResponse], error)); ok {
+ return rf(_a0, _a1)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, *connect.Request[mgmtv1alpha1.CheckSSHConnectionByIdRequest]) *connect.Response[mgmtv1alpha1.CheckSSHConnectionByIdResponse]); ok {
+ r0 = rf(_a0, _a1)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*connect.Response[mgmtv1alpha1.CheckSSHConnectionByIdResponse])
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, *connect.Request[mgmtv1alpha1.CheckSSHConnectionByIdRequest]) error); ok {
+ r1 = rf(_a0, _a1)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockConnectionServiceClient_CheckSSHConnectionById_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckSSHConnectionById'
+type MockConnectionServiceClient_CheckSSHConnectionById_Call struct {
+ *mock.Call
+}
+
+// CheckSSHConnectionById is a helper method to define mock.On call
+// - _a0 context.Context
+// - _a1 *connect.Request[mgmtv1alpha1.CheckSSHConnectionByIdRequest]
+func (_e *MockConnectionServiceClient_Expecter) CheckSSHConnectionById(_a0 interface{}, _a1 interface{}) *MockConnectionServiceClient_CheckSSHConnectionById_Call {
+ return &MockConnectionServiceClient_CheckSSHConnectionById_Call{Call: _e.mock.On("CheckSSHConnectionById", _a0, _a1)}
+}
+
+func (_c *MockConnectionServiceClient_CheckSSHConnectionById_Call) Run(run func(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.CheckSSHConnectionByIdRequest])) *MockConnectionServiceClient_CheckSSHConnectionById_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(*connect.Request[mgmtv1alpha1.CheckSSHConnectionByIdRequest]))
+ })
+ return _c
+}
+
+func (_c *MockConnectionServiceClient_CheckSSHConnectionById_Call) Return(_a0 *connect.Response[mgmtv1alpha1.CheckSSHConnectionByIdResponse], _a1 error) *MockConnectionServiceClient_CheckSSHConnectionById_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockConnectionServiceClient_CheckSSHConnectionById_Call) RunAndReturn(run func(context.Context, *connect.Request[mgmtv1alpha1.CheckSSHConnectionByIdRequest]) (*connect.Response[mgmtv1alpha1.CheckSSHConnectionByIdResponse], error)) *MockConnectionServiceClient_CheckSSHConnectionById_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
// CheckSqlQuery provides a mock function with given fields: _a0, _a1
func (_m *MockConnectionServiceClient) CheckSqlQuery(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.CheckSqlQueryRequest]) (*connect.Response[mgmtv1alpha1.CheckSqlQueryResponse], error) {
ret := _m.Called(_a0, _a1)