From 5991b8f0aa75c44d7e37d8c598903ed48adaecc7 Mon Sep 17 00:00:00 2001 From: Ed <4785890+edwardfward@users.noreply.github.com> Date: Sat, 30 Nov 2024 12:00:41 -0600 Subject: [PATCH] adding pemContents and errs for defer funcs Signed-off-by: Ed <4785890+edwardfward@users.noreply.github.com> --- bindings/mysql/metadata.yaml | 9 +++++ bindings/mysql/mysql.go | 46 +++++++++++++++++------- bindings/mysql/mysql_integration_test.go | 6 +++- bindings/mysql/mysql_test.go | 22 ++++++++++-- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/bindings/mysql/metadata.yaml b/bindings/mysql/metadata.yaml index 005b2e7637..5e2257b4fd 100644 --- a/bindings/mysql/metadata.yaml +++ b/bindings/mysql/metadata.yaml @@ -29,6 +29,15 @@ metadata: description: "Path to the PEM file. Used with SSL connection" example: '"path/to/pem/file"' type: string + - name: pemContents + required: false + description: "Base64-encoded PEM file contents. Used with SSL connection. Supersedes pemPath if both provided." + example: '"-----BEGIN CERTIFICATE----- + MIIFaDCCBFCgAwIBAgISESHkvZFwK9Qz0KsXD3x8p44aMA0GCSqGSIb3DQEBCwUA + ... + bml6YXRpb252YWxzaGEyZzIuY3JsMIGgBggrBgEFBQcBAQSBkzCBkDBNBggrBgEF + -----END CERTIFICATE-----"' + type: string - name: maxIdleConns required: false description: "The max idle connections. Integer greater than 0" diff --git a/bindings/mysql/mysql.go b/bindings/mysql/mysql.go index 4bcd99a009..c68ac1da8d 100644 --- a/bindings/mysql/mysql.go +++ b/bindings/mysql/mysql.go @@ -80,6 +80,10 @@ type mysqlMetadata struct { // PemPath is the path to the pem file to connect to MySQL over SSL. PemPath string `mapstructure:"pemPath"` + // PemContents is the contents of the pem file to connect to MySQL over SSL. + // PemContents supersedes PemPath if both are provided. + PemContents string `mapstructure:"pemContents"` + // MaxIdleConns is the maximum number of connections in the idle connection pool. MaxIdleConns int `mapstructure:"maxIdleConns"` @@ -117,7 +121,19 @@ func (m *Mysql) Init(ctx context.Context, md bindings.Metadata) error { return errors.New("missing MySql connection string") } - m.db, err = initDB(meta.URL, meta.PemPath) + var pemContents []byte + + // meta.PemContents supersedes meta.PemPath if both are provided. + if meta.PemContents != "" { + pemContents = []byte(meta.PemContents) + } else if meta.PemPath != "" { + pemContents, err = os.ReadFile(meta.PemPath) + if err != nil { + return fmt.Errorf("unable to read pem file: %w", err) + } + } + + m.db, err = initDB(meta.URL, pemContents) if err != nil { return err } @@ -234,7 +250,9 @@ func (m *Mysql) Close() error { } if m.db != nil { - m.db.Close() + if err := m.db.Close(); err != nil { + m.logger.Warnf("error closing DB: %v", err) + } m.db = nil } @@ -246,7 +264,12 @@ func (m *Mysql) query(ctx context.Context, sql string, params ...any) ([]byte, e if err != nil { return nil, fmt.Errorf("error executing query: %w", err) } - defer rows.Close() + + defer func() { + if err = rows.Close(); err != nil { + m.logger.Warnf("error closing rows: %v", err) + } + }() result, err := m.jsonify(rows) if err != nil { @@ -265,21 +288,15 @@ func (m *Mysql) exec(ctx context.Context, sql string, params ...any) (int64, err return res.RowsAffected() } -func initDB(url, pemPath string) (*sql.DB, error) { +func initDB(url string, pemContents []byte) (*sql.DB, error) { conf, err := mysql.ParseDSN(url) if err != nil { return nil, fmt.Errorf("illegal Data Source Name (DSN) specified by %s", connectionURLKey) } - if pemPath != "" { - var pem []byte + if len(pemContents) != 0 { rootCertPool := x509.NewCertPool() - pem, err = os.ReadFile(pemPath) - if err != nil { - return nil, fmt.Errorf("error reading PEM file from %s: %w", pemPath, err) - } - - ok := rootCertPool.AppendCertsFromPEM(pem) + ok := rootCertPool.AppendCertsFromPEM(pemContents) if !ok { return nil, errors.New("failed to append PEM") } @@ -373,6 +390,9 @@ func (m *Mysql) convert(columnTypes []*sql.ColumnType, values []any) map[string] // GetComponentMetadata returns the metadata of the component. func (m *Mysql) GetComponentMetadata() (metadataInfo metadata.MetadataMap) { metadataStruct := mysqlMetadata{} - metadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo, metadata.BindingType) + if err := metadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo, metadata.BindingType); err != nil { + m.logger.Warnf("error retrieving metadata info: %v", err) + } + return } diff --git a/bindings/mysql/mysql_integration_test.go b/bindings/mysql/mysql_integration_test.go index eadeaa31c1..1e669505ce 100644 --- a/bindings/mysql/mysql_integration_test.go +++ b/bindings/mysql/mysql_integration_test.go @@ -63,7 +63,11 @@ func TestMysqlIntegration(t *testing.T) { err := b.Init(context.Background(), m) require.NoError(t, err) - defer b.Close() + defer func() { + if err = b.Close(); err != nil { + t.Errorf("failed to close database: %s", err) + } + }() t.Run("Invoke create table", func(t *testing.T) { res, err := b.Invoke(context.Background(), &bindings.InvokeRequest{ diff --git a/bindings/mysql/mysql_test.go b/bindings/mysql/mysql_test.go index 7c7b3d8955..8f23d717bc 100644 --- a/bindings/mysql/mysql_test.go +++ b/bindings/mysql/mysql_test.go @@ -30,7 +30,12 @@ import ( func TestQuery(t *testing.T) { m, mock, _ := mockDatabase(t) - defer m.Close() + + defer func() { + if err := m.Close(); err != nil { + t.Errorf("failed to close database: %s", err) + } + }() t.Run("no dbType provided", func(t *testing.T) { rows := sqlmock.NewRows([]string{"id", "value", "timestamp"}). @@ -83,7 +88,13 @@ func TestQuery(t *testing.T) { func TestExec(t *testing.T) { m, mock, _ := mockDatabase(t) - defer m.Close() + + defer func() { + if err := m.Close(); err != nil { + t.Errorf("failed to close database: %s", err) + } + }() + mock.ExpectExec("INSERT INTO foo \\(id, v1, ts\\) VALUES \\(.*\\)").WillReturnResult(sqlmock.NewResult(1, 1)) i, err := m.exec(context.Background(), "INSERT INTO foo (id, v1, ts) VALUES (1, 'test-1', '2021-01-22')") assert.Equal(t, int64(1), i) @@ -92,7 +103,12 @@ func TestExec(t *testing.T) { func TestInvoke(t *testing.T) { m, mock, _ := mockDatabase(t) - defer m.Close() + + defer func() { + if err := m.Close(); err != nil { + t.Errorf("failed to close database: %s", err) + } + }() t.Run("exec operation succeeds", func(t *testing.T) { mock.ExpectExec("INSERT INTO foo \\(id, v1, ts\\) VALUES \\(.*\\)").WillReturnResult(sqlmock.NewResult(1, 1))