Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate config.yaml as valid config source; Add unit regression for correct config paths #1640

Merged
merged 9 commits into from
Mar 20, 2023
37 changes: 27 additions & 10 deletions internal/config/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"errors"
"fmt"
"os"
"path"
"reflect"
"sort"
Expand Down Expand Up @@ -208,6 +209,7 @@ func (cfg Application) String() string {
return string(appaStr)
}

// nolint:funlen
func loadConfig(v *viper.Viper, configPath string) error {
var err error
// use explicitly the given user config
Expand All @@ -223,13 +225,26 @@ func loadConfig(v *viper.Viper, configPath string) error {

// start searching for valid configs in order...
// 1. look for .<appname>.yaml (in the current directory)
confFilePath := "." + internal.ApplicationName

// TODO: Remove this before v1.0.0
// See syft #1634
v.AddConfigPath(".")
v.SetConfigName("." + internal.ApplicationName)
if err = v.ReadInConfig(); err == nil {
v.Set("config", v.ConfigFileUsed())
return nil
} else if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err)
v.SetConfigName(confFilePath)

// check if config.yaml exists in the current directory
// DEPRECATED: this will be removed in v1.0.0
if _, err := os.Stat("config.yaml"); err == nil {
log.Warn("DEPRECATED: ./config.yaml as a configuration file is deprecated and will be removed as an option in v1.0.0, please rename to .syft.yaml")
}

if _, err := os.Stat(confFilePath + ".yaml"); err == nil {
if err = v.ReadInConfig(); err == nil {
v.Set("config", v.ConfigFileUsed())
return nil
} else if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err)
}
}

// 2. look for .<appname>/config.yaml (in the current directory)
Expand All @@ -255,12 +270,14 @@ func loadConfig(v *viper.Viper, configPath string) error {
}
}

// 4. look for <appname>/config.yaml in xdg locations (starting with xdg home config dir, then moving upwards)
v.AddConfigPath(path.Join(xdg.ConfigHome, internal.ApplicationName))
// 4. look for .<appname>/config.yaml in xdg locations (starting with xdg home config dir, then moving upwards)

v.SetConfigName("config")
configPath = path.Join(xdg.ConfigHome, "."+internal.ApplicationName)
v.AddConfigPath(configPath)
for _, dir := range xdg.ConfigDirs {
v.AddConfigPath(path.Join(dir, internal.ApplicationName))
v.AddConfigPath(path.Join(dir, "."+internal.ApplicationName))
}
v.SetConfigName("config")
if err = v.ReadInConfig(); err == nil {
v.Set("config", v.ConfigFileUsed())
return nil
Expand Down
114 changes: 114 additions & 0 deletions internal/config/application_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package config

import (
"github.com/adrg/xdg"
spiffcs marked this conversation as resolved.
Show resolved Hide resolved
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"os"
"path"
"testing"
)

// TODO: set negative case when config.yaml is no longer a valid option
func TestApplicationConfig(t *testing.T) {
// config is picked up at desired configuration paths
// VALID: .syft.yaml, .syft/config.yaml, ~/.syft.yaml, <XDG_CONFIG_HOME>/syft/config.yaml
// DEPRECATED: .config.yaml is currently supported by
spiffcs marked this conversation as resolved.
Show resolved Hide resolved
tests := []struct {
name string
setup func(t *testing.T) string
assertions func(t *testing.T, app *Application)
}{
{
name: "explicit config",
setup: func(t *testing.T) string {
return "./test-fixtures/.syft.yaml"
}, // no-op for explicit config
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-explicit-config", app.File)
},
},
{
name: "current working directory named config",
setup: func(t *testing.T) string {
err := os.Chdir("./test-fixtures/config-wd-file") // change application cwd to test-fixtures
if err != nil {
t.Fatalf("%s failed to change cwd: %+v", t.Name(), err)
}
return ""
},
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-wd-named-config", app.File)
},
},
{
name: "current working directory syft dir config",
setup: func(t *testing.T) string {
err := os.Chdir("./test-fixtures/config-dir-test") // change application cwd to test-fixtures
if err != nil {
t.Fatalf("%s failed to change cwd: %+v", t.Name(), err)
}
return ""
},
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-dir-config", app.File)
},
},
{
name: "home directory file config",
setup: func(t *testing.T) string {
// Because Setenv affects the whole process, it cannot be used in parallel tests or
// tests with parallel ancestors: see separate XDG test for consequence of this
t.Setenv("HOME", "./test-fixtures/config-home-test") // set HOME to testdata
return ""
},
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-home-config", app.File)
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("failed to get working directory: %+v", err)
}
defer os.Chdir(wd) // reset working directory after test
application := &Application{}
viperInstance := viper.New()

configPath := test.setup(t)
err = application.LoadAllValues(viperInstance, configPath)
if err != nil {
t.Fatalf("failed to load application config: %+v", err)
}
test.assertions(t, application)
})
}
}

// NOTE: this has to be separate for now because of t.Setenv behavior
// if this was included in the above table test then HOMEDIR would always
// be set; we would never fall through to the XDG case
func TestApplication_LoadAllValues_XDG(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("failed to get working directory: %+v", err)
}
defer os.Chdir(wd) // reset working directory after test
application := &Application{}
viperInstance := viper.New()

// NOTE: we need to temporarily unset HOME or we never reach the XDG_CONFIG_HOME check
t.Setenv("HOME", "/foo/bar")
configDir := path.Join(wd, "./test-fixtures/config-xdg-dir-test") // set HOME to testdata
t.Setenv("XDG_CONFIG_DIRS", configDir)
xdg.Reload()

err = application.LoadAllValues(viperInstance, "")
if err != nil {
t.Fatalf("failed to load application config: %+v", err)
}

assert.Equal(t, "test-home-XDG-config", application.File)
}
1 change: 1 addition & 0 deletions internal/config/cataloger_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type catalogerOptions struct {

func (cfg catalogerOptions) loadDefaultValues(v *viper.Viper) {
v.SetDefault("package.cataloger.enabled", true)
v.SetDefault("package.cataloger.scope", "squashed")
spiffcs marked this conversation as resolved.
Show resolved Hide resolved
}

func (cfg *catalogerOptions) parseConfigValues() error {
Expand Down
25 changes: 25 additions & 0 deletions internal/config/test-fixtures/.syft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
output: "table"

# suppress all output (except for the SBOM report)
# same as -q ; SYFT_QUIET env var
quiet: false

# same as --file; write output report to a file (default is to write to stdout)
file: "test-explicit-config"

# enable/disable checking for application updates on startup
# same as SYFT_CHECK_FOR_APP_UPDATE env var
check-for-app-update: true

log:
# use structured logging
# same as SYFT_LOG_STRUCTURED env var
structured: false

# the log level; note: detailed logging suppress the ETUI
# same as SYFT_LOG_LEVEL env var
level: "error"

# location to write the log file (default is not to have a log file)
# same as SYFT_LOG_FILE env var
file: ""
25 changes: 25 additions & 0 deletions internal/config/test-fixtures/config-dir-test/.syft/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
output: "table"

# suppress all output (except for the SBOM report)
# same as -q ; SYFT_QUIET env var
quiet: false

# same as --file; write output report to a file (default is to write to stdout)
file: "test-dir-config"

# enable/disable checking for application updates on startup
# same as SYFT_CHECK_FOR_APP_UPDATE env var
check-for-app-update: true

log:
# use structured logging
# same as SYFT_LOG_STRUCTURED env var
structured: false

# the log level; note: detailed logging suppress the ETUI
# same as SYFT_LOG_LEVEL env var
level: "error"

# location to write the log file (default is not to have a log file)
# same as SYFT_LOG_FILE env var
file: ""
25 changes: 25 additions & 0 deletions internal/config/test-fixtures/config-home-test/.syft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
output: "table"

# suppress all output (except for the SBOM report)
# same as -q ; SYFT_QUIET env var
quiet: false

# same as --file; write output report to a file (default is to write to stdout)
file: "test-home-config"

# enable/disable checking for application updates on startup
# same as SYFT_CHECK_FOR_APP_UPDATE env var
check-for-app-update: true

log:
# use structured logging
# same as SYFT_LOG_STRUCTURED env var
structured: false

# the log level; note: detailed logging suppress the ETUI
# same as SYFT_LOG_LEVEL env var
level: "error"

# location to write the log file (default is not to have a log file)
# same as SYFT_LOG_FILE env var
file: ""
25 changes: 25 additions & 0 deletions internal/config/test-fixtures/config-wd-file/.syft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
output: "table"

# suppress all output (except for the SBOM report)
# same as -q ; SYFT_QUIET env var
quiet: false

# same as --file; write output report to a file (default is to write to stdout)
file: "test-wd-named-config"

# enable/disable checking for application updates on startup
# same as SYFT_CHECK_FOR_APP_UPDATE env var
check-for-app-update: true

log:
# use structured logging
# same as SYFT_LOG_STRUCTURED env var
structured: false

# the log level; note: detailed logging suppress the ETUI
# same as SYFT_LOG_LEVEL env var
level: "error"

# location to write the log file (default is not to have a log file)
# same as SYFT_LOG_FILE env var
file: ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
output: "table"

# suppress all output (except for the SBOM report)
# same as -q ; SYFT_QUIET env var
quiet: false

# same as --file; write output report to a file (default is to write to stdout)
file: "test-home-XDG-config"

# enable/disable checking for application updates on startup
# same as SYFT_CHECK_FOR_APP_UPDATE env var
check-for-app-update: true

log:
# use structured logging
# same as SYFT_LOG_STRUCTURED env var
structured: false

# the log level; note: detailed logging suppress the ETUI
# same as SYFT_LOG_LEVEL env var
level: "error"

# location to write the log file (default is not to have a log file)
# same as SYFT_LOG_FILE env var
file: ""