pomerium/internal/controlplane/filemgr/filemgr.go
Caleb Doxsey 10912add67
config: detect underlying file changes (#1775)
* wip

* cleanup

* add test

* use uuid for temp dir, derive root CA path from filemgr for tests

* fix comment

* fix double close

* use latest notify
2021-01-14 18:06:02 -07:00

91 lines
2.5 KiB
Go

package filemgr
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
"github.com/martinlindhe/base36"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/pkg/cryptutil"
)
// A Manager manages files for envoy.
type Manager struct {
cfg *config
}
// NewManager creates a new Manager.
func NewManager(options ...Option) *Manager {
cfg := newConfig(options...)
return &Manager{
cfg: cfg,
}
}
// BytesDataSource returns an envoy config data source based on bytes.
func (mgr *Manager) BytesDataSource(fileName string, data []byte) *envoy_config_core_v3.DataSource {
h := base36.EncodeBytes(cryptutil.Hash("filemgr", data))
ext := filepath.Ext(fileName)
fileName = fmt.Sprintf("%s-%x%s", fileName[:len(fileName)-len(ext)], h, ext)
if err := os.MkdirAll(mgr.cfg.cacheDir, 0o700); err != nil {
log.Error().Err(err).Msg("filemgr: error creating cache directory, falling back to inline bytes")
return inlineBytes(data)
}
filePath := filepath.Join(mgr.cfg.cacheDir, fileName)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
err = ioutil.WriteFile(filePath, data, 0o600)
if err != nil {
log.Error().Err(err).Msg("filemgr: error writing cache file, falling back to inline bytes")
return inlineBytes(data)
}
} else if err != nil {
log.Error().Err(err).Msg("filemgr: error reading cache file, falling back to inline bytes")
return inlineBytes(data)
}
return inlineFilename(filePath)
}
// ClearCache clears the file cache.
func (mgr *Manager) ClearCache() {
err := filepath.Walk(mgr.cfg.cacheDir, func(p string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
return os.Remove(p)
})
if err != nil {
log.Error().Err(err).Msg("failed to clear envoy file cache")
}
}
// FileDataSource returns an envoy config data source based on a file.
func (mgr *Manager) FileDataSource(filePath string) *envoy_config_core_v3.DataSource {
data, err := ioutil.ReadFile(filePath)
if err != nil {
return inlineFilename(filePath)
}
return mgr.BytesDataSource(filepath.Base(filePath), data)
}
func inlineBytes(data []byte) *envoy_config_core_v3.DataSource {
return &envoy_config_core_v3.DataSource{
Specifier: &envoy_config_core_v3.DataSource_InlineBytes{
InlineBytes: data,
},
}
}
func inlineFilename(name string) *envoy_config_core_v3.DataSource {
return &envoy_config_core_v3.DataSource{
Specifier: &envoy_config_core_v3.DataSource_Filename{
Filename: name,
},
}
}