Improve file provider behavior regarding dangling symlinks

This commit is contained in:
Yuxiao Zeng
2026-05-29 21:14:05 +09:00
committed by GitHub
parent 9c94e3b493
commit 8c05c1b1a7
2 changed files with 51 additions and 5 deletions
+19 -5
View File
@@ -71,9 +71,16 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
// ignore sub-dir // ignore sub-dir
continue continue
} }
if !isFileSupported(entry.Name()) {
// ignore unsupported file extension
continue
}
watchItems = append(watchItems, path.Join(p.Directory, entry.Name())) watchItems = append(watchItems, path.Join(p.Directory, entry.Name()))
} }
case len(p.Filename) > 0: case len(p.Filename) > 0:
if !isFileSupported(p.Filename) {
return fmt.Errorf("unsupported file extension for file %s", p.Filename)
}
watchItems = append(watchItems, filepath.Dir(p.Filename), p.Filename) watchItems = append(watchItems, filepath.Dir(p.Filename), p.Filename)
default: default:
return errors.New("error using file configuration provider, neither filename nor directory is defined") return errors.New("error using file configuration provider, neither filename nor directory is defined")
@@ -168,7 +175,7 @@ func (p *Provider) addWatcher(pool *safe.Pool, items []string, configurationChan
log.Debug().Msgf("add watcher on: %s", item) log.Debug().Msgf("add watcher on: %s", item)
err = watcher.Add(item) err = watcher.Add(item)
if err != nil { if err != nil {
return fmt.Errorf("error adding file watcher: %w", err) return fmt.Errorf("error adding file watcher for %s: %w", item, err)
} }
} }
@@ -420,10 +427,8 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
continue continue
} }
switch strings.ToLower(filepath.Ext(item.Name())) { if !isFileSupported(item.Name()) {
case ".toml", ".yaml", ".yml": logger.Debug().Msg("Skipping file, unsupported extension")
// noop
default:
continue continue
} }
@@ -627,3 +632,12 @@ func readFile(filename string) (string, error) {
} }
return "", fmt.Errorf("invalid filename: %s", filename) return "", fmt.Errorf("invalid filename: %s", filename)
} }
func isFileSupported(filename string) bool {
switch strings.ToLower(filepath.Ext(filename)) {
case ".toml", ".yaml", ".yml":
return true
default:
return false
}
}
+32
View File
@@ -197,6 +197,38 @@ func TestProvideWithWatch(t *testing.T) {
} }
} }
func TestProvideWatchWithNonConfigDanglingSymlink(t *testing.T) {
tempDir := t.TempDir()
err := copyFile("./fixtures/yaml/simple_file_01.yml", filepath.Join(tempDir, "simple_file_01.yml"))
require.NoError(t, err)
err = os.Symlink(filepath.Join(tempDir, "non_existent_file.txt"), filepath.Join(tempDir, "dangling_symlink.txt"))
require.NoError(t, err)
provider := &Provider{
Directory: tempDir,
Watch: true,
}
configChan := make(chan dynamic.Message)
go func() {
err := provider.Provide(configChan, safe.NewPool(t.Context()))
assert.NoError(t, err)
}()
timeout := time.After(time.Second)
select {
case conf := <-configChan:
require.NotNil(t, conf.Configuration.HTTP)
numServices := len(conf.Configuration.HTTP.Services) + len(conf.Configuration.TCP.Services) + len(conf.Configuration.UDP.Services)
numRouters := len(conf.Configuration.HTTP.Routers) + len(conf.Configuration.TCP.Routers) + len(conf.Configuration.UDP.Routers)
assert.Equal(t, 6, numServices)
assert.Equal(t, 3, numRouters)
case <-timeout:
t.Errorf("timeout while waiting for config")
}
}
func getTestCases() []ProvideTestCase { func getTestCases() []ProvideTestCase {
return []ProvideTestCase{ return []ProvideTestCase{
{ {