1
0
mirror of https://github.com/mxpv/podsync.git synced 2024-05-11 05:55:04 +00:00

Add CLI main and TOML configuration

This commit is contained in:
Maksym Pavlenko
2019-10-26 22:43:30 -07:00
parent 63d3edbe9a
commit a547647667
5 changed files with 198 additions and 3 deletions

76
cmd/podsync/main.go Normal file
View File

@ -0,0 +1,76 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/jessevdk/go-flags"
log "github.com/sirupsen/logrus"
"github.com/mxpv/podsync/pkg/config"
)
type Opts struct {
Config string `long:"config" short:"c" default:"config.toml"`
Debug bool `long:"debug" short:"d"`
}
func main() {
log.SetFormatter(&log.TextFormatter{})
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Parse args
opts := Opts{}
_, err := flags.Parse(&opts)
if err != nil {
log.WithError(err).Fatal("failed to parse command line arguments")
}
if opts.Debug {
log.SetLevel(log.DebugLevel)
}
// Load TOML file
log.Debugf("loading configuration %q", opts.Config)
cfg, err := config.LoadConfig(opts.Config)
if err != nil {
log.WithError(err).Fatal("failed to load configuration file")
}
// Create web server
if cfg.Port == 0 {
log.Debug("using default port 8080")
cfg.Port = 8080
}
srv := http.Server{
Addr: fmt.Sprintf(":%d", cfg.Port),
}
// Run listener
go func() {
log.Infof("running listener at %s", srv.Addr)
if err := srv.ListenAndServe(); err != nil {
log.WithError(err).Error("failed to listen")
}
}()
<-stop
log.Info("shutting down")
if err := srv.Shutdown(ctx); err != nil {
log.WithError(err).Error("server shutdown failed")
}
log.Info("gracefully stopped")
}

4
go.mod
View File

@ -3,6 +3,7 @@ module github.com/mxpv/podsync
require (
cloud.google.com/go v0.25.0 // indirect
github.com/BrianHicks/finch v0.0.0-20140409222414-419bd73c29ec
github.com/BurntSushi/toml v0.3.1
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20170929212804-61590edac4c7
github.com/aws/aws-sdk-go v1.15.81
github.com/boj/redistore v0.0.0-20160128113310-fc113767cd6b // indirect
@ -13,7 +14,6 @@ require (
github.com/gin-contrib/gzip v0.0.1
github.com/gin-contrib/sessions v0.0.0-20170731012558-a71ea9167c61
github.com/gin-gonic/gin v1.3.0
github.com/go-chi/chi v4.0.2+incompatible
github.com/go-pg/pg v6.14.2+incompatible
github.com/golang/mock v1.2.0
github.com/gorilla/sessions v1.1.1 // indirect
@ -38,3 +38,5 @@ require (
google.golang.org/appengine v1.1.0 // indirect
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
)
go 1.13

4
go.sum
View File

@ -2,6 +2,8 @@ cloud.google.com/go v0.25.0 h1:6vD6xZTc8Jo6To8gHxFDRVsMvWFDgY3rugNszcDalN8=
cloud.google.com/go v0.25.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BrianHicks/finch v0.0.0-20140409222414-419bd73c29ec h1:1VPruZMM1WQC7POhjxbZOWK564cuFz1hlpwYW6ocM4E=
github.com/BrianHicks/finch v0.0.0-20140409222414-419bd73c29ec/go.mod h1:+hWo/MWgY8VtjZvdrYM2nPRMaK40zX2iPsH/qD0+Xs0=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20170929212804-61590edac4c7 h1:Clo7QBZv+fHzjCgVp4ELlbIsY5rScCmj+4VCfoMfqtQ=
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20170929212804-61590edac4c7/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo=
github.com/aws/aws-sdk-go v1.15.81 h1:va7uoFaV9uKAtZ6BTmp1u7paoMsizYRRLvRuoC07nQ8=
@ -30,8 +32,6 @@ github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cou
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-pg/pg v6.14.2+incompatible h1:FrOgsHDUhC3V3wkBGAIN5LVj4nJczFPyy1YNFnetfIQ=
github.com/go-pg/pg v6.14.2+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=

64
pkg/config/config.go Normal file
View File

@ -0,0 +1,64 @@
package config
import (
"time"
"github.com/BurntSushi/toml"
"github.com/pkg/errors"
)
// Feed is a configuration for a feed
type Feed struct {
// URL is a full URL of the field
URL string `toml:"url"`
// PageSize is the number of pages to query from YouTube API.
// NOTE: larger page sizes/often requests might drain your API token.
PageSize int `toml:"page_size"`
// UpdatePeriod is how often to check for updates.
// Format is "300ms", "1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
// NOTE: too often update check might drain your API token.
UpdatePeriod Duration `toml:"update_period"`
}
type Tokens struct {
// YouTube API key.
// See https://developers.google.com/youtube/registering_an_application
YouTube string `toml:"youtube"`
// Vimeo developer key.
// See https://developer.vimeo.com/api/guides/start#generate-access-token
Vimeo string `toml:"vimeo"`
}
type Config struct {
// DataDir is a path to a directory to keep XML feeds and downloaded episodes
DataDir string `toml:"data_dir"`
// Port is a server port to listen to
Port int `toml:"port"`
// Feeds is a list of feeds to host by this app.
// ID will be used as feed ID in http://podsync.net/{FEED_ID}.xml
Feeds map[string]Feed
// Tokens is API keys to use to access YouTube/Vimeo APIs.
Tokens Tokens `toml:"tokens"`
}
// LoadConfig loads TOML configuration from a file path
func LoadConfig(path string) (*Config, error) {
config := Config{}
_, err := toml.DecodeFile(path, &config)
if err != nil {
return nil, errors.Wrapf(err, "failed to load config file %q", path)
}
return &config, nil
}
type Duration struct {
time.Duration
}
func (d *Duration) UnmarshalText(text []byte) error {
var err error
d.Duration, err = time.ParseDuration(string(text))
return err
}

53
pkg/config/config_test.go Normal file
View File

@ -0,0 +1,53 @@
package config
import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLoadConfig(t *testing.T) {
const file = `
data_dir = "test/data/"
port = 80
[tokens]
youtube = "123"
vimeo = "321"
[feeds]
[feeds.XYZ]
url = "https://youtube.com/watch?v=ygIUF678y40"
page_size = 50
update_period = "5h"
`
f, err := ioutil.TempFile("", "")
require.NoError(t, err)
defer os.Remove(f.Name())
_, err = f.WriteString(file)
require.NoError(t, err)
config, err := LoadConfig(f.Name())
assert.NoError(t, err)
assert.NotNil(t, config)
assert.Equal(t, "test/data/", config.DataDir)
assert.EqualValues(t, 80, config.Port)
assert.Equal(t, "123", config.Tokens.YouTube)
assert.Equal(t, "321", config.Tokens.Vimeo)
assert.Len(t, config.Feeds, 1)
feed, ok := config.Feeds["XYZ"]
assert.True(t, ok)
assert.Equal(t, "https://youtube.com/watch?v=ygIUF678y40", feed.URL)
assert.EqualValues(t, 50, feed.PageSize)
assert.EqualValues(t, Duration{5 * time.Hour}, feed.UpdatePeriod)
}