feat: Support watch for file.
This commit is contained in:
parent
c05755948d
commit
5b6b959074
@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
@ -24,7 +23,6 @@ func init() {
|
||||
kvCmd.PersistentFlags().String("prefix", "traefik", "Prefix used for KV store.")
|
||||
kvCmd.PersistentFlags().String("password", "", "Password for connection.")
|
||||
kvCmd.PersistentFlags().String("username", "", "Username for connection.")
|
||||
kvCmd.PersistentFlags().Bool("watch", false, "Enable watching changes.")
|
||||
|
||||
// FIXME review TLS parts
|
||||
// kvCmd.PersistentFlags().Bool("tls.enable", false, "Enable TLS encryption.")
|
||||
@ -43,8 +41,6 @@ func getKvConfig(cmd *cobra.Command) (*kv.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
watch, _ := strconv.ParseBool(cmd.Flag("watch").Value.String())
|
||||
|
||||
return &kv.Config{
|
||||
Endpoints: endpoints,
|
||||
Prefix: cmd.Flag("prefix").Value.String(),
|
||||
@ -53,6 +49,5 @@ func getKvConfig(cmd *cobra.Command) (*kv.Config, error) {
|
||||
Username: cmd.Flag("password").Value.String(),
|
||||
Password: cmd.Flag("username").Value.String(),
|
||||
},
|
||||
Watch: watch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
59
cmd/root.go
59
cmd/root.go
@ -60,6 +60,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().String("key-name", "privatekey", "The file name (without extension) of the generated private keys.")
|
||||
rootCmd.PersistentFlags().Bool("domain-subdir", false, "Use domain as sub-directory.")
|
||||
rootCmd.PersistentFlags().Bool("clean", true, "Clean destination folder before dumping content.")
|
||||
rootCmd.PersistentFlags().Bool("watch", false, "Enable watching changes.")
|
||||
}
|
||||
|
||||
// initConfig reads in config file and ENV variables if set.
|
||||
@ -88,32 +89,6 @@ func initConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
func getBaseConfig(cmd *cobra.Command) (*dumper.BaseConfig, error) {
|
||||
subDir, err := strconv.ParseBool(cmd.Flag("domain-subdir").Value.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clean, err := strconv.ParseBool(cmd.Flag("clean").Value.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dumper.BaseConfig{
|
||||
DumpPath: cmd.Flag("dest").Value.String(),
|
||||
CrtInfo: dumper.FileInfo{
|
||||
Name: cmd.Flag("crt-name").Value.String(),
|
||||
Ext: cmd.Flag("crt-ext").Value.String(),
|
||||
},
|
||||
KeyInfo: dumper.FileInfo{
|
||||
Name: cmd.Flag("key-name").Value.String(),
|
||||
Ext: cmd.Flag("key-ext").Value.String(),
|
||||
},
|
||||
DomainSubDir: subDir,
|
||||
Clean: clean,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func runE(apply func(*dumper.BaseConfig, *cobra.Command) error) func(*cobra.Command, []string) error {
|
||||
return func(cmd *cobra.Command, _ []string) error {
|
||||
baseConfig, err := getBaseConfig(cmd)
|
||||
@ -169,3 +144,35 @@ func tree(root, indent string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBaseConfig(cmd *cobra.Command) (*dumper.BaseConfig, error) {
|
||||
subDir, err := strconv.ParseBool(cmd.Flag("domain-subdir").Value.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clean, err := strconv.ParseBool(cmd.Flag("clean").Value.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
watch, err := strconv.ParseBool(cmd.Flag("watch").Value.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dumper.BaseConfig{
|
||||
DumpPath: cmd.Flag("dest").Value.String(),
|
||||
CrtInfo: dumper.FileInfo{
|
||||
Name: cmd.Flag("crt-name").Value.String(),
|
||||
Ext: cmd.Flag("crt-ext").Value.String(),
|
||||
},
|
||||
KeyInfo: dumper.FileInfo{
|
||||
Name: cmd.Flag("key-name").Value.String(),
|
||||
Ext: cmd.Flag("key-ext").Value.String(),
|
||||
},
|
||||
DomainSubDir: subDir,
|
||||
Clean: clean,
|
||||
Watch: watch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ Dump Let's Encrypt certificates from Traefik.
|
||||
### Options
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
@ -17,6 +18,7 @@ Dump Let's Encrypt certificates from Traefik.
|
||||
-h, --help help for traefik-certs-dumper
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
@ -25,4 +27,4 @@ Dump Let's Encrypt certificates from Traefik.
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
* [traefik-certs-dumper version](traefik-certs-dumper_version.md) - Display version
|
||||
|
||||
###### Auto generated by spf13/cobra on 20-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 22-Apr-2019
|
||||
|
||||
@ -20,6 +20,7 @@ traefik-certs-dumper file [flags]
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
@ -27,10 +28,11 @@ traefik-certs-dumper file [flags]
|
||||
--domain-subdir Use domain as sub-directory.
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [traefik-certs-dumper](traefik-certs-dumper.md) - Dump Let's Encrypt certificates from Traefik.
|
||||
|
||||
###### Auto generated by spf13/cobra on 20-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 22-Apr-2019
|
||||
|
||||
@ -15,12 +15,12 @@ Dump the content of a KV store.
|
||||
--password string Password for connection.
|
||||
--prefix string Prefix used for KV store. (default "traefik")
|
||||
--username string Username for connection.
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
@ -28,6 +28,7 @@ Dump the content of a KV store.
|
||||
--domain-subdir Use domain as sub-directory.
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
@ -38,4 +39,4 @@ Dump the content of a KV store.
|
||||
* [traefik-certs-dumper kv etcd](traefik-certs-dumper_kv_etcd.md) - Dump the content of etcd.
|
||||
* [traefik-certs-dumper kv zookeeper](traefik-certs-dumper_kv_zookeeper.md) - Dump the content of zookeeper.
|
||||
|
||||
###### Auto generated by spf13/cobra on 20-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 22-Apr-2019
|
||||
|
||||
@ -21,6 +21,7 @@ traefik-certs-dumper kv boltdb [flags]
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--connection-timeout int Connection timeout in seconds.
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
@ -40,4 +41,4 @@ traefik-certs-dumper kv boltdb [flags]
|
||||
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
|
||||
###### Auto generated by spf13/cobra on 20-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 22-Apr-2019
|
||||
|
||||
@ -20,6 +20,7 @@ traefik-certs-dumper kv consul [flags]
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--connection-timeout int Connection timeout in seconds.
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
@ -39,4 +40,4 @@ traefik-certs-dumper kv consul [flags]
|
||||
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
|
||||
###### Auto generated by spf13/cobra on 20-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 22-Apr-2019
|
||||
|
||||
@ -20,6 +20,7 @@ traefik-certs-dumper kv etcd [flags]
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--connection-timeout int Connection timeout in seconds.
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
@ -39,4 +40,4 @@ traefik-certs-dumper kv etcd [flags]
|
||||
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
|
||||
###### Auto generated by spf13/cobra on 20-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 22-Apr-2019
|
||||
|
||||
@ -19,6 +19,7 @@ traefik-certs-dumper kv zookeeper [flags]
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--connection-timeout int Connection timeout in seconds.
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
@ -38,4 +39,4 @@ traefik-certs-dumper kv zookeeper [flags]
|
||||
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
|
||||
###### Auto generated by spf13/cobra on 20-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 22-Apr-2019
|
||||
|
||||
@ -19,6 +19,7 @@ traefik-certs-dumper version [flags]
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
@ -26,10 +27,11 @@ traefik-certs-dumper version [flags]
|
||||
--domain-subdir Use domain as sub-directory.
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [traefik-certs-dumper](traefik-certs-dumper.md) - Dump Let's Encrypt certificates from Traefik.
|
||||
|
||||
###### Auto generated by spf13/cobra on 20-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 22-Apr-2019
|
||||
|
||||
@ -7,4 +7,5 @@ type BaseConfig struct {
|
||||
KeyInfo FileInfo
|
||||
DomainSubDir bool
|
||||
Clean bool
|
||||
Watch bool
|
||||
}
|
||||
|
||||
@ -1,14 +1,32 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
)
|
||||
|
||||
// Dump Dumps "acme.json" file to certificates.
|
||||
func Dump(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
err := dump(acmeFile, baseConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if baseConfig.Watch {
|
||||
return watch(acmeFile, baseConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dump(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
data, err := readFile(acmeFile)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -30,3 +48,104 @@ func readFile(acmeFile string) (*dumper.StoredData, error) {
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = watcher.Close() }()
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
var previousHash []byte
|
||||
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.EqualFold(os.Getenv("TCD_DEBUG"), "true") {
|
||||
log.Println("event:", event)
|
||||
}
|
||||
|
||||
switch {
|
||||
case event.Op&fsnotify.Rename == fsnotify.Rename:
|
||||
errW := watcher.Remove(acmeFile)
|
||||
if errW != nil {
|
||||
log.Println("error:", errW)
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
|
||||
errW = watcher.Add(acmeFile)
|
||||
if errW != nil {
|
||||
log.Println("error:", errW)
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case event.Op&fsnotify.Write == fsnotify.Write:
|
||||
hash, errH := calculateHash(acmeFile)
|
||||
if err != nil {
|
||||
log.Println("error:", errH)
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(previousHash, hash) {
|
||||
previousHash = hash
|
||||
|
||||
if strings.EqualFold(os.Getenv("TCD_DEBUG"), "true") {
|
||||
log.Println("detected changes on file:", event.Name)
|
||||
}
|
||||
|
||||
if errD := dump(acmeFile, baseConfig); errD != nil {
|
||||
log.Println("error:", errD)
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Dumped new certificate data.")
|
||||
}
|
||||
|
||||
}
|
||||
case errW, ok := <-watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
log.Println("error:", errW)
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err = watcher.Add(acmeFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
<-done
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculateHash(acmeFile string) ([]byte, error) {
|
||||
file, err := os.Open(acmeFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
h := md5.New()
|
||||
_, err = io.Copy(h, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
@ -7,6 +7,5 @@ type Config struct {
|
||||
Backend store.Backend
|
||||
Prefix string
|
||||
Endpoints []string
|
||||
Watch bool
|
||||
Options *store.Config
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ func Dump(config *Config, baseConfig *dumper.BaseConfig) error {
|
||||
|
||||
storeKey := config.Prefix + storeKeySuffix
|
||||
|
||||
if config.Watch {
|
||||
if baseConfig.Watch {
|
||||
return watch(kvStore, storeKey, baseConfig)
|
||||
}
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@ -10,6 +10,7 @@ require (
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/cpuguy83/go-md2man v1.0.10 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/go-acme/lego v2.5.0+incompatible
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
|
||||
Loading…
Reference in New Issue
Block a user