Skip to content

Commit 7948e57

Browse files
Create target-jsonl-blob (#1)
* it's working * cleanup * add workflows * fix release input values * unmarshall into singerMessage * support more fields in key template * refactor
1 parent ed400ff commit 7948e57

File tree

17 files changed

+1578
-7
lines changed

17 files changed

+1578
-7
lines changed

.github/workflows/release.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
on:
2+
release:
3+
types: [created]
4+
jobs:
5+
releases-matrix:
6+
name: Release Go Binaries
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
include:
11+
- goos: linux
12+
goarch: "386"
13+
- goos: linux
14+
goarch: "amd64"
15+
- goos: linux
16+
goarch: "arm64"
17+
- goos: darwin
18+
goarch: "amd64"
19+
- goos: darwin
20+
goarch: "arm64"
21+
- goos: windows
22+
goarch: "386"
23+
- goos: windows
24+
goarch: "amd64"
25+
steps:
26+
- name: Checkout code
27+
uses: actions/checkout@v2
28+
- name: Release binaries
29+
uses: wangyoucao577/go-release-action@v1.25
30+
with:
31+
github_token: ${{ secrets.GITHUB_TOKEN }}
32+
goos: ${{ matrix.goos }}
33+
goarch: ${{ matrix.goarch }}
34+
goversion: "1.17"
35+
project_path: ./
36+
binary_name: target-jsonl-blob
37+
extra_files: ./README.md

.github/workflows/test.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Test
2+
on:
3+
pull_request: {}
4+
push:
5+
branches: [main]
6+
jobs:
7+
test:
8+
strategy:
9+
matrix:
10+
go-version: [1.16.x, 1.17.x]
11+
os: [ubuntu-latest, macos-latest, windows-latest]
12+
runs-on: ${{ matrix.os }}
13+
steps:
14+
- name: Install Go
15+
uses: actions/setup-go@v2
16+
with:
17+
go-version: ${{ matrix.go-version }}
18+
- name: Checkout code
19+
uses: actions/checkout@v2
20+
- name: Test
21+
run: go test ./...

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1+
# Config files
2+
*.json
3+
*.yaml
4+
!*.example.*
5+
6+
# Data files
7+
**/*.jsonl
8+
19
# Binaries for programs and plugins
10+
target-jsonl-blob
211
*.exe
312
*.exe~
413
*.dll

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
Apache License
23
Version 2.0, January 2004
34
http://www.apache.org/licenses/

README.md

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,53 @@
11
# target-jsonl-blob
22

3-
JSONL Singer target for local storage, S3, GCS and Azure Blob Storage.
3+
JSONL Singer target for local storage, S3 and Azure Blob Storage.
44

5-
## Immediate Roadmap
5+
## Configuration
66

7-
- Use Go and https://gocloud.dev/howto/blob
8-
- Build a working singer target that can write to the destinations mentioned above
9-
- Make the destination easily configurable with a "bucket" URL (e.g. `"s3://my-bucket?region=us-west-1"`)
10-
- Use a configurable template for the object keys with a rich set of available inputs (e.g. `prefix/{stream_name}/{day}/{hour}.jsonl`)
7+
| Setting | Required | Default | Description |
8+
|----------------|----------|-------------------------|-------------------------------|
9+
| `bucket` | Yes | - | Blob storage [bucket URL](#bucket-urls) |
10+
| `key_template` | No | `{{.StreamName}}.jsonl` | Template string for file keys |
11+
12+
### Bucket URLs
13+
14+
| Storage | Example URL |
15+
|---------|-----------------------------|
16+
| local | `file:///path/to/directory` |
17+
| S3 | `s3://my-bucket` |
18+
| Azure | `azblob://my-container` |
19+
20+
### Available fields for `key_template`
21+
22+
- `StreamName`
23+
- `Date` (YYYY-MM-DD)
24+
- `TimestampSeconds`
25+
- `Minute`
26+
- `Hour`
27+
- `Day`
28+
- `Month`
29+
- `Year`
30+
31+
Example: `{{.StreamName}}/{{.Year}}/{{.Month}}/{{.Day}}/{{.Hour}}/{{.Minute}}/{{.StreamName}}.jsonl`
1132

1233
## Build from source
1334

14-
_TODO_
35+
```shell
36+
go build -o target-jsonl-blob
37+
```
1538

1639
## Usage with Meltano
1740

1841
_TODO_
42+
43+
## Roadmap
44+
45+
- Support GCS
46+
47+
Currently blocked by
48+
49+
```
50+
cloud.google.com/go/storage@v1.16.1/storage.go:1416:53: o.GetCustomerEncryption().GetKeySha256 undefined (type *"google.golang.org/genproto/googleapis/storage/v2".Object_CustomerEncryption has no field or method GetKeySha256)
51+
```
52+
53+
- Build a lighter binary

cmd/root.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
Copyright © 2022 Edgar Ramírez-Mondragón
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"context"
20+
"fmt"
21+
"io"
22+
"log"
23+
"os"
24+
25+
"github.com/spf13/cobra"
26+
"github.com/spf13/viper"
27+
"meltano.com/target-jsonl-blob/target"
28+
29+
"gocloud.dev/blob"
30+
// _ "gocloud.dev/blob/gcsblob"
31+
_ "gocloud.dev/blob/fileblob"
32+
_ "gocloud.dev/blob/s3blob"
33+
)
34+
35+
var (
36+
configFile string
37+
inputFile string
38+
C target.Config
39+
)
40+
41+
var rootCmd = &cobra.Command{
42+
Use: "target-jsonl-blob",
43+
Short: "JSONLines Singer target for blob storages",
44+
Run: func(cmd *cobra.Command, args []string) {},
45+
}
46+
47+
// Execute adds all child commands to the root command and sets flags appropriately.
48+
// This is called by main.main(). It only needs to happen once to the rootCmd.
49+
func Execute() {
50+
err := rootCmd.Execute()
51+
if err != nil {
52+
os.Exit(1)
53+
}
54+
55+
readConfig(configFile)
56+
57+
if C.Bucket == "" {
58+
log.Fatalf("Bucket is required")
59+
}
60+
61+
if C.KeyTemplate == "" {
62+
C.KeyTemplate = target.DEFAULT_KEY_TEMPLATE
63+
}
64+
65+
ctx := context.Background()
66+
bucket, err := blob.OpenBucket(ctx, C.Bucket)
67+
if err != nil {
68+
log.Fatalf("Unable to open bucket, %v", err)
69+
}
70+
defer bucket.Close()
71+
72+
var lines io.Reader
73+
74+
if inputFile == "" {
75+
lines = os.Stdin
76+
} else {
77+
lines, err = os.Open(inputFile)
78+
if err != nil {
79+
fmt.Print(err)
80+
}
81+
}
82+
83+
target.ProcessLines(lines, C, ctx, bucket)
84+
85+
if inputFile == "" {
86+
target.ProcessLines(os.Stdin, C, ctx, bucket)
87+
} else {
88+
inputLines, err := os.Open(inputFile)
89+
if err != nil {
90+
fmt.Print(err)
91+
}
92+
target.ProcessLines(inputLines, C, ctx, bucket)
93+
}
94+
95+
}
96+
97+
func init() {
98+
rootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "Config file")
99+
rootCmd.PersistentFlags().StringVarP(&inputFile, "input", "i", "", "Input file")
100+
rootCmd.MarkPersistentFlagRequired("config")
101+
}
102+
103+
// initConfig reads in config file and ENV variables if set.
104+
func readConfig(file string) {
105+
viper.SetConfigFile(file)
106+
107+
if err := viper.ReadInConfig(); err == nil {
108+
log.Printf("Using config file %s", viper.ConfigFileUsed())
109+
} else {
110+
fmt.Fprintln(os.Stderr, "Config file is not valid")
111+
os.Exit(1)
112+
}
113+
114+
if err := viper.Unmarshal(&C); err != nil {
115+
log.Fatalf("Unable to decode into struct, %v", err)
116+
}
117+
}

config.example.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"bucket": "file://./output/my-bucket",
3+
"key_template": "{{.StreamName}}.jsonl"
4+
}

config.example.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bucket: file://./output/my-bucket
2+
key_template: "{{.StreamName}}.jsonl"

configS3.example.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bucket: s3://my-bucket?endpoint=http://127.0.0.1:9000&disableSSL=true&s3ForcePathStyle=true
2+
key_template: "{{.StreamName}}/{{.Year}}/{{.Month}}/{{.Day}}/{{.Hour}}/{{.Minute}}/{{.StreamName}}.jsonl"

go.mod

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
module meltano.com/target-jsonl-blob
2+
3+
go 1.17
4+
5+
require (
6+
github.com/spf13/cobra v1.3.0
7+
github.com/spf13/viper v1.10.1
8+
gocloud.dev v0.24.0
9+
)
10+
11+
require (
12+
github.com/aws/aws-sdk-go v1.40.34 // indirect
13+
github.com/aws/aws-sdk-go-v2 v1.9.0 // indirect
14+
github.com/aws/aws-sdk-go-v2/config v1.7.0 // indirect
15+
github.com/aws/aws-sdk-go-v2/credentials v1.4.0 // indirect
16+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0 // indirect
17+
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2 // indirect
18+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0 // indirect
19+
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect
20+
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect
21+
github.com/aws/smithy-go v1.8.0 // indirect
22+
github.com/fsnotify/fsnotify v1.5.1 // indirect
23+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
24+
github.com/golang/protobuf v1.5.2 // indirect
25+
github.com/google/wire v0.5.0 // indirect
26+
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
27+
github.com/hashicorp/hcl v1.0.0 // indirect
28+
github.com/inconshreveable/mousetrap v1.0.0 // indirect
29+
github.com/jmespath/go-jmespath v0.4.0 // indirect
30+
github.com/magiconair/properties v1.8.5 // indirect
31+
github.com/mitchellh/mapstructure v1.4.3 // indirect
32+
github.com/pelletier/go-toml v1.9.4 // indirect
33+
github.com/spf13/afero v1.6.0 // indirect
34+
github.com/spf13/cast v1.4.1 // indirect
35+
github.com/spf13/jwalterweatherman v1.1.0 // indirect
36+
github.com/spf13/pflag v1.0.5 // indirect
37+
github.com/subosito/gotenv v1.2.0 // indirect
38+
go.opencensus.io v0.23.0 // indirect
39+
golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
40+
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
41+
golang.org/x/text v0.3.7 // indirect
42+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
43+
google.golang.org/api v0.63.0 // indirect
44+
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
45+
google.golang.org/grpc v1.43.0 // indirect
46+
google.golang.org/protobuf v1.27.1 // indirect
47+
gopkg.in/ini.v1 v1.66.2 // indirect
48+
gopkg.in/yaml.v2 v2.4.0 // indirect
49+
)

0 commit comments

Comments
 (0)