Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destination connector implementation. #1

Merged
merged 47 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
c61252d
feat: package sftp
parikshitg Dec 12, 2024
7eba265
feat: updated initial template and added config
parikshitg Dec 12, 2024
18c505f
feat: updated config
parikshitg Dec 12, 2024
dc7f09e
feat: destination configuration
parikshitg Dec 12, 2024
04f08b5
feat: config test and error handling
parikshitg Dec 12, 2024
046ca3a
feat: initial source implementation
Dec 13, 2024
b82c95a
feat: sftp client
parikshitg Dec 13, 2024
56a3958
feat: upload file
parikshitg Dec 16, 2024
5577c00
feat: handled host key callback
parikshitg Dec 17, 2024
51504ef
feat: adding test cases
parikshitg Dec 18, 2024
718a325
fix: lint
parikshitg Dec 18, 2024
f08eefb
seperated source logic into iterator
Dec 18, 2024
9d261ea
feat: acceptance
parikshitg Dec 18, 2024
973c84e
fix: small fix
parikshitg Dec 18, 2024
a7d4738
feat: readme
parikshitg Dec 18, 2024
80d9c03
added test cases
Dec 18, 2024
1fd6eee
added integration test
Dec 18, 2024
cbe9f1a
remove go generate directive from source
Dec 18, 2024
e89ed03
fix: test cases
Dec 18, 2024
4c4458d
fix: teardown
Dec 18, 2024
65ddecc
added go header
Dec 19, 2024
b1e919d
added file chunk mechanism for files larger than 3 mb
Dec 19, 2024
154627f
added configurable chunk
Dec 19, 2024
47bfd60
fix: updated readme
Dec 20, 2024
3ee28ab
fix: large file processing
Dec 20, 2024
49103a8
feat: upload large file
parikshitg Dec 20, 2024
59ddc6d
fix: refactored iterator
Dec 21, 2024
0860cb6
fix: handle file modification while read
Jan 6, 2025
bb4b335
fix: conflicts
Jan 6, 2025
5c1e4b9
fix: source integration test
Jan 6, 2025
ab09b17
modify README file
Jan 6, 2025
5f78243
Merge branch 'feat/source' of github.com:conduitio-labs/conduit-conne…
parikshitg Jan 8, 2025
76c91f7
feat: merged source
parikshitg Jan 8, 2025
84087a4
fix: source acceptance to be handled separately
parikshitg Jan 8, 2025
4c82f15
fix: refactored iterator to provide record on demand
Jan 9, 2025
39e1020
Merge branch 'feat/source' of github.com:conduitio-labs/conduit-conne…
parikshitg Jan 10, 2025
393adb2
feat: handled missing filename in metadata
parikshitg Jan 10, 2025
6c870ec
added source and destination directories in docker compose
Jan 10, 2025
e374c2d
fix: test workflow
Jan 10, 2025
b6947af
fix: refactored
parikshitg Jan 10, 2025
3fe8e59
Merge branch 'feat/source' of github.com:conduitio-labs/conduit-conne…
parikshitg Jan 10, 2025
6756d54
fix: source and destination tests
parikshitg Jan 10, 2025
2bba150
fix: linters
parikshitg Jan 10, 2025
c9b7c2a
Merge branch 'main' of github.com:conduitio-labs/conduit-connector-sf…
parikshitg Jan 14, 2025
e0fafc9
fix: actual filesize in source metadata and handle filename from rawd…
parikshitg Jan 14, 2025
16698e8
fix: source raw key
parikshitg Jan 15, 2025
3b0a58d
fix: source middleware
parikshitg Jan 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ Fixes # (issue)

### Quick checks:

- [ ] There is no other [pull request](https://github.com/conduitio/conduit-connector-connectorname/pulls) for the same update/change.
- [ ] There is no other [pull request](https://github.com/conduitio/conduit-connector-sftp/pulls) for the same update/change.
- [ ] I have written unit tests.
- [ ] I have made sure that the PR is of reasonable size and can be easily reviewed.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
.vscode

# Binary, built with `make build`
/conduit-connector-connectorname
/conduit-connector-sftp

### OS ###
.DS_Store
13 changes: 13 additions & 0 deletions .golangci.goheader.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright © {{ copyright-year }} Meroxa, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
7 changes: 6 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ linters-settings:
- .WithMessagef(
- .WithStack(
- (context.Context).Err()

goheader:
template-path: '.golangci.goheader.template'
values:
regexp:
copyright-year: 20[2-9]\d

issues:
exclude-rules:
- path: _test\.go
Expand Down
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ builds:
env:
- CGO_ENABLED=0
ldflags:
- "-s -w -X 'github.com/conduitio/conduit-connector-connectorname.version={{ .Tag }}'"
- "-s -w -X 'github.com/conduitio/conduit-connector-sftp.version={{ .Tag }}'"
checksum:
name_template: checksums.txt
archives:
Expand Down
22 changes: 13 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ VERSION=$(shell git describe --tags --dirty --always)

.PHONY: build
build:
go build -ldflags "-X 'github.com/conduitio/conduit-connector-connectorname.version=${VERSION}'" -o conduit-connector-connectorname cmd/connector/main.go
go build -ldflags "-X 'github.com/conduitio/conduit-connector-sftp.version=${VERSION}'" -o conduit-connector-sftp cmd/connector/main.go

.PHONY: test
test:
Expand All @@ -16,6 +16,18 @@ test-integration:
docker compose -f test/docker-compose.yml down; \
exit $$ret

.PHONY: gofumpt
gofumpt:
go install mvdan.cc/gofumpt@latest

.PHONY: fmt
fmt: gofumpt
gofumpt -l -w .

.PHONY: lint
lint:
golangci-lint run -v

.PHONY: generate
generate:
go generate ./...
Expand All @@ -25,11 +37,3 @@ install-tools:
@echo Installing tools from tools.go
@go list -e -f '{{ join .Imports "\n" }}' tools.go | xargs -I % go list -f "%@{{.Module.Version}}" % | xargs -tI % go install %
@go mod tidy

.PHONY: fmt
fmt:
gofumpt -l -w .

.PHONY: lint
lint:
golangci-lint run
85 changes: 34 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,48 @@
# Conduit Connector Template
# Conduit Connector SFTP

This is a template project for building [Conduit](https://conduit.io) connectors in Go. It makes it possible to
start working on a Conduit connector in a matter of seconds.
The SFTP connector is one of [Conduit](https://github.com/ConduitIO/conduit) plugins. It
provides both, a source and a destination SFTP connector.

## Quick start
## How to build it

1. Click [_Use this template_](https://github.com/new?template_name=conduit-connector-template&template_owner=ConduitIO) and clone your new repository.
2. Initialize the repository using [`setup.sh`](https://github.com/ConduitIO/conduit-connector-template/blob/main/setup.sh) and commit your changes.
```sh
./setup.sh github.com/myusername/conduit-connector-myconnector
git add -A
git commit -m "initialize repository"
```
3. Set up [automatic Dependabot PR merges](#automatically-merging-dependabot-prs).
Run `make build`.

With that, you're all set up and ready to start working on your connector! As a next step, we recommend that you
check out the [Conduit Connector SDK](https://github.com/ConduitIO/conduit-connector-sdk).
## Testing

## What's included?
Run `make test` to run all the unit and integration tests.

* Skeleton code for the connector's configuration, source and destination.
* Example unit tests.
* A [Makefile](/Makefile) with commonly used targets.
* A [GitHub workflow](/.github/workflows/test.yml) to build the code and run the tests.
* A [GitHub workflow](/.github/workflows/lint.yml) to run a pre-configured set of linters.
* A [GitHub workflow](/.github/workflows/release.yml) which automatically creates a release when a tag is pushed.
* A [Dependabot setup](/.github/dependabot.yml) which checks your dependencies for available updates and
[merges minor version upgrades](/.github/workflows/dependabot-auto-merge-go.yml) automatically.
* [Issue](/.github/ISSUE_TEMPLATE) and [PR templates](/.github/pull_request_template.md).
* A [README template](/README_TEMPLATE.md).
## Source

## Automatically merging Dependabot PRs
The source SFTP connector monitors a directory on an SFTP server for files matching a specified pattern. It reads these files and converts them into `opencdc.Record` that can be processed by Conduit. For handling large files, it splits them into smaller chunks, enabling smooth data handling through the Conduit pipeline.
The connector supports both password and private key authentication methods.

> [!NOTE]
> This applies only to public connector repositories, as branch protection rules are not enforced in private repositories.
### Configuration Options

The template makes it simple to keep your connector up-to-date using automatic merging of
[Dependabot](https://github.com/dependabot) PRs. To make use of this setup, you need to adjust
some repository settings.
| name | description | required | default value |
| -------------- | ----------------------------------------------------------------------------------------------------- | -------- | -------- |
| `address` | Address is the address of the sftp server to connect.| **true** | |
| `hostKey` | HostKey is the key used for host key callback validation.| **true** | |
| `username`| User is the username of the SFTP user. | **true** | |
| `password`| Password is the SFTP password (can be used as passphrase for private key). | false | |
| `privateKeyPath`| PrivateKeyPath is the private key for ssh login.| false | |
| `directoryPath` | DirectoryPath is the path to the directory to read data. | **true** | |
| `filePattern` | Pattern to match files that should be read (e.g., "*.txt") | false | `*` |
| `pollingPeriod` | Duration for polling SFTP for fetching new records. | false | `5s` |
| `fileChunkSizeBytes` | Maximum size of a file chunk in bytes to split large files. | false | `3145728` |

1. Navigate to Settings -> General and allow auto-merge of PRs.
## Destination

![Allow auto-merge](https://github.com/ConduitIO/conduit-connector-template/assets/8320753/695b15f0-85b4-49cb-966d-649e9bf03455)
Destination connects to a remote server. It takes an `opencdc.Record`, extracts filename from the metadata and upload the file to the remote server. The connector supports both password and private key authentication methods.

2. Navigate to Settings -> Branches and add a branch protection rule.
### Configuration Options

![Add branch protection rule](https://github.com/ConduitIO/conduit-connector-template/assets/8320753/9f5a07bc-d141-42b9-9918-e8d9cc648482)
| name | description | required |
| -------------- | ----------------------------------------------------------------------------------------------------- | -------- |
| `address` | Address is the address of the sftp server to connect.| **true** |
| `hostKey` | HostKey is the key used for host key callback validation.| **true** |
| `username`| User is the username of the SFTP user. | **true** |
| `password`| Password is the SFTP password (can be used as passphrase for private key). | false |
| `privateKeyPath`| PrivateKeyPath is the private key for ssh login.| false |
| `directoryPath` | DirectoryPath is the path to the directory to read/write data. | true |

3. Create a rule for branch `main` that requires status checks `build` and `golangci-lint`.

![Status checks](https://github.com/ConduitIO/conduit-connector-template/assets/8320753/96219185-c329-432a-8623-9b4462015f32)

## Recommended repository settings

- Allow squash merging only.
- Always suggest updating pull request branches.
- Automatically delete head branches.
- Branch protection rules on branch `main` (only in public repositories):
- Require a pull request before merging.
- Require approvals.
- Require status checks `build` and `golangci-lint`.
- Require branches to be up to date before merging.
- Require conversation resolution before merging.
- Do not allow bypassing the above settings.
![scarf pixel](https://static.scarf.sh/a.png?x-pxid=64b333ae-77ad-4895-a5cd-a73bb14362d9)
94 changes: 94 additions & 0 deletions acceptance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright © 2024 Meroxa, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sftp

import (
"fmt"
"os/exec"
"sync/atomic"
"testing"
"time"

"github.com/conduitio-labs/conduit-connector-sftp/config"
"github.com/conduitio-labs/conduit-connector-sftp/destination"
"github.com/conduitio/conduit-commons/opencdc"
sdk "github.com/conduitio/conduit-connector-sdk"
)

type driver struct {
sdk.ConfigurableAcceptanceTestDriver
id int64
}

func (d *driver) GenerateRecord(_ *testing.T, _ opencdc.Operation) opencdc.Record {
atomic.AddInt64(&d.id, 1)

content := []byte("hello world")
filename := fmt.Sprintf("%d.txt", d.id)

return sdk.Util.Source.NewRecordCreate(
nil,
map[string]string{
opencdc.MetadataCollection: "upload",
opencdc.MetadataCreatedAt: time.Now().UTC().Format(time.RFC3339),
"filename": filename,
"source_path": "/upload",
"file_size": fmt.Sprintf("%d", len(content)),
"mod_time": time.Now().UTC().Format(time.RFC3339),
},
opencdc.StructuredData{"filename": filename},
opencdc.RawData(content),
)
}

func (d *driver) ReadFromDestination(_ *testing.T, records []opencdc.Record) []opencdc.Record {
return records
}

func TestAcceptance(t *testing.T) {
hostKey, err := setupHostKey()
if err != nil {
fmt.Println(err)
return
}

sdk.AcceptanceTest(t, &driver{
ConfigurableAcceptanceTestDriver: sdk.ConfigurableAcceptanceTestDriver{
Config: sdk.ConfigurableAcceptanceTestDriverConfig{
Connector: sdk.Connector{
NewSpecification: Specification,
NewDestination: destination.NewDestination,
NewSource: nil,
},
DestinationConfig: map[string]string{
config.ConfigAddress: "localhost:2222",
config.ConfigHostKey: hostKey,
config.ConfigUsername: "user",
config.ConfigPassword: "pass",
config.ConfigDirectoryPath: "/upload",
},
},
},
})
}

func setupHostKey() (string, error) {
cmd := exec.Command("ssh-keyscan", "-t", "rsa", "-p", "2222", "localhost")
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("error setupHostKey: %w", err)
}
return string(output), nil
}
18 changes: 16 additions & 2 deletions cmd/connector/main.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
// Copyright © 2024 Meroxa, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
connectorname "github.com/conduitio/conduit-connector-connectorname"
sftp "github.com/conduitio-labs/conduit-connector-sftp"
sdk "github.com/conduitio/conduit-connector-sdk"
)

func main() {
sdk.Serve(connectorname.Connector)
sdk.Serve(sftp.Connector)
}
Loading
Loading